office-rs 0.1.1

A Rust library for reading and writing XML Office files
Documentation
use super::*;
use crate::common::{ xml_utils::*, namespaces::excel };
use crate::error::Result;

impl CellStyle {
    /// 将样式转换为XML元素
    pub fn to_xml(&self) -> XmlElement {
        let mut style = XmlElement::new("xf");

        // 添加字体引用
        if self.font.is_some() {
            style.add_attribute("applyFont", "1");
            // fontId将由StylesManager设置
        }

        // 添加填充引用
        if self.fill.is_some() {
            style.add_attribute("applyFill", "1");
            // fillId将由StylesManager设置
        }

        // 添加边框引用
        if self.border.is_some() {
            style.add_attribute("applyBorder", "1");
            // borderId将由StylesManager设置
        }

        // 添加对齐属性
        if let Some(alignment) = &self.alignment {
            style.add_attribute("applyAlignment", "1");
            let align_elem = alignment_to_xml(alignment);
            style.add_child(align_elem);
        }

        // 添加数字格式引用
        if self.number_format.is_some() {
            style.add_attribute("applyNumberFormat", "1");
            // numFmtId将由StylesManager设置
        }

        // 添加保护属性
        if let Some(protection) = &self.protection {
            style.add_attribute("applyProtection", "1");
            let prot_elem = protection_to_xml(protection);
            style.add_child(prot_elem);
        }

        style
    }
}

impl Font {
    /// 将字体转换为XML元素
    pub fn to_xml(&self) -> XmlElement {
        let mut font = XmlElement::new("font");

        // 字体名称
        let mut name = XmlElement::new("name");
        name.add_attribute("val", &self.name);
        font.add_child(name);

        // 字体大小
        let mut size = XmlElement::new("sz");
        size.add_attribute("val", &self.size.to_string());
        font.add_child(size);

        // 粗体
        if self.bold {
            font.add_child(XmlElement::new("b"));
        }

        // 斜体
        if self.italic {
            font.add_child(XmlElement::new("i"));
        }

        // 下划线
        if let Some(underline) = &self.underline {
            let mut u = XmlElement::new("u");
            match underline {
                UnderlineStyle::Single => u.add_attribute("val", "single"),
                UnderlineStyle::Double => u.add_attribute("val", "double"),
                UnderlineStyle::SingleAccounting => u.add_attribute("val", "singleAccounting"),
                UnderlineStyle::DoubleAccounting => u.add_attribute("val", "doubleAccounting"),
            }
            font.add_child(u);
        }

        // 颜色
        if let Some(color) = &self.color {
            let mut clr = XmlElement::new("color");
            clr.add_attribute("rgb", &color.rgb);
            font.add_child(clr);
        }

        font
    }
}

impl Border {
    /// 将边框转换为XML元素
    pub fn to_xml(&self) -> XmlElement {
        let mut border = XmlElement::new("border");

        // 左边框
        let left = border_style_to_xml("left", &self.left);
        border.add_child(left);

        // 右边框
        let right = border_style_to_xml("right", &self.right);
        border.add_child(right);

        // 上边框
        let top = border_style_to_xml("top", &self.top);
        border.add_child(top);

        // 下边框
        let bottom = border_style_to_xml("bottom", &self.bottom);
        border.add_child(bottom);

        // 对角线边框
        let diagonal = border_style_to_xml("diagonal", &self.diagonal);
        border.add_child(diagonal);

        border
    }
}

impl Fill {
    /// 将填充转换为XML元素
    pub fn to_xml(&self) -> XmlElement {
        let mut fill = XmlElement::new("fill");
        let mut pattern_fill = XmlElement::new("patternFill");

        // 填充类型
        match self.pattern_type {
            PatternType::None => pattern_fill.add_attribute("patternType", "none"),
            PatternType::Solid => pattern_fill.add_attribute("patternType", "solid"),
            PatternType::MediumGray => pattern_fill.add_attribute("patternType", "mediumGray"),
            PatternType::DarkGray => pattern_fill.add_attribute("patternType", "darkGray"),
            PatternType::LightGray => pattern_fill.add_attribute("patternType", "lightGray"),
        }

        // 前景色
        if let Some(fg_color) = &self.fg_color {
            let mut fgColor = XmlElement::new("fgColor");
            fgColor.add_attribute("rgb", &fg_color.rgb);
            pattern_fill.add_child(fgColor);
        }

        // 背景色
        if let Some(bg_color) = &self.bg_color {
            let mut bgColor = XmlElement::new("bgColor");
            bgColor.add_attribute("rgb", &bg_color.rgb);
            pattern_fill.add_child(bgColor);
        }

        fill.add_child(pattern_fill);
        fill
    }
}

impl StylesManager {
    /// 生成完整的styles.xml内容
    pub fn generate_styles_xml(&self) -> Result<String> {
        let mut root = XmlElement::new("styleSheet");
        root.add_attribute("xmlns", excel::STYLES);

        // 收集所有字体、填充和边框
        let (fonts, fills, borders) = self.collect_style_components();

        // 添加字体集合
        let mut fonts_elem = XmlElement::new("fonts");
        fonts_elem.add_attribute("count", &fonts.len().to_string());
        for font in fonts {
            fonts_elem.add_child(font.to_xml());
        }
        root.add_child(fonts_elem);

        // 添加填充集合
        let mut fills_elem = XmlElement::new("fills");
        fills_elem.add_attribute("count", &fills.len().to_string());
        for fill in fills {
            fills_elem.add_child(fill.to_xml());
        }
        root.add_child(fills_elem);

        // 添加边框集合
        let mut borders_elem = XmlElement::new("borders");
        borders_elem.add_attribute("count", &borders.len().to_string());
        for border in borders {
            borders_elem.add_child(border.to_xml());
        }
        root.add_child(borders_elem);

        // 添加单元格样式
        let mut cell_styles = XmlElement::new("cellStyles");
        cell_styles.add_attribute("count", &self.styles.len().to_string());
        for style in &self.styles {
            cell_styles.add_child(style.to_xml());
        }
        root.add_child(cell_styles);

        // 生成XML字符串
        let generator = XmlGenerator::new();
        generator.generate_string(&root)
    }

    // 收集所有样式组件
    fn collect_style_components(&self) -> (Vec<&Font>, Vec<&Fill>, Vec<&Border>) {
        let mut fonts = Vec::new();
        let mut fills = Vec::new();
        let mut borders = Vec::new();

        for style in &self.styles {
            if let Some(font) = &style.font {
                if !fonts.contains(&font) {
                    fonts.push(font);
                }
            }
            if let Some(fill) = &style.fill {
                if !fills.contains(&fill) {
                    fills.push(fill);
                }
            }
            if let Some(border) = &style.border {
                if !borders.contains(&border) {
                    borders.push(border);
                }
            }
        }

        (fonts, fills, borders)
    }
}

// 辅助函数

fn alignment_to_xml(alignment: &Alignment) -> XmlElement {
    let mut elem = XmlElement::new("alignment");

    // 水平对齐
    let horizontal = match alignment.horizontal {
        HorizontalAlignment::Left => "left",
        HorizontalAlignment::Center => "center",
        HorizontalAlignment::Right => "right",
        HorizontalAlignment::Fill => "fill",
        HorizontalAlignment::Justify => "justify",
        HorizontalAlignment::CenterContinuous => "centerContinuous",
        HorizontalAlignment::Distributed => "distributed",
    };
    elem.add_attribute("horizontal", horizontal);

    // 垂直对齐
    let vertical = match alignment.vertical {
        VerticalAlignment::Top => "top",
        VerticalAlignment::Center => "center",
        VerticalAlignment::Bottom => "bottom",
        VerticalAlignment::Justify => "justify",
        VerticalAlignment::Distributed => "distributed",
    };
    elem.add_attribute("vertical", vertical);

    // 文本换行
    if alignment.wrap_text {
        elem.add_attribute("wrapText", "1");
    }

    // 文本旋转
    if alignment.text_rotation != 0 {
        elem.add_attribute("textRotation", &alignment.text_rotation.to_string());
    }

    // 缩进
    if alignment.indent > 0 {
        elem.add_attribute("indent", &alignment.indent.to_string());
    }

    elem
}

fn protection_to_xml(protection: &Protection) -> XmlElement {
    let mut elem = XmlElement::new("protection");

    if protection.locked {
        elem.add_attribute("locked", "1");
    }
    if protection.hidden {
        elem.add_attribute("hidden", "1");
    }

    elem
}

fn border_style_to_xml(name: &str, style: &BorderStyle) -> XmlElement {
    let mut elem = XmlElement::new(name);

    if let Some(line_style) = &style.style {
        let style_str = match line_style {
            LineStyle::None => "none",
            LineStyle::Thin => "thin",
            LineStyle::Medium => "medium",
            LineStyle::Thick => "thick",
            LineStyle::Double => "double",
            LineStyle::Dotted => "dotted",
            LineStyle::Dashed => "dashed",
        };
        elem.add_attribute("style", style_str);
    }

    if let Some(color) = &style.color {
        let mut color_elem = XmlElement::new("color");
        color_elem.add_attribute("rgb", &color.rgb);
        elem.add_child(color_elem);
    }

    elem
}

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

    #[test]
    fn test_font_xml() {
        let font = Font {
            name: "Arial".to_string(),
            size: 12.0,
            bold: true,
            italic: false,
            underline: Some(UnderlineStyle::Single),
            color: Some(Color::from_rgb(0, 0, 0)),
        };

        let xml = font.to_xml();
        let generator = XmlGenerator::new();
        let xml_str = generator.generate_string(&xml).unwrap();

        assert!(xml_str.contains("Arial"));
        assert!(xml_str.contains("12"));
        assert!(xml_str.contains("<b"));
        assert!(xml_str.contains("FF000000"));
    }

    #[test]
    fn test_styles_xml() {
        let mut manager = StylesManager::new();
        manager.add_style(CellStyle::default_header());
        manager.add_style(CellStyle::default_body());

        let xml = manager.generate_styles_xml().unwrap();
        assert!(xml.contains("styleSheet"));
        assert!(xml.contains("fonts"));
        assert!(xml.contains("fills"));
        assert!(xml.contains("borders"));
        assert!(xml.contains("cellStyles"));
    }
}