rusty_xml_parser/
xinclude.rs1use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
4use crate::error::XmlError;
5use crate::parse::{default_parse_options, xml_read_memory};
6
7const XI: &str = "http://www.w3.org/2001/XInclude";
8
9#[doc(alias = "xmlXIncludeProcess")]
11pub fn xml_xinclude_process<F>(doc: &mut XmlDoc, mut loader: F) -> Result<i32, XmlError>
12where
13 F: FnMut(&str) -> Result<Vec<u8>, String>,
14{
15 xml_xinclude_process_tree(doc, NodeId::DOCUMENT, &mut loader)
16}
17
18fn xml_xinclude_process_tree<F>(
19 doc: &mut XmlDoc,
20 start: NodeId,
21 loader: &mut F,
22) -> Result<i32, XmlError>
23where
24 F: FnMut(&str) -> Result<Vec<u8>, String>,
25{
26 let mut subs: Vec<NodeId> = Vec::new();
27 collect_includes(doc, start, &mut subs);
28 let mut n = 0i32;
29 for id in subs {
30 if replace_include(doc, id, loader)? {
31 n += 1;
32 }
33 }
34 Ok(n)
35}
36
37fn collect_includes(doc: &XmlDoc, id: NodeId, out: &mut Vec<NodeId>) {
38 if doc.kind(id) == NodeKind::Element
39 && doc.name(id) == "include"
40 && (doc.ns_uri(id) == Some(XI) || doc.prefix(id) == Some("xi"))
41 {
42 out.push(id);
43 return; }
45 let mut c = doc.first_child(id);
46 while let Some(x) = c {
47 collect_includes(doc, x, out);
48 c = doc.next_sibling(x);
49 }
50}
51
52fn replace_include<F>(doc: &mut XmlDoc, id: NodeId, loader: &mut F) -> Result<bool, XmlError>
53where
54 F: FnMut(&str) -> Result<Vec<u8>, String>,
55{
56 let href = doc.xml_get_prop(id, "href");
57 let parse = doc.xml_get_prop(id, "parse").unwrap_or_else(|| "xml".into());
58 let parent = doc.parent(id).unwrap_or(NodeId::DOCUMENT);
59 if let Some(h) = href {
60 match loader(&h) {
61 Ok(bytes) => {
62 if parse == "text" {
63 let text = String::from_utf8_lossy(&bytes).into_owned();
64 let n = doc.alloc(NodeKind::Text, "#text");
65 doc.node_mut(n).content = text;
66 doc.xml_add_prev_sibling(id, n);
67 doc.xml_unlink_node(id);
68 return Ok(true);
69 }
70 let included = xml_read_memory(&bytes, Some(&h), None, default_parse_options())?;
71 if let Some(root) = included.xml_doc_get_root_element() {
72 graft(doc, parent, id, &included, root);
73 doc.xml_unlink_node(id);
74 return Ok(true);
75 }
76 }
77 Err(_) => {
78 if let Some(fb) = find_fallback(doc, id) {
79 let mut kids = Vec::new();
80 let mut c = doc.first_child(fb);
81 while let Some(x) = c {
82 kids.push(x);
83 c = doc.next_sibling(x);
84 }
85 for k in kids {
86 doc.xml_unlink_node(k);
87 doc.xml_add_prev_sibling(id, k);
88 }
89 doc.xml_unlink_node(id);
90 return Ok(true);
91 }
92 return Err(XmlError::new(
93 crate::error::XML_ERR_DOCUMENT_EMPTY,
94 format!("XInclude failed to load {h}"),
95 0,
96 0,
97 ));
98 }
99 }
100 }
101 Ok(false)
102}
103
104fn find_fallback(doc: &XmlDoc, include: NodeId) -> Option<NodeId> {
105 let mut c = doc.first_child(include);
106 while let Some(x) = c {
107 if doc.kind(x) == NodeKind::Element && doc.name(x) == "fallback" {
108 return Some(x);
109 }
110 c = doc.next_sibling(x);
111 }
112 None
113}
114
115fn graft(dst: &mut XmlDoc, _parent: NodeId, before: NodeId, src: &XmlDoc, src_id: NodeId) {
116 let new = copy_node(dst, src, src_id);
117 dst.xml_add_prev_sibling(before, new);
118}
119
120fn copy_node(dst: &mut XmlDoc, src: &XmlDoc, id: NodeId) -> NodeId {
121 let kind = src.kind(id);
122 let n = dst.alloc(kind, src.name(id));
123 {
124 let node = dst.node_mut(n);
125 node.prefix = src.prefix(id).map(str::to_string);
126 node.ns_uri = src.ns_uri(id).map(str::to_string);
127 node.content = src.content(id).to_string();
128 node.ns_defs = src.ns_defs(id).to_vec();
129 }
130 let mut a = src.first_attr(id);
131 while let Some(x) = a {
132 let an = copy_node(dst, src, x);
133 dst.xml_set_prop(n, src.name(x), src.content(x));
134 let _ = an;
135 a = src.next_sibling(x);
136 }
137 let mut c = src.first_child(id);
138 while let Some(x) = c {
139 let ch = copy_node(dst, src, x);
140 dst.xml_add_child(n, ch);
141 c = src.next_sibling(x);
142 }
143 n
144}