use crate::oxml::ns::{NS_DRAWING_MAIN, NS_DRAWING_RELS, NS_PRESENTATION_MAIN};
use crate::oxml::writer::XmlWriter;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Comment {
pub author_id: u32,
pub date_time: String,
pub idx: u32,
pub pos_x: i64,
pub pos_y: i64,
pub text: String,
}
impl Comment {
pub fn new(author_id: u32, idx: u32, pos_x: i64, pos_y: i64, text: impl Into<String>) -> Self {
Comment {
author_id,
date_time: String::new(),
idx,
pos_x,
pos_y,
text: text.into(),
}
}
pub fn write_xml(&self, w: &mut XmlWriter) {
w.open_with(
"p:cm",
&[
("authorId", &self.author_id.to_string()),
("dt", &self.date_time),
("idx", &self.idx.to_string()),
],
);
w.empty_with(
"p:pos",
&[
("x", &self.pos_x.to_string()),
("y", &self.pos_y.to_string()),
],
);
w.open("p:text");
w.text(&self.text);
w.close("p:text");
w.close("p:cm");
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CommentList {
pub comments: Vec<Comment>,
}
impl CommentList {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.comments.is_empty()
}
pub fn len(&self) -> usize {
self.comments.len()
}
pub fn push(&mut self, c: Comment) {
self.comments.push(c);
}
pub fn to_xml(&self) -> String {
let mut w = XmlWriter::with_decl();
let attrs: Vec<(&str, &str)> = vec![
("xmlns:a", NS_DRAWING_MAIN),
("xmlns:p", NS_PRESENTATION_MAIN),
("xmlns:r", NS_DRAWING_RELS),
];
w.open_with("p:cmLst", &attrs);
for c in &self.comments {
c.write_xml(&mut w);
}
w.close("p:cmLst");
w.into_string()
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CommentAuthor {
pub id: u32,
pub name: String,
pub initials: String,
}
impl CommentAuthor {
pub fn new(id: u32, name: impl Into<String>, initials: impl Into<String>) -> Self {
CommentAuthor {
id,
name: name.into(),
initials: initials.into(),
}
}
pub fn write_xml(&self, w: &mut XmlWriter) {
w.empty_with(
"p:cmAuthor",
&[
("id", &self.id.to_string()),
("name", &self.name),
("initials", &self.initials),
],
);
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CommentAuthorList {
pub authors: Vec<CommentAuthor>,
}
impl CommentAuthorList {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.authors.is_empty()
}
pub fn len(&self) -> usize {
self.authors.len()
}
pub fn push(&mut self, a: CommentAuthor) {
self.authors.push(a);
}
pub fn get_by_id(&self, id: u32) -> Option<&CommentAuthor> {
self.authors.iter().find(|a| a.id == id)
}
pub fn get_or_insert_id(&mut self, name: &str, initials: &str) -> u32 {
if let Some(a) = self.authors.iter().find(|a| a.name == name) {
return a.id;
}
let next_id = self
.authors
.iter()
.map(|a| a.id)
.max()
.unwrap_or(0)
.saturating_add(1);
self.authors
.push(CommentAuthor::new(next_id, name, initials));
next_id
}
pub fn to_xml(&self) -> String {
let mut w = XmlWriter::with_decl();
let attrs: Vec<(&str, &str)> = vec![("xmlns:p", NS_PRESENTATION_MAIN)];
w.open_with("p:cmAuthorLst", &attrs);
for a in &self.authors {
a.write_xml(&mut w);
}
w.close("p:cmAuthorLst");
w.into_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn comment_write_xml_basic() {
let c = Comment::new(0, 1, 100, 200, "Hello");
let mut w = XmlWriter::new();
c.write_xml(&mut w);
let xml = &w.buf;
assert!(xml.contains("<p:cm authorId=\"0\""));
assert!(xml.contains("idx=\"1\""));
assert!(xml.contains("<p:pos x=\"100\" y=\"200\"/>"));
assert!(xml.contains("<p:text>Hello</p:text>"));
}
#[test]
fn comment_list_to_xml() {
let mut lst = CommentList::new();
lst.push(Comment::new(0, 1, 100, 200, "First"));
lst.push(Comment::new(1, 2, 300, 400, "Second"));
let xml = lst.to_xml();
assert!(xml.contains("<?xml"));
assert!(xml.contains("<p:cmLst"));
assert!(xml.contains("xmlns:a="));
assert!(xml.contains("xmlns:p="));
assert!(xml.contains("First"));
assert!(xml.contains("Second"));
}
#[test]
fn comment_list_empty_to_xml() {
let lst = CommentList::new();
let xml = lst.to_xml();
assert!(xml.contains("<p:cmLst"));
assert!(xml.contains("</p:cmLst>"));
assert!(!xml.contains("<p:cm "));
}
#[test]
fn comment_author_write_xml() {
let a = CommentAuthor::new(0, "张三", "ZS");
let mut w = XmlWriter::new();
a.write_xml(&mut w);
let xml = &w.buf;
assert!(xml.contains("<p:cmAuthor id=\"0\""));
assert!(xml.contains("name=\"张三\""));
assert!(xml.contains("initials=\"ZS\""));
}
#[test]
fn comment_author_list_to_xml() {
let mut lst = CommentAuthorList::new();
lst.push(CommentAuthor::new(0, "张三", "ZS"));
lst.push(CommentAuthor::new(1, "李四", "LS"));
let xml = lst.to_xml();
assert!(xml.contains("<?xml"));
assert!(xml.contains("<p:cmAuthorLst"));
assert!(xml.contains("张三"));
assert!(xml.contains("李四"));
}
#[test]
fn comment_author_get_or_insert() {
let mut lst = CommentAuthorList::new();
let id1 = lst.get_or_insert_id("张三", "ZS");
assert_eq!(id1, 1);
let id2 = lst.get_or_insert_id("李四", "LS");
assert_eq!(id2, 2);
let id3 = lst.get_or_insert_id("张三", "ZS");
assert_eq!(id3, 1);
assert_eq!(lst.len(), 2);
}
#[test]
fn comment_author_get_by_id() {
let mut lst = CommentAuthorList::new();
lst.push(CommentAuthor::new(0, "张三", "ZS"));
lst.push(CommentAuthor::new(1, "李四", "LS"));
assert_eq!(lst.get_by_id(0).unwrap().name, "张三");
assert_eq!(lst.get_by_id(1).unwrap().name, "李四");
assert!(lst.get_by_id(2).is_none());
}
}