Skip to main content

fpr_cli/
lib.rs

1mod i;
2mod parse;
3mod util;
4
5mod com {
6    pub use crate::*;
7    pub use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
8    pub use inquire::{
9        autocompletion::Replacement, list_option::ListOption, validator::CustomTypeValidator,
10        validator::ErrorMessage, Autocomplete, CustomType, CustomUserError, InquireError,
11        MultiSelect, Select, Text,
12    };
13    pub use itertools::Itertools;
14    pub use std::{env::args, fmt::Display, path::PathBuf, str::FromStr};
15}
16
17pub use util::*;
18
19pub use fpr_cli_derives::*;
20pub use i::*;
21pub use parse::*;
22
23use com::*;
24
25pub enum ActsErr<'a> {
26    Run(ParseCtx<'a>, String),
27    Inquire(String),
28    ExpectedAct(ParseCtx<'a>, String),
29    UnknownAct(ParseCtx<'a>, &'a str),
30    Args(ParseCtx<'a>, ArgsParseErr<'a>, String),
31}
32
33#[derive(Clone, Debug)]
34pub struct ParseCtx<'a> {
35    pub pfx: Vec<Arg<'a>>,
36}
37
38impl<'a> ActsErr<'a> {
39    fn display(self, arg0: &'a Arg) -> DActsErr<'a> {
40        DActsErr { e: self, arg0 }
41    }
42}
43struct DActsErr<'a> {
44    e: ActsErr<'a>,
45    arg0: Arg<'a>,
46}
47
48impl<'a> Display for ActsErr<'a> {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        use ActsErr::*;
51        match self {
52            Run(_, ref e) => write!(f, "Failed to run:\n{e}"),
53            Inquire(ref e) => write!(f, "{e}"),
54            ExpectedAct(_, _) => write!(f, "Expected an act.\n"),
55            UnknownAct(_, ref e) => write!(f, "Unknown act '{e}.'\n"),
56            Args(_, ref e, _) => match e {
57                ArgsParseErr::Help(_) => write!(f, "{e}"),
58                _ => write!(f, "Failed to parse opts.\n{e}\n"),
59            },
60        }
61    }
62}
63impl<'a> Display for DActsErr<'a> {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        use ActsErr::*;
66        write!(f, "{}", self.e)?;
67        match self.e {
68            ExpectedAct(ref c, ref u) => {
69                write!(f, "Usage: {} {} <action>\n{u}", self.arg0, c.pfx.join(" "))?;
70            }
71            UnknownAct(ref c, ref u) => {
72                write!(f, "Usage: {} {} <action>\n{u}", self.arg0, c.pfx.join(" "))?;
73            }
74            Args(ref c, _, ref u) => {
75                write!(
76                    f,
77                    "Usage: {} {} <opts...>\nOpts:\n{u}",
78                    self.arg0,
79                    c.pfx.join(" ")
80                )?;
81            }
82            _ => (),
83        };
84        Ok(())
85    }
86}
87impl<'a> From<InquireError> for ActsErr<'a> {
88    fn from(v: InquireError) -> Self {
89        Self::Inquire(format!("{v}"))
90    }
91}
92
93pub type Arg<'a> = &'a str;
94pub trait Acts<C>: Sized {
95    fn run(c: &C) -> Result<(), String> {
96        let args = args().collect_vec();
97        let a: Vec<_> = args.iter().map(|e| e.as_str()).collect();
98        let mut s = ParseCtx { pfx: vec![] };
99        Self::next(c, &mut s, &a[1..]).map_err(|e| format!("{}", e.display(&a[0])))
100    }
101
102    fn next<'a>(c: &C, s: &mut ParseCtx<'a>, args: &[Arg<'a>]) -> Result<(), ActsErr<'a>> {
103        if args.is_empty() {
104            print!("{}", ActsErr::ExpectedAct(s.to_owned(), Self::usage()));
105            return Self::next(c, s, &[Self::select_act()?]);
106        };
107        let a = &args[0];
108        let args = &args[1..];
109
110        use ActsErr::*;
111        match Self::next_impl(c, s, a, args) {
112            Err(e) => match e {
113                UnknownAct(_, _) => {
114                    print!("{e}");
115                    Self::next(c, s, &[Self::select_act()?])
116                }
117                _ => return Err(e),
118            },
119            e => return e,
120        }
121    }
122    fn select_act<'a>() -> Result<&'static str, ActsErr<'a>> {
123        let opts: Vec<_> = to_lines(&Self::usage_v())
124            .into_iter()
125            .enumerate()
126            .map(|(i, o)| ListOption::new(i, o))
127            .collect();
128        Ok(Self::opts()[Select::new("Choose an action.", opts)
129            .with_page_size(50)
130            .prompt()?
131            .index])
132    }
133
134    fn list<'a>() -> Vec<Vec<Arg<'a>>> {
135        let pfx = vec![];
136        let mut res: Vec<Vec<Arg<'a>>> = vec![];
137        Self::add_paths(&pfx, &mut res);
138        return res;
139    }
140    fn usage() -> String {
141        to_table(&Self::usage_v())
142    }
143
144    fn opts() -> Vec<&'static str>;
145    fn next_impl<'a>(
146        c: &C,
147        s: &mut ParseCtx<'a>,
148        a: &Arg<'a>,
149        args: &[Arg<'a>],
150    ) -> Result<(), ActsErr<'a>>;
151    fn desc_act() -> &'static str;
152    fn usage_v() -> Vec<[&'static str; 2]>;
153    fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
154}
155pub trait Args<C>: Sized {
156    fn next_impl<'a>(c: &C, args: &[Arg<'a>]) -> Result<(), ArgsErr<'a>> {
157        let mut args = ParsedArgs::new(args).map_err(|e| {
158            use ParsedArgsErr::*;
159            match e {
160                UnexpectedToken(a) => ArgsParseErr::UnexpectedToken(a, Self::usage(c)),
161            }
162        })?;
163
164        if args.consume(&format!("{PFX}help")).is_some() {
165            return Err(ArgsParseErr::Help(Self::usage(c)).into());
166        }
167
168        let a = Self::new(c, &mut args)?;
169
170        let u = args
171            .k
172            .iter()
173            .filter(|k| !k.used && !k.k.is_none_or(|x| x == PFX))
174            .map(|k| {
175                match k.k {
176                    Some(e) => vec![e],
177                    None => vec![],
178                }
179                .into_iter()
180                .chain(k.v.iter().map(|e| *e))
181            })
182            .flatten()
183            .collect::<Vec<_>>();
184        if !u.is_empty() {
185            return Err(ArgsParseErr::UnknownArgs(u, Self::usage(c)).into());
186        }
187
188        let r = args
189            .k
190            .iter()
191            .filter(|k| k.k.is_none_or(|x| x == PFX))
192            .map(|k| k.v.iter())
193            .flatten()
194            .map(|e| *e)
195            .collect();
196
197        let _ = a.run(c, r).map_err(|s| ArgsErr::Run(s))?;
198        Ok(())
199    }
200    fn next<'a>(c: &C, s: &mut ParseCtx<'a>, args: &[Arg<'a>]) -> Result<(), ActsErr<'a>> {
201        match Self::next_impl(c, args) {
202            Err(e) => match e {
203                ArgsErr::Run(r) => Err(ActsErr::Run(s.to_owned(), r)),
204                ArgsErr::Parse(e) => Err(ActsErr::Args(s.to_owned(), e, Self::usage(c))),
205            },
206            Ok(o) => Ok(o),
207        }
208    }
209    fn usage(c: &C) -> String {
210        let mut r: Vec<[String; 4]> = vec![];
211        Self::add_usage(c, &mut r);
212        to_table(&r)
213    }
214    fn new<'a, 'b>(c: &C, args: &mut ParsedArgs<'b>) -> Result<Self, ArgsParseErr<'b>>;
215    fn desc_act() -> &'static str;
216    fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
217    fn add_usage(c: &C, r: &mut Vec<[String; 4]>);
218    fn default(c: &C) -> Self;
219
220    fn run(self, c: &C, r: Vec<&str>) -> Result<(), String>;
221}
222
223pub trait Parse<'a>
224where
225    Self: Sized + Display,
226{
227    fn parse(i: Arg<'a>) -> Result<Self, ParseErr<'a>>;
228    fn desc() -> &'static str;
229}
230#[derive(Debug)]
231pub struct ParseErr<'a> {
232    pub ty: &'static str,
233    pub i: Arg<'a>,
234    pub e: String,
235}
236
237pub struct DisplayVec<T: Display>(Vec<T>);
238impl<T: Display> Display for DisplayVec<T> {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        write!(f, "{}", self.0.iter().map(|t| format!("{t}")).join(", "))
241    }
242}
243impl<T: Display> From<Vec<T>> for DisplayVec<T> {
244    fn from(v: Vec<T>) -> Self {
245        Self(v)
246    }
247}
248impl<T: Display> Into<Vec<T>> for DisplayVec<T> {
249    fn into(self) -> Vec<T> {
250        self.0
251    }
252}