use std::path::{Path, PathBuf}; use crate::cli::ParseError; use crate::fragments::{Arg, Entry, Parser, flag_value, reject}; use avril_runtime::caps::PathTarget; fn single( parser: &mut Parser, bare: &[&str], own: &mut impl FnMut(&str, &Path) -> Result, ) -> Result, E> where E: From, { let mut raw = None; while let Some(arg) = parser.next()? { match arg { Arg::Positional(arg) => { if raw.replace(arg).is_some() { return Err( ParseError::Usage("Expected exactly one entry module".into()).into(), ); } } Arg::Flag(arg) if own(&arg, &parser.cwd)? => {} Arg::Flag(arg) => return Err(reject(&arg, bare).into()), } } Ok(raw) } pub(crate) fn parse_entry( name: &str, argv: Vec, cwd: PathBuf, bare: &[&str], own: impl FnMut(&str, &Path) -> Result, ) -> Result where E: From, { let mut parser = Parser::new(name, argv, &cwd)?; let mut own = own; let raw = single(&mut parser, bare, &mut own)?; let raw = raw.ok_or_else(|| parser.usage())?; Ok(parser.entry(&raw)?) } pub struct EntryArgs { pub entry: Entry, pub script: Vec, } #[derive(Debug, thiserror::Error)] pub enum ReplParseError { #[error(transparent)] Generic(#[from] ParseError), #[error("The REPL does not accept a module; run one with `avril run `")] UnexpectedModule, } impl EntryArgs { pub fn parse_repl( argv: Vec, script: Vec, cwd: PathBuf, ) -> Result { let mut parser = Parser::new("repl", argv, &cwd)?; if single::(&mut parser, &[], &mut |_, _| Ok(false))?.is_some() { return Err(ReplParseError::UnexpectedModule); } let entry = parser.synthetic(".avril-repl.js")?; Ok(Self { entry, script }) } pub fn parse_run( argv: Vec, script: Vec, cwd: PathBuf, ) -> Result { let entry = parse_entry::("run", argv, cwd, &[], |_, _| Ok(false))?; Ok(Self { entry, script }) } } pub struct ServeArgs { pub common: EntryArgs, pub listener: avril_http::Address, } #[derive(Debug, thiserror::Error)] pub enum ServeParseError { #[error(transparent)] Generic(#[from] ParseError), #[error("Invalid HTTP listen port")] BadPort, #[error("--unix cannot be combined with --hostname or --port")] UnixConflict, #[error("Unix socket paths must be UTF-8")] SocketUtf8, } impl ServeArgs { pub fn parse( argv: Vec, script: Vec, cwd: PathBuf, ) -> Result { let mut hostname = None; let mut port = None; let mut socket = None; let entry = parse_entry::("serve", argv, cwd, &[], |arg, cwd| { let (flag, value) = flag_value(arg)?; match flag { "--hostname" => hostname = Some(value.to_owned()), "--port" => { port = Some(value.parse::().map_err(|_| ServeParseError::BadPort)?) } "--unix" => socket = Some(cwd.join(value)), _ => return Ok(false), } Ok(true) })?; let listener = match socket { Some(socket) if hostname.is_none() && port.is_none() => { let target = PathTarget::for_entry(socket).map_err(ParseError::Io)?; avril_http::Address::Unix { path: Some( target .as_path() .to_str() .ok_or(ServeParseError::SocketUtf8)? .to_owned(), ), } } Some(_) => return Err(ServeParseError::UnixConflict), None => avril_http::Address::Tcp { hostname: hostname.unwrap_or_else(|| "127.0.0.1".into()), port: port.unwrap_or(8000), }, }; Ok(Self { common: EntryArgs { entry, script }, listener, }) } } pub(crate) fn parse_cache(argv: Vec, cwd: PathBuf) -> Result { parse_entry::("cache", argv, cwd, &[], |_, _| Ok(false)) } pub(crate) fn parse_info(argv: Vec, cwd: PathBuf) -> Result { parse_entry::("info", argv, cwd, &[], |_, _| Ok(false)) } pub struct UpdateArgs { pub entry: Entry, pub prune: bool, } impl UpdateArgs { pub fn parse(argv: Vec, cwd: PathBuf) -> Result { let mut prune = false; let mut entry = parse_entry::("update", argv, cwd, &["--prune"], |arg, _| { if arg == "--prune" { prune = true; return Ok(true); } Ok(false) })?; entry.packages.update = true; Ok(Self { entry, prune }) } } pub struct VendorArgs { pub entry: Entry, pub output: Option, } impl VendorArgs { pub fn parse(argv: Vec, cwd: PathBuf) -> Result { let mut out = None; let entry = parse_entry::("vendor", argv, cwd, &[], |arg, cwd| { let (flag, value) = flag_value(arg)?; if flag != "--out" { return Ok(false); } out = Some(cwd.join(value)); Ok(true) })?; Ok(Self { entry, output: out }) } }