use crate::engine::Units;
use crate::engine::output::CamelCase;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, clap::ValueEnum)]
pub enum IdStyle {
Spaces,
#[default]
Camel,
#[value(name = "uppercamel", alias = "upper-camel")]
UpperCamel,
}
impl From<IdStyle> for CamelCase {
fn from(s: IdStyle) -> Self {
match s {
IdStyle::Spaces => CamelCase::Off,
IdStyle::Camel => CamelCase::Lower,
IdStyle::UpperCamel => CamelCase::Upper,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, clap::ValueEnum)]
pub enum UnitSystem {
#[default]
Si,
Metric,
}
impl From<UnitSystem> for Units {
fn from(u: UnitSystem) -> Self {
match u {
UnitSystem::Si => Units::Si,
UnitSystem::Metric => Units::Metric,
}
}
}
#[derive(Debug, Clone, Default, clap::Args)]
pub struct ShapeArgs {
#[arg(long, value_enum, value_name = "STYLE",
conflicts_with_all = ["camel", "upper_camel"])]
id: Option<IdStyle>,
#[arg(long, value_enum, value_name = "SYSTEM", conflicts_with = "si")]
units: Option<UnitSystem>,
#[arg(long)]
wrap: bool,
#[arg(long, conflicts_with = "upper_camel", hide_short_help = true)]
camel: bool,
#[arg(long = "upper-camel", hide_short_help = true)]
upper_camel: bool,
#[arg(long, hide_short_help = true)]
si: bool,
}
impl ShapeArgs {
pub fn id_style(&self) -> IdStyle {
match self.id {
Some(id) => id,
None if self.upper_camel => IdStyle::UpperCamel,
None if self.camel => IdStyle::Camel,
None => IdStyle::default(),
}
}
pub fn camel_case(&self) -> CamelCase {
self.id_style().into()
}
pub fn unit_system(&self) -> UnitSystem {
match self.units {
Some(u) => u,
None if self.si => UnitSystem::Si,
None => UnitSystem::default(),
}
}
pub fn units(&self) -> Units {
self.unit_system().into()
}
pub fn wrap(&self) -> bool {
self.wrap || self.camel || self.upper_camel
}
pub fn is_si(&self) -> bool {
self.unit_system() == UnitSystem::Si
}
pub fn warn_deprecated(&self) {
if self.camel {
eprintln!(
"warning: --camel is deprecated; camelCase is now the default, so use --wrap \
if you still want records nested under their PGN id"
);
}
if self.upper_camel {
eprintln!("warning: --upper-camel is deprecated; use --id uppercamel --wrap");
}
if self.si {
eprintln!(
"warning: --si is deprecated and is now the default; drop it, or say --units si"
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Debug, Parser)]
struct Harness {
#[command(flatten)]
shape: ShapeArgs,
}
fn shape(args: &[&str]) -> ShapeArgs {
let mut argv = vec!["test"];
argv.extend_from_slice(args);
Harness::parse_from(argv).shape
}
#[test]
fn defaults_are_camel_si_and_unwrapped() {
let s = shape(&[]);
assert_eq!(s.camel_case(), CamelCase::Lower);
assert_eq!(s.units(), Units::Si);
assert!(!s.wrap());
}
#[test]
fn legacy_defaults_are_reachable() {
let s = shape(&["--id", "spaces", "--units", "metric"]);
assert_eq!(s.camel_case(), CamelCase::Off);
assert_eq!(s.units(), Units::Metric);
assert!(!s.wrap());
}
#[test]
fn deprecated_flags_still_resolve() {
assert_eq!(shape(&["--camel"]).camel_case(), CamelCase::Lower);
assert_eq!(shape(&["--upper-camel"]).camel_case(), CamelCase::Upper);
assert_eq!(shape(&["--si"]).units(), Units::Si);
}
#[test]
fn only_the_deprecated_camel_flags_imply_wrap() {
assert!(shape(&["--camel"]).wrap());
assert!(shape(&["--upper-camel"]).wrap());
assert!(!shape(&["--id", "camel"]).wrap());
assert!(!shape(&["--id", "uppercamel"]).wrap());
assert!(shape(&["--id", "uppercamel", "--wrap"]).wrap());
}
#[test]
fn uppercamel_accepts_both_spellings() {
assert_eq!(
shape(&["--id", "uppercamel"]).camel_case(),
CamelCase::Upper
);
assert_eq!(
shape(&["--id", "upper-camel"]).camel_case(),
CamelCase::Upper
);
}
#[test]
fn new_and_deprecated_forms_conflict() {
for argv in [
vec!["test", "--id", "camel", "--camel"],
vec!["test", "--id", "spaces", "--upper-camel"],
vec!["test", "--units", "metric", "--si"],
] {
assert!(
Harness::try_parse_from(&argv).is_err(),
"expected {argv:?} to be rejected"
);
}
}
}