use std::str::FromStr;
use clap::Args;
#[cfg(feature = "config")]
use serde::de;
use crate::{NodeColoringGradient, NodeColoringScheme};
type OptScheme = Option<NodeColoringScheme>;
#[cfg_attr(
feature = "config",
derive(serde::Deserialize),
serde(rename_all = "kebab-case")
)]
#[derive(Args)]
pub struct Config {
#[arg(short, long)]
pub package: Option<String>,
#[arg(long = "bin")]
pub binary: Option<String>,
#[arg(short = 'F', long)]
pub features: Option<String>,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub all_features: bool,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub no_default_features: bool,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub release: bool,
#[cfg(feature = "regex")]
#[arg(short = 'E', long)]
pub excludes: Option<Vec<String>>,
#[cfg(not(feature = "regex"))]
#[arg(short = 'E', long)]
pub excludes: Option<Vec<String>>,
#[cfg(feature = "regex")]
#[arg(short = 'R', long)]
pub root: Option<String>,
#[cfg(not(feature = "regex"))]
#[arg(short = 'R', long)]
pub root: Option<String>,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub std: bool,
#[cfg_attr(
feature = "config",
serde(deserialize_with = "de_scheme", default = "default_opt_scheme")
)]
#[arg(short, long, default_value = "cum-sum", hide_default_value = true, value_parser = parse_scheme, verbatim_doc_comment)]
pub scheme: OptScheme,
#[arg(short, long, verbatim_doc_comment)]
pub gradient: Option<NodeColoringGradient>,
#[arg(long, verbatim_doc_comment)]
pub gamma: Option<f32>,
#[arg(short, long, value_parser = parse_threshold, verbatim_doc_comment)]
#[cfg_attr(feature = "config", serde(deserialize_with = "de_threshold", default))]
pub threshold: Option<usize>,
#[arg(short = 'd', long)]
pub max_depth: Option<usize>,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub inverse_gradient: bool,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub dark_mode: bool,
#[arg(long)]
pub scale_factor: Option<f32>,
#[arg(long)]
pub separation_factor: Option<f32>,
#[arg(long, value_parser = parse_highlight, verbatim_doc_comment)]
#[cfg_attr(feature = "config", serde(deserialize_with = "de_highlight", default))]
pub highlight: Option<bool>,
#[arg(long, verbatim_doc_comment)]
pub highlight_amount: Option<f32>,
#[arg(long, verbatim_doc_comment)]
pub node_label_template: Option<String>,
#[arg(long, verbatim_doc_comment)]
pub node_tooltip_template: Option<String>,
#[arg(long, verbatim_doc_comment)]
pub edge_label_template: Option<String>,
#[arg(long, verbatim_doc_comment)]
pub edge_tooltip_template: Option<String>,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub dot_only: bool,
#[arg(short, long)]
pub output: Option<String>,
#[arg(long)]
#[cfg_attr(feature = "config", serde(default))]
pub no_open: bool,
}
#[cfg(feature = "config")]
fn default_opt_scheme() -> OptScheme {
Some(NodeColoringScheme::CumSum)
}
#[cfg(feature = "config")]
fn de_scheme<'de, D: de::Deserializer<'de>>(d: D) -> Result<Option<NodeColoringScheme>, D::Error> {
let str: String = de::Deserialize::deserialize(d)?;
parse_scheme(&str).map_err(de::Error::custom)
}
#[cfg(feature = "config")]
fn de_highlight<'de, D: de::Deserializer<'de>>(d: D) -> Result<Option<bool>, D::Error> {
let str: String = de::Deserialize::deserialize(d)?;
parse_highlight(&str)
.map(Option::Some)
.map_err(de::Error::custom)
}
#[cfg(feature = "config")]
fn de_threshold<'de, D: de::Deserializer<'de>>(d: D) -> Result<Option<usize>, D::Error> {
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum Threshold {
Usize(usize),
String(String),
}
let threshold: Threshold = de::Deserialize::deserialize(d)?;
match threshold {
Threshold::Usize(u) => Ok(Some(u)),
Threshold::String(s) => parse_threshold(&s)
.map(Option::Some)
.map_err(de::Error::custom),
}
}
fn parse_scheme(s: &str) -> Result<Option<NodeColoringScheme>, strum::ParseError> {
match s {
"none" => Ok(None),
_ => Ok(Some(NodeColoringScheme::from_str(s)?)),
}
}
fn parse_highlight(h: &str) -> Result<bool, &'static str> {
match h {
"dep" => Ok(true),
"rev-dep" => Ok(false),
_ => Err("invalid highlight value"),
}
}
fn parse_threshold(t: &str) -> Result<usize, parse_size::Error> {
if t == "non-zero" {
Ok(1)
} else {
parse_size::parse_size(t).map(|b| b as usize)
}
}