1use serde::{Deserialize, Serialize};
6
7use crate::namespaces;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(rename = "comments")]
12pub struct Comments {
13 #[serde(rename = "@xmlns")]
14 pub xmlns: String,
15
16 #[serde(rename = "authors")]
17 pub authors: Authors,
18
19 #[serde(rename = "commentList")]
20 pub comment_list: CommentList,
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct Authors {
26 #[serde(rename = "author", default)]
27 pub authors: Vec<String>,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct CommentList {
33 #[serde(rename = "comment", default)]
34 pub comments: Vec<Comment>,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39pub struct Comment {
40 #[serde(rename = "@ref")]
41 pub r#ref: String,
42
43 #[serde(rename = "@authorId")]
44 pub author_id: u32,
45
46 #[serde(rename = "text")]
47 pub text: CommentText,
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub struct CommentText {
53 #[serde(rename = "r", default)]
54 pub runs: Vec<CommentRun>,
55}
56
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59pub struct CommentRun {
60 #[serde(rename = "rPr", skip_serializing_if = "Option::is_none")]
61 pub rpr: Option<CommentRunProperties>,
62
63 #[serde(rename = "t")]
64 pub t: String,
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69pub struct CommentRunProperties {
70 #[serde(rename = "b", skip_serializing_if = "Option::is_none")]
71 pub b: Option<BoldFlag>,
72
73 #[serde(rename = "sz", skip_serializing_if = "Option::is_none")]
74 pub sz: Option<FontSize>,
75}
76
77#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct BoldFlag;
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub struct FontSize {
84 #[serde(rename = "@val")]
85 pub val: f64,
86}
87
88impl Default for Comments {
89 fn default() -> Self {
90 Self {
91 xmlns: namespaces::SPREADSHEET_ML.to_string(),
92 authors: Authors {
93 authors: Vec::new(),
94 },
95 comment_list: CommentList {
96 comments: Vec::new(),
97 },
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_comments_default() {
108 let comments = Comments::default();
109 assert_eq!(comments.xmlns, namespaces::SPREADSHEET_ML);
110 assert!(comments.authors.authors.is_empty());
111 assert!(comments.comment_list.comments.is_empty());
112 }
113
114 #[test]
115 fn test_comments_roundtrip() {
116 let comments = Comments {
117 xmlns: namespaces::SPREADSHEET_ML.to_string(),
118 authors: Authors {
119 authors: vec!["Author1".to_string()],
120 },
121 comment_list: CommentList {
122 comments: vec![Comment {
123 r#ref: "A1".to_string(),
124 author_id: 0,
125 text: CommentText {
126 runs: vec![CommentRun {
127 rpr: None,
128 t: "This is a comment".to_string(),
129 }],
130 },
131 }],
132 },
133 };
134
135 let xml = quick_xml::se::to_string(&comments).unwrap();
136 assert!(xml.contains("A1"));
137 assert!(xml.contains("This is a comment"));
138 assert!(xml.contains("Author1"));
139
140 let parsed: Comments = quick_xml::de::from_str(&xml).unwrap();
141 assert_eq!(parsed.authors.authors.len(), 1);
142 assert_eq!(parsed.comment_list.comments.len(), 1);
143 assert_eq!(parsed.comment_list.comments[0].r#ref, "A1");
144 assert_eq!(parsed.comment_list.comments[0].author_id, 0);
145 assert_eq!(
146 parsed.comment_list.comments[0].text.runs[0].t,
147 "This is a comment"
148 );
149 }
150}