1#![forbid(unsafe_code)]
4
5mod c14n;
6mod rng;
7mod xsd;
8mod schematron;
9
10use rusty_xml_tree::{AttrDefault, ElementDecl, NodeId, NodeKind, XmlDoc, XmlDtd};
11
12pub use c14n::*;
13pub use rng::*;
14pub use xsd::*;
15pub use schematron::*;
16
17#[doc(alias = "xmlValidateDocument")]
19pub fn xml_validate_document(doc: &XmlDoc) -> Result<(), String> {
20 let dtd = doc.dtd.as_ref().ok_or("no DTD")?;
21 xml_validate_dtd(doc, dtd)
22}
23
24#[doc(alias = "xmlValidateDtd")]
26pub fn xml_validate_dtd(doc: &XmlDoc, dtd: &XmlDtd) -> Result<(), String> {
27 let root = doc
28 .xml_doc_get_root_element()
29 .ok_or("document has no root")?;
30 if let Some(n) = &dtd.name {
31 if doc.name(root) != n.as_str() {
32 return Err(format!("root element {} does not match DOCTYPE {n}", doc.name(root)));
33 }
34 }
35 validate_element(doc, root, dtd)?;
36 Ok(())
37}
38
39fn validate_element(doc: &XmlDoc, id: NodeId, dtd: &XmlDtd) -> Result<(), String> {
40 if doc.kind(id) != NodeKind::Element {
41 return Ok(());
42 }
43 let name = doc.name(id).to_string();
44 if let Some(decl) = dtd.elements.get(&name) {
45 match decl {
46 ElementDecl::Empty => {
47 if doc.first_child(id).is_some() {
48 return Err(format!("element {name} must be EMPTY"));
49 }
50 }
51 ElementDecl::Any => {}
52 ElementDecl::Mixed(_) => {
53 let mut c = doc.first_child(id);
54 while let Some(x) = c {
55 match doc.kind(x) {
56 NodeKind::Element => {
57 if let ElementDecl::Mixed(allowed) = decl {
58 if !allowed.is_empty() && !allowed.iter().any(|n| n == doc.name(x)) {
59 return Err(format!("element {} not allowed in mixed {name}", doc.name(x)));
60 }
61 }
62 validate_element(doc, x, dtd)?;
63 }
64 NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi => {}
65 _ => {}
66 }
67 c = doc.next_sibling(x);
68 }
69 }
70 ElementDecl::Children(spec) => {
71 let kids: Vec<String> = {
72 let mut v = Vec::new();
73 let mut c = doc.first_child(id);
74 while let Some(x) = c {
75 if doc.kind(x) == NodeKind::Element {
76 v.push(doc.name(x).to_string());
77 validate_element(doc, x, dtd)?;
78 } else if doc.kind(x) == NodeKind::Text && !doc.xml_is_blank_node(x) {
79 return Err(format!("character data not allowed in {name}"));
80 }
81 c = doc.next_sibling(x);
82 }
83 v
84 };
85 if !match_children_spec(spec, &kids) {
86 return Err(format!("content of {name} does not match {spec}"));
87 }
88 }
89 }
90 }
91 for ((elem, aname), ad) in &dtd.attributes {
92 if elem != &name {
93 continue;
94 }
95 let have = doc.xml_get_prop(id, aname);
96 match ad.default {
97 AttrDefault::Required if have.is_none() => {
98 return Err(format!("attribute {aname} of {name} is required"));
99 }
100 AttrDefault::Fixed => {
101 if let (Some(v), Some(fix)) = (&have, &ad.default_value) {
102 if v != fix {
103 return Err(format!("attribute {aname} must be {fix}"));
104 }
105 }
106 }
107 _ => {}
108 }
109 if let Some(v) = &have {
110 if !ad.enumerated.is_empty() && !ad.enumerated.iter().any(|e| e == v) {
111 return Err(format!("attribute {aname} value not in enumeration"));
112 }
113 if ad.att_type == "ID" {
114 }
116 }
117 }
118 Ok(())
119}
120
121fn match_children_spec(spec: &str, kids: &[String]) -> bool {
122 let toks = tokenize_content(spec);
123 match_seq(&toks, kids, 0).contains(&kids.len())
124}
125
126#[derive(Clone, Debug)]
127enum Tok {
128 Name(String),
129 Seq(Vec<Tok>),
130 Choice(Vec<Tok>),
131 Star,
132 Plus,
133 Q,
134}
135
136fn tokenize_content(spec: &str) -> Vec<Tok> {
137 let p = spec.trim();
139 fn parse_choice<'a>(p: &mut &'a str) -> Vec<Tok> {
140 let mut alts = vec![Tok::Seq(parse_seq(p))];
141 loop {
142 skip(p);
143 if p.starts_with('|') {
144 *p = &p[1..];
145 alts.push(Tok::Seq(parse_seq(p)));
146 } else {
147 break;
148 }
149 }
150 alts
151 }
152 fn parse_seq<'a>(p: &mut &'a str) -> Vec<Tok> {
153 let mut v = Vec::new();
154 loop {
155 skip(p);
156 if p.is_empty() || p.starts_with('|') || p.starts_with(')') {
157 break;
158 }
159 if p.starts_with(',') {
160 *p = &p[1..];
161 continue;
162 }
163 v.push(parse_particle(p));
164 }
165 v
166 }
167 fn parse_particle<'a>(p: &mut &'a str) -> Tok {
168 skip(p);
169 let mut inner = if p.starts_with('(') {
170 *p = &p[1..];
171 let c = parse_choice(p);
172 skip(p);
173 if p.starts_with(')') {
174 *p = &p[1..];
175 }
176 if c.len() == 1 {
177 Tok::Seq(match c.into_iter().next().unwrap() {
178 Tok::Seq(s) => s,
179 other => vec![other],
180 })
181 } else {
182 Tok::Choice(c)
183 }
184 } else {
185 let name = take_name(p);
186 Tok::Name(name)
187 };
188 skip(p);
189 inner = match p.chars().next() {
190 Some('*') => {
191 *p = &p[1..];
192 Tok::Seq(vec![inner, Tok::Star])
193 }
194 Some('+') => {
195 *p = &p[1..];
196 Tok::Seq(vec![inner, Tok::Plus])
197 }
198 Some('?') => {
199 *p = &p[1..];
200 Tok::Seq(vec![inner, Tok::Q])
201 }
202 _ => inner,
203 };
204 inner
205 }
206 fn take_name<'a>(p: &mut &'a str) -> String {
207 let bytes = p.as_bytes();
208 let mut i = 0;
209 while i < bytes.len() {
210 let c = bytes[i] as char;
211 if c.is_ascii_alphanumeric() || "-._:".contains(c) {
212 i += 1;
213 } else {
214 break;
215 }
216 }
217 let s = p[..i].to_string();
218 *p = &p[i..];
219 s
220 }
221 fn skip(p: &mut &str) {
222 *p = p.trim_start();
223 }
224 let mut tmp = p;
225 parse_choice(&mut tmp)
226}
227
228fn match_seq(toks: &[Tok], kids: &[String], i: usize) -> Vec<usize> {
229 if toks.is_empty() {
230 return vec![i];
231 }
232 match &toks[0] {
233 Tok::Star => {
234 let rest = &toks[1..];
235 match_seq(rest, kids, i)
237 }
238 Tok::Plus | Tok::Q => match_seq(&toks[1..], kids, i),
239 Tok::Name(n) => {
240 if i < kids.len() && &kids[i] == n {
241 match_seq(&toks[1..], kids, i + 1)
242 } else {
243 vec![]
244 }
245 }
246 Tok::Seq(inner) => {
247 let (body, quant) = split_quant(inner);
248 apply_quant(body, quant, &toks[1..], kids, i)
249 }
250 Tok::Choice(alts) => {
251 let mut out = Vec::new();
252 for a in alts {
253 let one = match_seq(&[a.clone()], kids, i);
254 for pos in one {
255 out.extend(match_seq(&toks[1..], kids, pos));
256 }
257 }
258 out.sort();
259 out.dedup();
260 out
261 }
262 }
263}
264
265enum Quant {
266 One,
267 Q,
268 Star,
269 Plus,
270}
271
272fn split_quant(inner: &[Tok]) -> (&[Tok], Quant) {
273 if inner.len() >= 2 {
274 match inner.last() {
275 Some(Tok::Star) => return (&inner[..inner.len() - 1], Quant::Star),
276 Some(Tok::Plus) => return (&inner[..inner.len() - 1], Quant::Plus),
277 Some(Tok::Q) => return (&inner[..inner.len() - 1], Quant::Q),
278 _ => {}
279 }
280 }
281 (inner, Quant::One)
282}
283
284fn apply_quant(body: &[Tok], q: Quant, rest: &[Tok], kids: &[String], i: usize) -> Vec<usize> {
285 match q {
286 Quant::One => {
287 let mut out = Vec::new();
288 for p in match_seq(body, kids, i) {
289 out.extend(match_seq(rest, kids, p));
290 }
291 out
292 }
293 Quant::Q => {
294 let mut out = match_seq(rest, kids, i);
295 for p in match_seq(body, kids, i) {
296 out.extend(match_seq(rest, kids, p));
297 }
298 out.sort();
299 out.dedup();
300 out
301 }
302 Quant::Star => {
303 let mut out = match_seq(rest, kids, i);
304 let mut frontier = vec![i];
305 while let Some(p) = frontier.pop() {
306 for n in match_seq(body, kids, p) {
307 if n > p {
308 out.extend(match_seq(rest, kids, n));
309 frontier.push(n);
310 }
311 }
312 }
313 out.sort();
314 out.dedup();
315 out
316 }
317 Quant::Plus => {
318 let mut out = Vec::new();
319 for p in match_seq(body, kids, i) {
320 out.extend(apply_quant(body, Quant::Star, rest, kids, p));
321 }
322 out.sort();
323 out.dedup();
324 out
325 }
326 }
327}