back to dev
[pharext/pharext] / src / pharext / Cli / Args / Help.php
1 <?php
2
3 namespace pharext\Cli\Args;
4
5 use function array_column;
6 use pharext\Cli\Args;
7
8 class Help
9 {
10 private $args;
11
12 function __construct($prog, Args $args) {
13 $this->prog = $prog;
14 $this->args = $args;
15 }
16
17 function __toString() {
18 $usage = "Usage:\n\n \$ ";
19 $usage .= $this->prog;
20
21 list($flags, $required, $optional, $positional) = $this->listSpec();
22 if ($flags) {
23 $usage .= $this->dumpFlags($flags);
24 }
25 if ($required) {
26 $usage .= $this->dumpRequired($required);
27 }
28 if ($optional) {
29 $usage .= $this->dumpOptional($optional);
30 }
31 if ($positional) {
32 $usage .= $this->dumpPositional($positional);
33 }
34
35 $help = $this->dumpHelp($positional);
36
37 return $usage . "\n\n" . $help . "\n";
38 }
39
40 function listSpec() {
41 $flags = [];
42 $required = [];
43 $optional = [];
44 $positional = [];
45 foreach ($this->args->getSpec() as $spec) {
46 if (is_numeric($spec[0])) {
47 $positional[] = $spec;
48 } elseif ($spec[3] & Args::REQUIRED) {
49 $required[] = $spec;
50 } elseif ($spec[3] & (Args::OPTARG|Args::REQARG)) {
51 $optional[] = $spec;
52 } else {
53 $flags[] = $spec;
54 }
55 }
56
57 return [$flags, $required, $optional, $positional]
58 + compact("flags", "required", "optional", "positional");
59 }
60
61 function dumpFlags(array $flags) {
62 return sprintf(" [-%s]", implode("", array_column($flags, 0)));
63 }
64
65 function dumpRequired(array $required) {
66 $dump = "";
67 foreach ($required as $req) {
68 $dump .= sprintf(" -%s <%s>", $req[0], $req[1]);
69 }
70 return $dump;
71 }
72
73 function dumpOptional(array $optional) {
74 $req = array_filter($optional, function($a) {
75 return $a[3] & Args::REQARG;
76 });
77 $opt = array_filter($optional, function($a) {
78 return $a[3] & Args::OPTARG;
79 });
80
81 $dump = "";
82 if ($req) {
83 $short = array_filter($req, function($a) {
84 return is_string($a[0]);
85 });
86 if ($short) {
87 $dump .= sprintf(" [-%s <arg>]", implode("|-", array_column($short, 0)));
88 }
89 $long = array_filter($req, function($a) {
90 return !is_string($a[0]);
91 });
92 if ($long) {
93 $dump .= sprintf(" [--%s <arg>]", implode("|--", array_column($long, 1)));
94 }
95 }
96 if ($opt) {
97 $short = array_filter($opt, function($a) {
98 return is_string($a[0]);
99 });
100 if ($short) {
101 $dump .= sprintf(" [-%s [<arg>]]", implode("|-", array_column($short, 0)));
102 }
103 $long = array_filter($opt, function($a) {
104 return !is_string($a[0]);
105 });
106 if ($long) {
107 $dump .= sprintf(" [--%s [<arg>]]", implode("|--", array_column($long, 1)));
108 }
109 }
110 return $dump;
111 }
112
113 function dumpPositional(array $positional) {
114 $dump = " [--]";
115 $conv = [];
116 foreach ($positional as $pos) {
117 $conv[$pos[0]][] = $pos;
118 }
119 $opts = [];
120 foreach ($conv as $positional) {
121 $args = implode("|", array_column($positional, 1));
122 if ($positional[0][3] & Args::REQUIRED) {
123 $dump .= sprintf(" <%s>", $args);
124 } else {
125 $dump .= sprintf(" [<%s>]", $args);
126 }
127 if ($positional[0][3] & Args::MULTI) {
128 $dump .= sprintf(" [<%s>]...", $args);
129 }
130 /*
131 foreach ($positional as $pos) {
132 if ($pos[3] & Args::REQUIRED) {
133 $dump .= sprintf(" <%s>", $pos[1]);
134 } else {
135 $opts[] = $pos;
136 //$dump .= sprintf(" [<%s>]", $pos[1]);
137 }
138 if ($pos[3] & Args::MULTI) {
139 $dump .= sprintf(" [<%s>]...", $pos[1]);
140 }
141 }
142 */
143 }
144 return $dump;
145 }
146
147 function calcMaxLen() {
148 $spc = $this->args->getSpec();
149 $max = max(array_map("strlen", array_column($spc, 1)));
150 $max += $max % 8 + 2;
151 return $max;
152 }
153
154 function dumpHelp() {
155 $max = $this->calcMaxLen();
156 $dump = "";
157 foreach ($this->args->getSpec() as $spec) {
158 $dump .= " ";
159 if (is_numeric($spec[0])) {
160 $dump .= sprintf(" <%s> ", $spec[1]);
161 } elseif (isset($spec[0])) {
162 $dump .= sprintf("-%s|", $spec[0]);
163 }
164 if (!is_numeric($spec[0])) {
165 $dump .= sprintf("--%s ", $spec[1]);
166 }
167 if ($spec[3] & Args::REQARG) {
168 $dump .= "<arg> ";
169 } elseif ($spec[3] & Args::OPTARG) {
170 $dump .= "[<arg>]";
171 } else {
172 $dump .= " ";
173 }
174
175 $space = str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0]));
176 $dump .= $space;
177 $dump .= str_replace("\n", "\n $space", $spec[2]);
178
179 if ($spec[3] & Args::REQUIRED) {
180 $dump .= " (REQUIRED)";
181 }
182 if ($spec[3] & Args::MULTI) {
183 $dump .= " (MULTIPLE)";
184 }
185 if (isset($spec[4])) {
186 $dump .= sprintf(" [%s]", $spec[4]);
187 }
188 $dump .= "\n";
189 }
190 return $dump;
191 }
192 }