spreadsheet_ods/
xmltree.rs

1//! Defines an XML-Tree. This is used for parts of the spreadsheet
2//! that are not destructured in detail, but simply passed through.
3//! With a little bit of luck there is still some meaning left after
4//! modifying the rest.
5//!
6//! ```
7//! use spreadsheet_ods::xmltree::XmlTag;
8//!
9//! let tag = XmlTag::new("table:shapes")
10//!         .tag(XmlTag::new("draw:frame")
11//!             .attr("draw:z", "0")
12//!             .attr("draw:name", "Bild 1")
13//!             .attr("draw:style:name", "gr1")
14//!             .attr("draw:text-style-name", "P1")
15//!             .attr("svg:width", "10.198cm")
16//!             .attr("svg:height", "1.75cm")
17//!             .attr("svg:x", "0cm")
18//!             .attr("svg:y", "0cm")
19//!             .tag(XmlTag::new("draw:image")
20//!                 .attr_slice([
21//!                     ("xlink:href", "Pictures/10000000000011D7000003105281DD09B0E0B8D4.jpg"),
22//!                     ("xlink:type", "simple"),
23//!                     ("xlink:show", "embed"),
24//!                     ("xlink:actuate", "onLoad"),
25//!                     ("loext:mime-type", "image/jpeg"),
26//!                 ])
27//!                 .tag(XmlTag::new("text:p")
28//!                     .text("sometext")
29//!                 )
30//!             )
31//!         );
32//!
33//! // or
34//! let mut tag = XmlTag::new("table:shapes");
35//! tag.set_attr("draw:z", "0".to_string());
36//! tag.set_attr("draw:name", "Bild 1".to_string());
37//! tag.set_attr("draw:style:name", "gr1".to_string());
38//!
39//! let mut tag2 = XmlTag::new("draw:image");
40//! tag2.set_attr("xlink:type", "simple".to_string());
41//! tag2.set_attr("xlink:show", "embed".to_string());
42//! tag2.add_text("some text");
43//! tag.add_tag(tag2);
44//!
45//! ```
46
47use get_size2::GetSize;
48use std::fmt::{Display, Formatter};
49
50use crate::attrmap2::AttrMap2;
51use crate::text::TextP;
52use crate::OdsError;
53
54/// Defines a XML tag and it's children.
55#[derive(Debug, Clone, Default, PartialEq, GetSize)]
56pub struct XmlTag {
57    name: String,
58    attr: AttrMap2,
59    content: Vec<XmlContent>,
60}
61
62impl From<&str> for XmlTag {
63    fn from(name: &str) -> Self {
64        XmlTag::new(name)
65    }
66}
67
68impl From<String> for XmlTag {
69    fn from(name: String) -> Self {
70        XmlTag::new(name)
71    }
72}
73
74/// Functionality for vectors of XmlTag's.
75pub trait XmlVec {
76    /// Adds the text as new XmlTag of text:p.
77    fn add_text<S: Into<String>>(&mut self, txt: S);
78
79    /// Adds the tag as new XmlTag.
80    fn add_tag<T: Into<XmlTag>>(&mut self, tag: T);
81}
82
83impl XmlVec for &mut Vec<XmlTag> {
84    fn add_text<S: Into<String>>(&mut self, txt: S) {
85        self.push(TextP::new().text(txt).into());
86    }
87
88    fn add_tag<T: Into<XmlTag>>(&mut self, tag: T) {
89        self.push(tag.into());
90    }
91}
92
93impl XmlTag {
94    /// New Tag.
95    pub fn new<S: Into<String>>(name: S) -> Self {
96        Self {
97            name: name.into(),
98            attr: Default::default(),
99            content: vec![],
100        }
101    }
102
103    /// Name
104    #[inline]
105    pub fn set_name<S: Into<String>>(&mut self, name: S) {
106        self.name = name.into();
107    }
108
109    /// Name
110    #[inline]
111    pub fn name(&self) -> &str {
112        &self.name
113    }
114
115    /// Any text or child elements?
116    #[inline]
117    pub fn is_empty(&self) -> bool {
118        self.content.is_empty()
119    }
120
121    /// Attributes
122    #[inline]
123    pub fn attrmap(&self) -> &AttrMap2 {
124        &self.attr
125    }
126
127    /// Attributes
128    #[inline]
129    pub fn attrmap_mut(&mut self) -> &mut AttrMap2 {
130        &mut self.attr
131    }
132
133    /// Sets an attribute
134    #[inline]
135    pub fn set_attr<'a, S: Into<&'a str>, T: Into<String>>(&mut self, name: S, value: T) {
136        self.attr.set_attr(name.into(), value.into());
137    }
138
139    /// Gets an attribute
140    #[inline]
141    pub fn get_attr<'a, S: Into<&'a str>>(&self, name: S) -> Option<&str> {
142        self.attr.attr(name.into())
143    }
144
145    /// Adds more attributes.
146    #[inline]
147    pub fn add_attr_slice<'a, V: Into<String>, I: IntoIterator<Item = (&'a str, V)>>(
148        &mut self,
149        attr: I,
150    ) {
151        self.attr.add_all(attr);
152    }
153
154    /// Add an element.
155    #[inline]
156    pub fn add_tag<T: Into<XmlTag>>(&mut self, tag: T) {
157        self.content.push(XmlContent::Tag(tag.into()));
158    }
159
160    /// Add text.
161    #[inline]
162    pub fn add_text<S: Into<String>>(&mut self, text: S) {
163        self.content.push(XmlContent::Text(text.into()));
164    }
165
166    /// Sets an attribute. Allows for cascading.
167    #[inline]
168    pub fn attr<'a, S: Into<&'a str>, T: Into<String>>(mut self, name: S, value: T) -> Self {
169        self.set_attr(name, value);
170        self
171    }
172
173    /// Adds more attributes.
174    #[inline]
175    pub fn attr_slice<'a, V: Into<String>, I: IntoIterator<Item = (&'a str, V)>>(
176        mut self,
177        attr: I,
178    ) -> Self {
179        self.add_attr_slice(attr);
180        self
181    }
182
183    /// Adds an element. Allows for cascading.
184    #[inline]
185    pub fn tag<T: Into<XmlTag>>(mut self, tag: T) -> Self {
186        self.add_tag(tag);
187        self
188    }
189
190    /// Adds text. Allows for cascading.
191    #[inline]
192    pub fn text<S: Into<String>>(mut self, text: S) -> Self {
193        self.add_text(text);
194        self
195    }
196
197    /// Returns the content vec.
198    #[inline]
199    pub fn content(&self) -> &Vec<XmlContent> {
200        &self.content
201    }
202
203    /// Returns the content vec.
204    #[inline]
205    pub fn content_mut(&mut self) -> &mut Vec<XmlContent> {
206        &mut self.content
207    }
208
209    /// Extracts the plain text from this tag and its content.
210    pub fn extract_text(&self, buf: &mut String) {
211        for c in &self.content {
212            match c {
213                XmlContent::Text(t) => {
214                    buf.push_str(t.as_str());
215                }
216                XmlContent::Tag(t) => {
217                    t.extract_text(buf);
218                }
219            }
220        }
221    }
222
223    /// Converts the content into a `Vec<XmlTag>`. Any occurring text content
224    /// is an error.
225    pub fn into_vec(self) -> Result<Vec<XmlTag>, OdsError> {
226        let mut content = Vec::new();
227
228        for c in self.content {
229            match c {
230                XmlContent::Text(v) => {
231                    if !v.trim().is_empty() {
232                        return Err(OdsError::Parse("Unexpected literal text ", Some(v)));
233                    }
234                    // whitespace is allowable
235                }
236                XmlContent::Tag(v) => content.push(v),
237            }
238        }
239
240        Ok(content)
241    }
242
243    /// Converts the content into a `Vec<XmlTag>`. Any occurring text content
244    /// is ok.
245    pub fn into_mixed_vec(self) -> Vec<XmlContent> {
246        self.content
247    }
248}
249
250impl Display for XmlTag {
251    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
252        write!(f, "<{}", self.name)?;
253        for (n, v) in self.attr.iter() {
254            write!(f, " {}=\"{}\"", n, v)?;
255        }
256        if self.content.is_empty() {
257            writeln!(f, "/>")?;
258        } else {
259            writeln!(f, ">")?;
260
261            for c in &self.content {
262                match c {
263                    XmlContent::Text(t) => {
264                        writeln!(f, "{}", t)?;
265                    }
266                    XmlContent::Tag(t) => {
267                        t.fmt(f)?;
268                    }
269                }
270            }
271
272            writeln!(f, "</{}>", self.name)?;
273        }
274
275        Ok(())
276    }
277}
278
279///
280/// A XmlTag can contain any mixture of XmlTags and text content.
281///
282#[derive(Debug, Clone, PartialEq, GetSize)]
283#[allow(variant_size_differences)]
284pub enum XmlContent {
285    /// Text content.
286    Text(String),
287    /// Contained xml-tags.
288    Tag(XmlTag),
289}