cote/
lib.rs

1#![doc = include_str!("../README.md")]
2pub mod _reference;
3pub(crate) mod help;
4pub(crate) mod infer;
5pub(crate) mod meta;
6pub(crate) mod parser;
7pub(crate) mod rctx;
8pub(crate) mod value;
9
10pub mod valid;
11
12pub use aopt;
13use aopt::set::Set;
14pub use aopt::Error;
15pub use aopt_help;
16pub use cote_derive;
17
18pub type Result<T> = std::result::Result<T, Error>;
19
20pub mod prelude {
21    pub use aopt::opt::AnyOpt;
22    pub use aopt::opt::Cmd;
23    pub use aopt::opt::Main;
24    pub use aopt::opt::MutOpt;
25    pub use aopt::opt::Pos;
26    pub use aopt::parser::UserStyle;
27    pub use aopt::prelude::ctor_default_name;
28    pub use aopt::prelude::AOpt;
29    pub use aopt::prelude::ARef;
30    pub use aopt::prelude::Action;
31    pub use aopt::prelude::AppServices;
32    pub use aopt::prelude::AppStorage;
33    pub use aopt::prelude::Args;
34    pub use aopt::prelude::Commit;
35    pub use aopt::prelude::ConfigBuild;
36    pub use aopt::prelude::ConfigBuildInfer;
37    pub use aopt::prelude::ConfigBuildWith;
38    pub use aopt::prelude::ConfigBuilder;
39    pub use aopt::prelude::ConfigBuilderWith;
40    pub use aopt::prelude::ConfigValue;
41    pub use aopt::prelude::Ctor;
42    pub use aopt::prelude::Ctx;
43    pub use aopt::prelude::DefaultSetChecker;
44    pub use aopt::prelude::ErasedTy;
45    pub use aopt::prelude::ErasedValue;
46    pub use aopt::prelude::FilterMatcher;
47    pub use aopt::prelude::HandlerCollection;
48    pub use aopt::prelude::Index;
49    pub use aopt::prelude::Infer;
50    pub use aopt::prelude::Information;
51    pub use aopt::prelude::InitializeValue;
52    pub use aopt::prelude::Invoker;
53    pub use aopt::prelude::Opt;
54    pub use aopt::prelude::OptParser;
55    pub use aopt::prelude::OptValidator;
56    pub use aopt::prelude::OptValueExt;
57    pub use aopt::prelude::Policy;
58    pub use aopt::prelude::PolicyParser;
59    pub use aopt::prelude::PolicySettings;
60    pub use aopt::prelude::PrefixOptValidator;
61    pub use aopt::prelude::PrefixedValidator;
62    pub use aopt::prelude::RawValParser;
63    pub use aopt::prelude::Return;
64    pub use aopt::prelude::Set;
65    pub use aopt::prelude::SetCfg;
66    pub use aopt::prelude::SetChecker;
67    pub use aopt::prelude::SetExt;
68    pub use aopt::prelude::SetValueFindExt;
69    pub use aopt::prelude::Store;
70    pub use aopt::prelude::Style;
71    pub use aopt::prelude::ValInitializer;
72    pub use aopt::prelude::ValStorer;
73    pub use aopt::prelude::ValValidator;
74    pub use aopt::prelude::VecStore;
75    pub use aopt::raise_error;
76    pub use aopt::raise_failure;
77    pub use aopt::value::raw2str;
78    pub use aopt::value::Placeholder;
79    pub use aopt::value::Stop;
80    pub use aopt::GetoptRes;
81    pub use aopt::Uid;
82    pub use cote_derive::Cote;
83    pub use cote_derive::CoteOpt;
84    pub use cote_derive::CoteVal;
85
86    pub use crate::help::display_set_help;
87    pub use crate::help::HelpContext;
88    pub use crate::help::HelpDisplay;
89    pub use crate::help::DEFAULT_OPTION_WIDTH;
90    pub use crate::help::DEFAULT_USAGE_WIDTH;
91    pub use crate::infer::InferOverride;
92    pub use crate::meta::OptionMeta;
93    pub use crate::parser::Parser;
94    pub use crate::rctx::Failure;
95    pub use crate::rctx::Frame;
96    pub use crate::rctx::HideValue;
97    pub use crate::rctx::RunningCtx;
98    pub use crate::valid;
99    pub use crate::value::fetch_uid_impl;
100    pub use crate::value::fetch_vec_uid_impl;
101    pub use crate::value::Fetch;
102    pub use crate::CoteRes;
103    pub use crate::ExtractFromSetDerive;
104    pub use crate::IntoParserDerive;
105    pub use crate::NullPolicy;
106    pub use crate::Status;
107    pub use aopt::prelude::ASet as CoteSet;
108
109    pub type FwdPolicy<'inv, S> =
110        aopt::prelude::FwdPolicy<Parser<'inv, S>, DefaultSetChecker<Parser<'inv, S>>>;
111
112    pub type DelayPolicy<'inv, S> =
113        aopt::prelude::DelayPolicy<Parser<'inv, S>, DefaultSetChecker<Parser<'inv, S>>>;
114
115    pub type SeqPolicy<'inv, S> =
116        aopt::prelude::SeqPolicy<Parser<'inv, S>, DefaultSetChecker<Parser<'inv, S>>>;
117}
118
119use std::marker::PhantomData;
120
121use aopt::args::Args;
122use aopt::ctx::Invoker;
123use aopt::parser::Policy;
124use aopt::parser::PolicySettings;
125use aopt::parser::Return;
126use aopt::parser::UserStyle;
127use aopt::prelude::ConfigValue;
128use aopt::prelude::OptParser;
129use aopt::prelude::OptStyleManager;
130use aopt::prelude::OptValidator;
131use aopt::prelude::SetCfg;
132use aopt::prelude::SetValueFindExt;
133
134use crate::prelude::Parser;
135
136pub trait IntoParserDerive<'inv, S>
137where
138    SetCfg<S>: ConfigValue + Default,
139    S: Set + OptParser + OptValidator + Default,
140{
141    fn into_parser() -> Result<Parser<'inv, S>> {
142        let mut parser = Parser::default();
143        Self::update(&mut parser)?;
144        Ok(parser)
145    }
146    fn update(parser: &mut Parser<'inv, S>) -> Result<()>;
147}
148
149pub trait ExtractFromSetDerive<'set, S: SetValueFindExt>
150where
151    SetCfg<S>: ConfigValue + Default,
152{
153    fn try_extract(set: &'set mut S) -> Result<Self>
154    where
155        Self: Sized;
156}
157
158#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
159pub struct CoteRes<P, Policy>
160where
161    Policy: crate::prelude::Policy,
162{
163    pub policy: P,
164
165    pub ret: Policy::Ret,
166
167    pub parser: Policy::Set,
168}
169
170pub trait Status {
171    fn status(&self) -> bool;
172}
173
174impl Status for Return {
175    fn status(&self) -> bool {
176        Return::status(self)
177    }
178}
179
180#[derive(Debug, Clone)]
181pub struct NullPolicy<'inv, S> {
182    style_manager: OptStyleManager,
183
184    marker: PhantomData<(S, &'inv ())>,
185}
186
187impl<S> Default for NullPolicy<'_, S> {
188    fn default() -> Self {
189        Self {
190            style_manager: OptStyleManager::default(),
191            marker: Default::default(),
192        }
193    }
194}
195
196impl<'inv, S> Policy for NullPolicy<'inv, S> {
197    type Ret = Return;
198
199    type Set = Parser<'inv, S>;
200
201    type Inv<'a> = Invoker<'a, Parser<'inv, S>>;
202
203    type Error = crate::Error;
204
205    fn parse(&mut self, _: &mut Self::Set, _: &mut Self::Inv<'_>, _: Args) -> Result<Self::Ret> {
206        Ok(Return::default())
207    }
208}
209
210impl<S> PolicySettings for NullPolicy<'_, S> {
211    fn style_manager(&self) -> &OptStyleManager {
212        &self.style_manager
213    }
214
215    fn style_manager_mut(&mut self) -> &mut OptStyleManager {
216        &mut self.style_manager
217    }
218
219    fn strict(&self) -> bool {
220        false
221    }
222
223    fn styles(&self) -> &[UserStyle] {
224        &self.style_manager
225    }
226
227    fn no_delay(&self) -> Option<&[String]> {
228        None
229    }
230
231    fn overload(&self) -> bool {
232        false
233    }
234
235    fn prepolicy(&self) -> bool {
236        false
237    }
238
239    fn set_strict(&mut self, _: bool) -> &mut Self {
240        self
241    }
242
243    fn set_styles(&mut self, _: Vec<UserStyle>) -> &mut Self {
244        self
245    }
246
247    fn set_no_delay(&mut self, _: impl Into<String>) -> &mut Self {
248        self
249    }
250
251    fn set_overload(&mut self, _: bool) -> &mut Self {
252        self
253    }
254
255    fn set_prepolicy(&mut self, _: bool) -> &mut Self {
256        self
257    }
258}
259
260#[cfg(test)]
261mod test {
262    #[test]
263    fn test_example_simple() {
264        use crate as cote;
265        use crate::prelude::*;
266        // macro generate the code depend on crate name
267        use aopt::opt::Pos;
268
269        #[derive(Debug, Cote)]
270        pub struct Example {
271            /// a flag argument
272            foo: bool,
273
274            /// a position argument
275            #[arg(index = "1")]
276            bar: Pos<usize>,
277        }
278
279        let example = Example::parse(Args::from(["app", "--foo", "42"]));
280
281        assert!(example.is_ok());
282
283        let example = example.unwrap();
284
285        assert!(example.foo);
286        assert_eq!(example.bar.0, 42);
287
288        let parser = Example::into_parser().unwrap();
289
290        assert_eq!(parser["--foo"].help(), "a flag argument");
291        assert_eq!(parser["bar"].help(), "a position argument");
292    }
293
294    #[test]
295    fn test_multiple_pos_arguments() {
296        use crate::prelude::*;
297        // macro generate the code depend on crate name
298        use crate as cote;
299        use aopt::opt::Pos;
300        use std::path::PathBuf;
301
302        #[derive(Debug, Cote)]
303        #[cote(help)]
304        pub struct CopyTool {
305            #[arg(alias = "-f")]
306            force: bool,
307
308            /// Enable the recursive mode
309            #[arg(alias = "-r")]
310            recursive: bool,
311
312            #[arg(index = "1", help = "The copy destination")]
313            destination: Pos<String>,
314
315            /// Specify path to copy
316            #[arg(index = 2..)]
317            sources: Vec<Pos<PathBuf>>,
318        }
319
320        let example = CopyTool::parse(Args::from(["app", "--force"]));
321
322        assert!(example.is_err());
323
324        let example = CopyTool::parse(Args::from([
325            "app", "--force", ".", "../foo", "../bar/", "other",
326        ]))
327        .unwrap();
328
329        assert!(example.force);
330        assert!(!example.recursive);
331        assert_eq!(example.destination.0, String::from("."));
332        assert_eq!(
333            example.sources,
334            ["../foo", "../bar/", "other"]
335                .into_iter()
336                .map(|v| Pos::new(PathBuf::from(v)))
337                .collect::<Vec<_>>()
338        );
339    }
340
341    #[test]
342    fn test_fallback() {
343        use crate::prelude::*;
344        // macro generate the code depend on crate name
345        use crate as cote;
346        use aopt::opt::Pos;
347
348        #[derive(Debug, Cote)]
349        #[cote(policy = delay, help, on = find_main, name = "find")]
350        pub struct Find {
351            /// Do not follow symbolic link
352            #[arg(name = "-H", nodelay)]
353            hard: bool,
354
355            /// Fllow symbolic link
356            #[arg(name = "-L", nodelay)]
357            symbol: bool,
358
359            #[arg(name = "-P", nodelay, value = true)]
360            never: bool,
361
362            #[arg(name = "-name", help = "Search the file base on file name")]
363            name: Option<String>,
364
365            /// List the file large than the size
366            #[arg(name = "-size")]
367            size: Option<usize>,
368
369            #[arg(index = "1", help = "Search starting point", fallback = search, then = VecStore)]
370            destination: Vec<Pos<String>>,
371        }
372
373        #[allow(dead_code)]
374        fn search<Set>(_: &mut Set, _: &mut Ctx) -> Result<Option<Vec<String>>, aopt::Error> {
375            Ok(Some(
376                ["file1", "file2", "dir1", "dir2"]
377                    .into_iter()
378                    .map(|v| v.to_string())
379                    .collect(),
380            ))
381        }
382
383        fn find_main<Set>(set: &mut Set, _: &mut Ctx) -> Result<Option<()>, aopt::Error>
384        where
385            Set: SetValueFindExt,
386            SetCfg<Set>: ConfigValue + Default,
387        {
388            let tool = Find::try_extract(set)?;
389
390            assert!(tool.hard,);
391            assert!(!tool.symbol);
392            assert!(tool.never);
393            assert_eq!(tool.name, Some("foo".to_owned()));
394            assert_eq!(tool.size, Some(42));
395            assert_eq!(
396                tool.destination,
397                ["file1", "file2", "dir1", "dir2"]
398                    .into_iter()
399                    .map(|v| Pos::new(v.to_string()))
400                    .collect::<Vec<_>>()
401            );
402
403            Ok(Some(()))
404        }
405
406        let args = Args::from(["app", ".", "-H", "-name=foo", "-size", "42"]);
407
408        let CoteRes { ret, .. } = Find::parse_args(args).unwrap();
409
410        ret.ok().unwrap();
411    }
412
413    #[test]
414    fn sub_test() {
415        assert!(sub_test_impl().is_ok());
416    }
417
418    fn sub_test_impl() -> Result<(), aopt::Error> {
419        use crate::prelude::*;
420        // macro generate the code depend on crate name
421        use crate as cote;
422        use std::path::PathBuf;
423
424        #[derive(Debug, Cote, PartialEq, Default)]
425        #[cote(prepolicy, help, name = "app")]
426        pub struct App {
427            /// Set the count value
428            #[arg(values = [1usize, 2, 3])]
429            count: Option<Vec<usize>>,
430
431            #[sub(alias = "ls", help = "list subcommand list file of given path")]
432            list: Option<List>,
433
434            #[sub(help = "find something under directory")]
435            find: Option<Find>,
436        }
437
438        #[derive(Debug, Cote, PartialEq)]
439        #[cote(help)]
440        pub struct List {
441            #[arg(help = "list all the file")]
442            all: bool,
443
444            #[arg(valid = valid!([1, 42, 68]))]
445            depth: usize,
446
447            #[arg(index = "1")]
448            path: Pos<PathBuf>,
449        }
450
451        #[derive(Debug, Cote, PartialEq)]
452        #[cote(help)]
453        pub struct Find {
454            recursive: bool,
455
456            #[arg(index = "1")]
457            path: Pos<PathBuf>,
458        }
459
460        let args = Args::from(["app", "ls", "--all", "--depth=42", "."]);
461
462        let app = App::parse(args)?;
463
464        assert_eq!(
465            app,
466            App {
467                count: Some(vec![1, 2, 3]),
468                list: Some(List {
469                    all: true,
470                    depth: 42,
471                    path: Pos(PathBuf::from("."))
472                }),
473                find: None,
474            }
475        );
476
477        let args = Args::from(["app", "list", "--all", "--depth=6", "."]);
478
479        let app = App::parse(args);
480
481        assert!(app.is_err());
482
483        let args = Args::from(["app", "--count=8", "find", "something"]);
484
485        let app = App::parse(args)?;
486
487        assert_eq!(
488            app,
489            App {
490                count: Some(vec![1, 2, 3, 8]),
491                list: None,
492                find: Some(Find {
493                    recursive: false,
494                    path: Pos(PathBuf::from("something")),
495                }),
496            }
497        );
498
499        let args = Args::from(["app", "--count", "42"]);
500
501        let app = App::parse(args);
502
503        assert!(app.is_err());
504
505        let args = Args::from(["app", "--count=42", "list"]);
506
507        let CoteRes {
508            ret,
509            parser: mut app,
510            ..
511        } = App::parse_args(args)?;
512
513        assert!(!ret.status());
514        assert_eq!(
515            app.extract_type::<App>()?,
516            App {
517                count: Some(vec![1, 2, 3, 42]),
518                list: None,
519                find: None,
520            }
521        );
522
523        Ok(())
524    }
525}