1use std::cmp::Ordering;
4use std::fmt::{Debug, Formatter};
5use std::rc::Rc;
6use url::Url;
7
8use crate::transform::context::{Context, ContextBuilder, StaticContext};
9use crate::transform::{Order, Transform, do_sort};
10use crate::xdmerror::Error;
11use crate::{Node, Pattern, Sequence};
12use qualname::QName;
13
14#[derive(Clone)]
15pub struct Template<N: Node> {
16 pub(crate) pattern: Pattern<N>,
17 pub(crate) body: Transform<N>,
18 pub(crate) priority: Option<f64>,
19 pub(crate) import: Vec<usize>,
20 pub(crate) document_order: Option<usize>,
21 pub(crate) mode: Option<QName>,
22 pub(crate) mtch: String, }
24
25impl<N: Node> Template<N> {
26 pub fn new(
27 pattern: Pattern<N>,
28 body: Transform<N>,
29 priority: Option<f64>,
30 import: Vec<usize>,
31 document_order: Option<usize>,
32 mode: Option<QName>,
33 mtch: String,
34 ) -> Self {
35 Template {
36 pattern,
37 body,
38 priority,
39 import,
40 document_order,
41 mode,
42 mtch,
43 }
44 }
45}
46
47impl<N: Node> PartialEq for Template<N> {
49 fn eq(&self, other: &Self) -> bool {
50 self.priority == other.priority && self.import == other.import && self.mode == other.mode
51 }
52}
53impl<N: Node> Eq for Template<N> {}
54
55impl<N: Node> PartialOrd for Template<N> {
56 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
57 Some(self.cmp(other))
58 }
59}
60impl<N: Node> Ord for Template<N> {
61 fn cmp(&self, other: &Self) -> Ordering {
62 self.priority.map_or_else(
63 || {
64 other
65 .priority
66 .map_or_else(|| Ordering::Equal, |_| Ordering::Greater)
67 },
68 |s| {
69 other.priority.map_or_else(
70 || Ordering::Less,
71 |t| {
72 if s < t {
73 Ordering::Greater
74 } else {
75 Ordering::Less
76 }
77 },
78 )
79 },
80 )
81 }
82}
83
84impl<N: Node> Debug for Template<N> {
85 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86 write!(
87 f,
88 "template match \"{:?}\" ({:?}) priority {:?} mode {:?}",
89 self.mtch, self.pattern, self.priority, self.mode
90 )
91 }
92}
93
94pub(crate) fn apply_templates<
96 N: Node,
97 F: FnMut(&str) -> Result<(), Error>,
98 G: FnMut(&str) -> Result<N, Error>,
99 H: FnMut(&Url) -> Result<String, Error>,
100>(
101 ctxt: &Context<N>,
102 stctxt: &mut StaticContext<N, F, G, H>,
103 s: &Transform<N>,
104 m: &Option<QName>,
105 o: &Vec<(Order, Transform<N>)>, ) -> Result<Sequence<N>, Error> {
107 let mut seq = ctxt.dispatch(stctxt, s)?;
110 do_sort(&mut seq, o, ctxt, stctxt)?;
111 seq.iter().try_fold(vec![], |mut result, i| {
112 let templates = ctxt.find_templates(stctxt, i, m)?;
113 let matching = if templates.len() > 1 {
115 if templates[0].priority == templates[1].priority
116 && templates[0].import.len() == templates[1].import.len()
117 {
118 let mut candidates: Vec<Rc<Template<N>>> = templates
119 .iter()
120 .take_while(|t| {
121 t.priority == templates[0].priority
122 && t.import.len() == templates[0].import.len()
123 })
124 .cloned()
125 .collect();
126 candidates.sort_unstable_by(|a, b| {
127 a.document_order.map_or(Ordering::Greater, |v| {
128 b.document_order.map_or(Ordering::Less, |u| v.cmp(&u))
129 })
130 });
131 candidates.last().unwrap().clone()
132 } else {
133 templates[0].clone()
134 }
135 } else {
136 templates[0].clone()
137 };
138
139 if let Some(md) = ctxt.max_depth {
143 if md == ctxt.depth {
144 return Err(Error::new(
145 crate::ErrorKind::LimitExceeded,
146 format!("exceeded evaluation depth ({})", ctxt.depth),
147 ));
148 }
149 }
150 let mut u = ContextBuilder::from(ctxt)
152 .context(vec![i.clone()])
153 .context_item(Some(i.clone()))
154 .current_templates(templates)
155 .depth(ctxt.depth + 1)
156 .build()
157 .dispatch(stctxt, &matching.body)?;
158 result.append(&mut u);
159 Ok(result)
160 })
161}
162
163pub(crate) fn apply_imports<
165 N: Node,
166 F: FnMut(&str) -> Result<(), Error>,
167 G: FnMut(&str) -> Result<N, Error>,
168 H: FnMut(&Url) -> Result<String, Error>,
169>(
170 ctxt: &Context<N>,
171 stctxt: &mut StaticContext<N, F, G, H>,
172) -> Result<Sequence<N>, Error> {
173 if ctxt.current_templates.is_empty() {
176 return Ok(vec![]);
178 }
179 let cur = &(ctxt.current_templates[0]);
180 let next: Vec<Rc<Template<N>>> = ctxt
181 .current_templates
182 .iter()
183 .skip(1)
184 .skip_while(|t| t.import.len() == cur.import.len()) .cloned()
186 .collect();
187
188 if !next.is_empty() {
189 ContextBuilder::from(ctxt)
190 .current_templates(next.clone())
191 .build()
192 .dispatch(stctxt, &next[0].body)
193 } else {
194 Ok(vec![])
195 }
196}
197
198pub(crate) fn next_match<
200 N: Node,
201 F: FnMut(&str) -> Result<(), Error>,
202 G: FnMut(&str) -> Result<N, Error>,
203 H: FnMut(&Url) -> Result<String, Error>,
204>(
205 ctxt: &Context<N>,
206 stctxt: &mut StaticContext<N, F, G, H>,
207) -> Result<Sequence<N>, Error> {
208 if ctxt.current_templates.len() > 2 {
209 ContextBuilder::from(ctxt)
210 .current_templates(ctxt.current_templates.iter().skip(1).cloned().collect())
211 .build()
212 .dispatch(stctxt, &ctxt.current_templates[1].body)
213 } else {
214 Ok(vec![])
215 }
216}