diff --git a/src/cli.rs b/src/cli.rs index 3782ab2..85a99b2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use crate::display::format_dentition; +use crate::display::{ToothCliFormatter, ToothDisplay}; use crate::lib::NotationKind; use crate::lib::Tooth; use clap::{Parser, Subcommand, ValueEnum}; @@ -26,6 +26,8 @@ enum Action { notation: NotationKindArg, #[clap(takes_value = false, short = 'p', long)] primary: bool, + #[clap(value_parser)] + value: Option, }, } #[derive(Parser, Debug)] @@ -43,9 +45,9 @@ impl Into for NotationKindArg { impl Into for &NotationKindArg { fn into(self) -> NotationKind { match self { - NotationKindArg::Iso => NotationKind::Iso, - NotationKindArg::Uns => NotationKind::Uns, - NotationKindArg::Alphanumeric => NotationKind::Alphanumeric, + NotationKindArg::Iso => NotationKind::Iso, + NotationKindArg::Uns => NotationKind::Uns, + NotationKindArg::Alphanumeric => NotationKind::Alphanumeric, } } } @@ -64,8 +66,28 @@ pub fn run_cli() { println!("{}", output_string); } - Action::Display { notation, primary } => { - println!("{}", format_dentition(!primary, &convert_kind(notation))) + Action::Display { + notation, + primary, + value, + } => { + let converted_value = match value { + Some(t) => { + let tooth_result = Tooth::from(t, ¬ation.into()); + match tooth_result { + Ok(tr) => Some(tr), + Err(err) => { + eprintln!("{}", err); + return; + } + } + } + None => None, + }; + println!( + "{}", + ToothCliFormatter::format(¬ation.into(), !primary, &converted_value) + ); } }; } diff --git a/src/display.rs b/src/display.rs deleted file mode 100644 index 9ade6bd..0000000 --- a/src/display.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::lib::NotationKind; -use crate::lib::{QuadrantKind, Tooth, ToothType}; -use owo_colors::{OwoColorize, Style}; - -enum JawKind { - Top, - Bottom, -} - -fn format_tooth(tooth: &Tooth, notation: &NotationKind) -> String { - let tooth_label = tooth.to(notation); - - let mut style = Style::new(); - style = match &tooth.get_type() { - ToothType::Canine => style.yellow(), - ToothType::Incisor => style.green(), - ToothType::Premolar => style.cyan(), - ToothType::Molar => style.blue(), - }; - format!(" {}", tooth_label.style(style)) -} - -fn format_quadrant(quadrant: QuadrantKind, permanent: bool, notation: &NotationKind) -> String { - let max = Tooth::quadrant_max(permanent); - let format_tooth = |i| { - let tooth = Tooth::new(i, quadrant, permanent).unwrap(); - format_tooth(&tooth, notation) - }; - if quadrant == QuadrantKind::TopLeft || quadrant == QuadrantKind::BottomLeft { - (1..=max).rev().map(format_tooth).collect() - } else { - (1..=max).map(format_tooth).collect() - } -} - -fn format_jaw(jaw: JawKind, permanent: bool, notation: &NotationKind) -> String { - let (quadrant_1, quadrant_2) = match jaw { - JawKind::Top => (QuadrantKind::TopLeft, QuadrantKind::TopRight), - JawKind::Bottom => (QuadrantKind::BottomLeft, QuadrantKind::BottomRight), - }; - format!( - " {} |{}", - format_quadrant(quadrant_1, permanent, notation), - format_quadrant(quadrant_2, permanent, notation) - ) -} - -pub fn format_dentition(permanent: bool, notation: &NotationKind) -> String { - let item_count = Tooth::quadrant_max(permanent); - let item_width = match (notation, permanent) { - (NotationKind::Iso, _) => 2, - (NotationKind::Uns, true) => 2, - (NotationKind::Uns, false) => 1, - (NotationKind::Alphanumeric, _) => 3, - }; - let jaw_len = (item_width + 1) * (item_count * 2 + 1); - let mut result = String::with_capacity((jaw_len * 4).into()); - result.push_str(&format_jaw(JawKind::Top, permanent, notation)); - result.push_str("\nR "); - for _ in 1..=jaw_len { - result.push_str("-"); - } - result.push_str(" L\n"); - result.push_str(&format_jaw(JawKind::Bottom, permanent, notation)); - - result -} diff --git a/src/display/cli.rs b/src/display/cli.rs new file mode 100644 index 0000000..a153aa5 --- /dev/null +++ b/src/display/cli.rs @@ -0,0 +1,110 @@ +use super::JawKind; +use crate::display::ToothDisplay; +use crate::lib::{NotationKind, QuadrantKind, Tooth, ToothType}; +use owo_colors::{OwoColorize, Style}; + +pub struct ToothCliFormatter; + +impl ToothCliFormatter { + fn format_tooth( + tooth: &Tooth, + notation: &NotationKind, + selected_value: &Option, + ) -> String { + let tooth_label = tooth.to(notation); + + let mut style = Style::new(); + style = match &tooth.get_type() { + ToothType::Canine => style.yellow(), + ToothType::Incisor => style.green(), + ToothType::Premolar => style.cyan(), + ToothType::Molar => style.blue(), + }; + + if let Some(t) = selected_value { + if t == tooth { + style = style.reversed(); + } + } + format!(" {}", tooth_label.style(style)) + } + + fn format_quadrant( + notation: &NotationKind, + + permanent: bool, + quadrant: QuadrantKind, + selected_value: &Option, + ) -> String { + let max = Tooth::quadrant_max(permanent); + let format_tooth = |i| { + let tooth = Tooth::new(i, quadrant, permanent).unwrap(); + ToothCliFormatter::format_tooth(&tooth, notation, selected_value) + }; + if quadrant == QuadrantKind::TopLeft || quadrant == QuadrantKind::BottomLeft { + (1..=max).rev().map(format_tooth).collect() + } else { + (1..=max).map(format_tooth).collect() + } + } + + fn format_jaw( + notation: &NotationKind, + permanent: bool, + jaw: JawKind, + selected_value: &Option, + ) -> String { + let (quadrant_1, quadrant_2) = match jaw { + JawKind::Top => (QuadrantKind::TopLeft, QuadrantKind::TopRight), + JawKind::Bottom => (QuadrantKind::BottomLeft, QuadrantKind::BottomRight), + }; + format!( + " {} |{}", + ToothCliFormatter::format_quadrant(notation, permanent, quadrant_1, selected_value), + ToothCliFormatter::format_quadrant(notation, permanent, quadrant_2, selected_value) + ) + } + + fn calculate_separator_len(notation: &NotationKind, permanent: bool) -> u8 { + let item_count = Tooth::quadrant_max(permanent); + let item_width = match (notation, permanent) { + (NotationKind::Iso, _) => 2, + (NotationKind::Uns, true) => 2, + (NotationKind::Uns, false) => 1, + (NotationKind::Alphanumeric, _) => 3, + }; + (item_width + 1) * (item_count * 2 + 1) + } + + pub fn format_dentition( + notation: &NotationKind, + permanent: bool, + selected_value: &Option, + ) -> String { + let jaw_len = ToothCliFormatter::calculate_separator_len(notation, permanent); + let mut result = String::with_capacity((jaw_len * 4).into()); + result.push_str(&ToothCliFormatter::format_jaw( + notation, + permanent, + JawKind::Top, + selected_value, + )); + result.push_str("\nR "); + result.push_str(&"-".repeat(jaw_len.into())); + result.push_str(" L\n"); + result.push_str(&ToothCliFormatter::format_jaw( + notation, + permanent, + JawKind::Bottom, + selected_value, + )); + + result + } +} + +impl ToothDisplay for ToothCliFormatter { + fn format(notation: &NotationKind, permanent: bool, selected_value: &Option) -> String { + ToothCliFormatter::format_dentition(notation, permanent, selected_value) + } +} diff --git a/src/display/mod.rs b/src/display/mod.rs new file mode 100644 index 0000000..6977820 --- /dev/null +++ b/src/display/mod.rs @@ -0,0 +1,13 @@ +pub use crate::display::cli::*; +use crate::lib::NotationKind; +use crate::lib::Tooth; +mod cli; + +pub enum JawKind { + Top, + Bottom, +} + +pub trait ToothDisplay { + fn format(notation: &NotationKind, permanent: bool, selected_value: &Option) -> String; +} diff --git a/src/lib.rs b/src/lib.rs index 2dab6a9..8a8e48d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub enum ToothType { Molar, } +#[derive(PartialEq)] pub struct Tooth { quadrant: QuadrantKind, number: u8,