umya_spreadsheet/structs/
numbering_format.rs

1use crate::reader::driver::*;
2use crate::writer::driver::*;
3use md5::Digest;
4use quick_xml::escape;
5use quick_xml::events::BytesStart;
6use quick_xml::Reader;
7use quick_xml::Writer;
8use std::collections::HashMap;
9use std::io::Cursor;
10
11#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
12pub struct NumberingFormat {
13    number_format_id: u32,
14    format_code: Box<str>,
15    is_build_in: bool,
16}
17
18impl Default for NumberingFormat {
19    #[inline]
20    fn default() -> Self {
21        Self {
22            number_format_id: 0,
23            format_code: NumberingFormat::FORMAT_GENERAL.into(),
24            is_build_in: true,
25        }
26    }
27}
28
29impl NumberingFormat {
30    // Pre-defined formats
31    pub const FORMAT_GENERAL: &'static str = "General";
32
33    pub const FORMAT_TEXT: &'static str = "@";
34
35    pub const FORMAT_NUMBER: &'static str = "0";
36    pub const FORMAT_NUMBER_00: &'static str = "0.00";
37    pub const FORMAT_NUMBER_COMMA_SEPARATED1: &'static str = "#,##0.00";
38    pub const FORMAT_NUMBER_COMMA_SEPARATED2: &'static str = "#,##0.00_-";
39
40    pub const FORMAT_PERCENTAGE: &'static str = "0%";
41    pub const FORMAT_PERCENTAGE_00: &'static str = "0.00%";
42
43    pub const FORMAT_DATE_YYYYMMDD2: &'static str = "yyyy-mm-dd";
44    pub const FORMAT_DATE_YYYYMMDD: &'static str = "yyyy-mm-dd";
45    pub const FORMAT_DATE_DDMMYYYY: &'static str = "dd-mm-yyyy";
46    pub const FORMAT_DATE_DDMMYYYYSLASH: &'static str = "dd/mm/yyyy";
47    pub const FORMAT_DATE_DMYSLASH: &'static str = "d/m/yy";
48    pub const FORMAT_DATE_DMYMINUS: &'static str = "d-m-yy";
49    pub const FORMAT_DATE_DMMINUS: &'static str = "d-m";
50    pub const FORMAT_DATE_MYMINUS: &'static str = "m-yy";
51    pub const FORMAT_DATE_XLSX14: &'static str = "mm-dd-yy";
52    pub const FORMAT_DATE_XLSX15: &'static str = "d-mmm-yy";
53    pub const FORMAT_DATE_XLSX16: &'static str = "d-mmm";
54    pub const FORMAT_DATE_XLSX17: &'static str = "mmm-yy";
55    pub const FORMAT_DATE_XLSX22: &'static str = "m/d/yy h:mm";
56    pub const FORMAT_DATE_DATETIME: &'static str = "d/m/yy h:mm";
57    pub const FORMAT_DATE_TIME1: &'static str = "h:mm AM/PM";
58    pub const FORMAT_DATE_TIME2: &'static str = "h:mm:ss AM/PM";
59    pub const FORMAT_DATE_TIME3: &'static str = "h:mm";
60    pub const FORMAT_DATE_TIME4: &'static str = "h:mm:ss";
61    pub const FORMAT_DATE_TIME5: &'static str = "mm:ss";
62    pub const FORMAT_DATE_TIME6: &'static str = "h:mm:ss";
63    pub const FORMAT_DATE_TIME8: &'static str = "h:mm:ss;@";
64    pub const FORMAT_DATE_YYYYMMDDSLASH: &'static str = "yyyy/mm/dd;@";
65
66    pub const FORMAT_CURRENCY_USD_SIMPLE: &'static str = r##""$"#,##0.00_-"##;
67    pub const FORMAT_CURRENCY_USD: &'static str = r###"$#,##0_-"###;
68    pub const FORMAT_CURRENCY_EUR_SIMPLE: &'static str = r#"#,##0.00_-"€""#;
69    pub const FORMAT_CURRENCY_EUR: &'static str = r#"#,##0_-"€""#;
70    pub const FORMAT_ACCOUNTING_USD: &'static str =
71        r#"_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)"#;
72    pub const FORMAT_ACCOUNTING_EUR: &'static str =
73        r#"_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)"#;
74
75    #[inline]
76    pub fn get_number_format_id(&self) -> &u32 {
77        &self.number_format_id
78    }
79
80    pub fn set_number_format_id(&mut self, value: u32) -> &mut Self {
81        let format_code_result = FILL_BUILT_IN_FORMAT_CODES.iter().find_map(|(key, val)| {
82            if key == &value {
83                Some(val.clone())
84            } else {
85                None
86            }
87        });
88
89        self.format_code = format_code_result
90            .expect("Not Found NumberFormatId.")
91            .into_boxed_str();
92        self.number_format_id = value;
93        self.is_build_in = true;
94        self
95    }
96
97    #[inline]
98    pub(crate) fn set_number_format_id_crate(&mut self, value: u32) -> &mut Self {
99        self.number_format_id = value;
100        self
101    }
102
103    /// Set the format code.
104    /// # Arguments
105    /// * `value` - format code. (umya_spreadsheet::NumberingFormat)
106    /// # Examples
107    /// ```
108    /// let mut book = umya_spreadsheet::new_file();
109    /// let mut worksheet = book.get_sheet_mut(&0).unwrap();
110    /// let _ = worksheet.get_style_mut("C30")
111    /// .get_number_format_mut()
112    /// .set_format_code(umya_spreadsheet::NumberingFormat::FORMAT_DATE_XLSX17);
113    /// ```
114    pub fn set_format_code<S: Into<String>>(&mut self, value: S) -> &mut Self {
115        self.format_code = value.into().into_boxed_str();
116        for (index, format) in FILL_BUILT_IN_FORMAT_CODES.iter() {
117            if &*self.format_code == format {
118                self.number_format_id = *index;
119                self.is_build_in = true;
120                return self;
121            }
122        }
123        self.number_format_id = 999999;
124        self.is_build_in = false;
125        self
126    }
127
128    #[inline]
129    pub(crate) fn set_format_code_crate<S: Into<String>>(&mut self, value: S) -> &mut Self {
130        self.format_code = value.into().into_boxed_str();
131        self
132    }
133
134    #[inline]
135    pub fn get_format_code(&self) -> &str {
136        &self.format_code
137    }
138
139    #[inline]
140    pub(crate) fn get_is_build_in(&self) -> &bool {
141        &self.is_build_in
142    }
143
144    #[inline]
145    pub(crate) fn get_hash_code(&self) -> String {
146        format!("{:x}", md5::Md5::digest(&*self.format_code))
147    }
148
149    pub(crate) fn set_attributes<R: std::io::BufRead>(
150        &mut self,
151        _reader: &mut Reader<R>,
152        e: &BytesStart,
153    ) {
154        self.number_format_id = get_attribute(e, b"numFmtId")
155            .unwrap()
156            .parse::<u32>()
157            .unwrap();
158        self.format_code = escape::unescape(get_attribute(e, b"formatCode").unwrap().as_str())
159            .unwrap()
160            .to_string()
161            .into_boxed_str();
162        self.is_build_in = false;
163    }
164
165    pub(crate) fn write_to(&self, writer: &mut Writer<Cursor<Vec<u8>>>, number_format_id: &u32) {
166        // numFmt
167        write_start_tag(
168            writer,
169            "numFmt",
170            vec![
171                ("numFmtId", &number_format_id.to_string()),
172                ("formatCode", &self.format_code),
173            ],
174            true,
175        );
176    }
177}
178
179lazy_static! {
180    pub(crate) static ref FILL_BUILT_IN_FORMAT_CODES: HashMap<u32, String> = {
181        let mut map:HashMap<u32, String> = HashMap::new();
182        // General
183        map.insert(0, NumberingFormat::FORMAT_GENERAL.to_string());
184        map.insert(1, "0".to_string());
185        map.insert(2, "0.00".to_string());
186        map.insert(3, "#,##0".to_string());
187        map.insert(4, "#,##0.00".to_string());
188
189        map.insert(9, "0%".to_string());
190        map.insert(10, "0.00%".to_string());
191        map.insert(11, "0.00E+00".to_string());
192        map.insert(12, "# ?/?".to_string());
193        map.insert(13, "# ??/??".to_string());
194        map.insert(14, "m/d/yyyy".to_string()); // Despite ECMA 'mm-dd-yy");
195        map.insert(15, "d-mmm-yy".to_string());
196        map.insert(16, "d-mmm".to_string());
197        map.insert(17, "mmm-yy".to_string());
198        map.insert(18, "h:mm AM/PM".to_string());
199        map.insert(19, "h:mm:ss AM/PM".to_string());
200        map.insert(20, "h:mm".to_string());
201        map.insert(21, "h:mm:ss".to_string());
202        map.insert(22, "m/d/yyyy h:mm".to_string()); // Despite ECMA 'm/d/yy h:mm");
203
204        map.insert(37, "#,##0_);(#,##0)".to_string()); //  Despite ECMA '#,##0 ;(#,##0)");
205        map.insert(38, "#,##0_);[Red](#,##0)".to_string()); //  Despite ECMA '#,##0 ;[Red](#,##0)");
206        map.insert(39, "#,##0.00_);(#,##0.00)".to_string()); //  Despite ECMA '#,##0.00;(#,##0.00)");
207        map.insert(40, "#,##0.00_);[Red](#,##0.00)".to_string()); //  Despite ECMA '#,##0.00;[Red](#,##0.00)");
208
209        map.insert(44, r#"_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)"#.to_string());
210        map.insert(45, "mm:ss".to_string());
211        map.insert(46, "[h]:mm:ss".to_string());
212        map.insert(47, "mm:ss.0".to_string()); //  Despite ECMA 'mmss.0");
213        map.insert(48, "##0.0E+0".to_string());
214        map.insert(49, "@".to_string());
215
216        // CHT
217        map.insert(27, "[$-404]e/m/d".to_string());
218        map.insert(30, "m/d/yy".to_string());
219        map.insert(36, "[$-404]e/m/d".to_string());
220        map.insert(50, "[$-404]e/m/d".to_string());
221        map.insert(57, "[$-404]e/m/d".to_string());
222
223        // THA
224        map.insert(59, "t0".to_string());
225        map.insert(60, "t0.00".to_string());
226        map.insert(61, "t#,##0".to_string());
227        map.insert(62, "t#,##0.00".to_string());
228        map.insert(67, "t0%".to_string());
229        map.insert(68, "t0.00%".to_string());
230        map.insert(69, "t# ?/?".to_string());
231        map.insert(70, "t# ??/??".to_string());
232
233        // JPN
234        map.insert(28, r#"[$-411]ggge"年"m"月"d"日""#.to_string());
235        map.insert(29, r#"[$-411]ggge"年"m"月"d"日""#.to_string());
236        map.insert(31, r#"yyyy"年"m"月"d"日""#.to_string());
237        map.insert(32, r#"h"時"mm"分""#.to_string());
238        map.insert(33, r#"h"時"mm"分"ss"秒""#.to_string());
239        map.insert(34, r#"yyyy"年"m"月""#.to_string());
240        map.insert(35, r#"m"月"d"日""#.to_string());
241        map.insert(51, r#"[$-411]ggge"年"m"月"d"日""#.to_string());
242        map.insert(52, r#"yyyy"年"m"月""#.to_string());
243        map.insert(53, r#"m"月"d"日""#.to_string());
244        map.insert(54, r#"[$-411]ggge"年"m"月"d"日""#.to_string());
245        map.insert(55, r#"yyyy"年"m"月""#.to_string());
246        map.insert(56, r#"m"月"d"日""#.to_string());
247        map.insert(58, r#"[$-411]ggge"年"m"月"d"日""#.to_string());
248
249        map
250    };
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn set_number_format_id() {
259        let mut obj = NumberingFormat::default();
260
261        obj.set_number_format_id(0);
262        assert_eq!(obj.get_format_code(), "General");
263
264        obj.set_number_format_id(1);
265        assert_eq!(obj.get_format_code(), "0");
266    }
267}