aopt_help/
lib.rs

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>>>, // store command sections
45
46    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    /// If the app has command except global one.
145    pub fn has_cmd(&self) -> bool {
146        self.cmds.len() >= 2
147    }
148
149    /// If the app has position args in any command.
150    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}