1use std::collections::HashMap;
72use std::rc::Rc;
73
74use crate::item::{Item, Node, NodeType, Sequence};
75use crate::output::{OutputDefinition, OutputSpec};
76use crate::parser::avt::parse as parse_avt;
77use crate::parser::xpath::parse;
78use crate::pattern::{Branch, Pattern};
79use crate::transform::callable::{ActualParameters, Callable, FormalParameters};
80use crate::transform::context::{Context, ContextBuilder};
81use crate::transform::numbers::{Level, Numbering};
82use crate::transform::template::Template;
83use crate::transform::{
84 Axis, Grouping, KindTest, NameTest, NodeMatch, NodeTest, Order, Transform, WildcardOrName,
85 WildcardOrNamespaceUri, in_scope_namespaces,
86};
87use crate::value::Value;
88use crate::xdmerror::*;
89use qualname::{NamespaceUri, NcName, QName};
90use std::convert::TryFrom;
91use std::sync::LazyLock;
92use url::Url;
93
94static XSLTNS: LazyLock<Option<NamespaceUri>> =
96 LazyLock::new(|| Some(NamespaceUri::try_from("http://www.w3.org/1999/XSL/Transform").unwrap()));
97static XRUSTNS: LazyLock<Option<NamespaceUri>> =
98 LazyLock::new(|| Some(NamespaceUri::try_from("http://github.com/ballsteve/xrust").unwrap()));
99static XSLSTYLESHEET: LazyLock<QName> = LazyLock::new(|| {
100 QName::new_from_parts(NcName::try_from("stylesheet").unwrap(), XSLTNS.clone())
101});
102static XSLTRANSFORM: LazyLock<QName> =
103 LazyLock::new(|| QName::new_from_parts(NcName::try_from("transform").unwrap(), XSLTNS.clone()));
104static XSLOUTPUT: LazyLock<QName> =
105 LazyLock::new(|| QName::new_from_parts(NcName::try_from("output").unwrap(), XSLTNS.clone()));
106static XSLINCLUDE: LazyLock<QName> =
107 LazyLock::new(|| QName::new_from_parts(NcName::try_from("include").unwrap(), XSLTNS.clone()));
108static XRUSTIMPORT: LazyLock<QName> =
109 LazyLock::new(|| QName::new_from_parts(NcName::try_from("import").unwrap(), XRUSTNS.clone()));
110static XSLIMPORT: LazyLock<QName> =
111 LazyLock::new(|| QName::new_from_parts(NcName::try_from("import").unwrap(), XSLTNS.clone()));
112static XSLATTRIBUTESET: LazyLock<QName> = LazyLock::new(|| {
113 QName::new_from_parts(NcName::try_from("attribute-set").unwrap(), XSLTNS.clone())
114});
115static XSLATTRIBUTE: LazyLock<QName> =
116 LazyLock::new(|| QName::new_from_parts(NcName::try_from("attribute").unwrap(), XSLTNS.clone()));
117static XSLTEMPLATE: LazyLock<QName> =
118 LazyLock::new(|| QName::new_from_parts(NcName::try_from("template").unwrap(), XSLTNS.clone()));
119static XSLKEY: LazyLock<QName> =
120 LazyLock::new(|| QName::new_from_parts(NcName::try_from("key").unwrap(), XSLTNS.clone()));
121static XSLPARAM: LazyLock<QName> =
122 LazyLock::new(|| QName::new_from_parts(NcName::try_from("param").unwrap(), XSLTNS.clone()));
123static XSLFUNCTION: LazyLock<QName> =
124 LazyLock::new(|| QName::new_from_parts(NcName::try_from("function").unwrap(), XSLTNS.clone()));
125static XSLVARIABLE: LazyLock<QName> =
126 LazyLock::new(|| QName::new_from_parts(NcName::try_from("variable").unwrap(), XSLTNS.clone()));
127static XSLVALUEOF: LazyLock<QName> =
128 LazyLock::new(|| QName::new_from_parts(NcName::try_from("value-of").unwrap(), XSLTNS.clone()));
129static XSLTEXT: LazyLock<QName> =
130 LazyLock::new(|| QName::new_from_parts(NcName::try_from("text").unwrap(), XSLTNS.clone()));
131static XSLAPPLYTEMPLATES: LazyLock<QName> = LazyLock::new(|| {
132 QName::new_from_parts(NcName::try_from("apply-templates").unwrap(), XSLTNS.clone())
133});
134static XSLAPPLYIMPORTS: LazyLock<QName> = LazyLock::new(|| {
135 QName::new_from_parts(NcName::try_from("apply-imports").unwrap(), XSLTNS.clone())
136});
137static XSLSEQUENCE: LazyLock<QName> =
138 LazyLock::new(|| QName::new_from_parts(NcName::try_from("sequence").unwrap(), XSLTNS.clone()));
139static XSLIF: LazyLock<QName> =
140 LazyLock::new(|| QName::new_from_parts(NcName::try_from("if").unwrap(), XSLTNS.clone()));
141static XSLCHOOSE: LazyLock<QName> =
142 LazyLock::new(|| QName::new_from_parts(NcName::try_from("choose").unwrap(), XSLTNS.clone()));
143static XSLWHEN: LazyLock<QName> =
144 LazyLock::new(|| QName::new_from_parts(NcName::try_from("when").unwrap(), XSLTNS.clone()));
145static XSLOTHERWISE: LazyLock<QName> =
146 LazyLock::new(|| QName::new_from_parts(NcName::try_from("otherwise").unwrap(), XSLTNS.clone()));
147static XSLFOREACH: LazyLock<QName> =
148 LazyLock::new(|| QName::new_from_parts(NcName::try_from("for-each").unwrap(), XSLTNS.clone()));
149static XSLFOREACHGROUP: LazyLock<QName> = LazyLock::new(|| {
150 QName::new_from_parts(NcName::try_from("for-each-group").unwrap(), XSLTNS.clone())
151});
152static XSLCOPY: LazyLock<QName> =
153 LazyLock::new(|| QName::new_from_parts(NcName::try_from("copy").unwrap(), XSLTNS.clone()));
154static XSLCOPYOF: LazyLock<QName> =
155 LazyLock::new(|| QName::new_from_parts(NcName::try_from("copy-of").unwrap(), XSLTNS.clone()));
156static XSLCALLTEMPLATE: LazyLock<QName> = LazyLock::new(|| {
157 QName::new_from_parts(NcName::try_from("call-template").unwrap(), XSLTNS.clone())
158});
159static XSLWITHPARAM: LazyLock<QName> = LazyLock::new(|| {
160 QName::new_from_parts(NcName::try_from("with-param").unwrap(), XSLTNS.clone())
161});
162static XSLELEMENT: LazyLock<QName> =
163 LazyLock::new(|| QName::new_from_parts(NcName::try_from("element").unwrap(), XSLTNS.clone()));
164static XSLCOMMENT: LazyLock<QName> =
165 LazyLock::new(|| QName::new_from_parts(NcName::try_from("comment").unwrap(), XSLTNS.clone()));
166static XSLPROCESSINGINSTRUCTION: LazyLock<QName> = LazyLock::new(|| {
167 QName::new_from_parts(
168 NcName::try_from("processing-instruction").unwrap(),
169 XSLTNS.clone(),
170 )
171});
172static XSLMESSAGE: LazyLock<QName> =
173 LazyLock::new(|| QName::new_from_parts(NcName::try_from("message").unwrap(), XSLTNS.clone()));
174static XSLNUMBER: LazyLock<QName> =
175 LazyLock::new(|| QName::new_from_parts(NcName::try_from("number").unwrap(), XSLTNS.clone()));
176static XSLDECIMALFORMAT: LazyLock<QName> = LazyLock::new(|| {
177 QName::new_from_parts(NcName::try_from("decimal-format").unwrap(), XSLTNS.clone())
178});
179static XSLSORT: LazyLock<QName> =
180 LazyLock::new(|| QName::new_from_parts(NcName::try_from("sort").unwrap(), XSLTNS.clone()));
181static XSLSTRIPSPACE: LazyLock<QName> = LazyLock::new(|| {
182 QName::new_from_parts(NcName::try_from("strip-space").unwrap(), XSLTNS.clone())
183});
184static XSLPRESERVESPACE: LazyLock<QName> = LazyLock::new(|| {
185 QName::new_from_parts(NcName::try_from("preserve-space").unwrap(), XSLTNS.clone())
186});
187static ATTRINDENT: LazyLock<QName> =
188 LazyLock::new(|| QName::new_from_parts(NcName::try_from("indent").unwrap(), None));
189static ATTRHREF: LazyLock<QName> =
190 LazyLock::new(|| QName::new_from_parts(NcName::try_from("href").unwrap(), None));
191static ATTRNAME: LazyLock<QName> =
192 LazyLock::new(|| QName::new_from_parts(NcName::try_from("name").unwrap(), None));
193static ATTRMATCH: LazyLock<QName> =
194 LazyLock::new(|| QName::new_from_parts(NcName::try_from("match").unwrap(), None));
195static ATTRMODE: LazyLock<QName> =
196 LazyLock::new(|| QName::new_from_parts(NcName::try_from("mode").unwrap(), None));
197static ATTRPRIORITY: LazyLock<QName> =
198 LazyLock::new(|| QName::new_from_parts(NcName::try_from("priority").unwrap(), None));
199static ATTRUSE: LazyLock<QName> =
200 LazyLock::new(|| QName::new_from_parts(NcName::try_from("use").unwrap(), None));
201static ATTRSELECT: LazyLock<QName> =
202 LazyLock::new(|| QName::new_from_parts(NcName::try_from("select").unwrap(), None));
203static ATTRDOE: LazyLock<QName> = LazyLock::new(|| {
204 QName::new_from_parts(NcName::try_from("disable-output-escaping").unwrap(), None)
205});
206static ATTRTEST: LazyLock<QName> =
207 LazyLock::new(|| QName::new_from_parts(NcName::try_from("test").unwrap(), None));
208static ATTRGROUPBY: LazyLock<QName> =
209 LazyLock::new(|| QName::new_from_parts(NcName::try_from("group-by").unwrap(), None));
210static ATTRGROUPADJACENT: LazyLock<QName> =
211 LazyLock::new(|| QName::new_from_parts(NcName::try_from("group-adjacent").unwrap(), None));
212static ATTRGROUPSTARTINGWITH: LazyLock<QName> =
213 LazyLock::new(|| QName::new_from_parts(NcName::try_from("group-starting-with").unwrap(), None));
214static ATTRGROUPENDINGWITH: LazyLock<QName> =
215 LazyLock::new(|| QName::new_from_parts(NcName::try_from("group-ending-with").unwrap(), None));
216static XSLATTRUSEATTRIBUTESETS: LazyLock<QName> = LazyLock::new(|| {
219 QName::new_from_parts(
220 NcName::try_from("use-attribute-sets").unwrap(),
221 XSLTNS.clone(),
222 )
223});
224static ATTRTERMINATE: LazyLock<QName> =
225 LazyLock::new(|| QName::new_from_parts(NcName::try_from("terminate").unwrap(), None));
226static ATTRVALUE: LazyLock<QName> =
227 LazyLock::new(|| QName::new_from_parts(NcName::try_from("value").unwrap(), None));
228static ATTRLEVEL: LazyLock<QName> =
229 LazyLock::new(|| QName::new_from_parts(NcName::try_from("level").unwrap(), None));
230static ATTRCOUNT: LazyLock<QName> =
231 LazyLock::new(|| QName::new_from_parts(NcName::try_from("count").unwrap(), None));
232static ATTRFROM: LazyLock<QName> =
233 LazyLock::new(|| QName::new_from_parts(NcName::try_from("from").unwrap(), None));
234static ATTRFORMAT: LazyLock<QName> =
235 LazyLock::new(|| QName::new_from_parts(NcName::try_from("format").unwrap(), None));
236static ATTRORDER: LazyLock<QName> =
237 LazyLock::new(|| QName::new_from_parts(NcName::try_from("order").unwrap(), None));
238static ATTRELEMENTS: LazyLock<QName> =
239 LazyLock::new(|| QName::new_from_parts(NcName::try_from("elements").unwrap(), None));
240
241pub trait XSLT: Node {
243 fn transform<N: Node, F, G>(
246 &self,
247 src: Rc<Item<N>>,
248 b: Option<Url>,
249 f: F,
250 g: G,
251 ) -> Result<Sequence<N>, Error>
252 where
253 F: Fn(&str) -> Result<N, Error>,
254 G: Fn(&Url) -> Result<String, Error>;
255 }
263
264pub fn from_document<N: Node, F, G>(
271 styledoc: N,
272 base: Option<Url>,
273 f: F,
274 g: G,
275) -> Result<Context<N>, Error>
276where
277 F: Fn(&str) -> Result<N, Error>,
278 G: Fn(&Url) -> Result<String, Error>,
279{
280 let mut rnit = styledoc.child_iter();
283 let stylenode = match rnit.next() {
284 Some(root) => {
285 if !(root
287 .name()
288 .is_some_and(|rn| rn == *XSLSTYLESHEET || rn == *XSLTRANSFORM))
289 {
290 return Result::Err(Error::new(
291 ErrorKind::TypeError,
292 String::from("not an XSLT stylesheet"),
293 ));
294 } else {
295 root
296 }
297 }
298 None => {
299 return Result::Err(Error::new(
300 ErrorKind::TypeError,
301 String::from("document does not have document element"),
302 ));
303 }
304 };
305 if rnit.next().is_some() {
306 return Result::Err(Error::new(
307 ErrorKind::TypeError,
308 String::from("extra element: not an XSLT stylesheet"),
309 ));
310 }
311
312 strip_whitespace(
316 styledoc.clone(),
317 true,
318 &vec![NodeTest::Name(NameTest::Wildcard(
319 WildcardOrNamespaceUri::Wildcard,
320 WildcardOrName::Wildcard,
321 ))],
322 &vec![NodeTest::Name(NameTest::Name(XSLTEXT.clone()))],
323 )?;
324
325 let mut od = OutputDefinition::new();
327 if let Some(c) = stylenode
328 .child_iter()
329 .find(|c| !(c.is_element() && c.name().is_some_and(|cn| cn == *XSLOUTPUT)))
330 {
331 let b: bool = matches!(
332 c.get_attribute(&ATTRINDENT).to_string().as_str(),
333 "yes" | "true" | "1"
334 );
335
336 od.set_indent(b);
337 };
338
339 stylenode
345 .child_iter()
346 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLINCLUDE))
347 .try_for_each(|mut c| {
348 let h = c.get_attribute(&ATTRHREF);
349 let url = match base.clone().map_or_else(
350 || Url::parse(h.to_string().as_str()),
351 |full| full.join(h.to_string().as_str()),
352 ) {
353 Ok(u) => u,
354 Err(_) => {
355 return Result::Err(Error::new(
356 ErrorKind::Unknown,
357 format!(
358 "unable to parse href URL \"{}\" baseurl \"{}\"",
359 h,
360 base.clone()
361 .map_or(String::from("--no base--"), |b| b.to_string())
362 ),
363 ));
364 }
365 };
366 let xml = g(&url)?;
367 let module = f(xml.as_str().trim())?;
368 let moddoc = module.first_child().unwrap();
372 moddoc.child_iter().try_for_each(|mc| {
373 c.insert_before(mc)?;
374 Ok::<(), Error>(())
375 })?;
376 c.pop()?;
378 Ok(())
379 })?;
380
381 stylenode
387 .child_iter()
388 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLIMPORT))
389 .try_for_each(|mut c| {
390 let h = c.get_attribute(&ATTRHREF);
391 let url = match base.clone().map_or_else(
392 || Url::parse(h.to_string().as_str()),
393 |full| full.join(h.to_string().as_str()),
394 ) {
395 Ok(u) => u,
396 Err(_) => {
397 return Result::Err(Error::new(
398 ErrorKind::Unknown,
399 format!(
400 "unable to parse href URL \"{}\" baseurl \"{}\"",
401 h,
402 base.clone()
403 .map_or(String::from("--no base--"), |b| b.to_string())
404 ),
405 ));
406 }
407 };
408 let xml = g(&url)?;
409 let module = f(xml.as_str().trim())?;
410 let moddoc = module.first_child().unwrap();
415 moddoc.child_iter().try_for_each(|mc| {
416 if mc.node_type() == NodeType::Element {
417 let newnode = mc.deep_copy()?;
419 let newat =
420 styledoc.new_attribute(XRUSTIMPORT.clone(), Rc::new(Value::from(1)))?;
421 newnode.add_attribute(newat)?;
422 c.insert_before(newnode)?;
423 } else {
424 let newnode = mc.deep_copy()?;
425 c.insert_before(newnode)?;
426 }
427 Ok::<(), Error>(())
428 })?;
429 c.pop()?;
431 Ok::<(), Error>(())
432 })?;
433
434 let mut attr_sets: HashMap<QName, Vec<Transform<N>>> = HashMap::new();
438
439 stylenode
440 .child_iter()
441 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLATTRIBUTESET))
442 .try_for_each(|c| {
443 let name = c.get_attribute(&ATTRNAME);
444 let eqname = c.to_qname(name.to_string())?;
445 if eqname.to_string().is_empty() {
446 return Err(Error::new(
447 ErrorKind::DynamicAbsent,
448 "attribute sets must have a name",
449 ));
450 }
451 let mut attrs = vec![];
454 c.child_iter()
455 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLATTRIBUTE))
456 .try_for_each(|a| {
457 attrs.push(to_transform(a, &attr_sets)?);
458 Ok(())
459 })?;
460 attr_sets.insert(eqname, attrs);
461 Ok(())
462 })?;
463
464 let mut templates: Vec<Template<N>> = vec![];
469 stylenode
470 .child_iter()
471 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLTEMPLATE))
472 .filter(|c| c.get_attribute_node(&ATTRMATCH).is_some())
473 .try_for_each(|c| {
474 let m = c.get_attribute(&ATTRMATCH);
475 let pat = Pattern::try_from((m.to_string(), c.clone())).map_err(|e| {
476 Error::new(
477 e.kind,
478 format!(
479 "Error parsing match pattern \"{}\": {}",
480 m.to_string(),
481 e.message
482 ),
483 )
484 })?;
485 if pat.is_err() {
486 return Err(pat.get_err().unwrap());
487 }
488 if let Pattern::Selection(Branch::Error(e)) = pat {
489 return Err(e.clone());
490 }
491 let mut body = vec![];
492 let mode = c.get_attribute_node(&ATTRMODE);
493 c.child_iter().try_for_each(|d| {
494 body.push(to_transform(d, &attr_sets)?);
495 Ok::<(), Error>(())
496 })?;
497 let pr = c.get_attribute(&ATTRPRIORITY);
501 let prio: f64 = match pr.to_string().as_str() {
502 "" => {
503 match &pat {
506 Pattern::Predicate(p) => match p {
507 Transform::Empty => -1.0,
508 _ => 1.0,
509 },
510 Pattern::Selection(s) => {
511 if let Branch::Error(e) = s {
512 return Err(e.clone());
513 }
514
515 let (t, nt, q) = s.terminal_node_test();
516 match (t, nt) {
518 (Axis::SelfAttribute, _) => -0.5,
519 (Axis::SelfAxis, Axis::Parent)
520 | (Axis::SelfAxis, Axis::Ancestor)
521 | (Axis::SelfAxis, Axis::AncestorOrSelf) => match q {
522 NodeTest::Name(nm) => match nm {
523 NameTest::Wildcard(_, _) => -0.5,
524 _ => 0.0,
525 },
526 NodeTest::Kind(_kt) => -0.5,
527 },
528 _ => 0.5,
529 }
530 }
531 _ => -1.0,
532 }
533 }
534 _ => pr.to_string().parse::<f64>().unwrap(), };
536 let mut import: usize = 0;
538 let im = c.get_attribute(&XRUSTIMPORT);
539 if im.to_string() != "" {
540 import = im.to_int()? as usize
541 }
542 let mut qmode = None;
543 if let Some(modenode) = mode {
544 qmode = Some(modenode.to_qname(modenode.to_string())?)
545 }
546 templates.push(Template::new(
547 pat,
548 Transform::SequenceItems(body),
549 Some(prio),
550 vec![import],
551 None,
552 qmode,
553 m.to_string(),
554 ));
555 Ok::<(), Error>(())
556 })?;
557
558 let mut keys = vec![];
561 stylenode
562 .child_iter()
563 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLKEY))
564 .try_for_each(|c| {
565 let name = c.get_attribute(&ATTRNAME);
566 let m = c.get_attribute(&ATTRMATCH);
567 let pat = Pattern::try_from(m.to_string())?;
568 let u = c.get_attribute(&ATTRUSE);
569 keys.push((
570 name,
571 pat,
572 parse::<N>(&u.to_string(), Some(c.clone()), None)?,
573 ));
574 Ok(())
575 })?;
576
577 let mut newctxt = ContextBuilder::new()
578 .template(Template::new(
583 Pattern::try_from("/")?,
584 Transform::ApplyTemplates(
585 Box::new(Transform::Step(NodeMatch::new(
586 Axis::Child,
587 NodeTest::Kind(KindTest::Any),
588 ))),
589 None,
590 vec![],
591 ),
592 None,
593 vec![0],
594 None,
595 None,
596 String::from("/"),
597 ))
598 .template(Template::new(
600 Pattern::try_from("child::*")?,
601 Transform::ApplyTemplates(
602 Box::new(Transform::Step(NodeMatch::new(
603 Axis::Child,
604 NodeTest::Kind(KindTest::Any),
605 ))),
606 None,
607 vec![],
608 ),
609 None,
610 vec![0],
611 None,
612 None,
613 String::from("child::*"),
614 ))
615 .template(Template::new(
617 Pattern::try_from("child::text()")?,
618 Transform::ContextItem,
619 None,
620 vec![0],
621 None,
622 None,
623 String::from("child::text()"),
624 ))
625 .template_all(templates)
626 .output_definition(od)
627 .build();
628
629 keys.iter()
630 .for_each(|(name, m, u)| newctxt.declare_key(name.to_string(), m.clone(), u.clone()));
631
632 stylenode
634 .child_iter()
635 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLTEMPLATE))
636 .filter(|c| !c.get_attribute(&ATTRNAME).to_string().is_empty())
637 .try_for_each(|c| {
638 let name = c.get_attribute(&ATTRNAME);
639 let mut params: Vec<(QName, Option<Transform<N>>)> = Vec::new();
643 c.child_iter()
644 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLPARAM))
645 .try_for_each(|c| {
646 let p_name = c.get_attribute(&ATTRNAME);
647 if p_name.to_string().is_empty() {
648 Err(Error::new(
649 ErrorKind::StaticAbsent,
650 "name attribute is missing",
651 ))
652 } else {
653 let sel = c.get_attribute(&ATTRSELECT);
654 if sel.to_string().is_empty() {
655 let mut body = vec![];
657 c.child_iter().try_for_each(|d| {
658 body.push(to_transform(d, &attr_sets)?);
659 Ok(())
660 })?;
661 params.push((
662 QName::from_local_name(
663 NcName::try_from(p_name.to_string().as_str()).map_err(
664 |_| Error::new(ErrorKind::ParseError, "not a QName"),
665 )?,
666 ),
667 Some(Transform::SequenceItems(body)),
668 ));
669 Ok(())
670 } else {
671 params.push((
673 QName::from_local_name(
674 NcName::try_from(p_name.to_string().as_str()).map_err(
675 |_| Error::new(ErrorKind::ParseError, "not a QName"),
676 )?,
677 ),
678 Some(parse::<N>(&sel.to_string(), Some(c.clone()), None)?),
679 ));
680 Ok(())
681 }
682 }
683 })?;
684 let mut body = vec![];
686 c.child_iter()
687 .filter(|c| !(c.is_element() && c.name().is_some_and(|cn| cn == *XSLPARAM)))
688 .try_for_each(|d| {
689 body.push(to_transform(d, &attr_sets)?);
690 Ok::<(), Error>(())
691 })?;
692 newctxt.callable_push(
693 QName::from_local_name(
694 NcName::try_from(name.to_string().as_str())
695 .map_err(|_| Error::new(ErrorKind::ParseError, "not a QName"))?,
696 ),
697 Callable::new(
698 Transform::SequenceItems(body),
699 FormalParameters::Named(params),
700 ),
701 );
702 Ok(())
703 })?;
704
705 stylenode
707 .child_iter()
708 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLFUNCTION))
709 .try_for_each(|c| {
710 let name = c.get_attribute(&ATTRNAME);
711 let eqname = c.to_qname(name.to_string())?;
713 if eqname.namespace_uri().is_none() {
714 return Err(Error::new_with_code(
715 ErrorKind::StaticAbsent,
716 "function name must have a namespace",
717 Some(QName::from_local_name(
718 NcName::try_from("XTSE0740").unwrap(),
719 )),
720 ));
721 }
722 let mut params: Vec<QName> = Vec::new();
726 c.child_iter()
727 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLPARAM))
728 .try_for_each(|c| {
729 let p_name = c.get_attribute(&ATTRNAME);
730 if p_name.to_string().is_empty() {
731 Err(Error::new(
732 ErrorKind::StaticAbsent,
733 "name attribute is missing",
734 ))
735 } else {
736 params.push(QName::from_local_name(
738 NcName::try_from(p_name.to_string().as_str()).map_err(|_| {
739 Error::new(ErrorKind::ParseError, "not a valid QName")
740 })?,
741 ));
742 Ok(())
743 }
744 })?;
745 let mut body = vec![];
747 c.child_iter()
748 .filter(|c| !(c.is_element() && c.name().is_some_and(|cn| cn == *XSLPARAM)))
749 .try_for_each(|d| {
750 body.push(to_transform(d, &attr_sets)?);
751 Ok::<(), Error>(())
752 })?;
753 newctxt.callable_push(
754 eqname,
755 Callable::new(
756 Transform::SequenceItems(body),
757 FormalParameters::Positional(params),
758 ),
759 );
760 Ok(())
761 })?;
762 stylenode
765 .child_iter()
766 .filter(|c| c.is_element() && c.name().is_some_and(|cn| cn == *XSLVARIABLE))
767 .try_for_each(|c| {
768 let name = c.get_attribute(&ATTRNAME).to_string();
769 if name.is_empty() {
770 return Err(Error::new(
771 ErrorKind::StaticAbsent,
772 "variable must have a name",
773 ));
774 }
775 let sel = c.get_attribute(&ATTRSELECT).to_string();
776 if sel.is_empty() {
777 newctxt.pre_var_push(
779 name,
780 Transform::SequenceItems(c.child_iter().try_fold(vec![], |mut body, e| {
781 body.push(to_transform(e, &attr_sets)?);
782 Ok(body)
783 })?),
784 );
785 Ok(())
786 } else {
787 newctxt.pre_var_push(name, parse::<N>(&sel.to_string(), Some(c.clone()), None)?);
789 Ok(())
790 }
791 })?;
792
793 Ok(newctxt)
794}
795
796fn to_transform<N: Node>(
798 n: N,
799 attr_sets: &HashMap<QName, Vec<Transform<N>>>,
800) -> Result<Transform<N>, Error> {
801 match n.node_type() {
805 NodeType::Text => Ok(Transform::Literal(Item::Value(Rc::new(Value::from(
806 n.to_string(),
807 ))))),
808 NodeType::Element => {
809 let qn = n.name().unwrap();
810 if qn == *XSLTEXT {
811 let doe = n.get_attribute(&ATTRDOE);
812 if !doe.to_string().is_empty() {
813 match &doe.to_string()[..] {
814 "yes" => Ok(Transform::Literal(Item::Value(Rc::new(Value::from(
815 n.to_string(),
816 ))))),
817 "no" => {
818 let text = n
819 .to_string()
820 .replace('&', "&")
821 .replace('>', ">")
822 .replace('<', "<")
823 .replace('\'', "'")
824 .replace('\"', """);
825 Ok(Transform::Literal(Item::Value(Rc::new(Value::from(text)))))
826 }
827 _ => Err(Error::new(
828 ErrorKind::TypeError,
829 "disable-output-escaping only accepts values yes or no.".to_string(),
830 )),
831 }
832 } else {
833 let text = n
834 .to_string()
835 .replace('&', "&")
836 .replace('>', ">")
837 .replace('<', "<")
838 .replace('\'', "'")
839 .replace('\"', """);
840 Ok(Transform::Literal(Item::Value(Rc::new(Value::from(text)))))
841 }
842 } else if qn == *XSLVALUEOF {
843 let sel = n.get_attribute(&ATTRSELECT);
844 let doe = n.get_attribute(&ATTRDOE);
845 if !doe.to_string().is_empty() {
846 match &doe.to_string()[..] {
847 "yes" => Ok(Transform::LiteralText(
848 Box::new(parse::<N>(&sel.to_string(), Some(n.clone()), None)?),
849 OutputSpec::NoEscape,
850 )),
851 "no" => Ok(Transform::LiteralText(
852 Box::new(parse::<N>(&sel.to_string(), Some(n.clone()), None)?),
853 OutputSpec::Normal,
854 )),
855 _ => Err(Error::new(
856 ErrorKind::TypeError,
857 "disable-output-escaping only accepts values yes or no.".to_string(),
858 )),
859 }
860 } else {
861 Ok(Transform::LiteralText(
862 Box::new(parse::<N>(&sel.to_string(), Some(n.clone()), None)?),
863 OutputSpec::Normal,
864 ))
865 }
866 } else if qn == *XSLAPPLYTEMPLATES {
867 let sel = n.get_attribute(&ATTRSELECT);
868 let m = n.get_attribute_node(&ATTRMODE);
869
870 let mut qm = None;
873 if let Some(s) = m {
874 qm = Some(n.to_qname(s.value().to_string())?)
875 }
876
877 let sort_keys = get_sort_keys(&n)?;
878 if !sel.to_string().is_empty() {
879 Ok(Transform::ApplyTemplates(
880 Box::new(parse::<N>(&sel.to_string(), Some(n.clone()), None)?),
881 qm,
882 sort_keys,
883 )) } else {
885 Ok(Transform::ApplyTemplates(
887 Box::new(Transform::Step(NodeMatch::new(
888 Axis::Child,
889 NodeTest::Kind(KindTest::Any),
890 ))),
891 qm,
892 sort_keys,
893 )) }
895 } else if qn == *XSLAPPLYIMPORTS {
896 Ok(Transform::ApplyImports)
897 } else if qn == *XSLSEQUENCE {
898 let s = n.get_attribute(&ATTRSELECT);
899 if !s.to_string().is_empty() {
900 Ok(parse::<N>(&s.to_string(), Some(n.clone()), None)?)
901 } else {
902 Result::Err(Error::new(
903 ErrorKind::TypeError,
904 "missing select attribute".to_string(),
905 ))
906 }
907 } else if qn == *XSLIF {
908 let t = n.get_attribute(&ATTRTEST);
909 if !t.to_string().is_empty() {
910 Ok(Transform::Switch(
911 vec![(
912 parse::<N>(&t.to_string(), Some(n.clone()), None)?,
913 Transform::SequenceItems(n.child_iter().try_fold(
914 vec![],
915 |mut body, e| {
916 body.push(to_transform(e, attr_sets)?);
917 Ok(body)
918 },
919 )?),
920 )],
921 Box::new(Transform::Empty),
922 ))
923 } else {
924 Result::Err(Error::new(
925 ErrorKind::TypeError,
926 "missing test attribute".to_string(),
927 ))
928 }
929 } else if qn == *XSLCHOOSE {
930 let mut clauses: Vec<(Transform<N>, Transform<N>)> = Vec::new();
931 let mut otherwise: Option<Transform<N>> = None;
932 let mut status: Option<Error> = None;
933 n.child_iter().try_for_each(|m| {
934 match m.node_type() {
938 NodeType::Element => {
939 let mn = m.name().unwrap();
940 if mn == *XSLWHEN {
941 if otherwise.is_none() {
942 let t = m.get_attribute(&ATTRTEST);
943 if !t.to_string().is_empty() {
944 clauses.push((
945 parse::<N>(&t.to_string(), Some(n.clone()), None)?,
946 Transform::SequenceItems(m.child_iter().try_fold(
947 vec![],
948 |mut body, e| {
949 body.push(to_transform(e, attr_sets)?);
950 Ok(body)
951 },
952 )?),
953 ));
954 } else {
955 status.replace(Error::new(
956 ErrorKind::TypeError,
957 "missing test attribute".to_string(),
958 ));
959 }
960 } else {
961 status.replace(Error::new(
962 ErrorKind::TypeError,
963 "invalid content in choose element: when follows otherwise"
964 .to_string(),
965 ));
966 }
967 } else if mn == *XSLOTHERWISE {
968 if !clauses.is_empty() {
969 otherwise = Some(Transform::SequenceItems(
970 m.child_iter().try_fold(vec![], |mut o, e| {
971 o.push(to_transform(e, attr_sets)?);
972 Ok(o)
973 })?,
974 ));
975 } else {
976 status.replace(Error::new(
977 ErrorKind::TypeError,
978 "invalid content in choose element: no when elements"
979 .to_string(),
980 ));
981 }
982 } else {
983 status.replace(Error::new(
984 ErrorKind::TypeError,
985 "invalid element content in choose element".to_string(),
986 ));
987 }
988 }
989 NodeType::Text => {
990 if !n.to_string().trim().is_empty() {
991 status.replace(Error::new(
992 ErrorKind::TypeError,
993 "invalid text content in choose element".to_string(),
994 ));
995 }
996 }
997 NodeType::Comment | NodeType::ProcessingInstruction => {}
998 _ => {
999 status.replace(Error::new(
1000 ErrorKind::TypeError,
1001 "invalid content in choose element".to_string(),
1002 ));
1003 }
1004 }
1005 Ok::<(), Error>(())
1006 })?;
1007 match status {
1008 Some(e) => Result::Err(e),
1009 None => Ok(Transform::Switch(
1010 clauses,
1011 otherwise.map_or(Box::new(Transform::Empty), Box::new),
1012 )),
1013 }
1014 } else if qn == *XSLFOREACH {
1015 let s = n.get_attribute(&ATTRSELECT);
1016 if !s.to_string().is_empty() {
1017 Ok(Transform::ForEach(
1018 None,
1019 Box::new(parse::<N>(&s.to_string(), Some(n.clone()), None)?),
1020 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1021 vec![],
1022 |mut body, e| {
1023 body.push(to_transform(e, attr_sets)?);
1024 Ok(body)
1025 },
1026 )?)),
1027 get_sort_keys(&n)?,
1028 ))
1029 } else {
1030 Result::Err(Error::new(
1031 ErrorKind::TypeError,
1032 "missing select attribute".to_string(),
1033 ))
1034 }
1035 } else if qn == *XSLFOREACHGROUP {
1036 let ord = get_sort_keys(&n)?;
1037 let s = n.get_attribute(&ATTRSELECT);
1038 if !s.to_string().is_empty() {
1039 match (
1040 n.get_attribute(&ATTRGROUPBY).to_string().as_str(),
1041 n.get_attribute(&ATTRGROUPADJACENT).to_string().as_str(),
1042 n.get_attribute(&ATTRGROUPSTARTINGWITH).to_string().as_str(),
1043 n.get_attribute(&ATTRGROUPENDINGWITH).to_string().as_str(),
1044 ) {
1045 (by, "", "", "") => Ok(Transform::ForEach(
1046 Some(Grouping::By(vec![parse::<N>(by, Some(n.clone()), None)?])),
1047 Box::new(parse::<N>(&s.to_string(), Some(n.clone()), None)?),
1048 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1049 vec![],
1050 |mut body, e| {
1051 body.push(to_transform(e, attr_sets)?);
1052 Ok(body)
1053 },
1054 )?)),
1055 ord,
1056 )),
1057 ("", adj, "", "") => Ok(Transform::ForEach(
1058 Some(Grouping::Adjacent(vec![parse::<N>(
1059 adj,
1060 Some(n.clone()),
1061 None,
1062 )?])),
1063 Box::new(parse::<N>(&s.to_string(), Some(n.clone()), None)?),
1064 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1065 vec![],
1066 |mut body, e| {
1067 body.push(to_transform(e, attr_sets)?);
1068 Ok(body)
1069 },
1070 )?)),
1071 ord,
1072 )),
1073 ("", "", start, "") => Ok(Transform::ForEach(
1074 Some(Grouping::StartingWith(Box::new(Pattern::try_from(start)?))),
1075 Box::new(parse::<N>(&s.to_string(), Some(n.clone()), None)?),
1076 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1077 vec![],
1078 |mut body, e| {
1079 body.push(to_transform(e, attr_sets)?);
1080 Ok(body)
1081 },
1082 )?)),
1083 ord,
1084 )),
1085 _ => Result::Err(Error::new(
1087 ErrorKind::NotImplemented,
1088 "invalid grouping attribute(s) specified".to_string(),
1089 )),
1090 }
1091 } else {
1092 Result::Err(Error::new(
1093 ErrorKind::TypeError,
1094 "missing select attribute".to_string(),
1095 ))
1096 }
1097 } else if qn == *XSLCOPY {
1098 let mut content: Vec<Transform<N>> =
1100 n.child_iter().try_fold(vec![], |mut body, e| {
1101 body.push(to_transform(e, attr_sets)?);
1102 Ok(body)
1103 })?;
1104 let use_atts = n.get_attribute(&XSLATTRUSEATTRIBUTESETS);
1106 let mut attrs = vec![];
1107 use_atts.to_string().split_whitespace().try_for_each(|a| {
1108 let eqa = n.to_qname(a)?;
1109 attr_sets
1110 .get(&eqa)
1111 .iter()
1112 .cloned()
1113 .for_each(|a| attrs.append(&mut a.clone()));
1114 Ok(())
1115 })?;
1116 Ok(Transform::Copy(
1117 Box::new(Transform::ContextItem), Box::new(if content.is_empty() && attrs.is_empty() {
1120 Transform::Empty
1121 } else {
1122 attrs.append(&mut content);
1124 Transform::SequenceItems(attrs)
1125 }),
1126 ))
1127 } else if qn == *XSLCOPYOF {
1128 let s = n.get_attribute(&ATTRSELECT);
1129 if !s.to_string().is_empty() {
1130 Ok(Transform::DeepCopy(Box::new(parse::<N>(
1131 &s.to_string(),
1132 Some(n.clone()),
1133 None,
1134 )?)))
1135 } else {
1136 Ok(Transform::DeepCopy(Box::new(Transform::ContextItem)))
1137 }
1138 } else if qn == *XSLCALLTEMPLATE {
1139 let name = n.get_attribute(&ATTRNAME);
1140 if !name.to_string().is_empty() {
1141 let mut ap = vec![];
1144 n.child_iter()
1145 .filter(|c| c.is_element() && c.name().unwrap() == *XSLWITHPARAM)
1146 .try_for_each(|c| {
1147 let wp_name = c.get_attribute(&ATTRNAME);
1148 if !wp_name.to_string().is_empty() {
1149 let sel = c.get_attribute(&ATTRSELECT);
1150 if sel.to_string().is_empty() {
1151 let mut body = vec![];
1153 c.child_iter().try_for_each(|d| {
1154 body.push(to_transform(d, attr_sets)?);
1155 Ok(())
1156 })?;
1157 ap.push((
1158 QName::from_local_name(
1159 NcName::try_from(wp_name.to_string().as_str())
1160 .map_err(|_| {
1161 Error::new(ErrorKind::ParseError, "not a QName")
1162 })?,
1163 ),
1164 Transform::SequenceItems(body),
1165 ));
1166 Ok(())
1167 } else {
1168 ap.push((
1170 QName::from_local_name(
1171 NcName::try_from(wp_name.to_string().as_str())
1172 .map_err(|_| {
1173 Error::new(ErrorKind::ParseError, "not a QName")
1174 })?,
1175 ),
1176 parse::<N>(&sel.to_string(), Some(n.clone()), None)?,
1177 ));
1178 Ok(())
1179 }
1180 } else {
1181 Err(Error::new(
1182 ErrorKind::StaticAbsent,
1183 "missing name attribute",
1184 ))
1185 }
1186 })?;
1187 Ok(Transform::Invoke(
1188 QName::from_local_name(
1189 NcName::try_from(name.to_string().as_str())
1190 .map_err(|_| Error::new(ErrorKind::ParseError, "not a NcName"))?,
1191 ),
1192 ActualParameters::Named(ap),
1193 in_scope_namespaces(Some(n)),
1194 ))
1195 } else {
1196 Err(Error::new(
1197 ErrorKind::StaticAbsent,
1198 "name attribute missing",
1199 ))
1200 }
1201 } else if qn == *XSLELEMENT {
1202 let m = n.get_attribute(&ATTRNAME);
1204 if m.to_string().is_empty() {
1205 return Err(Error::new(ErrorKind::TypeError, "missing name attribute"));
1206 }
1207 let mut content = n.child_iter().try_fold(vec![], |mut body, e| {
1208 body.push(to_transform(e, attr_sets)?);
1209 Ok(body)
1210 })?;
1211 let use_atts = n.get_attribute(&XSLATTRUSEATTRIBUTESETS);
1213 let mut attrs = vec![];
1214 use_atts.to_string().split_whitespace().try_for_each(|a| {
1215 let eqa = n.to_qname(a)?;
1216 attr_sets
1217 .get(&eqa)
1218 .iter()
1219 .cloned()
1220 .for_each(|a| attrs.append(&mut a.clone()));
1221 Ok(())
1222 })?;
1223
1224 Ok(Transform::Element(
1225 Box::new(parse_avt(m.to_string().as_str(), Some(n.clone()))?),
1226 Box::new(if content.is_empty() && attrs.is_empty() {
1227 Transform::Empty
1228 } else {
1229 attrs.append(&mut content);
1231 Transform::SequenceItems(attrs)
1232 }),
1233 ))
1234 } else if qn == *XSLATTRIBUTE {
1235 let m = n.get_attribute(&ATTRNAME);
1236 if !m.to_string().is_empty() {
1237 Ok(Transform::LiteralAttribute(
1238 QName::from_local_name(
1239 NcName::try_from(m.to_string().as_str())
1240 .map_err(|_| Error::new(ErrorKind::ParseError, "not a NcName"))?,
1241 ),
1242 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1243 vec![],
1244 |mut body, e| {
1245 body.push(to_transform(e, attr_sets)?);
1246 Ok(body)
1247 },
1248 )?)),
1249 ))
1250 } else {
1251 Err(Error::new(ErrorKind::TypeError, "missing name attribute"))
1252 }
1253 } else if qn == *XSLCOMMENT {
1254 Ok(Transform::LiteralComment(Box::new(
1255 Transform::SequenceItems(n.child_iter().try_fold(vec![], |mut body, e| {
1256 body.push(to_transform(e, attr_sets)?);
1257 Ok(body)
1258 })?),
1259 )))
1260 } else if qn == *XSLPROCESSINGINSTRUCTION {
1261 let m = n.get_attribute(&ATTRNAME);
1262 if m.to_string().is_empty() {
1263 return Result::Err(Error::new(ErrorKind::TypeError, "missing name attribute"));
1264 }
1265 Ok(Transform::LiteralProcessingInstruction(
1266 Box::new(parse_avt(m.to_string().as_str(), Some(n.clone()))?),
1267 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1268 vec![],
1269 |mut body, e| {
1270 body.push(to_transform(e, attr_sets)?);
1271 Ok(body)
1272 },
1273 )?)),
1274 ))
1275 } else if qn == *XSLMESSAGE {
1276 let t = n.get_attribute(&ATTRTERMINATE);
1277 Ok(Transform::Message(
1278 Box::new(Transform::SequenceItems(n.child_iter().try_fold(
1279 vec![],
1280 |mut body, e| {
1281 body.push(to_transform(e, attr_sets)?);
1282 Ok(body)
1283 },
1284 )?)),
1285 None,
1286 Box::new(Transform::Empty),
1287 Box::new(if t.to_string().is_empty() {
1288 Transform::False
1289 } else {
1290 Transform::Literal(Item::Value(Rc::new(Value::from(t.to_string()))))
1291 }),
1292 ))
1293 } else if qn == *XSLNUMBER {
1294 let value = n.get_attribute(&ATTRVALUE);
1295 let sel = n.get_attribute(&ATTRSELECT);
1296 let level = n.get_attribute(&ATTRLEVEL);
1297 if level.to_string() != "" && level.to_string() != "single" {
1298 return Err(Error::new(
1299 ErrorKind::NotImplemented,
1300 "only single level numbering is supported",
1301 ));
1302 }
1303 let count = n.get_attribute(&ATTRCOUNT);
1304 let from = n.get_attribute(&ATTRFROM);
1305 let format = n.get_attribute(&ATTRFORMAT);
1306 if value.to_string().is_empty() {
1308 Ok(Transform::FormatInteger(
1310 Box::new(Transform::GenerateIntegers(
1311 Box::new(Transform::Empty), Box::new(if sel.to_string().is_empty() {
1313 Transform::ContextItem
1314 } else {
1315 parse::<N>(&sel.to_string(), Some(n.clone()), None)?
1316 }), Box::new(Numbering::new(
1318 Level::Single, if count.to_string().is_empty() {
1320 None
1321 } else {
1322 Some(Pattern::try_from(count.to_string())?)
1323 },
1324 if from.to_string().is_empty() {
1325 None
1326 } else {
1327 Some(Pattern::try_from(from.to_string())?)
1328 },
1329 )),
1330 )),
1331 Box::new(Transform::Literal(Item::Value(
1332 if format.to_string().is_empty() {
1333 Rc::new(Value::from("1"))
1334 } else {
1335 format
1336 },
1337 ))),
1338 ))
1339 } else {
1340 Ok(Transform::FormatInteger(
1342 Box::new(parse::<N>(&value.to_string(), Some(n.clone()), None)?),
1343 Box::new(Transform::Literal(Item::Value(
1344 if format.to_string().is_empty() {
1345 Rc::new(Value::from("1"))
1346 } else {
1347 format
1348 },
1349 ))),
1350 ))
1351 }
1352 } else if qn == *XSLDECIMALFORMAT {
1353 Ok(Transform::NotImplemented(String::from(
1354 "unsupported XSL element \"decimal-format\"",
1355 )))
1356 } else if qn.namespace_uri() == *XSLTNS {
1357 Ok(Transform::NotImplemented(format!(
1358 "unsupported XSL element \"{}\"",
1359 qn.local_name().to_string()
1360 )))
1361 } else {
1362 let u = qn.namespace_uri();
1363 let a = qn.local_name();
1364
1365 let mut prefix = None;
1368 if let Some(nsuri) = u.as_ref() {
1369 if let Some(p) = n
1370 .namespace_iter()
1371 .find(|nsd| nsd.as_namespace_uri().unwrap() == nsuri)
1372 {
1373 if let Some(pp) = p.as_namespace_prefix()? {
1374 prefix = Some(Box::new(Transform::Literal(Item::Value(Rc::new(
1375 Value::from(pp.to_string()),
1376 )))));
1377 }
1378 }
1379 }
1380
1381 let use_atts = n.get_attribute(&XSLATTRUSEATTRIBUTESETS);
1383 let mut attrs = vec![];
1384 use_atts.to_string().split_whitespace().try_for_each(|a| {
1385 let eqa = n.to_qname(a)?; attr_sets
1387 .get(&eqa)
1388 .iter()
1389 .cloned()
1390 .for_each(|a| attrs.append(&mut a.clone()));
1391 Ok(())
1392 })?;
1393 let mut content = vec![];
1394
1395 if u.is_some() {
1397 content.push(Transform::NamespaceDeclaration(
1398 prefix,
1399 Box::new(Transform::Literal(Item::Value(Rc::new(Value::from(
1400 u.clone().unwrap(),
1401 ))))),
1402 Box::new(Transform::Literal(Item::Value(Rc::new(Value::from(true))))),
1403 ));
1404 }
1405
1406 n.attribute_iter()
1408 .filter(|e| e.name().unwrap().namespace_uri() != *XSLTNS)
1409 .try_for_each(|e| {
1410 content.push(to_transform(e, attr_sets)?);
1411 Ok::<(), Error>(())
1412 })?;
1413 n.child_iter().try_for_each(|e| {
1414 content.push(to_transform(e, attr_sets)?);
1415 Ok::<(), Error>(())
1416 })?;
1417 Ok(Transform::LiteralElement(
1418 QName::new_from_parts(a, u),
1419 Box::new(if content.is_empty() && attrs.is_empty() {
1420 Transform::Empty
1421 } else {
1422 attrs.append(&mut content);
1424 Transform::SequenceItems(attrs)
1425 }),
1426 ))
1427 }
1428 }
1429 NodeType::Attribute => {
1430 let x = parse_avt(n.to_string().as_str(), Some(n.clone()))?;
1431 Ok(Transform::LiteralAttribute(
1433 n.name().unwrap(),
1434 Box::new(x),
1435 ))
1439 }
1440 _ => {
1441 Ok(Transform::NotImplemented(
1443 "other template content".to_string(),
1444 ))
1445 }
1446 }
1447}
1448
1449fn get_sort_keys<N: Node>(n: &N) -> Result<Vec<(Order, Transform<N>)>, Error> {
1450 let mut result = vec![];
1451 let mut nit = n.child_iter();
1452 loop {
1453 match nit.next() {
1454 None => break,
1455 Some(c) => match c.node_type() {
1456 NodeType::Element => {
1457 if c.name().is_some_and(|d| d == *XSLSORT) {
1458 let ordval = c.get_attribute(&ATTRORDER);
1459 let ord = match ordval.to_string().as_str() {
1460 "descending" => Order::Descending,
1461 _ => Order::Ascending,
1462 };
1463 let sortsel = c.get_attribute(&ATTRSELECT);
1464 result.push((
1465 ord,
1466 parse::<N>(&sortsel.to_string(), Some(n.clone()), None)?,
1467 ));
1468 } else {
1469 break;
1470 }
1471 }
1472 NodeType::Text => {
1473 if c.value()
1474 .to_string()
1475 .as_str()
1476 .find(|d: char| !d.is_whitespace())
1477 .is_some()
1478 {
1479 break;
1480 }
1481 }
1482 NodeType::Comment | NodeType::ProcessingInstruction => {}
1483 _ => break,
1484 },
1485 }
1486 }
1487 if nit.any(|c| c.node_type() == NodeType::Element && c.name().is_some_and(|d| d == *XSLSORT)) {
1489 Err(Error::new(ErrorKind::TypeError, "sort elements in body"))
1490 } else {
1491 Ok(result)
1492 }
1493}
1494
1495pub fn strip_whitespace<N: Node>(
1499 t: N,
1500 cpi: bool, strip: &Vec<NodeTest>,
1502 preserve: &Vec<NodeTest>,
1503) -> Result<(), Error> {
1504 t.child_iter().try_for_each(|n| {
1505 strip_whitespace_node(n, cpi, strip, preserve, true)?;
1506 Ok(())
1507 })?;
1508 Ok(())
1509}
1510
1511pub fn strip_source_document<N: Node>(src: N, style: N) -> Result<(), Error> {
1514 let mut ss: Vec<NodeTest> = vec![];
1517 let mut ps: Vec<NodeTest> = vec![];
1518 style.child_iter().try_for_each(|n| {
1519 n.child_iter().try_for_each(|m| {
1521 let nm = m.name();
1522 if nm.as_ref().is_some_and(|nms| *nms == *XSLSTRIPSPACE) {
1523 let v = m.get_attribute(&ATTRELEMENTS);
1524 if !v.to_string().is_empty() {
1525 v.to_string().split_whitespace().try_for_each(|t| {
1526 ss.push(NodeTest::try_from(t)?);
1527 Ok::<(), Error>(())
1528 })?
1529 } else {
1530 return Result::Err(Error::new(
1531 ErrorKind::Unknown,
1532 String::from("missing elements attribute"),
1533 ));
1534 }
1535 } else if nm.as_ref().is_some_and(|nms| *nms == *XSLPRESERVESPACE) {
1536 let v = m.get_attribute(&ATTRELEMENTS);
1537 if !v.to_string().is_empty() {
1538 v.to_string().split_whitespace().try_for_each(|t| {
1539 ps.push(NodeTest::try_from(t)?);
1540 Ok::<(), Error>(())
1541 })?
1542 } else {
1543 return Result::Err(Error::new(
1544 ErrorKind::Unknown,
1545 String::from("missing elements attribute"),
1546 ));
1547 }
1548 }
1549 Ok::<(), Error>(())
1550 })?;
1551 Ok::<(), Error>(())
1552 })?;
1553
1554 strip_whitespace(src, false, &ss, &ps)
1555}
1556
1557fn strip_whitespace_node<N: Node>(
1559 mut n: N,
1560 cpi: bool, strip: &Vec<NodeTest>,
1562 preserve: &Vec<NodeTest>,
1563 keep: bool,
1564) -> Result<(), Error> {
1565 match n.node_type() {
1566 NodeType::Comment | NodeType::ProcessingInstruction => {
1567 if cpi {
1568 n.pop()?;
1569 }
1571 }
1572 NodeType::Element => {
1573 let mut ss = -1.0;
1577 let mut ps = -1.0;
1578 strip.iter().for_each(|t| match t {
1579 NodeTest::Kind(KindTest::Any) | NodeTest::Kind(KindTest::Element) => ss = -0.5,
1580 NodeTest::Name(nt) => match nt {
1581 NameTest::Wildcard(
1582 WildcardOrNamespaceUri::Wildcard,
1583 WildcardOrName::Name(_),
1584 ) => ss = -0.25,
1585 NameTest::Wildcard(
1586 WildcardOrNamespaceUri::NamespaceUri(_),
1587 WildcardOrName::Wildcard,
1588 ) => ss = -0.25,
1589 NameTest::Wildcard(_, _) => ss = -0.5,
1590 NameTest::Name(qn) => {
1591 if *qn == n.name().unwrap() {
1592 ss = 0.5
1593 }
1594 }
1595 },
1596 _ => {}
1597 });
1598 preserve.iter().for_each(|t| match t {
1599 NodeTest::Kind(KindTest::Any) | NodeTest::Kind(KindTest::Element) => ps = -0.5,
1600 NodeTest::Name(nt) => match nt {
1601 NameTest::Wildcard(
1602 WildcardOrNamespaceUri::Wildcard,
1603 WildcardOrName::Name(_),
1604 ) => ss = -0.25,
1605 NameTest::Wildcard(
1606 WildcardOrNamespaceUri::NamespaceUri(_),
1607 WildcardOrName::Wildcard,
1608 ) => ss = -0.25,
1609 NameTest::Wildcard(_, _) => ss = -0.5,
1610 NameTest::Name(qn) => {
1611 if *qn == n.name().unwrap() {
1612 ss = 0.5
1613 }
1614 }
1615 },
1616 _ => {}
1617 });
1618 n.child_iter().try_for_each(|m| {
1619 strip_whitespace_node(
1620 m,
1621 cpi,
1622 strip,
1623 preserve,
1624 if ss > -1.0 {
1625 ps >= ss
1626 } else if ps > -1.0 {
1627 true
1628 } else {
1629 keep
1630 },
1631 )
1632 })?
1633 }
1634 NodeType::Text => {
1635 if n.to_string().trim().is_empty() && !keep {
1636 n.pop()?;
1637 }
1638 }
1639 _ => {}
1640 }
1641 Ok(())
1642}