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 pfx: Vec<Arg<'a>>,
29}
30
31#[derive(Error, Debug)]
32pub enum ErrActs {
33    #[error("Needs help")]
34    Help,
35    #[error("Unknown act '{act}'")]
36    Unknown { act: String },
37    #[error("Expected act.")]
38    Missing,
39    #[error("{e}")]
40    Args {
41        #[from]
42        e: ErrArgs,
43    },
44}
45pub type Arg<'a> = &'a str;
46pub trait Acts<C>: Sized {
47    fn run(c: &C) -> Result<(), ErrActs> {
48        let args = args().collect_vec();
49        let a: Vec<_> = args.iter().map(|e| e.as_str()).collect();
50        let mut s = ParseCtx { pfx: vec![] };
51        let r = Self::next(c, &mut s, &a[1..]);
52        if let Err(e) = &r {
53            println!("Available acts:\n{}", Self::usage());
54            match e {
55                ErrActs::Help => return Ok(()),
56                _ => (),
57            }
58        }
59        r
60    }
61
62    fn next<'a>(c: &C, s: &mut ParseCtx<'a>, args: &[Arg<'a>]) -> Result<(), ErrActs> {
63        if args.is_empty() {
64            return Err(ErrActs::Missing)?;
65        };
66        let a = &args[0];
67        let args = &args[1..];
68        Self::next_impl(c, s, a, args)
69    }
70    fn list<'a>() -> Vec<Vec<Arg<'a>>> {
71        let pfx = vec![];
72        let mut res: Vec<Vec<Arg<'a>>> = vec![];
73        Self::add_paths(&pfx, &mut res);
74        return res;
75    }
76    fn usage() -> String {
77        to_table(&Self::usage_v())
78    }
79
80    fn opts() -> Vec<&'static str>;
81    fn next_impl<'a>(
82        c: &C,
83        s: &mut ParseCtx<'a>,
84        a: &Arg<'a>,
85        args: &[Arg<'a>],
86    ) -> Result<(), ErrActs>;
87    fn desc_act() -> &'static str;
88    fn usage_v() -> Vec<[&'static str; 2]>;
89    fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
90}
91
92#[derive(Error, Debug)]
93pub enum ErrArgs {
94    #[error("Needs help")]
95    Help,
96    #[error("Unknown args '{}'", arg.join(", "))]
97    Unknown { arg: Vec<String> },
98    #[error("Failed while parsing arg '{arg}': '{e}'")]
99    Arg { arg: String, e: ErrArg },
100    #[error("{e}")]
101    Run { e: String },
102}
103pub trait Args<C>: Sized {
104    fn next_impl<'a>(c: &C, args: &[Arg<'a>]) -> Result<(), ErrArgs> {
105        let mut args = ParsedArgs::new(args);
106        if args.consume(&format!("{PFX}help")).is_some() {
107            return Err(ErrArgs::Help);
108        }
109
110        let a = Self::new(c, &mut args)?;
111
112        let u = args
113            .k
114            .iter()
115            .filter(|k| !k.used && !k.k.is_none_or(|x| x == PFX))
116            .map(|k| {
117                match k.k {
118                    Some(e) => vec![e],
119                    None => vec![],
120                }
121                .into_iter()
122                .chain(k.v.iter().map(|e| *e))
123            })
124            .flatten()
125            .map(|e| e.to_owned())
126            .collect_vec();
127        if !u.is_empty() {
128            return Err(ErrArgs::Unknown { arg: u });
129        }
130
131        let r = args
132            .k
133            .iter()
134            .filter(|k| k.k.is_none_or(|x| x == PFX))
135            .map(|k| k.v.iter())
136            .flatten()
137            .map(|e| *e)
138            .collect();
139
140        let _ = a.run(c, r).map_err(|e| ErrArgs::Run { e })?;
141        Ok(())
142    }
143    fn next<'a>(c: &C, _: &mut ParseCtx<'a>, args: &[Arg<'a>]) -> Result<(), ErrArgs> {
144        Self::next_impl(c, args)
145    }
146    fn usage(c: &C) -> String {
147        let mut r: Vec<[String; 4]> = vec![];
148        Self::add_usage(c, &mut r);
149        to_table(&r)
150    }
151    fn new<'a, 'b>(c: &C, args: &mut ParsedArgs<'b>) -> Result<Self, ErrArgs>;
152    fn desc_act() -> &'static str;
153    fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
154    fn add_usage(c: &C, r: &mut Vec<[String; 4]>);
155    fn default(c: &C) -> Self;
156
157    fn run(self, c: &C, r: Vec<&str>) -> Result<(), String>;
158}
159
160#[derive(Error, Debug)]
161pub enum ErrVal {
162    #[error("Failed to parse '{input}' as type '{typename}': {error}")]
163    Err {
164        input: String,
165        typename: String,
166        error: String,
167    },
168}
169pub trait Parse<'a>
170where
171    Self: Sized + Display,
172{
173    fn parse(i: Arg<'a>) -> Result<Self, ErrVal>;
174    fn desc() -> &'static str;
175}
176#[derive(Debug)]
177pub struct ParseErr<'a> {
178    pub ty: &'static str,
179    pub i: Arg<'a>,
180    pub e: String,
181}
182
183pub struct DisplayVec<T: Display>(Vec<T>);
184impl<T: Display> Display for DisplayVec<T> {
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186        write!(f, "{}", self.0.iter().map(|t| format!("{t}")).join(", "))
187    }
188}
189impl<T: Display> From<Vec<T>> for DisplayVec<T> {
190    fn from(v: Vec<T>) -> Self {
191        Self(v)
192    }
193}
194impl<T: Display> Into<Vec<T>> for DisplayVec<T> {
195    fn into(self) -> Vec<T> {
196        self.0
197    }
198}