1use std::any::Any;
38use std::cell::OnceCell;
39use std::cmp::Ordering;
40use std::collections::HashMap;
41use std::fmt::Formatter;
42
43use std::hash::{Hash, Hasher};
44use std::path::Path;
45use regex::Regex;
46use crate::errors::*;
47
48pub struct Document {
52 declaration: Option<Declaration>,
54 dtds: Vec<DTD>,
56 root_element: Element
58}
59
60impl Document {
61 pub fn new(root: Element) -> Self {
65 Document::new_with_decl_dtd(root, Some(Declaration::default()), None)
66 }
67 pub fn new_with_decl_dtd(root: Element, declaration: Option<Declaration>, dtd: Option<&[DTD]>) -> Self {
71 Self{
72 declaration: declaration,
73 dtds: match dtd{
74 None => Vec::with_capacity(1),
75 Some(dtds) => Vec::from(dtds)
76 },
77 root_element: root
78 }
79 }
80 pub fn doctype_defs(&self) -> impl Iterator<Item = &DTD> {
84 self.dtds.iter()
85 }
86 pub fn doctype_defs_mut(&mut self) -> impl Iterator<Item = &mut DTD> {
90 self.dtds.iter_mut()
91 }
92 pub fn set_doctype_defs(&mut self, dtds: Option<&[DTD]>) {
96 match dtds {
97 None => self.dtds = Vec::with_capacity(1),
98 Some(dlist) => self.dtds = Vec::from(dlist)
99 }
100 }
101 pub fn declaration(&self) -> &Option<Declaration> {
105 &self.declaration
106 }
107 pub fn set_declaration(&mut self, decl: Option<Declaration>) {
111 self.declaration = decl
112 }
113
114 pub fn to_string(&self) -> String {
118 self.to_string_with_indent(" ")
119 }
120
121 pub fn to_string_with_indent(&self, indent: impl Into<String>) -> String {
129 let mut indent = indent.into();
130 match crate::validate_indent(indent.as_str()){
131 Ok(_) => {},
132 Err(_) => {
133 eprintln!("WARNING: {:?} is not a valid indentation. Must be either 1 tab or any number of spaces. The default of 2 spaces will be used instead", indent);
134 indent = " ".to_string();
135 }
136 };
137 let mut builder = String::new();
138 match &self.declaration{
139 None => {},
140 Some(decl) => {
141 builder.push_str(decl.to_string().as_str());
142 builder.push_str("\n");
143 }
144 }
145 for dtd in &self.dtds {
146 builder.push_str(dtd.to_string().as_str());
147 builder.push_str("\n");
148 }
149 builder.push_str(&self.root_element.to_string_with_indent(indent.as_str()));
150 builder.push_str("\n");
151 return builder;
152 }
153
154 pub fn write_to_filepath(&self, path: impl AsRef<Path>) -> std::io::Result<()> {
158 self.write_to_filepath_with_indent(path, " ")
159 }
160
161 pub fn write_to_filepath_with_indent(&self, path: impl AsRef<Path>, indent: impl Into<String>) -> std::io::Result<()> {
165 use std::fs;
166 match path.as_ref().parent(){
168 None => {}
169 Some(dir) => fs::create_dir_all(dir)?
170 };
171 fs::write(path, self.to_string_with_indent(indent))
173 }
174
175 pub fn write_to_file(&self, out: &mut impl std::io::Write) -> std::io::Result<()> {
179 self.write_to_file_with_indent(out, " ")
180 }
181
182 pub fn write_to_file_with_indent(&self, out: &mut impl std::io::Write, indent: impl Into<String>) -> std::io::Result<()> {
186 write!(out, "{}", self.to_string_with_indent(indent))
187 }
188
189 pub fn root_element(&self) -> &Element {
193 &self.root_element
194 }
195
196 pub fn root_element_mut(&mut self) -> &mut Element {
200 &mut self.root_element
201 }
202}
203
204impl std::fmt::Display for Document{
205 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
206 write!(f, "{}", self.to_string_with_indent(" "))
207 }
208}
209
210impl std::fmt::Debug for Document{
211 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
212 write!(f, "{}", self.to_string_with_indent(" "))
213 }
214}
215
216impl PartialEq<Self> for Document {
217 fn eq(&self, other: &Self) -> bool {
218 self.declaration == other.declaration
219 && self.dtds == other.dtds
220 && self.root_element == other.root_element
221 }
222}
223
224#[derive(PartialEq, Eq, Clone, Copy, Debug)]
226pub enum DomNodeType {
227 CDataNode,
229 CommentNode,
231 ElementNode,
233 TextNode
235}
236
237impl From<Box<dyn Node>> for DomNodeType {
238 fn from(value: Box<dyn Node>) -> Self {
239 value.node_type()
240 }
241}
242
243impl std::fmt::Display for DomNodeType {
244 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245 match self {
246 DomNodeType::CDataNode => write!(f, "CDATA"),
247 DomNodeType::CommentNode => write!(f, "Comment"),
248 DomNodeType::ElementNode => write!(f, "Element"),
249 DomNodeType::TextNode => write!(f, "Text"),
250 }
251 }
252}
253
254pub trait Node: dyn_clone::DynClone + std::fmt::Debug + std::fmt::Display + ToString {
258
259 fn text(&self) -> String;
263
264 fn is_element(&self) -> bool;
268
269 fn is_text(&self) -> bool;
273
274 fn is_comment(&self) -> bool;
278
279 fn is_cdata(&self) -> bool;
283
284 fn node_type(&self) -> DomNodeType {
288 if self.is_cdata() {
289 DomNodeType::CDataNode
290 } else if self.is_comment() {
291 DomNodeType::CommentNode
292 } else if self.is_element() {
293 DomNodeType::ElementNode
294 } else if self.is_text() {
295 DomNodeType::TextNode
296 } else {
297 panic!("Logic error! Box<dyn Node> value has no corresponding type in enum DomNodeType")
298 }
299 }
300
301 fn as_element(&self) -> Result<&Element, TypeCastError>;
305
306 fn as_comment(&self) -> Result<&Comment, TypeCastError>;
310
311 fn as_text(&self) -> Result<&Text, TypeCastError>;
315
316 fn as_cdata(&self) -> Result<&CData, TypeCastError>;
320
321 fn as_element_mut(&mut self) -> Result<&mut Element, TypeCastError>;
325
326 fn as_comment_mut(&mut self) -> Result<&mut Comment, TypeCastError>;
330
331 fn as_text_mut(&mut self) -> Result<&mut Text, TypeCastError>;
335
336 fn as_cdata_mut(&mut self) -> Result<&mut CData, TypeCastError>;
340
341 fn as_node(&self) -> &dyn Node;
345
346 fn as_node_mut(&mut self) -> &mut dyn Node;
350
351 fn as_any(&self) -> &dyn Any;
355
356 fn as_any_mut(&mut self) -> &mut dyn Any;
360
361 fn to_string_with_indent(&self, indent: &str) -> String;
369
370 fn boxed(self) -> Box<dyn Node>;
372}
373
374pub fn clone_node(node: &Box<dyn Node>) -> Box<dyn Node> {
376 if node.is_element() {
377 Box::new(node.as_element().expect("logic error").clone())
378 } else if node.is_text() {
379 Box::new(node.as_text().expect("logic error").clone())
380 } else if node.is_comment() {
381 Box::new(node.as_comment().expect("logic error").clone())
382 } else if node.is_cdata() {
383 Box::new(node.as_cdata().expect("logic error").clone())
384 } else {
385 panic!("logic error: Node is neither of Element, Text, Comment, nor CData");
386 }
387}
388
389pub fn node_eq(n1: &Box<dyn Node>, n2: &Box<dyn Node>) -> bool {
391 let t1 = n1.node_type();
392 let t2 = n2.node_type();
393 if t1 != t2 {
394 return false;
395 }
396 return match t1 {
397 DomNodeType::CDataNode =>
398 n1.as_cdata().unwrap() == n2.as_cdata().unwrap(),
399 DomNodeType::CommentNode =>
400 n1.as_comment().unwrap() == n2.as_comment().unwrap(),
401 DomNodeType::ElementNode =>
402 n1.as_element().unwrap() == n2.as_element().unwrap(),
403 DomNodeType::TextNode =>
404 n1.as_text().unwrap() == n2.as_text().unwrap()
405 }
406}
407
408pub struct Element {
410 name: String,
412 child_nodes: Vec<Box<dyn Node>>,
414 attributes: HashMap<String, String>,
416 xmlns: Option<String>,
418 xmlns_prefix: Option<String>,
420 xmlns_context: HashMap<String, String>
422}
423
424impl Element {
425 pub fn new<TEXT1: Into<String>+Clone, TEXT2: Into<String>+Clone>(
436 name: impl Into<String>, text: Option<String>,
437 attributes: Option<HashMap<TEXT1, TEXT2>>,
438 xmlns: Option<String>,
439 xmlns_prefix: Option<String>,
440 children: Option<Vec<Box<dyn Node>>>
441 ) -> Result<Self, KissXmlError> {
442 let name = name.into();
444 Element::check_elem_name(name.as_str())?;
445 let mut attrs: HashMap<String, String> = HashMap::new();
447 match attributes {
448 None => {}
449 Some(attr_map) => {
450 for (k, v) in attr_map.iter() {
451 let n: String = k.clone().into();
452 Element::check_attr_name(n.as_str())?;
453 attrs.insert(n, v.clone().into());
454 }
455 }
456 }
457 let mut xmlns = xmlns;
459 if xmlns.is_none() {
460 match &xmlns_prefix {
461 None => {
462 xmlns = match attrs.get("xmlns"){
464 None => None,
465 Some(ns) => Some(ns.to_string())
466 }
467 },
468 Some(prefix) => {
469 xmlns = match attrs.get(&format!("xmlns:{prefix}")){
471 None => None,
472 Some(ns) => Some(ns.to_string())
473 }
474 }
475 };
476 }
477 let mut elem = Self {
479 name: name,
480 child_nodes: Vec::new(),
481 xmlns_context: Element::xmlns_context_from_attributes(&attrs),
482 attributes: attrs,
483 xmlns: xmlns.map(|s| s.to_string()),
484 xmlns_prefix: xmlns_prefix.map(|s| s.to_string())
485 };
486 match text {
489 None => {}
490 Some(t) => elem.append(Text::new(t))
491 }
492 match children {
493 None => {},
494 Some(child_vec) => elem.append_all(child_vec)
495 };
496 return Ok(elem);
497 }
498 pub fn new_from_name(name: &str) -> Result<Self, KissXmlError> {
500 Element::check_elem_name(name)?;
502 Ok(Self {
503 name: name.to_string(),
504 ..Default::default()
505 })
506 }
507 pub fn new_with_attributes<TEXT1: Into<String>+Clone, TEXT2: Into<String>+Clone>(name: &str, attributes: HashMap<TEXT1, TEXT2>) -> Result<Self, KissXmlError> {
522 Self::new(name, None, Some(attributes), None, None, None)
523 }
524 pub fn new_with_text(name: &str, text: impl Into<String>) -> Result<Self, KissXmlError> {
526 Self::new(name, Some(text.into()), Option::<HashMap<String,String>>::None, None, None, None)
527 }
528 pub fn new_with_attributes_and_text<TEXT1: Into<String>+Clone, TEXT2: Into<String>+Clone>(name: &str, attributes: HashMap<TEXT1, TEXT2>, text: impl Into<String>) -> Result<Self, KissXmlError> {
547 Self::new(name, Some(text.into()), Some(attributes), None, None, None)
548 }
549 pub fn new_with_attributes_and_children<TEXT1: Into<String>+Clone, TEXT2: Into<String>+Clone>(name: &str, attributes: HashMap<TEXT1, TEXT2>, children: Vec<Box<dyn Node>>) -> Result<Self, KissXmlError> {
574 Self::new(name, None, Some(attributes), None, None, Some(children))
575 }
576
577 pub fn new_with_children(name: &str, children: Vec<Box<dyn Node>>) -> Result<Self, KissXmlError> {
599 Self::new(name, None, Option::<HashMap<String,String>>::None, None, None, Some(children))
600 }
601 fn xmlns_context_from_attributes(attrs: &HashMap<String, String>) -> HashMap<String, String> {
607 let mut prefixes: HashMap<String, String> = HashMap::new();
609 for (k, v) in attrs.iter() {
610 let key = k.as_str();
611 if key.starts_with("xmlns:") {
612 let split: Vec<&str> = key.splitn(2, ":").collect();
613 let prefix = split[1].to_string();
614 let ns = v.clone();
615 prefixes.insert(prefix, ns);
616 }
617 }
618 return prefixes;
619 }
620 pub fn name(&self) -> String {
622 self.name.clone()
623 }
624 pub fn namespace(&self) -> Option<String> {
628 self.xmlns.clone()
629 }
630 pub fn default_namespace(&self) -> Option<String> {
634 match self.xmlns_prefix{
635 None => self.xmlns.clone(),
636 Some(_) => None
637 }
638 }
639 pub fn tag_name(&self) -> String {
643 match &self.xmlns_prefix{
644 None => self.name.clone(),
645 Some(prefix) => format!("{}:{}", prefix, self.name)
646 }
647 }
648 pub fn namespace_prefix(&self) -> Option<String> {
652 self.xmlns_prefix.clone()
653 }
654
655 pub fn elements_by_namespace(&self, namespace: Option<&str>) -> impl Iterator<Item = &Element>{
686 let ns = namespace.map(|s| s.to_string());
687 self.child_elements().filter(move |c| c.xmlns == ns)
688 }
689 pub fn elements_by_namespace_mut(&mut self, namespace: Option<&str>) -> impl Iterator<Item = &mut Element>{
722 let ns = namespace.map(|s| s.to_string());
723 self.child_elements_mut().filter(move |c| c.xmlns == ns)
724 }
725 pub fn elements_by_namespace_prefix(&self, prefix: Option<&str>) -> impl Iterator<Item = &Element>{
755 let pfx = prefix.map(|p| p.to_string());
756 self.child_elements().filter(move |c| c.xmlns_prefix == pfx)
757 }
758 pub fn elements_by_namespace_prefix_mut(&mut self, prefix: Option<&str>) -> impl Iterator<Item = &mut Element>{
791 let pfx = prefix.map(|p| p.to_string());
792 self.child_elements_mut().filter(move |c| c.xmlns_prefix == pfx)
793 }
794 pub fn namespace_prefixes(&self) -> Option<HashMap<String, String>> {
796 let prefixes = Self::xmlns_context_from_attributes(&self.attributes);
797 if prefixes.is_empty() {
798 None
799 } else {
800 Some(prefixes)
801 }
802 }
803 pub(crate) fn get_namespace_context(&self) -> HashMap<String, String> {self.xmlns_context.clone()}
805 pub(crate) fn set_namespace_context(&mut self, parent_default_namespace: Option<String>, parent_prefixes: Option<HashMap<String, String>>) {
807 match self.xmlns_prefix {
809 None => {
810 match self.default_namespace() {
811 None => self.xmlns = parent_default_namespace,
812 Some(_) => {}
813 }
814 }
815 Some(_) => {}
816 }
817 match parent_prefixes {
819 None => {}
820 Some(prefixes) => {
821 for (prefix, ns) in prefixes {
822 if ! self.xmlns_context.contains_key(prefix.as_str()) {
823 let _ = &self.xmlns_context.insert(prefix, ns);
824 }
825 }
826 }
827 }
828 if self.xmlns.is_none() {
830 match &self.xmlns_prefix {
831 None => {}
832 Some(prefix) => {
833 self.xmlns = self.xmlns_context.get(prefix).map(String::clone);
835 }
836 };
837 }
838 }
839 pub(crate) fn reverse_children(&mut self) {
841 self.child_nodes.reverse();
842 }
843 pub fn child_elements(&self) -> impl Iterator<Item = &Element>{
845 self.child_nodes.iter()
846 .filter(|n| n.is_element())
847 .map(|n| n.as_element().expect("logic error"))
848 }
849 pub fn child_elements_mut(&mut self) -> impl Iterator<Item = &mut Element>{
851 self.child_nodes.iter_mut()
852 .filter(|n| n.is_element())
853 .map(|n| n.as_element_mut().expect("logic error"))
854 }
855 pub fn children(&self) -> impl Iterator<Item = &Box<dyn Node>>{
857 self.child_nodes.iter()
858 }
859 pub fn all_children(&self) -> impl Iterator<Item = &Box<dyn Node>>{
861 self.search(|_| true)
862 }
863 pub fn children_mut(&mut self) -> impl Iterator<Item = &mut Box<dyn Node>>{
865 self.child_nodes.iter_mut()
866 }
867 pub fn children_recursive(&self) -> Box<dyn Iterator<Item = &Box<dyn Node>> + '_> {
869 Box::new(
870 self.child_nodes.iter()
871 .chain(
872 self.child_elements().map(|e| e.children_recursive()
873 ).flatten()
874 )
875 )
876 }
877
878 pub fn clear_children(&mut self) {self.child_nodes.clear()}
880 pub fn set_text(&mut self, text: impl Into<String>) {
882 self.clear_children();
883 self.append(Text::new(text));
884 }
885 pub fn first_element_by_name(&self, name: impl Into<String>) -> Result<&Element, DoesNotExistError> {
908 let n: String = name.into();
909 for e in self.child_elements() {
910 if e.name() == n {
911 return Ok(e);
912 }
913 }
914 Err(DoesNotExistError::default())
915 }
916 pub fn first_element_by_name_mut(&mut self, name: impl Into<String>) -> Result<&mut Element, DoesNotExistError> {
936 let n: String = name.into();
937 for e in self.child_elements_mut() {
938 if e.name() == n {
939 return Ok(e);
940 }
941 }
942 Err(DoesNotExistError::default())
943 }
944 pub fn elements_by_name(&self, name: impl Into<String>) -> impl Iterator<Item = &Element>{
949 let n: String = name.into();
950 self.child_elements().filter(move |c| c.name == n)
951 }
952 pub fn elements_by_name_mut(&mut self, name: impl Into<String>) -> impl Iterator<Item = &mut Element>{
957 let n: String = name.into();
958 self.child_elements_mut().filter(move |c| c.name == n)
959 }
960 pub fn attributes(&self) -> &HashMap<String, String> {
962 &self.attributes
963 }
964 pub fn get_attr(&self, attr_name: impl Into<String>) -> Option<&String> {
966 let n: String = attr_name.into();
967 self.attributes.get(&n)
968 }
969 pub fn set_attr(&mut self, attr_name: impl Into<String>, value: impl Into<String>) -> Result<(), InvalidAttributeName> {
971 let n: String = attr_name.into();
972 Element::check_attr_name(n.as_str())?;
973 let v: String = value.into();
974 self.attributes.insert(n, v);
975 Ok(())
976 }
977
978
979 const ATTR_NAME_CHECKER_SINGLETON: OnceCell<Regex> = OnceCell::new();
981 fn check_attr_name(name: &str) -> Result<(), InvalidAttributeName> {
983 let singleton = Element::ATTR_NAME_CHECKER_SINGLETON;
984 let checker = singleton.get_or_init(
985 || Regex::new(r#"^[_a-zA-Z]\S*$"#).unwrap()
986 );
987 if checker.is_match(name) {
988 Ok(())
989 } else {
990 Err(InvalidAttributeName::new(format!("'{}' is not a valid attribute name", name)))
991 }
992 }
993 const NAME_CHECKER_SINGLETON: OnceCell<Regex> = OnceCell::new();
995 fn check_elem_name(name: &str) -> Result<(), InvalidElementName> {
997 let singleton = Element::NAME_CHECKER_SINGLETON;
998 let checker = singleton.get_or_init(
999 || Regex::new(r#"^[_a-zA-Z]\S*$"#).unwrap()
1000 );
1001 if checker.is_match(name) {
1002 Ok(())
1003 } else {
1004 Err(InvalidElementName::new(format!("'{}' is not a valid name", name)))
1005 }
1006 }
1007 pub fn remove_attr(&mut self, attr_name: impl Into<String>) -> Option<String> {
1009 let n: String = attr_name.into();
1010 self.attributes.remove(&n)
1011 }
1012 pub fn clear_attributes(&mut self) {
1014 self.attributes.clear()
1015 }
1016 pub fn search<'a, P>(&'a self, predicate: P) -> Box<dyn Iterator<Item = &'a Box<dyn Node>> + 'a> where P: FnMut(&&Box<dyn Node>) -> bool + 'a {
1046 Box::new(
1048 self.children_recursive().filter(predicate)
1049 )
1050 }
1051 pub fn search_elements<'a, P>(&'a self, predicate: P) -> Box<dyn Iterator<Item = &'a Element> + 'a> where P: FnMut(&&Element) -> bool + 'a {
1081 Box::new(
1083 self.search(|n| n.is_element())
1084 .map(|n| n.as_element().expect("logic error"))
1085 .filter(predicate)
1086 )
1087 }
1088 pub fn search_elements_by_name(&self, name: impl Into<String>) -> impl Iterator<Item = &Element>{
1118 let n: String = name.into();
1120 self.search_elements(move |e| e.name() == n)
1121 }
1122 pub fn search_text<'a, P>(&'a self, predicate: P) -> Box<dyn Iterator<Item = &'a Text> + 'a> where P: Fn(&&Text) -> bool + 'a {
1124 Box::new(
1126 self.search(|n| n.is_text())
1127 .map(|n| n.as_text().expect("logic error"))
1128 .filter(predicate)
1129 )
1130 }
1131
1132 pub fn search_comments<'a, P>(&'a self, predicate: P) -> Box<dyn Iterator<Item = &'a Comment> + 'a> where P: Fn(&&Comment) -> bool + 'a {
1134 Box::new(
1136 self.search(|n| n.is_comment())
1137 .map(|n| n.as_comment().expect("logic error"))
1138 .filter(predicate)
1139 )
1140 }
1141 pub fn append(&mut self, node: impl Node) {
1163 self.append_boxed(node.boxed());
1165 }
1166 pub fn append_boxed(&mut self, mut node: Box<dyn Node>) {
1168 Self::apply_xmlns_context_to_child_node(self.default_namespace(), self.xmlns_context.clone(), &mut node);
1169 self.child_nodes.push(node);
1170 self.cleanup_text_nodes();
1172 }
1173 fn apply_xmlns_context_to_child_node(df_xmlns: Option<String>, xmlns_context: HashMap<String, String>, node: &mut Box<dyn Node>) {
1175 let is_element = node.is_element();
1176 if is_element {
1177 Self::apply_xmlns_context_to_child_element(
1178 df_xmlns, xmlns_context,
1179 node.as_element_mut().expect("logic error")
1180 );
1181 }
1182 }
1183 fn apply_xmlns_context_to_child_element(df_xmlns: Option<String>, xmlns_context: HashMap<String, String>, child: &mut Element) {
1185 child.set_namespace_context(
1187 df_xmlns,
1188 Some(xmlns_context)
1189 );
1190 }
1191 fn cleanup_text_nodes(&mut self) {
1193 if self.child_nodes.len() == 0 {return;}
1195 let mut index = self.child_nodes.len() - 1;
1197 while index > 0 {
1198 if self.child_nodes[index].is_text()
1199 && self.child_nodes[index-1].is_text() {
1200 let back = self.child_nodes.remove(index);
1202 let front = self.child_nodes.remove(index-1);
1203 let merged = Text::concat(front.as_text().expect("logic error"), back.as_text().expect("logic error"));
1204 self.child_nodes.insert(index-1, merged.boxed());
1205 }
1206 index -= 1;
1207 }
1208 assert!(self.child_nodes.len() > 0, "logic error: self.child_nodes should never be empty here!");
1210 let mut index = self.child_nodes.len() - 1;
1211 loop {
1212 if self.child_nodes[index].is_text()
1213 && self.child_nodes[index]
1214 .as_text().expect("logic error").is_whitespace() {
1215 self.child_nodes.remove(index);
1216 }
1217 if index == 0 {break;}
1218 index = index.wrapping_sub(1);
1219 }
1220 }
1222 pub fn append_all(&mut self, children: Vec<Box<dyn Node>>) {
1250 let mut elem_indices: Vec<usize> = Vec::with_capacity(children.len());
1252 let mut i = self.child_nodes.len();
1253 for child in children {
1254 if child.is_element() {
1255 elem_indices.push(i);
1256 }
1257 self.child_nodes.push(child);
1258 i += 1;
1259 }
1260 for i in elem_indices {
1262 Self::apply_xmlns_context_to_child_node(
1263 self.default_namespace(), self.xmlns_context.clone(),
1264 &mut self.child_nodes[i]
1265 );
1266 }
1267 self.cleanup_text_nodes();
1269 }
1270 pub fn insert(&mut self, index: usize, node: impl Node) -> Result<(), IndexOutOfBounds> {
1274 if index > self.child_nodes.len() {
1275 return Err(IndexOutOfBounds::new(index as isize, Some((0, self.child_nodes.len() as isize))));
1276 }
1277 self.child_nodes.insert(index, node.boxed());
1279 Self::apply_xmlns_context_to_child_node(
1280 self.default_namespace(), self.xmlns_context.clone(),
1281 self.child_nodes.last_mut().unwrap()
1282 );
1283 self.cleanup_text_nodes();
1285 Ok(())
1287 }
1288 pub fn remove(&mut self, index: usize) -> Result<Box<dyn Node>, IndexOutOfBounds> {
1292 if index >= self.child_nodes.len() {
1293 return Err(IndexOutOfBounds::new(index as isize, Some((0, self.child_nodes.len() as isize))));
1294 }
1295 Ok(self.child_nodes.remove(index))
1296 }
1297 pub fn remove_all<P>(&mut self, predicate: &P) -> usize where P: Fn(&Box<dyn Node>) -> bool {
1329 let mut count = self.remove_by(predicate);
1330 for e in self.child_elements_mut() {
1331 count += e.remove_all(predicate);
1332 }
1333 return count;
1334 }
1335
1336 pub fn remove_by<P>(&mut self, predicate: &P) -> usize where P: Fn(&Box<dyn Node>) -> bool {
1341 let mut rm_indices: Vec<usize> = Vec::new();
1342 for i in (0..self.child_nodes.len()).rev() {
1343 if predicate(&self.child_nodes[i]) {
1344 rm_indices.push(i);
1345 }
1346 }
1347 let count = rm_indices.len();
1348 for i in rm_indices {
1349 self.child_nodes.remove(i);
1350 }
1351 return count;
1352 }
1353 pub fn remove_element(&mut self, index: usize) -> Result<Element, IndexOutOfBounds> {
1355 let mut elems: Vec<usize> = Vec::new();
1357 for i in 0..self.child_nodes.len() {
1358 if self.child_nodes[i].is_element(){ elems.push(i); }
1359 }
1360 if index >= elems.len() {
1362 return Err(IndexOutOfBounds::new(index as isize, Some((0, elems.len() as isize))));
1363 }
1364 let removed = self.child_nodes.remove(elems[index]);
1365 Ok(removed.as_element().expect("logic error").clone())
1366 }
1367 pub fn remove_elements<P>(&mut self, predicate: P) -> usize where P: Fn(&Element) -> bool {
1371 let mut rm_indices: Vec<usize> = Vec::new();
1372 for i in (0..self.child_nodes.len()).rev() {
1373 if self.child_nodes[i].is_element() {
1374 if predicate(
1375 self.child_nodes[i].as_element().expect("logic error")
1376 ) {
1377 rm_indices.push(i);
1378 }
1379 }
1380 }
1381 let count = rm_indices.len();
1382 for i in rm_indices {
1383 self.child_nodes.remove(i);
1384 }
1385 return count;
1386 }
1387
1388 pub fn remove_all_elements<P>(&mut self, predicate: P) -> usize where P: Fn(&Element) -> bool {
1393 let new_pred = |n: &Box<dyn Node>| {
1394 match n.is_element(){
1395 true => predicate(n.as_element().unwrap()),
1396 false => false
1397 }
1398 };
1399 let mut count = self.remove_by(&new_pred);
1400 for e in self.child_elements_mut() {
1401 count += e.remove_all(&new_pred);
1402 }
1403 return count;
1404 }
1405 pub fn remove_elements_by_name(&mut self, name: impl Into<String>) -> usize {
1409 let n: String = name.into();
1410 self.remove_elements(move |e| e.name == n)
1411 }
1412
1413 fn to_string_with_prefix_and_indent(&self, prefix: &str, indent: &str, mut inline: bool) -> String {
1416 let mut out = String::new();
1417 if !inline {out.push_str(prefix)}
1418 let tag_name = self.tag_name();
1420 out.push_str("<");
1421 out.push_str(tag_name.as_str());
1422
1423 let mut attrs: Vec<(&String, &String)> = self.attributes().iter().map(|kv| (kv.0, kv.1)).collect();
1425 attrs.sort_by(crate::attribute_order); for (k, v) in attrs {
1427 out.push_str(" ");
1428 out.push_str(k.as_str());
1429 out.push_str("=\"");
1430 out.push_str(crate::attribute_escape(v).as_str());
1431 out.push_str("\"");
1432 }
1433 let child_count = self.child_nodes.len();
1435 if child_count == 0 {
1436 out.push_str("/>");
1437 } else if child_count == 1 && !self.child_nodes[0].is_element() {
1438 out.push_str(">");
1440 out.push_str(&self.child_nodes[0].to_string_with_indent(""));
1441 out.push_str("</");
1442 out.push_str(tag_name.as_str());
1443 out.push_str(">");
1444 } else {
1445 out.push('>');
1447 inline = inline || self.child_nodes.iter().any(|n| n.is_text());
1457 if !inline{out.push('\n');}
1458 let mut next_prefix = String::from(prefix);
1460 next_prefix.push_str(indent);
1461 for c in &self.child_nodes {
1462 if c.is_text() {
1463 let text = crate::text_escape(c.text());
1465 out.push_str(text.as_str());
1466 } else if c.is_element() {
1467 out.push_str(
1469 c.as_element().expect("logic error")
1470 .to_string_with_prefix_and_indent(next_prefix.as_str(), indent, inline).as_str()
1471 );
1472 } else {
1473 if !(inline) {out.push_str(next_prefix.as_str());}
1475 out.push_str(c.to_string_with_indent(indent).as_str());
1476 }
1477 if !inline {out.push('\n');}
1478 }
1479 if !inline {out.push_str(prefix);}
1481 out.push_str("</");
1482 out.push_str(tag_name.as_str());
1483 out.push_str(">");
1484 }
1485 return out;
1486 }
1487
1488}
1489
1490impl Node for Element {
1491
1492 fn text(&self) -> String {
1493 let mut builder = String::new();
1495 for c in &self.child_nodes {
1496 if c.is_text() || c.is_element() {
1497 builder.push_str(c.text().as_str())
1498 }
1499 }
1500 builder
1501 }
1502
1503 fn is_element(&self) -> bool {
1504 true
1505 }
1506
1507 fn is_text(&self) -> bool {
1508 false
1509 }
1510
1511 fn is_comment(&self) -> bool {
1512 false
1513 }
1514
1515 fn is_cdata(&self) -> bool {
1516 false
1517 }
1518
1519 fn as_element(&self) -> Result<&Element, TypeCastError> {Ok(&self)}
1520
1521 fn as_comment(&self) -> Result<&Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as Comment"))}
1522
1523 fn as_text(&self) -> Result<&Text, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as Text"))}
1524
1525 fn as_cdata(&self) -> Result<&CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as CData"))}
1526
1527 fn as_element_mut(&mut self) -> Result<&mut Element, TypeCastError> {Ok(self)}
1528
1529 fn as_comment_mut(&mut self) -> Result<&mut Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as Comment"))}
1530
1531 fn as_text_mut(&mut self) -> Result<&mut Text, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as Text"))}
1532
1533 fn as_cdata_mut(&mut self) -> Result<&mut CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Element as CData"))}
1534
1535 fn as_node(&self) -> &dyn Node {self}
1536
1537 fn as_node_mut(&mut self) -> &mut dyn Node {self}
1538
1539 fn as_any(&self) -> &dyn Any {self}
1540
1541 fn as_any_mut(&mut self) -> &mut dyn Any{self}
1542
1543 fn to_string_with_indent(&self, indent: &str) -> String {
1544 match crate::validate_indent(indent){
1545 Ok(_) => self.to_string_with_prefix_and_indent("", indent, false),
1546 Err(_) => {
1547 eprintln!("WARNING: {:?} is not a valid indentation. Must be either 1 tab or any number of spaces. The default of 2 spaces will be used instead", indent);
1548 self.to_string_with_prefix_and_indent("", " ", false)
1549 }
1550 }
1551 }
1552
1553 fn boxed(self) -> Box<dyn Node> {
1554 Box::new(self)
1555 }
1556}
1557
1558impl Clone for Element {
1559 fn clone(&self) -> Self {
1560 let mut new_children: Vec<Box<dyn Node>> = Vec::with_capacity(self.child_nodes.len());
1561 for c in &self.child_nodes {
1562 new_children.push(clone_node(c))
1563 }
1564 Self {
1565 name: self.name.clone(),
1566 child_nodes: new_children,
1567 attributes: self.attributes.clone(),
1568 xmlns: self.xmlns.clone(),
1569 xmlns_prefix: self.xmlns_prefix.clone(),
1570 xmlns_context: self.xmlns_context.clone(),
1571 }
1572 }
1573}
1574
1575impl Default for Element {
1576 fn default() -> Self {
1577 Self {
1578 name: "x".to_string(),
1579 child_nodes: Vec::new(),
1580 attributes: Default::default(),
1581 xmlns: None,
1582 xmlns_prefix: None,
1583 xmlns_context: HashMap::new(),
1584 }
1585 }
1586}
1587
1588impl PartialOrd for Element {
1589 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1590 self.name.partial_cmp(&other.name)
1591 }
1592}
1593
1594impl PartialEq<Self> for Element {
1595 fn eq(&self, other: &Self) -> bool {
1596 if self.name == other.name && self.xmlns == other.xmlns
1597 && self.xmlns_prefix == other.xmlns_prefix
1598 && self.attributes == other.attributes
1599 && self.child_nodes.len() == other.child_nodes.len() {
1600 for i in 0..self.child_nodes.len() {
1601 if !node_eq(&self.child_nodes[i], &other.child_nodes[i]) {
1602 return false;
1603 }
1604 }
1605 return true;
1606 }
1607 return false;
1608 }
1609}
1610
1611impl Hash for Element {
1612 fn hash<H: Hasher>(&self, state: &mut H) {
1613 self.name.hash(state);
1614 self.xmlns.hash(state);
1615 }
1616}
1617
1618
1619impl std::fmt::Display for Element {
1620 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1621 write!(f, "{}", self.to_string_with_indent(" "))
1622 }
1623}
1624
1625impl std::fmt::Debug for Element {
1626 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1627 write!(f, "{}", self.to_string_with_indent(" "))
1628 }
1629}
1630
1631#[derive(Clone)]
1633pub struct Text {
1634 pub content: String
1636}
1637
1638const WSP_MATCHER_SINGLETON: OnceCell<Regex> = OnceCell::new();
1640
1641impl Text {
1642 pub fn new(text: impl Into<String>) -> Self {
1644 let content: String = text.into();
1645 Self{content}
1646 }
1647
1648 pub fn concat(&self, other: &Text) -> Text {
1650 let mut content = String::new();
1651 content.push_str(self.content.as_str());
1652 content.push_str(other.content.as_str());
1653 Text{content}
1654 }
1655
1656 fn is_whitespace(&self) -> bool {
1658 let singleton = WSP_MATCHER_SINGLETON;
1659 let wsp_matcher = singleton.get_or_init(|| Regex::new(r#"^\s+$"#).unwrap());
1660 wsp_matcher.is_match(self.content.as_str())
1661 }
1662}
1663
1664impl From<&str> for Text {
1665 fn from(value: &str) -> Self {
1666 Text::new(value)
1667 }
1668}
1669
1670impl From<String> for Text {
1671 fn from(value: String) -> Self {
1672 Text::new(value)
1673 }
1674}
1675
1676impl Node for Text {
1677
1678 fn text(&self) -> String {
1679 self.content.clone()
1680 }
1681
1682 fn is_element(&self) -> bool {
1683 false
1684 }
1685
1686 fn is_text(&self) -> bool {
1687 true
1688 }
1689
1690 fn is_comment(&self) -> bool {
1691 false
1692 }
1693
1694 fn is_cdata(&self) -> bool {
1695 false
1696 }
1697
1698 fn as_element(&self) -> Result<&Element, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as Element"))}
1699
1700 fn as_comment(&self) -> Result<&Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as Comment"))}
1701
1702 fn as_text(&self) -> Result<&Text, TypeCastError> {Ok(&self)}
1703
1704 fn as_cdata(&self) -> Result<&CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as CData"))}
1705
1706 fn as_element_mut(&mut self) -> Result<&mut Element, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as Element"))}
1707
1708 fn as_comment_mut(&mut self) -> Result<&mut Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as Comment"))}
1709
1710 fn as_text_mut(&mut self) -> Result<&mut Text, TypeCastError> {Ok(self)}
1711
1712 fn as_cdata_mut(&mut self) -> Result<&mut CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Text as CData"))}
1713
1714 fn as_node(&self) -> &dyn Node {self}
1715
1716 fn as_node_mut(&mut self) -> &mut dyn Node {self}
1717
1718 fn as_any(&self) -> &dyn Any {self}
1719
1720 fn as_any_mut(&mut self) -> &mut dyn Any{self}
1721
1722 fn to_string_with_indent(&self, _indent: &str) -> String {
1723 crate::text_escape(self.content.clone())
1724 }
1725
1726 fn boxed(self) -> Box<dyn Node> {
1727 Box::new(self)
1728 }
1729}
1730
1731impl PartialOrd for Text {
1732 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1733 self.content.partial_cmp(&other.content)
1734 }
1735}
1736
1737impl PartialEq<Self> for Text {
1738 fn eq(&self, other: &Self) -> bool {
1739 self.content.eq(&other.content)
1740 }
1741}
1742
1743impl Hash for Text {
1744 fn hash<H: Hasher>(&self, state: &mut H) {
1745 self.content.hash(state)
1746 }
1747}
1748
1749
1750impl std::fmt::Display for Text {
1751 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1752 write!(f, "{}", self.to_string_with_indent(" "))
1753 }
1754}
1755
1756impl std::fmt::Debug for Text {
1757 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1758 write!(f, "{}", self.to_string_with_indent(" "))
1759 }
1760}
1761
1762#[derive(Clone)]
1764pub struct Comment{
1765 comment: String
1767}
1768
1769impl Comment {
1770 pub fn new(comment: impl Into<String>) -> Result<Self, InvalidContent> {
1772 let content: String = comment.into();
1773 if content.contains("-->") {
1774 Err(InvalidContent::new("Comments cannot contain '-->'"))
1775 } else {
1776 Ok(Self { comment: content })
1777 }
1778 }
1779
1780 pub fn get_content(&self) -> &str {
1782 self.comment.as_str()
1783 }
1784 pub fn set_content(&mut self, content: impl Into<String>) -> Result<(), InvalidContent> {
1786 let content = content.into();
1787 if content.contains("-->") {
1788 Err(InvalidContent::new("Comments cannot contain '-->'"))
1789 } else {
1790 self.comment = content.into();
1791 Ok(())
1792 }
1793 }
1794}
1795
1796impl Node for Comment {
1797
1798 fn text(&self) -> String {
1799 self.comment.clone()
1800 }
1801
1802 fn is_element(&self) -> bool {
1803 false
1804 }
1805
1806 fn is_text(&self) -> bool {
1807 false
1808 }
1809
1810 fn is_comment(&self) -> bool {
1811 true
1812 }
1813
1814 fn is_cdata(&self) -> bool {
1815 false
1816 }
1817
1818 fn as_element(&self) -> Result<&Element, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as Element"))}
1819
1820 fn as_comment(&self) -> Result<&Comment, TypeCastError> {Ok(&self)}
1821
1822 fn as_text(&self) -> Result<&Text, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as Text"))}
1823
1824 fn as_cdata(&self) -> Result<&CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as CData"))}
1825
1826 fn as_element_mut(&mut self) -> Result<&mut Element, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as Element"))}
1827
1828 fn as_comment_mut(&mut self) -> Result<&mut Comment, TypeCastError> {Ok(self)}
1829
1830 fn as_text_mut(&mut self) -> Result<&mut Text, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as Text"))}
1831
1832 fn as_cdata_mut(&mut self) -> Result<&mut CData, TypeCastError> {Err(TypeCastError::new("Cannot cast Comment as CData"))}
1833
1834 fn as_node(&self) -> &dyn Node {self}
1835
1836 fn as_node_mut(&mut self) -> &mut dyn Node {self}
1837
1838 fn as_any(&self) -> &dyn Any {self}
1839
1840 fn as_any_mut(&mut self) -> &mut dyn Any{self}
1841
1842 fn to_string_with_indent(&self, _indent: &str) -> String {
1843 format!("<!--{}-->", self.comment)
1844 }
1845
1846 fn boxed(self) -> Box<dyn Node> {
1847 Box::new(self)
1848 }
1849}
1850
1851impl From<&str> for Comment {
1852 fn from(value: &str) -> Self {
1853 Comment::new(value).unwrap()
1854 }
1855}
1856
1857impl From<String> for Comment {
1858 fn from(value: String) -> Self {
1859 Comment::new(value).unwrap()
1860 }
1861}
1862
1863impl PartialOrd for Comment {
1864 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1865 self.comment.partial_cmp(&other.comment)
1866 }
1867}
1868
1869impl PartialEq<Self> for Comment {
1870 fn eq(&self, other: &Self) -> bool {
1871 self.comment.eq(&other.comment)
1872 }
1873}
1874
1875impl Hash for Comment {
1876 fn hash<H: Hasher>(&self, state: &mut H) {
1877 self.comment.hash(state)
1878 }
1879}
1880
1881impl std::fmt::Display for Comment {
1882 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1883 write!(f, "{}", self.to_string_with_indent(" "))
1884 }
1885}
1886
1887impl std::fmt::Debug for Comment {
1888 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1889 write!(f, "{}", self.to_string_with_indent(" "))
1890 }
1891}
1892
1893#[derive(Clone)]
1895pub struct CData{
1896 cdata: String
1898}
1899
1900impl CData {
1901 pub fn new(cdata: impl Into<String>) -> Result<Self, InvalidContent> {
1903 let content: String = cdata.into();
1904 if content.contains("]]>") {
1905 Err(InvalidContent::new("CDATA cannot contain ']]>' as content"))
1906 } else {
1907 Ok(Self { cdata: content })
1908 }
1909 }
1910
1911 pub fn set_text(&mut self, content: impl Into<String>) -> Result<(), InvalidContent> {
1913 let content = content.into();
1914 if content.contains("]]>") {
1915 Err(InvalidContent::new("CDATA cannot contain ']]>'"))
1916 } else {
1917 self.cdata = content.into();
1918 Ok(())
1919 }
1920 }
1921
1922 pub fn get_content(&self) -> &str {
1924 self.cdata.as_str()
1925 }
1926}
1927
1928impl Node for CData {
1929
1930 fn text(&self) -> String {
1931 self.cdata.clone()
1932 }
1933
1934 fn is_element(&self) -> bool {
1935 false
1936 }
1937
1938 fn is_text(&self) -> bool {
1939 false
1940 }
1941
1942 fn is_comment(&self) -> bool {
1943 false
1944 }
1945
1946 fn is_cdata(&self) -> bool {
1947 true
1948 }
1949
1950 fn as_element(&self) -> Result<&Element, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Element"))}
1951
1952 fn as_comment(&self) -> Result<&Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Comment"))}
1953
1954 fn as_text(&self) -> Result<&Text, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Text"))}
1955
1956 fn as_cdata(&self) -> Result<&CData, TypeCastError> {Ok(&self)}
1957
1958 fn as_element_mut(&mut self) -> Result<&mut Element, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Element"))}
1959
1960 fn as_comment_mut(&mut self) -> Result<&mut Comment, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Comment"))}
1961
1962 fn as_text_mut(&mut self) -> Result<&mut Text, TypeCastError> {Err(TypeCastError::new("Cannot cast CData as Text"))}
1963
1964 fn as_cdata_mut(&mut self) -> Result<&mut CData, TypeCastError> {Ok(self)}
1965
1966 fn as_node(&self) -> &dyn Node {self}
1967
1968 fn as_node_mut(&mut self) -> &mut dyn Node {self}
1969
1970 fn as_any(&self) -> &dyn Any {self}
1971
1972 fn as_any_mut(&mut self) -> &mut dyn Any{self}
1973
1974 fn to_string_with_indent(&self, _indent: &str) -> String {
1975 format!("<![CDATA[{}]]>", self.cdata)
1976 }
1977
1978 fn boxed(self) -> Box<dyn Node> {
1979 Box::new(self)
1980 }
1981}
1982
1983impl From<&str> for CData {
1984 fn from(value: &str) -> Self {
1985 CData::new(value).unwrap()
1986 }
1987}
1988
1989impl From<String> for CData {
1990 fn from(value: String) -> Self {
1991 CData::new(value).unwrap()
1992 }
1993}
1994
1995impl PartialOrd for CData {
1996 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1997 self.cdata.partial_cmp(&other.cdata)
1998 }
1999}
2000
2001impl PartialEq<Self> for CData {
2002 fn eq(&self, other: &Self) -> bool {
2003 self.cdata.eq(&other.cdata)
2004 }
2005}
2006
2007impl Hash for CData {
2008 fn hash<H: Hasher>(&self, state: &mut H) {
2009 self.cdata.hash(state)
2010 }
2011}
2012
2013impl std::fmt::Display for CData {
2014 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2015 write!(f, "{}", self.to_string_with_indent(" "))
2016 }
2017}
2018
2019impl std::fmt::Debug for CData {
2020 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2021 write!(f, "{}", self.to_string_with_indent(" "))
2022 }
2023}
2024
2025
2026#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2030pub struct Declaration {
2031 decl_str: String
2032}
2033
2034impl Declaration {
2035 pub fn from_str(decl: &str) -> Result<Self, KissXmlError> {
2037 let buffer: String = decl.trim().to_string();
2039 if buffer.starts_with("<?") && buffer.ends_with("?>"){
2040 Ok(Self{decl_str: buffer.strip_prefix("<?").unwrap().strip_suffix("?>").unwrap().to_string()})
2041 } else {
2042 Err(ParsingError::new("Invalid XML declaration syntax").into())
2043 }
2044 }
2045 pub fn new() -> Self {
2047 Self::default()
2048 }
2049}
2050
2051impl Default for Declaration {
2052 fn default() -> Self {
2053 Declaration::from_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#).unwrap()
2054 }
2055}
2056
2057impl std::fmt::Display for Declaration {
2058 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2059 write!(f, "<?{}?>", self.decl_str)
2060 }
2061}
2062
2063impl std::fmt::Debug for Declaration {
2064 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2065 write!(f, "<?{}?>", self.decl_str)
2066 }
2067}
2068
2069#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2073pub struct DTD {
2074 dtd_str: String
2075}
2076
2077impl DTD {
2078 pub fn from_string(text: impl Into<String>) -> Result<DTD, KissXmlError> {
2080 let buffer: String = text.into().trim().to_string();
2082 if buffer.starts_with("<!DOCTYPE") && buffer.ends_with(">"){
2083 Ok(Self{dtd_str: buffer.strip_prefix("<!DOCTYPE").unwrap().strip_suffix(">").unwrap().to_string()})
2084 } else {
2085 Err(ParsingError::new("Invalid DTD syntax").into())
2086 }
2087 }
2088}
2089
2090impl std::fmt::Display for DTD {
2091 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2092 write!(f, "{}", self.dtd_str)
2093 }
2094}