Skip to main content

umya_spreadsheet/structs/
numbering_format.rs

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