1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! # Callables
//! Sequence constructors that are invoked by stylesheet code, such as named templates and functions.
//! The difference between them is that named templates have named parameters,
//! whereas functions have positional parameters.
// TODO: tunneling parameters
use crate::item::Node;
use crate::transform::context::StaticContext;
use crate::transform::{NamespaceMap, Transform};
use crate::{Context, Error, ErrorKind, Sequence};
use qualname::QName;
use std::collections::HashMap;
use url::Url;
#[derive(Clone, Debug)]
pub struct Callable<N: Node> {
pub body: Transform<N>,
pub parameters: FormalParameters<N>,
// TODO: return type
}
impl<N: Node> Callable<N> {
pub fn new(body: Transform<N>, parameters: FormalParameters<N>) -> Self {
Callable { body, parameters }
}
}
// TODO: parameter type ("as" attribute)
#[derive(Clone, Debug)]
pub enum FormalParameters<N: Node> {
Named(Vec<(QName, Option<Transform<N>>)>), // parameter name, default value
Positional(Vec<QName>),
}
#[derive(Clone, Debug)]
pub enum ActualParameters<N: Node> {
Named(Vec<(QName, Transform<N>)>), // parameter name, value
Positional(Vec<Transform<N>>),
}
/// Invoke a callable component
pub fn invoke<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
qn: &QName,
a: &ActualParameters<N>,
_ns: &NamespaceMap,
) -> Result<Sequence<N>, Error> {
match ctxt.callables.get(qn) {
Some(t) => {
match &t.parameters {
FormalParameters::Named(v) => {
let mut newctxt = ctxt.clone();
// Put the actual parameters in a HashMap for easy access
let mut actuals = HashMap::new();
if let ActualParameters::Named(av) = a {
av.iter().try_for_each(|(a_name, a_value)| {
actuals.insert(a_name, ctxt.dispatch(stctxt, a_value)?);
Ok(())
})?
} else {
return Err(Error::new(ErrorKind::TypeError, "argument mismatch"));
}
// Match each actual parameter to a formal parameter by name
v.iter().try_for_each(|(name, dflt)| {
match actuals.get(name) {
Some(val) => {
newctxt.var_push(name.to_string(), val.clone());
Ok(())
}
None => {
// Use default value
if let Some(d) = dflt {
newctxt.var_push(name.to_string(), ctxt.dispatch(stctxt, d)?)
} else {
newctxt.var_push(name.to_string(), vec![])
}
Ok(())
}
}
})?;
// Check that the maximum depth limit will not be exceeded,
// if there is one set
if let Some(md) = ctxt.max_depth {
if md == ctxt.depth {
return Err(Error::new(
crate::ErrorKind::LimitExceeded,
format!("exceeded evaluation depth ({})", ctxt.depth),
));
}
}
newctxt.depth = ctxt.depth + 1;
newctxt.dispatch(stctxt, &t.body)
}
FormalParameters::Positional(v) => {
if let ActualParameters::Positional(av) = a {
// Make sure number of parameters are equal, then set up variables by position
if v.len() == av.len() {
let mut newctxt = ctxt.clone();
v.iter().zip(av.iter()).try_for_each(|(qn, t)| {
newctxt.var_push(qn.to_string(), ctxt.dispatch(stctxt, t)?);
Ok(())
})?;
// Check that the maximum depth limit will not be exceeded,
// if there is one set
if let Some(md) = ctxt.max_depth {
if md == ctxt.depth {
return Err(Error::new(
crate::ErrorKind::LimitExceeded,
format!("exceeded evaluation depth ({})", ctxt.depth),
));
}
}
newctxt.depth = ctxt.depth + 1;
newctxt.dispatch(stctxt, &t.body)
} else {
Err(Error::new(ErrorKind::TypeError, "argument mismatch"))
}
} else {
Err(Error::new(ErrorKind::TypeError, "argument mismatch"))
}
}
}
}
None => Err(Error::new(
ErrorKind::Unknown,
format!("unknown callable \"{}\"", qn),
)),
}
}