use crate::{Result, XmlWritable, XmlWriter}; use indexmap::{indexmap, IndexSet}; use std::cell::RefCell; use std::rc::Rc; const CUSTOM_FORMAT_OFFSET: usize = 164usize; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub enum NumFormat { Builtin(Builtin), Custom(String), } impl Default for NumFormat { fn default() -> Self { NumFormat::Builtin(Default::default()) } } impl NumFormat { pub fn to_builtin_if_possible(self) -> Self { match self { NumFormat::Builtin(_) => self, NumFormat::Custom(s) => Self::from_format_string(&s), } } pub fn from_format_string(s: &str) -> Self { use NumFormat::{Builtin as B, Custom as C}; match s { "" => B(Builtin::Index0), "0" => B(Builtin::Index1), "0.00" => B(Builtin::Index2), "#,##0" => B(Builtin::Index3), "#,##0.00" => B(Builtin::Index4), "($#,##0_);($#,##0)" => B(Builtin::Index5), "($#,##0_);[Red]($#,##0)" => B(Builtin::Index6), "($#,##0.00_);($#,##0.00)" => B(Builtin::Index7), "($#,##0.00_);[Red]($#,##0.00)" => B(Builtin::Index8), "0%" => B(Builtin::Index9), "0.00%" => B(Builtin::Index10), "0.00E+00" => B(Builtin::Index11), "# ?/?" => B(Builtin::Index12), "# ??/??" => B(Builtin::Index13), "m/d/yy" => B(Builtin::Index14), "d-mmm-yy" => B(Builtin::Index15), "d-mmm" => B(Builtin::Index16), "mmm-yy" => B(Builtin::Index17), "h:mm AM/PM" => B(Builtin::Index18), "h:mm:ss AM/PM" => B(Builtin::Index19), "h:mm" => B(Builtin::Index20), "h:mm:ss" => B(Builtin::Index21), "m/d/yy h:mm" => B(Builtin::Index22), "(#,##0_);(#,##0)" => B(Builtin::Index37), "(#,##0_);[Red](#,##0)" => B(Builtin::Index38), "(#,##0.00_);(#,##0.00)" => B(Builtin::Index39), "(#,##0.00_);[Red](#,##0.00)" => B(Builtin::Index40), "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)" => { B(Builtin::Index41) } "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)" => { B(Builtin::Index42) } "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)" => { B(Builtin::Index43) } "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)" => { B(Builtin::Index44) } "mm:ss" => B(Builtin::Index45), "[h]:mm:ss" => B(Builtin::Index46), "mm:ss.0" => B(Builtin::Index47), "##0.0E+0" => B(Builtin::Index48), "@" => B(Builtin::Index49), s => C(s.to_string()), } } } /// Excel built-in number formats. /// /// * Numeric formats 23 to 36 are not documented by Microsoft and my /// differ in international versions. The listed date and currency /// formats may also vary depending on system settings. /// * The dollar sign in the above format appears as the defined local /// currency symbol. /// * These formats can also be set via `Format::set_num_format()`. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[repr(u8)] pub enum Builtin { /// General Index0 = 0u8, /// `0` Index1, /// `0.00` Index2, /// `#,##0` Index3, /// `#,##0.00` Index4, /// `($#,##0_);($#,##0)` Index5, /// `($#,##0_);[Red]($#,##0)` Index6, /// `($#,##0.00_);($#,##0.00)` Index7, /// `($#,##0.00_);[Red]($#,##0.00)` Index8, /// `0%` Index9, /// `0.00%` Index10, /// `0.00E+00` Index11, /// `# ?/?` Index12, /// `# ??/??` Index13, /// `m/d/yy` Index14, /// `d-mmm-yy` Index15, /// `d-mmm` Index16, /// `mmm-yy` Index17, /// `h:mm AM/PM` Index18, /// `h:mm:ss AM/PM` Index19, /// `h:mm` Index20, /// `h:mm:ss` Index21, /// `m/d/yy h:mm` Index22, /// `(#,##0_);(#,##0)` Index37 = 37u8, /// `(#,##0_);[Red](#,##0)` Index38, /// `(#,##0.00_);(#,##0.00)` Index39, /// `(#,##0.00_);[Red](#,##0.00)` Index40, /// `_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)` Index41, /// `_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)` Index42, /// `_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)` Index43, /// `_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)` Index44, /// `mm:ss` Index45, /// `[h]:mm:ss` Index46, /// `mm:ss.0` Index47, /// `##0.0E+0` Index48, /// `@` Index49, } impl Default for Builtin { fn default() -> Self { Builtin::Index0 } } impl Builtin { pub fn get_builtin_index(&self) -> usize { *self as usize } } #[derive(Default, Debug)] struct Inner { num_formats: IndexSet, } #[derive(Clone, Default, Debug)] pub(crate) struct NumFormats { inner: Rc>, } impl NumFormats { pub fn create_or_get_index( &mut self, num_format: Option<&NumFormat>, ) -> Option { let mut inner = self.inner.borrow_mut(); num_format.map(|f| match f { NumFormat::Builtin(f) => f.get_builtin_index(), NumFormat::Custom(s) => { inner.num_formats.insert_full(s.clone()).0 + CUSTOM_FORMAT_OFFSET } }) } } impl XmlWritable for Inner { fn write_xml(&self, w: &mut W) -> Result<()> { if !self.num_formats.is_empty() { let tag = "numFmts"; let attrs = indexmap! { "count" => format!("{}", self.num_formats.len()) }; w.start_tag_with_attrs(tag, attrs)?; for (index, format) in self.num_formats.iter().enumerate() { let attrs = indexmap! { "numFmtId" => format!("{}", index + CUSTOM_FORMAT_OFFSET), "formatCode" => format.replace("\"", """) }; w.empty_tag_with_attrs("numFmt", attrs)?; } w.end_tag(tag)?; } Ok(()) } } impl XmlWritable for NumFormats { fn write_xml(&self, w: &mut W) -> Result<()> { self.inner.borrow().write_xml(w) } }