char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somsnapshot: embed shared main and worker startup in the CLI688e96e

main
5.7 KiB196 linesraw
1use std::path::{Path, PathBuf};
2
3use crate::cli::ParseError;
4use crate::fragments::{Arg, Entry, Parser, flag_value, reject};
5use avril_runtime::caps::PathTarget;
6
7fn single<E>(
8    parser: &mut Parser,
9    bare: &[&str],
10    own: &mut impl FnMut(&str, &Path) -> Result<bool, E>,
11) -> Result<Option<String>, E>
12where
13    E: From<ParseError>,
14{
15    let mut raw = None;
16    while let Some(arg) = parser.next()? {
17        match arg {
18            Arg::Positional(arg) => {
19                if raw.replace(arg).is_some() {
20                    return Err(
21                        ParseError::Usage("Expected exactly one entry module".into()).into(),
22                    );
23                }
24            }
25            Arg::Flag(arg) if own(&arg, &parser.cwd)? => {}
26            Arg::Flag(arg) => return Err(reject(&arg, bare).into()),
27        }
28    }
29    Ok(raw)
30}
31
32pub(crate) fn parse_entry<E>(
33    name: &str,
34    argv: Vec<String>,
35    cwd: PathBuf,
36    bare: &[&str],
37    own: impl FnMut(&str, &Path) -> Result<bool, E>,
38) -> Result<Entry, E>
39where
40    E: From<ParseError>,
41{
42    let mut parser = Parser::new(name, argv, &cwd)?;
43    let mut own = own;
44    let raw = single(&mut parser, bare, &mut own)?;
45    let raw = raw.ok_or_else(|| parser.usage())?;
46    Ok(parser.entry(&raw)?)
47}
48
49pub struct EntryArgs {
50    pub entry: Entry,
51    pub script: Vec<String>,
52}
53
54#[derive(Debug, thiserror::Error)]
55pub enum ReplParseError {
56    #[error(transparent)]
57    Generic(#[from] ParseError),
58    #[error("The REPL does not accept a module; run one with `avril run <module>`")]
59    UnexpectedModule,
60}
61
62impl EntryArgs {
63    pub fn parse_repl(
64        argv: Vec<String>,
65        script: Vec<String>,
66        cwd: PathBuf,
67    ) -> Result<Self, ReplParseError> {
68        let mut parser = Parser::new("repl", argv, &cwd)?;
69        if single::<ReplParseError>(&mut parser, &[], &mut |_, _| Ok(false))?.is_some() {
70            return Err(ReplParseError::UnexpectedModule);
71        }
72        let entry = parser.synthetic(".avril-repl.js")?;
73        Ok(Self { entry, script })
74    }
75
76    pub fn parse_run(
77        argv: Vec<String>,
78        script: Vec<String>,
79        cwd: PathBuf,
80    ) -> Result<Self, ParseError> {
81        let entry = parse_entry::<ParseError>("run", argv, cwd, &[], |_, _| Ok(false))?;
82        Ok(Self { entry, script })
83    }
84}
85
86pub struct ServeArgs {
87    pub common: EntryArgs,
88    pub listener: avril_http::Address,
89}
90
91#[derive(Debug, thiserror::Error)]
92pub enum ServeParseError {
93    #[error(transparent)]
94    Generic(#[from] ParseError),
95    #[error("Invalid HTTP listen port")]
96    BadPort,
97    #[error("--unix cannot be combined with --hostname or --port")]
98    UnixConflict,
99    #[error("Unix socket paths must be UTF-8")]
100    SocketUtf8,
101}
102
103impl ServeArgs {
104    pub fn parse(
105        argv: Vec<String>,
106        script: Vec<String>,
107        cwd: PathBuf,
108    ) -> Result<Self, ServeParseError> {
109        let mut hostname = None;
110        let mut port = None;
111        let mut socket = None;
112        let entry = parse_entry::<ServeParseError>("serve", argv, cwd, &[], |arg, cwd| {
113            let (flag, value) = flag_value(arg)?;
114            match flag {
115                "--hostname" => hostname = Some(value.to_owned()),
116                "--port" => {
117                    port = Some(value.parse::<u16>().map_err(|_| ServeParseError::BadPort)?)
118                }
119                "--unix" => socket = Some(cwd.join(value)),
120                _ => return Ok(false),
121            }
122            Ok(true)
123        })?;
124        let listener = match socket {
125            Some(socket) if hostname.is_none() && port.is_none() => {
126                let target = PathTarget::for_entry(socket).map_err(ParseError::Io)?;
127                avril_http::Address::Unix {
128                    path: Some(
129                        target
130                            .as_path()
131                            .to_str()
132                            .ok_or(ServeParseError::SocketUtf8)?
133                            .to_owned(),
134                    ),
135                }
136            }
137            Some(_) => return Err(ServeParseError::UnixConflict),
138            None => avril_http::Address::Tcp {
139                hostname: hostname.unwrap_or_else(|| "127.0.0.1".into()),
140                port: port.unwrap_or(8000),
141            },
142        };
143        Ok(Self {
144            common: EntryArgs { entry, script },
145            listener,
146        })
147    }
148}
149
150pub(crate) fn parse_cache(argv: Vec<String>, cwd: PathBuf) -> Result<Entry, ParseError> {
151    parse_entry::<ParseError>("cache", argv, cwd, &[], |_, _| Ok(false))
152}
153
154pub(crate) fn parse_info(argv: Vec<String>, cwd: PathBuf) -> Result<Entry, ParseError> {
155    parse_entry::<ParseError>("info", argv, cwd, &[], |_, _| Ok(false))
156}
157
158pub struct UpdateArgs {
159    pub entry: Entry,
160    pub prune: bool,
161}
162
163impl UpdateArgs {
164    pub fn parse(argv: Vec<String>, cwd: PathBuf) -> Result<Self, ParseError> {
165        let mut prune = false;
166        let mut entry = parse_entry::<ParseError>("update", argv, cwd, &["--prune"], |arg, _| {
167            if arg == "--prune" {
168                prune = true;
169                return Ok(true);
170            }
171            Ok(false)
172        })?;
173        entry.packages.update = true;
174        Ok(Self { entry, prune })
175    }
176}
177
178pub struct VendorArgs {
179    pub entry: Entry,
180    pub output: Option<PathBuf>,
181}
182
183impl VendorArgs {
184    pub fn parse(argv: Vec<String>, cwd: PathBuf) -> Result<Self, ParseError> {
185        let mut out = None;
186        let entry = parse_entry::<ParseError>("vendor", argv, cwd, &[], |arg, cwd| {
187            let (flag, value) = flag_value(arg)?;
188            if flag != "--out" {
189                return Ok(false);
190            }
191            out = Some(cwd.join(value));
192            Ok(true)
193        })?;
194        Ok(Self { entry, output: out })
195    }
196}