@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class DivinerAtomizeWorkflow extends DivinerWorkflow {
4
5 protected function didConstruct() {
6 $this
7 ->setName('atomize')
8 ->setSynopsis(pht('Build atoms from source.'))
9 ->setArguments(
10 array(
11 array(
12 'name' => 'atomizer',
13 'param' => 'class',
14 'help' => pht('Specify a subclass of %s.', 'DivinerAtomizer'),
15 ),
16 array(
17 'name' => 'book',
18 'param' => 'path',
19 'help' => pht('Path to a Diviner book configuration.'),
20 ),
21 array(
22 'name' => 'files',
23 'wildcard' => true,
24 ),
25 array(
26 'name' => 'ugly',
27 'help' => pht('Produce ugly (but faster) output.'),
28 ),
29 ));
30 }
31
32 public function execute(PhutilArgumentParser $args) {
33 $this->readBookConfiguration($args->getArg('book'));
34
35 $console = PhutilConsole::getConsole();
36
37 $atomizer_class = $args->getArg('atomizer');
38 if (!$atomizer_class) {
39 throw new PhutilArgumentUsageException(
40 pht(
41 'Specify an atomizer class with %s.',
42 '--atomizer'));
43 }
44
45 $symbols = id(new PhutilSymbolLoader())
46 ->setName($atomizer_class)
47 ->setConcreteOnly(true)
48 ->setAncestorClass(DivinerAtomizer::class)
49 ->selectAndLoadSymbols();
50 if (!$symbols) {
51 throw new PhutilArgumentUsageException(
52 pht(
53 "Atomizer class '%s' must be a concrete subclass of %s.",
54 $atomizer_class,
55 'DivinerAtomizer'));
56 }
57
58 $atomizer = newv($atomizer_class, array());
59
60 $files = $args->getArg('files');
61 if (!$files) {
62 throw new Exception(pht('Specify one or more files to atomize.'));
63 }
64
65 $file_atomizer = new DivinerFileAtomizer();
66
67 foreach (array($atomizer, $file_atomizer) as $configure) {
68 $configure->setBook($this->getConfig('name'));
69 }
70
71 $group_rules = array();
72 foreach ($this->getConfig('groups', array()) as $group => $spec) {
73 $include = (array)idx($spec, 'include', array());
74 foreach ($include as $pattern) {
75 $group_rules[$pattern] = $group;
76 }
77 }
78
79 $all_atoms = array();
80 $context = array(
81 'group' => null,
82 );
83 foreach ($files as $file) {
84 $abs_path = Filesystem::resolvePath($file, $this->getConfig('root'));
85 $data = Filesystem::readFile($abs_path);
86
87 if (!$this->shouldAtomizeFile($file, $data)) {
88 $console->writeLog("%s\n", pht('Skipping %s...', $file));
89 continue;
90 } else {
91 $console->writeLog("%s\n", pht('Atomizing %s...', $file));
92 }
93
94 $context['group'] = null;
95 foreach ($group_rules as $rule => $group) {
96 if (preg_match($rule, $file)) {
97 $context['group'] = $group;
98 break;
99 }
100 }
101
102 $file_atoms = $file_atomizer->atomize($file, $data, $context);
103 $all_atoms[] = $file_atoms;
104
105 if (count($file_atoms) !== 1) {
106 throw new Exception(
107 pht('Expected exactly one atom from file atomizer.'));
108 }
109 $file_atom = head($file_atoms);
110
111 $atoms = $atomizer->atomize($file, $data, $context);
112
113 foreach ($atoms as $atom) {
114 if (!$atom->hasParent()) {
115 $file_atom->addChild($atom);
116 }
117 }
118
119 $all_atoms[] = $atoms;
120 }
121
122 $all_atoms = array_mergev($all_atoms);
123
124 $all_atoms = mpull($all_atoms, 'toDictionary');
125 $all_atoms = ipull($all_atoms, null, 'hash');
126
127 if ($args->getArg('ugly')) {
128 $json = json_encode($all_atoms);
129 } else {
130 $json = id(new PhutilJSON())->encodeFormatted($all_atoms);
131 }
132
133 $console->writeOut('%s', $json);
134 return 0;
135 }
136
137 private function shouldAtomizeFile($file_name, $file_data) {
138 return strpos($file_data, '@'.'undivinable') === false;
139 }
140
141}