dental-notation 1.0.0

Library converting between different tooth numbering systems
Documentation
use super::JawKind;
use crate::display::ToothDisplay;
use crate::{NotationKind, QuadrantKind, Tooth, ToothType};
use alloc::format;
use alloc::string::String;
use owo_colors::{OwoColorize, Style};

/// Formatter used to display a complete jaw in a CLI.
pub struct ToothCliFormatter;

impl ToothCliFormatter {
    fn format_tooth(
        tooth: &Tooth,
        notation: &NotationKind,
        selected_value: &Option<Tooth>,
    ) -> 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<Tooth>,
    ) -> 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<Tooth>,
    ) -> 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)
    }
}

impl ToothDisplay for ToothCliFormatter {
    fn format(notation: &NotationKind, permanent: bool, selected_value: &Option<Tooth>) -> 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
    }
}