#[cfg(test)]
mod tests;
use crate::{Result, XmlWritable, XmlWriter};
use indexmap::{indexmap, IndexMap, IndexSet};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Default)]
struct Inner {
count: usize,
strings: IndexSet<SharedString>,
}
#[derive(Clone, Default)]
pub(crate) struct SharedStrings {
inner: Rc<RefCell<Inner>>,
}
impl SharedStrings {
pub fn create_or_get_index<S: AsRef<str>>(
&mut self,
string: S,
rich: bool,
) -> usize {
let mut inner = self.inner.borrow_mut();
inner.count += 1;
inner
.strings
.insert_full(SharedString {
string: string.as_ref().to_string(),
rich,
})
.0
}
pub fn is_empty(&self) -> bool {
self.inner.borrow().strings.is_empty()
}
}
#[derive(PartialEq, Eq, Hash)]
struct SharedString {
pub string: String,
pub rich: bool,
}
impl XmlWritable for SharedStrings {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
self.inner.borrow().write_xml(w)
}
}
impl XmlWritable for Inner {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
let tag = "sst";
let attrs = indexmap! {
"xmlns" =>
"http://schemas.openxmlformats.org/spreadsheetml/2006/main".to_string(),
"count" => format!("{}", self.count),
"uniqueCount" => format!("{}", self.strings.len())
};
w.start_tag_with_attrs(tag, attrs)?;
for string in self.strings.iter() {
string.write_xml(w)?;
}
w.end_tag(tag)?;
Ok(())
}
}
impl XmlWritable for SharedString {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
let tag = "si";
w.start_tag(tag)?;
if self.rich {
w.text(&self.string)?;
} else {
let tag = "t";
let string = Self::escape_control_chars(&self.string);
let mut attrs = IndexMap::new();
match (string.chars().next(), string.chars().rev().next()) {
(Some(c), _) | (_, Some(c)) if c.is_ascii_whitespace() => {
attrs.insert("xml:space", "preserve");
}
_ => {}
}
w.tag_with_attrs_and_text(tag, attrs, &string)?;
}
w.end_tag(tag)?;
Ok(())
}
}
impl SharedString {
fn escape_control_chars(s: &str) -> String {
let mut result = String::new();
for c in s.chars() {
match c {
'\x01' | '\x02' | '\x03' | '\x04' | '\x05' | '\x06'
| '\x07' | '\x08' | '\x0B' | '\x0C' | '\x0D' | '\x0E'
| '\x0F' | '\x10' | '\x11' | '\x12' | '\x13' | '\x14'
| '\x15' | '\x16' | '\x17' | '\x18' | '\x19' | '\x1A'
| '\x1B' | '\x1C' | '\x1D' | '\x1E' | '\x1F' => {
result.push_str(&format!("_x{:04X}_", c as u32));
}
_ => result.push(c),
}
}
result
}
}