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!(
76 "Usage: {}\nActs:\n{}",
77 [s.exec].iter().chain(&s.pfx).chain(&["<act>"]).join(" "),
78 Self::usage()
79 );
80 Ok(())
81 }
82 e => Err(e),
83 },
84 }
85 }
86 fn list<'a>() -> Vec<Vec<Arg<'a>>> {
87 let pfx = vec![];
88 let mut res: Vec<Vec<Arg<'a>>> = vec![];
89 Self::add_paths(&pfx, &mut res);
90 return res;
91 }
92 fn usage() -> String {
93 to_table(&Self::usage_v())
94 }
95
96 fn opts() -> Vec<&'static str>;
97 fn next_impl<'a>(
98 c: &C,
99 s: &mut ParseCtx<'a>,
100 a: &Arg<'a>,
101 args: &[Arg<'a>],
102 ) -> Result<(), ErrActs>;
103 fn desc_act() -> &'static str;
104 fn usage_v() -> Vec<[&'static str; 2]>;
105 fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
106}
107
108#[derive(Error, Debug)]
109pub enum ErrArgs {
110 #[error("Needs help")]
111 Help,
112 #[error("Unknown args '{}'", arg.join(", "))]
113 Unknown { arg: Vec<String> },
114 #[error("Failed while parsing arg '{arg}': '{e}'")]
115 Arg { arg: String, e: ErrArg },
116 #[error("{e}")]
117 Run { e: String },
118}
119pub trait Args<C>: Sized {
120 fn next_impl<'a>(c: &C, args: &[Arg<'a>]) -> Result<(), ErrArgs> {
121 let mut args = ParsedArgs::new(args);
122 if args.consume(&format!("{PFX}help")).is_some() {
123 return Err(ErrArgs::Help);
124 }
125
126 let a = Self::new(c, &mut args)?;
127
128 let u = args
129 .k
130 .iter()
131 .filter(|k| !k.used && !k.k.is_none_or(|x| x == PFX))
132 .map(|k| {
133 match k.k {
134 Some(e) => vec![e],
135 None => vec![],
136 }
137 .into_iter()
138 .chain(k.v.iter().map(|e| *e))
139 })
140 .flatten()
141 .map(|e| e.to_owned())
142 .collect_vec();
143 if !u.is_empty() {
144 return Err(ErrArgs::Unknown { arg: u });
145 }
146
147 let r = args
148 .k
149 .iter()
150 .filter(|k| k.k.is_none_or(|x| x == PFX))
151 .map(|k| k.v.iter())
152 .flatten()
153 .map(|e| *e)
154 .collect();
155
156 let _ = a.run(c, r).map_err(|e| ErrArgs::Run { e })?;
157 Ok(())
158 }
159 fn next<'a>(c: &C, s: &mut ParseCtx<'a>, args: &[Arg<'a>]) -> Result<(), ErrArgs> {
160 match Self::next_impl(c, args) {
161 Err(e) => match e {
162 ErrArgs::Help => {
163 let u = Self::usage(c);
164 let has_positional = u.split("\n").any(|e| !e.starts_with(PFX));
165 let v = if !has_positional {
166 [s.exec]
167 .iter()
168 .chain(&s.pfx)
169 .chain(&["<opts...>"])
170 .join(" ")
171 } else {
172 [s.exec]
173 .iter()
174 .chain(&s.pfx)
175 .chain(&["<positional...>", "<opts...>", "--", "<positional...>"])
176 .join(" ")
177 };
178 println!("Usage: {v}\nOptions:\n{u}");
179 Ok(())
180 }
181 e => Err(e),
182 },
183 Ok(_) => Ok(()),
184 }
185 }
186 fn usage(c: &C) -> String {
187 let mut r: Vec<[String; 4]> = vec![];
188 Self::add_usage(c, &mut r);
189 to_table(&r)
190 }
191 fn new<'a, 'b>(c: &C, args: &mut ParsedArgs<'b>) -> Result<Self, ErrArgs>;
192 fn desc_act() -> &'static str;
193 fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>);
194 fn add_usage(c: &C, r: &mut Vec<[String; 4]>);
195 fn default(c: &C) -> Self;
196
197 fn run(self, c: &C, r: Vec<&str>) -> Result<(), String>;
198}
199
200#[derive(Error, Debug)]
201pub enum ErrVal {
202 #[error("Failed to parse '{input}' as type '{typename}': {error}")]
203 Err {
204 input: String,
205 typename: String,
206 error: String,
207 },
208}
209pub trait Parse<'a>
210where
211 Self: Sized + Display,
212{
213 fn parse(i: Arg<'a>) -> Result<Self, ErrVal>;
214 fn desc() -> &'static str;
215}
216#[derive(Debug)]
217pub struct ParseErr<'a> {
218 pub ty: &'static str,
219 pub i: Arg<'a>,
220 pub e: String,
221}
222
223pub struct DisplayVec<T: Display>(Vec<T>);
224impl<T: Display> Display for DisplayVec<T> {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(f, "{}", self.0.iter().map(|t| format!("{t}")).join(", "))
227 }
228}
229impl<T: Display> From<Vec<T>> for DisplayVec<T> {
230 fn from(v: Vec<T>) -> Self {
231 Self(v)
232 }
233}
234impl<T: Display> Into<Vec<T>> for DisplayVec<T> {
235 fn into(self) -> Vec<T> {
236 self.0
237 }
238}