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()),
}
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[repr(u8)]
pub enum Builtin {
Index0 = 0u8,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9,
Index10,
Index11,
Index12,
Index13,
Index14,
Index15,
Index16,
Index17,
Index18,
Index19,
Index20,
Index21,
Index22,
Index37 = 37u8,
Index38,
Index39,
Index40,
Index41,
Index42,
Index43,
Index44,
Index45,
Index46,
Index47,
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<String>,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct NumFormats {
inner: Rc<RefCell<Inner>>,
}
impl NumFormats {
pub fn create_or_get_index(
&mut self,
num_format: Option<&NumFormat>,
) -> Option<usize> {
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<W: XmlWriter>(&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<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
self.inner.borrow().write_xml(w)
}
}