1use rusty_xml_parser::{default_parse_options, xml_read_memory};
4use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
5use std::collections::HashMap;
6
7const XS: &str = "http://www.w3.org/2001/XMLSchema";
8
9#[derive(Clone, Debug)]
10struct ElementDecl {
11 name: String,
12 min: u32,
13 max: u32,
14 attrs: Vec<AttrDecl>,
15 content: Content,
16}
17
18#[derive(Clone, Debug)]
19struct AttrDecl {
20 name: String,
21 required: bool,
22}
23
24#[derive(Clone, Debug)]
25enum Content {
26 Any,
27 Empty,
28 Text,
29 Sequence(Vec<ElementDecl>),
30 Choice(Vec<ElementDecl>),
31}
32
33struct Schema {
34 elements: HashMap<String, ElementDecl>,
35}
36
37#[doc(alias = "xmlSchemaValidateDoc")]
39pub fn xml_schema_validate_doc(xsd: &[u8], doc: &XmlDoc) -> Result<(), String> {
40 let sdoc = xml_read_memory(xsd, None, None, default_parse_options()).map_err(|e| e.to_string())?;
41 let schema = compile(&sdoc)?;
42 let root = doc.xml_doc_get_root_element().ok_or("no root")?;
43 let name = doc.name(root);
44 let decl = schema
45 .elements
46 .get(name)
47 .ok_or_else(|| format!("element {name} not declared"))?;
48 check_element(doc, root, decl, &schema)
49}
50
51fn is_xs(doc: &XmlDoc, id: NodeId, name: &str) -> bool {
52 doc.kind(id) == NodeKind::Element
53 && doc.name(id) == name
54 && (doc.ns_uri(id) == Some(XS) || doc.ns_uri(id).is_none())
55}
56
57fn compile(doc: &XmlDoc) -> Result<Schema, String> {
58 let root = doc.xml_doc_get_root_element().ok_or("empty schema")?;
59 let mut elements = HashMap::new();
60 let mut c = doc.first_child(root);
61 while let Some(x) = c {
62 if is_xs(doc, x, "element") {
63 if let Some(d) = compile_element(doc, x) {
64 elements.insert(d.name.clone(), d);
65 }
66 }
67 c = doc.next_sibling(x);
68 }
69 Ok(Schema { elements })
70}
71
72fn compile_element(doc: &XmlDoc, id: NodeId) -> Option<ElementDecl> {
73 let name = doc.xml_get_prop(id, "name")?;
74 let min = doc
75 .xml_get_prop(id, "minOccurs")
76 .and_then(|s| s.parse().ok())
77 .unwrap_or(1);
78 let max = match doc.xml_get_prop(id, "maxOccurs").as_deref() {
79 Some("unbounded") => u32::MAX,
80 Some(s) => s.parse().unwrap_or(1),
81 None => 1,
82 };
83 let mut attrs = Vec::new();
84 let mut content = Content::Text;
85 if let Some(ct) = find(doc, id, "complexType") {
86 content = Content::Empty;
87 let mut ch = doc.first_child(ct);
88 while let Some(x) = ch {
89 if is_xs(doc, x, "sequence") {
90 content = Content::Sequence(child_elements(doc, x));
91 } else if is_xs(doc, x, "choice") {
92 content = Content::Choice(child_elements(doc, x));
93 } else if is_xs(doc, x, "all") {
94 content = Content::Sequence(child_elements(doc, x));
95 } else if is_xs(doc, x, "attribute") {
96 if let Some(n) = doc.xml_get_prop(x, "name") {
97 let required = doc.xml_get_prop(x, "use").as_deref() == Some("required");
98 attrs.push(AttrDecl { name: n, required });
99 }
100 } else if is_xs(doc, x, "simpleContent") || is_xs(doc, x, "complexContent") {
101 content = Content::Any;
102 }
103 ch = doc.next_sibling(x);
104 }
105 } else if find(doc, id, "simpleType").is_some() {
106 content = Content::Text;
107 }
108 Some(ElementDecl {
109 name,
110 min,
111 max,
112 attrs,
113 content,
114 })
115}
116
117fn child_elements(doc: &XmlDoc, id: NodeId) -> Vec<ElementDecl> {
118 let mut v = Vec::new();
119 let mut c = doc.first_child(id);
120 while let Some(x) = c {
121 if is_xs(doc, x, "element") {
122 if let Some(d) = compile_element(doc, x) {
123 v.push(d);
124 } else if let Some(r) = doc.xml_get_prop(x, "ref") {
125 let local = r.rsplit(':').next().unwrap_or(&r).to_string();
126 let min = doc
127 .xml_get_prop(x, "minOccurs")
128 .and_then(|s| s.parse().ok())
129 .unwrap_or(1);
130 let max = match doc.xml_get_prop(x, "maxOccurs").as_deref() {
131 Some("unbounded") => u32::MAX,
132 Some(s) => s.parse().unwrap_or(1),
133 None => 1,
134 };
135 v.push(ElementDecl {
136 name: local,
137 min,
138 max,
139 attrs: vec![],
140 content: Content::Any,
141 });
142 }
143 }
144 c = doc.next_sibling(x);
145 }
146 v
147}
148
149fn find(doc: &XmlDoc, id: NodeId, name: &str) -> Option<NodeId> {
150 let mut c = doc.first_child(id);
151 while let Some(x) = c {
152 if is_xs(doc, x, name) {
153 return Some(x);
154 }
155 c = doc.next_sibling(x);
156 }
157 None
158}
159
160fn check_element(doc: &XmlDoc, id: NodeId, decl: &ElementDecl, schema: &Schema) -> Result<(), String> {
161 for a in &decl.attrs {
162 if a.required && doc.xml_get_prop(id, &a.name).is_none() {
163 return Err(format!("required attribute {} missing", a.name));
164 }
165 }
166 let kids: Vec<NodeId> = {
167 let mut v = Vec::new();
168 let mut c = doc.first_child(id);
169 while let Some(x) = c {
170 if doc.kind(x) == NodeKind::Element {
171 v.push(x);
172 }
173 c = doc.next_sibling(x);
174 }
175 v
176 };
177 match &decl.content {
178 Content::Any | Content::Text => {}
179 Content::Empty => {
180 if !kids.is_empty() {
181 return Err(format!("{} must be empty", decl.name));
182 }
183 }
184 Content::Sequence(seq) => {
185 let mut i = 0;
186 for part in seq {
187 let mut seen = 0u32;
188 while i < kids.len() && doc.name(kids[i]) == part.name {
189 let sub = schema.elements.get(&part.name).unwrap_or(part);
190 check_element(doc, kids[i], sub, schema)?;
191 i += 1;
192 seen += 1;
193 if seen == part.max {
194 break;
195 }
196 }
197 if seen < part.min {
198 return Err(format!("need {} of {}", part.min, part.name));
199 }
200 }
201 if i != kids.len() {
202 return Err("extra children in sequence".into());
203 }
204 }
205 Content::Choice(alts) => {
206 if kids.len() != 1 {
207 return Err("choice expects one child".into());
208 }
209 let n = doc.name(kids[0]);
210 let part = alts.iter().find(|a| a.name == n).ok_or("choice mismatch")?;
211 let sub = schema.elements.get(&part.name).unwrap_or(part);
212 check_element(doc, kids[0], sub, schema)?;
213 }
214 }
215 Ok(())
216}