Skip to main content

umya_spreadsheet/structs/
numbering_format.rs

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