use std::{fmt::Display, path::PathBuf};
use clap::{Parser, ValueEnum};
use futures::StreamExt;
use super::{
reader::Reader,
types::{AppError, Event, EventTx, Output},
};
#[derive(Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
pub(crate) struct Config {
#[clap(short, long = "format", default_value = "html", value_parser = clap::builder::NonEmptyStringValueParser::new(), global = true)]
pub format: Option<String>,
#[clap(long, global = true)]
pub interactive: bool,
#[clap(short, long, value_parser = parse_out, hide_default_value = true, global = true)]
pub output: Option<Output>,
#[clap(long = "log", default_value = "auto", value_parser = parse_log_format, require_equals = true, hide_default_value = true)]
#[clap(global = true)]
pub log_format: &'static str,
#[clap(short, action = clap::ArgAction::Count, global = true)]
pub verbosity: u8,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Parser, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Ord, PartialOrd))]
pub(crate) enum Command {
Clear,
Graph {
#[clap(long = "as", value_name = "HEADERS")]
headers: Option<String>,
#[arg(value_enum, value_name = "TYPE")]
graph_type: GraphType,
#[clap(value_name = "PATH")]
#[arg(default_values_t = [".".to_string()])]
paths: Vec<String>,
},
Index {
paths: Vec<String>,
},
#[clap(hide = true)]
Ping,
Build {
#[clap(long = "as", value_name = "HEADERS")]
headers: Option<String>,
#[clap(short, long = "split", value_name = "SPLIT")]
#[arg(default_values_t = ["ROOT".to_string(), "DOCUMENT".to_string()])]
splits: Vec<String>,
#[clap(value_name = "PATH")]
#[arg(default_values_t = [".".to_string()])]
paths: Vec<String>,
},
#[clap(hide = true)]
Exit,
}
impl Config {
pub fn defaults(mut self) -> Self {
match (self.log_format, self.output.as_ref()) {
("html", None) => self.output = Some(Output::StdOutLog),
("html", Some(Output::StdOut)) => self.output = Some(Output::StdOutLog),
(_, None) => self.output = Some(Output::Path(PathBuf::from("./build"))),
_ => {}
};
self
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, PartialOrd, Ord)]
pub(crate) enum GraphType {
Dependencies,
}
impl Display for GraphType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GraphType::Dependencies => write!(f, "dependencies"),
}
}
}
pub async fn handle(event_tx: EventTx, config: &Config) -> Result<(), AppError> {
let mut reader = Reader::from(config);
while let Some(cmd) = reader.next().await {
event_tx
.send(Event::Command(cmd))
.map_err(|_| AppError::send_error())?;
}
Ok(())
}
fn parse_out(arg: &str) -> Result<Output, &'static str> {
match arg {
uri if uri.starts_with("stdout") => Ok(Output::StdOut),
uri if uri.starts_with("file://") || !uri.contains("://") => {
let path: PathBuf = arg.trim_start_matches("file://").into();
if path.is_file() {
Err("Output must be a directory")
} else {
Ok(Output::Path(path))
}
}
_ => Err("Unknown scheme"),
}
}
fn parse_log_format(arg: &str) -> Result<&'static str, &'static str> {
match arg {
"auto" => Ok("auto"),
"html" => Ok("html"),
"plain" => Ok("plain"),
_ => Err("Unknown progress"),
}
}