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
//! # 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(crate) 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> {
/*let mut qnr = qn.clone();
qnr.resolve(|p| {
ns.get(&p).map_or(
Err(Error::new(
ErrorKind::DynamicAbsent,
"no namespace for prefix",
)),
|r| Ok(r.clone()),
)
})?;*/
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(())
}
}
})?;
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(())
})?;
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),
)),
}
}