office-rs 0.1.1

A Rust library for reading and writing XML Office files
Documentation
//! Excel单元格模块
//! 提供单元格数据类型、值、格式等功能

use crate::common::types::{ Color, FontSize };
use crate::error::{ OfficeError, Result, XlsxError };
use std::fmt;

/// 单元格数据类型
#[derive(Debug, Clone, PartialEq)]
pub enum CellType {
    /// 空单元格
    Empty,
    /// 数字
    Number,
    /// 文本
    Text,
    /// 布尔值
    Boolean,
    /// 日期时间
    DateTime,
    /// 公式
    Formula,
    /// 错误值
    Error,
}

/// 单元格值
#[derive(Debug, Clone, PartialEq)]
pub enum CellValue {
    /// 空值
    Empty,
    /// 数字值
    Number(f64),
    /// 文本值
    Text(String),
    /// 布尔值
    Boolean(bool),
    /// 日期时间(以Excel序列号表示)
    DateTime(f64),
    /// 公式
    Formula(String),
    /// 错误值
    Error(String),
}

/// 单元格引用(如A1, B2等)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CellReference {
    /// 列索引(0开始)
    pub column: u32,
    /// 行索引(0开始)
    pub row: u32,
}

/// 单元格格式
#[derive(Debug, Clone, PartialEq)]
pub struct CellFormat {
    /// 数字格式
    pub number_format: Option<String>,
    /// 字体大小
    pub font_size: Option<FontSize>,
    /// 字体颜色
    pub font_color: Option<Color>,
    /// 背景颜色
    pub background_color: Option<Color>,
    /// 是否加粗
    pub bold: bool,
    /// 是否斜体
    pub italic: bool,
    /// 是否下划线
    pub underline: bool,
}

/// Excel单元格
#[derive(Debug, Clone, PartialEq)]
pub struct Cell {
    /// 单元格引用
    pub reference: CellReference,
    /// 单元格值
    pub value: CellValue,
    /// 单元格格式
    pub format: Option<CellFormat>,
    /// 样式ID
    pub style_id: Option<u32>,
}

impl CellReference {
    /// 创建新的单元格引用
    pub fn new(column: u32, row: u32) -> Self {
        Self { column, row }
    }

    /// 从A1格式字符串解析单元格引用
    pub fn from_a1(reference: &str) -> Result<Self> {
        let reference = reference.trim().to_uppercase();

        // 分离字母和数字部分
        let mut col_str = String::new();
        let mut row_str = String::new();
        let mut in_number = false;

        for ch in reference.chars() {
            if ch.is_ascii_digit() {
                in_number = true;
                row_str.push(ch);
            } else if ch.is_ascii_alphabetic() && !in_number {
                col_str.push(ch);
            } else {
                return Err(
                    OfficeError::Xlsx(XlsxError::InvalidCellReference {
                        reference: reference.to_string(),
                    })
                );
            }
        }

        if col_str.is_empty() || row_str.is_empty() {
            return Err(
                OfficeError::Xlsx(XlsxError::InvalidCellReference {
                    reference: reference.to_string(),
                })
            );
        }

        // 解析列(A=0, B=1, ..., Z=25, AA=26, ...)
        let mut column = 0u32;
        for ch in col_str.chars() {
            column = column * 26 + ((ch as u32) - ('A' as u32) + 1);
        }
        column -= 1; // 转换为0开始的索引

        // 解析行(1开始转换为0开始)
        let row = row_str
            .parse::<u32>()
            .map_err(|_| {
                OfficeError::Xlsx(XlsxError::InvalidCellReference {
                    reference: reference.to_string(),
                })
            })?
            .saturating_sub(1);

        Ok(Self::new(column, row))
    }

    /// 转换为A1格式字符串
    pub fn to_a1(&self) -> String {
        let mut col_str = String::new();
        let mut col = self.column + 1; // 转换为1开始的索引

        while col > 0 {
            col -= 1;
            col_str.insert(0, (b'A' + ((col % 26) as u8)) as char);
            col /= 26;
        }

        format!("{}{}", col_str, self.row + 1)
    }
}

impl CellValue {
    /// 获取单元格类型
    pub fn cell_type(&self) -> CellType {
        match self {
            CellValue::Empty => CellType::Empty,
            CellValue::Number(_) => CellType::Number,
            CellValue::Text(_) => CellType::Text,
            CellValue::Boolean(_) => CellType::Boolean,
            CellValue::DateTime(_) => CellType::DateTime,
            CellValue::Formula(_) => CellType::Formula,
            CellValue::Error(_) => CellType::Error,
        }
    }

    /// 尝试转换为数字
    pub fn as_number(&self) -> Option<f64> {
        match self {
            CellValue::Number(n) => Some(*n),
            CellValue::DateTime(d) => Some(*d),
            CellValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
            CellValue::Text(s) => s.parse().ok(),
            _ => None,
        }
    }

    /// 尝试转换为文本
    pub fn as_text(&self) -> String {
        match self {
            CellValue::Empty => String::new(),
            CellValue::Number(n) => n.to_string(),
            CellValue::Text(s) => s.clone(),
            CellValue::Boolean(b) => b.to_string(),
            CellValue::DateTime(d) => d.to_string(),
            CellValue::Formula(f) => f.clone(),
            CellValue::Error(e) => e.clone(),
        }
    }

    /// 判断是否为空
    pub fn is_empty(&self) -> bool {
        matches!(self, CellValue::Empty)
    }
}

impl CellFormat {
    /// 创建默认格式
    pub fn new() -> Self {
        Self {
            number_format: None,
            font_size: None,
            font_color: None,
            background_color: None,
            bold: false,
            italic: false,
            underline: false,
        }
    }

    /// 设置数字格式
    pub fn with_number_format(mut self, format: String) -> Self {
        self.number_format = Some(format);
        self
    }

    /// 设置字体大小
    pub fn with_font_size(mut self, size: FontSize) -> Self {
        self.font_size = Some(size);
        self
    }

    /// 设置字体颜色
    pub fn with_font_color(mut self, color: Color) -> Self {
        self.font_color = Some(color);
        self
    }

    /// 设置背景颜色
    pub fn with_background_color(mut self, color: Color) -> Self {
        self.background_color = Some(color);
        self
    }

    /// 设置加粗
    pub fn with_bold(mut self, bold: bool) -> Self {
        self.bold = bold;
        self
    }

    /// 设置斜体
    pub fn with_italic(mut self, italic: bool) -> Self {
        self.italic = italic;
        self
    }

    /// 设置下划线
    pub fn with_underline(mut self, underline: bool) -> Self {
        self.underline = underline;
        self
    }
}

impl Cell {
    /// 创建新的单元格
    pub fn new(reference: CellReference, value: CellValue) -> Self {
        Self {
            reference,
            value,
            format: None,
            style_id: None,
        }
    }

    /// 创建空单元格
    pub fn empty(reference: CellReference) -> Self {
        Self::new(reference, CellValue::Empty)
    }

    /// 设置单元格值
    pub fn set_value(&mut self, value: CellValue) {
        self.value = value;
    }

    /// 设置单元格格式
    pub fn set_format(&mut self, format: CellFormat) {
        self.format = Some(format);
    }

    /// 设置样式ID
    pub fn set_style_id(&mut self, style_id: u32) {
        self.style_id = Some(style_id);
    }

    /// 获取单元格类型
    pub fn cell_type(&self) -> CellType {
        self.value.cell_type()
    }

    /// 判断是否为空
    pub fn is_empty(&self) -> bool {
        self.value.is_empty()
    }
}

impl Default for CellFormat {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Display for CellReference {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_a1())
    }
}

impl fmt::Display for CellValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_text())
    }
}

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

    #[test]
    fn test_cell_reference_a1() {
        // 测试A1格式解析
        let ref1 = CellReference::from_a1("A1").unwrap();
        assert_eq!(ref1.column, 0);
        assert_eq!(ref1.row, 0);
        assert_eq!(ref1.to_a1(), "A1");

        let ref2 = CellReference::from_a1("Z26").unwrap();
        assert_eq!(ref2.column, 25);
        assert_eq!(ref2.row, 25);
        assert_eq!(ref2.to_a1(), "Z26");

        let ref3 = CellReference::from_a1("AA1").unwrap();
        assert_eq!(ref3.column, 26);
        assert_eq!(ref3.row, 0);
        assert_eq!(ref3.to_a1(), "AA1");
    }

    #[test]
    fn test_cell_value_conversion() {
        let val = CellValue::Number(42.5);
        assert_eq!(val.as_number(), Some(42.5));
        assert_eq!(val.as_text(), "42.5");

        let val = CellValue::Text("Hello".to_string());
        assert_eq!(val.as_text(), "Hello");
        assert_eq!(val.as_number(), None);
    }
}