ironcalc_base 0.7.1

Open source spreadsheet engine
Documentation
use crate::types::BorderItem;

fn parse_color(s: &str) -> Option<(u8, u8, u8)> {
    let s = s.trim_start_matches('#');
    match s.len() {
        6 => {
            let r = u8::from_str_radix(&s[0..2], 16).ok()?;
            let g = u8::from_str_radix(&s[2..4], 16).ok()?;
            let b = u8::from_str_radix(&s[4..6], 16).ok()?;
            Some((r, g, b))
        }
        3 => {
            let r = u8::from_str_radix(&s[0..1], 16).ok()?;
            let g = u8::from_str_radix(&s[1..2], 16).ok()?;
            let b = u8::from_str_radix(&s[2..3], 16).ok()?;
            // Expand single hex digits to full bytes
            Some((r * 17, g * 17, b * 17))
        }
        _ => None,
    }
}

fn compute_luminance(r: u8, g: u8, b: u8) -> f64 {
    // Normalize RGB values to [0, 1]
    let r = r as f64 / 255.0;
    let g = g as f64 / 255.0;
    let b = b as f64 / 255.0;
    // Calculate luminance using the Rec. 601 formula
    0.299 * r + 0.587 * g + 0.114 * b
}

fn is_max_color(a: &str, b: &str) -> bool {
    let (ar, ag, ab) = match parse_color(a) {
        Some(rgb) => rgb,
        None => return false, // Invalid color format for 'a'
    };

    let (br, bg, bb) = match parse_color(b) {
        Some(rgb) => rgb,
        None => return false, // Invalid color format for 'b'
    };

    let luminance_a = compute_luminance(ar, ag, ab);
    let luminance_b = compute_luminance(br, bg, bb);

    // 'b' is heavier if its luminance is less than 'a's luminance
    luminance_b < luminance_a
}

/// Is border b "heavier" than a?
pub(crate) fn is_max_border(a: Option<&BorderItem>, b: Option<&BorderItem>) -> bool {
    match (a, b) {
        (_, None) => false,
        (None, Some(_)) => true,
        (Some(item_a), Some(item_b)) => {
            if item_a.style < item_b.style {
                return true;
            } else if item_a.style > item_b.style {
                return false;
            }
            match (&item_a.color, &item_b.color) {
                (_, None) => false,
                (None, Some(_)) => true,
                (Some(color_a), Some(color_b)) => is_max_color(color_a, color_b),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::BorderStyle;

    #[test]
    fn compare_borders() {
        let b = BorderItem {
            style: BorderStyle::Thin,
            color: Some("#FFF".to_string()),
        };
        // Some border *always* beats no border
        assert!(is_max_border(None, Some(&b)));

        // No border is beaten by some border
        assert!(!is_max_border(Some(&b), None));
    }

    #[test]
    fn basic_colors() {
        // Black vs White
        assert!(is_max_color("#FFFFFF", "#000000"));
        assert!(!is_max_color("#000000", "#FFFFFF"));

        // Red vs Dark Red
        assert!(is_max_color("#FF0000", "#800000"));
        assert!(!is_max_color("#800000", "#FF0000"));

        // Green vs Dark Green
        assert!(is_max_color("#00FF00", "#008000"));
        assert!(!is_max_color("#008000", "#00FF00"));

        // Blue vs Dark Blue
        assert!(is_max_color("#0000FF", "#000080"));
        assert!(!is_max_color("#000080", "#0000FF"));
    }

    #[test]
    fn same_color() {
        // Comparing the same color should return false
        assert!(!is_max_color("#123456", "#123456"));
    }

    #[test]
    fn edge_cases() {
        // Colors with minimal luminance difference
        assert!(!is_max_color("#000000", "#010101"));
        assert!(!is_max_color("#FEFEFE", "#FFFFFF"));
        assert!(!is_max_color("#7F7F7F", "#808080"));
    }

    #[test]
    fn luminance_ordering() {
        // Colors with known luminance differences
        assert!(is_max_color("#CCCCCC", "#333333")); // Light gray vs Day
        assert!(is_max_color("#FFFF00", "#808000")); // Yellow ve
        assert!(is_max_color("#FF00FF", "#800080")); // Magenta vle
    }

    #[test]
    fn borderline_cases() {
        // Testing colors with equal luminance
        assert!(!is_max_color("#777777", "#777777"));

        // Testing black against near-black
        assert!(!is_max_color("#000000", "#010000"));
    }
}