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