#![warn(missing_docs)]
use std::collections::BTreeMap;
use std::io::Cursor;
use crate::xmlwriter::{
xml_data_element, xml_data_element_only, xml_declaration, xml_empty_tag, xml_empty_tag_only,
xml_end_tag, xml_start_tag, xml_start_tag_only,
};
use crate::{utility, ColNum, Note, RowNum};
pub struct Comment {
pub(crate) writer: Cursor<Vec<u8>>,
pub(crate) notes: BTreeMap<RowNum, BTreeMap<ColNum, Note>>,
pub(crate) note_authors: Vec<String>,
}
impl Comment {
pub(crate) fn new() -> Comment {
let writer = Cursor::new(Vec::with_capacity(2048));
Comment {
writer,
notes: BTreeMap::new(),
note_authors: vec![],
}
}
pub(crate) fn assemble_xml_file(&mut self) {
xml_declaration(&mut self.writer);
self.write_comments();
self.write_authors();
self.write_comment_list();
xml_end_tag(&mut self.writer, "comments");
}
fn write_comments(&mut self) {
let attributes = [(
"xmlns",
"http://schemas.openxmlformats.org/spreadsheetml/2006/main",
)];
xml_start_tag(&mut self.writer, "comments", &attributes);
}
fn write_authors(&mut self) {
if self.note_authors.is_empty() {
return;
}
xml_start_tag_only(&mut self.writer, "authors");
for author in &self.note_authors.clone() {
self.write_author(author);
}
xml_end_tag(&mut self.writer, "authors");
}
fn write_author(&mut self, author: &str) {
xml_data_element_only(&mut self.writer, "author", author);
}
fn write_comment_list(&mut self) {
xml_start_tag_only(&mut self.writer, "commentList");
for (row, columns) in &self.notes.clone() {
for (col, note) in columns {
self.write_comment(*row, *col, note);
}
}
xml_end_tag(&mut self.writer, "commentList");
}
fn write_comment(&mut self, row: RowNum, col: ColNum, note: &Note) {
let cell = utility::row_col_to_cell(row, col);
let attributes = vec![("ref", cell), ("authorId", note.author_id.to_string())];
xml_start_tag(&mut self.writer, "comment", &attributes);
self.write_text_block(note);
xml_end_tag(&mut self.writer, "comment");
}
fn write_text_block(&mut self, note: &Note) {
xml_start_tag_only(&mut self.writer, "text");
if note.has_author_prefix {
xml_start_tag_only(&mut self.writer, "r");
self.write_paragraph_run(note, true);
let author = match &self.note_authors.get(note.author_id) {
Some(author) => format!("{author}:"),
None => "Author:".to_string(),
};
self.write_text(&author);
xml_end_tag(&mut self.writer, "r");
xml_start_tag_only(&mut self.writer, "r");
self.write_paragraph_run(note, false);
let text = format!("\n{}", note.text);
self.write_text(&text);
xml_end_tag(&mut self.writer, "r");
} else {
xml_start_tag_only(&mut self.writer, "r");
self.write_paragraph_run(note, false);
self.write_text(¬e.text);
xml_end_tag(&mut self.writer, "r");
}
xml_end_tag(&mut self.writer, "text");
}
fn write_paragraph_run(&mut self, note: &Note, has_bold: bool) {
xml_start_tag_only(&mut self.writer, "rPr");
if has_bold {
xml_empty_tag_only(&mut self.writer, "b");
}
self.write_font_size(note);
self.write_font_color();
self.write_font_name(note);
self.write_font_family(note);
xml_end_tag(&mut self.writer, "rPr");
}
fn write_font_size(&mut self, note: &Note) {
let attributes = [("val", note.format.font.size.clone())];
xml_empty_tag(&mut self.writer, "sz", &attributes);
}
fn write_font_color(&mut self) {
let attributes = [("indexed", "81".to_string())];
xml_empty_tag(&mut self.writer, "color", &attributes);
}
fn write_font_name(&mut self, note: &Note) {
let attributes = [("val", note.format.font.name.clone())];
xml_empty_tag(&mut self.writer, "rFont", &attributes);
}
fn write_font_family(&mut self, note: &Note) {
let attributes = [("val", note.format.font.family.to_string())];
xml_empty_tag(&mut self.writer, "family", &attributes);
}
fn write_text(&mut self, text: &str) {
let whitespace = ['\t', '\n', ' '];
let attributes = if text.starts_with(whitespace) || text.ends_with(whitespace) {
vec![("xml:space", "preserve")]
} else {
vec![]
};
xml_data_element(&mut self.writer, "t", text, &attributes);
}
}