xrust/transform/
callable.rs1use crate::item::Node;
9use crate::transform::context::StaticContext;
10use crate::transform::{NamespaceMap, Transform};
11use crate::{Context, Error, ErrorKind, Sequence};
12use qualname::QName;
13use std::collections::HashMap;
14use url::Url;
15
16#[derive(Clone, Debug)]
17pub struct Callable<N: Node> {
18 pub body: Transform<N>,
19 pub parameters: FormalParameters<N>,
20 }
22
23impl<N: Node> Callable<N> {
24 pub fn new(body: Transform<N>, parameters: FormalParameters<N>) -> Self {
25 Callable { body, parameters }
26 }
27}
28
29#[derive(Clone, Debug)]
31pub enum FormalParameters<N: Node> {
32 Named(Vec<(QName, Option<Transform<N>>)>), Positional(Vec<QName>),
34}
35#[derive(Clone, Debug)]
36pub enum ActualParameters<N: Node> {
37 Named(Vec<(QName, Transform<N>)>), Positional(Vec<Transform<N>>),
39}
40
41pub fn invoke<
43 N: Node,
44 F: FnMut(&str) -> Result<(), Error>,
45 G: FnMut(&str) -> Result<N, Error>,
46 H: FnMut(&Url) -> Result<String, Error>,
47>(
48 ctxt: &Context<N>,
49 stctxt: &mut StaticContext<N, F, G, H>,
50 qn: &QName,
51 a: &ActualParameters<N>,
52 _ns: &NamespaceMap,
53) -> Result<Sequence<N>, Error> {
54 match ctxt.callables.get(qn) {
55 Some(t) => {
56 match &t.parameters {
57 FormalParameters::Named(v) => {
58 let mut newctxt = ctxt.clone();
59 let mut actuals = HashMap::new();
61 if let ActualParameters::Named(av) = a {
62 av.iter().try_for_each(|(a_name, a_value)| {
63 actuals.insert(a_name, ctxt.dispatch(stctxt, a_value)?);
64 Ok(())
65 })?
66 } else {
67 return Err(Error::new(ErrorKind::TypeError, "argument mismatch"));
68 }
69 v.iter().try_for_each(|(name, dflt)| {
71 match actuals.get(name) {
72 Some(val) => {
73 newctxt.var_push(name.to_string(), val.clone());
74 Ok(())
75 }
76 None => {
77 if let Some(d) = dflt {
79 newctxt.var_push(name.to_string(), ctxt.dispatch(stctxt, d)?)
80 } else {
81 newctxt.var_push(name.to_string(), vec![])
82 }
83 Ok(())
84 }
85 }
86 })?;
87
88 if let Some(md) = ctxt.max_depth {
92 if md == ctxt.depth {
93 return Err(Error::new(
94 crate::ErrorKind::LimitExceeded,
95 format!("exceeded evaluation depth ({})", ctxt.depth),
96 ));
97 }
98 }
99 newctxt.depth = ctxt.depth + 1;
100
101 newctxt.dispatch(stctxt, &t.body)
102 }
103 FormalParameters::Positional(v) => {
104 if let ActualParameters::Positional(av) = a {
105 if v.len() == av.len() {
107 let mut newctxt = ctxt.clone();
108 v.iter().zip(av.iter()).try_for_each(|(qn, t)| {
109 newctxt.var_push(qn.to_string(), ctxt.dispatch(stctxt, t)?);
110 Ok(())
111 })?;
112
113 if let Some(md) = ctxt.max_depth {
117 if md == ctxt.depth {
118 return Err(Error::new(
119 crate::ErrorKind::LimitExceeded,
120 format!("exceeded evaluation depth ({})", ctxt.depth),
121 ));
122 }
123 }
124 newctxt.depth = ctxt.depth + 1;
125
126 newctxt.dispatch(stctxt, &t.body)
127 } else {
128 Err(Error::new(ErrorKind::TypeError, "argument mismatch"))
129 }
130 } else {
131 Err(Error::new(ErrorKind::TypeError, "argument mismatch"))
132 }
133 }
134 }
135 }
136 None => Err(Error::new(
137 ErrorKind::Unknown,
138 format!("unknown callable \"{}\"", qn),
139 )),
140 }
141}