Skip to main content

sheetkit_xml/
comments.rs

1//! Comments XML schema structures.
2//!
3//! Represents `xl/comments{N}.xml` in the OOXML package.
4
5use serde::{Deserialize, Serialize};
6
7use crate::namespaces;
8
9/// Comments root element.
10#[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/// Authors container.
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct Authors {
26    #[serde(rename = "author", default)]
27    pub authors: Vec<String>,
28}
29
30/// Comment list container.
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct CommentList {
33    #[serde(rename = "comment", default)]
34    pub comments: Vec<Comment>,
35}
36
37/// Individual comment.
38#[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/// Comment text content.
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub struct CommentText {
53    #[serde(rename = "r", default)]
54    pub runs: Vec<CommentRun>,
55}
56
57/// A text run within a comment.
58#[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/// Run properties for comment text formatting.
68#[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/// Bold flag element.
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct BoldFlag;
80
81/// Font size element.
82#[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}