spreadsheet_ods/
xmltree.rs1use get_size2::GetSize;
48use std::fmt::{Display, Formatter};
49
50use crate::attrmap2::AttrMap2;
51use crate::text::TextP;
52use crate::OdsError;
53
54#[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
74pub trait XmlVec {
76 fn add_text<S: Into<String>>(&mut self, txt: S);
78
79 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 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 #[inline]
105 pub fn set_name<S: Into<String>>(&mut self, name: S) {
106 self.name = name.into();
107 }
108
109 #[inline]
111 pub fn name(&self) -> &str {
112 &self.name
113 }
114
115 #[inline]
117 pub fn is_empty(&self) -> bool {
118 self.content.is_empty()
119 }
120
121 #[inline]
123 pub fn attrmap(&self) -> &AttrMap2 {
124 &self.attr
125 }
126
127 #[inline]
129 pub fn attrmap_mut(&mut self) -> &mut AttrMap2 {
130 &mut self.attr
131 }
132
133 #[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 #[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 #[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 #[inline]
156 pub fn add_tag<T: Into<XmlTag>>(&mut self, tag: T) {
157 self.content.push(XmlContent::Tag(tag.into()));
158 }
159
160 #[inline]
162 pub fn add_text<S: Into<String>>(&mut self, text: S) {
163 self.content.push(XmlContent::Text(text.into()));
164 }
165
166 #[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 #[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 #[inline]
185 pub fn tag<T: Into<XmlTag>>(mut self, tag: T) -> Self {
186 self.add_tag(tag);
187 self
188 }
189
190 #[inline]
192 pub fn text<S: Into<String>>(mut self, text: S) -> Self {
193 self.add_text(text);
194 self
195 }
196
197 #[inline]
199 pub fn content(&self) -> &Vec<XmlContent> {
200 &self.content
201 }
202
203 #[inline]
205 pub fn content_mut(&mut self) -> &mut Vec<XmlContent> {
206 &mut self.content
207 }
208
209 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 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 }
236 XmlContent::Tag(v) => content.push(v),
237 }
238 }
239
240 Ok(content)
241 }
242
243 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#[derive(Debug, Clone, PartialEq, GetSize)]
283#[allow(variant_size_differences)]
284pub enum XmlContent {
285 Text(String),
287 Tag(XmlTag),
289}