1pub mod block;
2pub mod cmd;
3pub mod error;
4pub mod format;
5pub mod store;
6pub mod style;
7pub mod wrapper;
8
9pub use error::Error;
10pub use error::Result;
11
12pub mod prelude {
13 pub use crate::block::Block;
14 pub use crate::cmd::AddStore2Block;
15 pub use crate::cmd::BlockMut;
16 pub use crate::cmd::Command;
17 pub use crate::format::DefaultAppPolicy;
18 pub use crate::format::DefaultPolicy;
19 pub use crate::format::HelpDisplay;
20 pub use crate::format::HelpPolicy;
21 pub use crate::store::Store;
22 pub use crate::style::Align;
23 pub use crate::style::Style;
24 pub use crate::AppHelp;
25}
26
27use crate::block::Block;
28use crate::cmd::Command;
29use crate::format::DefaultAppPolicy;
30use crate::format::DefaultPolicy;
31use crate::format::HelpPolicy;
32use crate::store::Store;
33use crate::style::Style;
34
35use std::io::Stdout;
36use std::{borrow::Cow, io::Write};
37
38#[derive(Debug, Clone)]
39pub struct AppHelp<'a, W> {
40 style: Style,
41
42 writer: W,
43
44 blocks: Vec<Block<'a, Cow<'a, str>>>, cmds: Vec<Command<'a>>,
47
48 global: usize,
49
50 wrap_max_width: usize,
51
52 usage_new_line: usize,
53}
54
55impl Default for AppHelp<'_, Stdout> {
56 fn default() -> Self {
57 Self {
58 style: Default::default(),
59 writer: std::io::stdout(),
60 blocks: Default::default(),
61 cmds: Default::default(),
62 global: 0,
63 wrap_max_width: 0,
64 usage_new_line: 0,
65 }
66 }
67}
68
69impl<'a, W: Write> AppHelp<'a, W> {
70 pub fn new<S: Into<Cow<'a, str>>>(
71 name: S,
72 head: S,
73 foot: S,
74 style: Style,
75 writer: W,
76 max_width: usize,
77 usage_new_line: usize,
78 ) -> Self {
79 Self {
80 writer,
81 style,
82 blocks: vec![],
83 cmds: vec![],
84 global: 0,
85 wrap_max_width: max_width,
86 usage_new_line,
87 }
88 .with_global(name, head, foot)
89 }
90
91 pub fn with_global<S: Into<Cow<'a, str>>>(mut self, name: S, head: S, foot: S) -> Self {
92 let name = name.into();
93 let hint = Cow::from("");
94 let help = Cow::from("");
95 let head = head.into();
96 let foot = foot.into();
97 let global = Command::new(name, hint, help, head, foot);
98
99 self.cmds.push(global);
100 self.global = self.cmds.len() - 1;
101 self
102 }
103
104 pub fn foot(&self) -> Cow<'a, str> {
105 self.global().foot()
106 }
107
108 pub fn head(&self) -> Cow<'a, str> {
109 self.global().head()
110 }
111
112 pub fn name(&self) -> Cow<'a, str> {
113 self.global().name()
114 }
115
116 pub fn style(&self) -> &Style {
117 &self.style
118 }
119
120 pub fn wrap_max_width(&self) -> usize {
121 self.wrap_max_width
122 }
123
124 pub fn usage_new_line(&self) -> usize {
125 self.usage_new_line
126 }
127
128 pub fn global(&self) -> &Command<'a> {
129 &self.cmds[self.global]
130 }
131
132 pub fn global_mut(&mut self) -> &mut Command<'a> {
133 &mut self.cmds[self.global]
134 }
135
136 pub fn block(&self) -> &[Block<'a, Cow<'a, str>>] {
137 &self.blocks
138 }
139
140 pub fn block_mut(&mut self) -> &mut [Block<'a, Cow<'a, str>>] {
141 &mut self.blocks
142 }
143
144 pub fn has_cmd(&self) -> bool {
146 self.cmds.len() >= 2
147 }
148
149 pub fn has_pos(&self) -> bool {
151 self.cmds.iter().any(|cmd| cmd.has_position())
152 }
153
154 pub fn find_cmd<S: Into<Cow<'a, str>>>(&self, cmd: S) -> Option<&Command<'a>> {
155 let name = cmd.into();
156
157 self.cmds.iter().find(|v| v.name() == name)
158 }
159
160 pub fn find_cmd_mut<S: Into<Cow<'a, str>>>(&mut self, cmd: S) -> Option<&mut Command<'a>> {
161 let name = cmd.into();
162
163 self.cmds.iter_mut().find(|v| v.name() == name)
164 }
165
166 pub fn find_block<S: Into<Cow<'a, str>>>(&self, block: S) -> Option<&Block<'a, Cow<'a, str>>> {
167 let name = block.into();
168
169 self.blocks.iter().find(|v| v.name() == name)
170 }
171
172 pub fn find_block_mut<S: Into<Cow<'a, str>>>(
173 &mut self,
174 block: S,
175 ) -> Option<&mut Block<'a, Cow<'a, str>>> {
176 let name = block.into();
177
178 self.blocks.iter_mut().find(|v| v.name() == name)
179 }
180
181 pub fn with_name<S: Into<Cow<'a, str>>>(mut self, name: S) -> Self {
182 self.global_mut().set_name(name);
183 self
184 }
185
186 pub fn with_head<S: Into<Cow<'a, str>>>(mut self, head: S) -> Self {
187 self.global_mut().set_head(head);
188 self
189 }
190
191 pub fn with_foot<S: Into<Cow<'a, str>>>(mut self, foot: S) -> Self {
192 self.global_mut().set_foot(foot);
193 self
194 }
195
196 pub fn with_style(mut self, style: Style) -> Self {
197 self.style = style;
198 self
199 }
200
201 pub fn with_writer(mut self, writer: W) -> Self {
202 self.writer = writer;
203 self
204 }
205
206 pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
207 self.global_mut().set_name(name);
208 self
209 }
210
211 pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, head: S) -> &mut Self {
212 self.global_mut().set_head(head);
213 self
214 }
215
216 pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, foot: S) -> &mut Self {
217 self.global_mut().set_foot(foot);
218 self
219 }
220
221 pub fn set_style(&mut self, style: Style) -> &mut Self {
222 self.style = style;
223 self
224 }
225
226 pub fn set_write(&mut self, writer: W) -> &mut Self {
227 self.writer = writer;
228 self
229 }
230
231 pub fn add_block(&mut self, block: Block<'a, Cow<'a, str>>) -> Result<&mut Self> {
232 if self.find_block(block.name()).is_some() {
233 Err(Error::DuplicatedBlockName(block.name().to_string()))
234 } else {
235 self.blocks.push(block);
236 Ok(self)
237 }
238 }
239
240 pub fn add_cmd<S: Into<Cow<'a, str>>>(
241 &mut self,
242 block: S,
243 cmd: Command<'a>,
244 ) -> Result<&mut Self> {
245 let block = block.into();
246
247 self.find_block_mut(block.clone())
248 .ok_or_else(|| Error::InvalidBlockName(block.to_string()))?
249 .push(cmd.name());
250 if self.find_cmd(cmd.name()).is_some() {
251 Err(Error::DuplicatedCommandName(cmd.name().to_string()))
252 } else {
253 self.cmds.push(cmd);
254 Ok(self)
255 }
256 }
257
258 pub fn new_block<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Result<AddBlock2App<'a, '_>> {
259 let name = name.into();
260
261 if self.find_block(name.clone()).is_some() {
262 Err(Error::DuplicatedBlockName(name.to_string()))
263 } else {
264 Ok(AddBlock2App::new(&mut self.blocks, name))
265 }
266 }
267
268 pub fn new_cmd<S: Into<Cow<'a, str>>>(
269 &mut self,
270 block: S,
271 name: S,
272 ) -> Result<AddCmd2App<'a, '_, W>> {
273 let name = name.into();
274 let block = block.into();
275
276 self.find_block_mut(block.clone())
277 .ok_or_else(|| Error::InvalidBlockName(block.to_string()))?;
278 if self.find_cmd(name.clone()).is_some() {
279 Err(Error::DuplicatedCommandName(name.to_string()))
280 } else {
281 Ok(AddCmd2App::new(self, block, name))
282 }
283 }
284
285 pub fn display(&mut self, show_global: bool) -> Result<()> {
286 let policy = DefaultAppPolicy::new(
287 vec![],
288 self.wrap_max_width,
289 show_global,
290 self.usage_new_line,
291 );
292 let help = policy.format(self).ok_or_else(|| {
293 Error::raise("Can not format app help with DefaultAppPolicy".to_string())
294 })?;
295
296 writeln!(&mut self.writer, "{}", help)
297 .map_err(|e| Error::raise(format!("Can not write to handler: {:?}", e)))
298 }
299
300 pub fn display_with<P>(&mut self, policy: P) -> Result<()>
301 where
302 P: HelpPolicy<'a, Self>,
303 {
304 let help = policy
305 .format(self)
306 .ok_or_else(|| Error::raise("Can not format app help with given policy".to_string()))?;
307
308 writeln!(&mut self.writer, "{}", help)
309 .map_err(|e| Error::raise(format!("Can not write to handler: {:?}", e)))
310 }
311
312 pub fn display_cmd<S>(&mut self, cmd: S) -> Result<()>
313 where
314 S: Into<Cow<'a, str>>,
315 {
316 let name = cmd.into();
317 let cmd = self
318 .cmds
319 .iter()
320 .find(|v| v.name() == name)
321 .ok_or_else(|| Error::raise(format!("Can not find cmd {name}")))?;
322 let policy = DefaultPolicy::new(
323 self.name(),
324 self.style.clone(),
325 vec![],
326 self.wrap_max_width,
327 true,
328 self.usage_new_line,
329 );
330 let help = policy
331 .format(cmd)
332 .ok_or_else(|| Error::raise("Can not format cmd help with given policy".to_string()))?;
333
334 writeln!(&mut self.writer, "{}", help)
335 .map_err(|e| Error::raise(format!("Can not write to handler: {:?}", e)))
336 }
337
338 pub fn display_cmd_with<S, P>(&mut self, cmd: S, policy: P) -> Result<()>
339 where
340 P: HelpPolicy<'a, Command<'a>>,
341 S: Into<Cow<'a, str>>,
342 {
343 let name = cmd.into();
344 let cmd = self
345 .cmds
346 .iter()
347 .find(|v| v.name() == name)
348 .ok_or_else(|| Error::raise(format!("Can not find cmd {name}")))?;
349 let help = policy.format(cmd).ok_or_else(|| {
350 Error::raise(format!("Can not format help of {name} with given policy"))
351 })?;
352
353 writeln!(&mut self.writer, "{}", help)
354 .map_err(|e| Error::raise(format!("Can not write to handler: {:?}", e)))
355 }
356}
357
358pub struct AddStore2App<'a, 'b> {
359 store: Store<'a>,
360
361 block: &'b mut Block<'a, Store<'a>>,
362
363 added: bool,
364}
365
366impl<'a, 'b> AddStore2App<'a, 'b> {
367 pub fn new<S: Into<Cow<'a, str>>>(block: &'b mut Block<'a, Store<'a>>, name: S) -> Self {
368 let mut store = Store::default();
369
370 store.set_optional(true);
371 store.set_name(name);
372 Self {
373 block,
374 store,
375 added: false,
376 }
377 }
378
379 pub fn set_optional(mut self, optional: bool) -> Self {
380 self.store.set_optional(optional);
381 self
382 }
383
384 pub fn set_position(mut self, position: bool) -> Self {
385 self.store.set_position(position);
386 self
387 }
388
389 pub fn set_hint<S: Into<Cow<'a, str>>>(mut self, hint: S) -> Self {
390 self.store.set_hint(hint);
391 self
392 }
393
394 pub fn set_help<S: Into<Cow<'a, str>>>(mut self, help: S) -> Self {
395 self.store.set_help(help);
396 self
397 }
398
399 pub fn set_name<S: Into<Cow<'a, str>>>(mut self, name: S) -> Self {
400 self.store.set_name(name);
401 self
402 }
403
404 pub fn set_type<S: Into<Cow<'a, str>>>(mut self, type_name: S) -> Self {
405 self.store.set_type(type_name);
406 self
407 }
408
409 pub fn submit(mut self) {
410 if !self.added {
411 let store = std::mem::take(&mut self.store);
412
413 self.block.attach(store);
414 self.added = true;
415 }
416 }
417}
418
419impl Drop for AddStore2App<'_, '_> {
420 fn drop(&mut self) {
421 if !self.added {
422 let store = std::mem::take(&mut self.store);
423
424 self.block.attach(store);
425 self.added = true;
426 }
427 }
428}
429
430pub struct AddBlock2App<'a, 'b> {
431 block: Block<'a, Cow<'a, str>>,
432
433 blocks: &'b mut Vec<Block<'a, Cow<'a, str>>>,
434
435 added: bool,
436}
437
438impl<'a, 'b> AddBlock2App<'a, 'b> {
439 pub fn new<S: Into<Cow<'a, str>>>(
440 blocks: &'b mut Vec<Block<'a, Cow<'a, str>>>,
441 name: S,
442 ) -> Self {
443 let mut block = Block::default();
444
445 block.set_name(name);
446 Self {
447 blocks,
448 block,
449 added: false,
450 }
451 }
452
453 pub fn attach<S: Into<Cow<'a, str>>>(&mut self, store: S) -> &mut Self {
454 self.block.attach(store.into());
455 self
456 }
457
458 pub fn set_hint<S: Into<Cow<'a, str>>>(mut self, hint: S) -> Self {
459 self.block.set_hint(hint);
460 self
461 }
462
463 pub fn set_help<S: Into<Cow<'a, str>>>(mut self, help: S) -> Self {
464 self.block.set_help(help);
465 self
466 }
467
468 pub fn set_name<S: Into<Cow<'a, str>>>(mut self, name: S) -> Self {
469 self.block.set_name(name);
470 self
471 }
472
473 pub fn set_head<S: Into<Cow<'a, str>>>(mut self, head: S) -> Self {
474 self.block.set_head(head);
475 self
476 }
477
478 pub fn set_foot<S: Into<Cow<'a, str>>>(mut self, foot: S) -> Self {
479 self.block.set_foot(foot);
480 self
481 }
482
483 pub fn submit(mut self) {
484 if !self.added {
485 let block = std::mem::take(&mut self.block);
486
487 self.blocks.push(block);
488 self.added = true;
489 }
490 }
491}
492
493impl Drop for AddBlock2App<'_, '_> {
494 fn drop(&mut self) {
495 if !self.added {
496 let block = std::mem::take(&mut self.block);
497
498 self.blocks.push(block);
499 self.added = true;
500 }
501 }
502}
503
504pub struct AddCmd2App<'a, 'b, W: Write> {
505 cmd: Command<'a>,
506
507 app: &'b mut AppHelp<'a, W>,
508
509 block: Cow<'a, str>,
510
511 added: bool,
512}
513
514impl<'a, 'b, W: Write> AddCmd2App<'a, 'b, W> {
515 pub fn new<S: Into<Cow<'a, str>>>(app: &'b mut AppHelp<'a, W>, block: S, name: S) -> Self {
516 let mut cmd = Command::default();
517
518 cmd.set_name(name);
519 Self {
520 app,
521 cmd,
522 block: block.into(),
523 added: false,
524 }
525 }
526
527 pub fn inner(&mut self) -> &mut Command<'a> {
528 &mut self.cmd
529 }
530
531 pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
532 self.cmd.set_name(name);
533 self
534 }
535
536 pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
537 self.cmd.set_hint(hint);
538 self
539 }
540
541 pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
542 self.cmd.set_help(help);
543 self
544 }
545
546 pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, foot: S) -> &mut Self {
547 self.cmd.set_foot(foot);
548 self
549 }
550
551 pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, head: S) -> &mut Self {
552 self.cmd.set_head(head);
553 self
554 }
555
556 pub fn submit(mut self) {
557 if !self.added {
558 let store = std::mem::take(&mut self.cmd);
559
560 self.app.add_cmd(self.block.clone(), store).unwrap();
561 self.added = true;
562 }
563 }
564}
565
566impl<W: Write> Drop for AddCmd2App<'_, '_, W> {
567 fn drop(&mut self) {
568 if !self.added {
569 let store = std::mem::take(&mut self.cmd);
570
571 self.app.add_cmd(self.block.clone(), store).unwrap();
572 self.added = true;
573 }
574 }
575}