Skip to main content

xsd_schema/xpath/functions/
signature.rs

1//! Function signature definitions for XPath 2.0 functions.
2//!
3//! This module provides types for describing function signatures,
4//! including parameter types and arity constraints.
5
6use crate::types::sequence::{ItemType, SequenceType};
7
8/// XPath function namespace (default for unprefixed function calls)
9pub const FN_NAMESPACE: &str = "http://www.w3.org/2005/xpath-functions";
10
11/// XPath 2010 function namespace (alias for compatibility)
12pub const FN_2010_NAMESPACE: &str = "http://www.w3.org/xpath-functions";
13
14/// Function arity specification
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum FunctionArity {
17    /// Fixed number of arguments
18    Exact(usize),
19    /// Minimum number of arguments (variadic function)
20    /// For example, `concat` takes at least 2 arguments
21    Variadic(usize),
22    /// Range of arguments (min, max inclusive)
23    Range(usize, usize),
24}
25
26impl FunctionArity {
27    /// Check if a given argument count matches this arity
28    pub fn matches(&self, count: usize) -> bool {
29        match self {
30            Self::Exact(n) => count == *n,
31            Self::Variadic(min) => count >= *min,
32            Self::Range(min, max) => count >= *min && count <= *max,
33        }
34    }
35
36    /// Get the minimum required argument count
37    pub fn min_args(&self) -> usize {
38        match self {
39            Self::Exact(n) => *n,
40            Self::Variadic(min) => *min,
41            Self::Range(min, _) => *min,
42        }
43    }
44
45    /// Get the maximum allowed argument count (None for variadic)
46    pub fn max_args(&self) -> Option<usize> {
47        match self {
48            Self::Exact(n) => Some(*n),
49            Self::Variadic(_) => None,
50            Self::Range(_, max) => Some(*max),
51        }
52    }
53}
54
55/// Function signature describing parameter and return types
56#[derive(Debug, Clone)]
57pub struct FunctionSignature {
58    /// The function namespace URI
59    pub namespace: &'static str,
60    /// The local name of the function
61    pub local_name: &'static str,
62    /// The arity specification
63    pub arity: FunctionArity,
64    /// Parameter types (may be shorter than actual args for variadic functions)
65    pub param_types: Vec<SequenceType>,
66    /// Return type
67    pub return_type: SequenceType,
68}
69
70impl FunctionSignature {
71    /// Create a new function signature with exact arity
72    pub fn new(
73        namespace: &'static str,
74        local_name: &'static str,
75        param_types: Vec<SequenceType>,
76        return_type: SequenceType,
77    ) -> Self {
78        let arity = FunctionArity::Exact(param_types.len());
79        Self {
80            namespace,
81            local_name,
82            arity,
83            param_types,
84            return_type,
85        }
86    }
87
88    /// Create a function signature with variadic arity
89    pub fn variadic(
90        namespace: &'static str,
91        local_name: &'static str,
92        min_args: usize,
93        param_types: Vec<SequenceType>,
94        return_type: SequenceType,
95    ) -> Self {
96        Self {
97            namespace,
98            local_name,
99            arity: FunctionArity::Variadic(min_args),
100            param_types,
101            return_type,
102        }
103    }
104
105    /// Create a function signature with range arity
106    pub fn range(
107        namespace: &'static str,
108        local_name: &'static str,
109        min_args: usize,
110        max_args: usize,
111        param_types: Vec<SequenceType>,
112        return_type: SequenceType,
113    ) -> Self {
114        Self {
115            namespace,
116            local_name,
117            arity: FunctionArity::Range(min_args, max_args),
118            param_types,
119            return_type,
120        }
121    }
122
123    /// Check if this signature matches the given arity
124    pub fn matches_arity(&self, count: usize) -> bool {
125        self.arity.matches(count)
126    }
127
128    /// Get the expected type for a parameter at the given index
129    ///
130    /// For variadic functions, if the index exceeds the param_types length,
131    /// returns the last parameter type (if any) or item()*.
132    pub fn param_type(&self, index: usize) -> SequenceType {
133        if index < self.param_types.len() {
134            self.param_types[index].clone()
135        } else if !self.param_types.is_empty() {
136            // For variadic functions, use the last parameter type
137            self.param_types.last().unwrap().clone()
138        } else {
139            // Default to any sequence
140            SequenceType::any()
141        }
142    }
143}
144
145// Convenience constructors for common signature patterns
146
147/// Create a signature for fn:xxx() with no arguments
148pub fn sig_0(local_name: &'static str, return_type: SequenceType) -> FunctionSignature {
149    FunctionSignature::new(FN_NAMESPACE, local_name, vec![], return_type)
150}
151
152/// Create a signature for fn:xxx($arg) with one argument
153pub fn sig_1(
154    local_name: &'static str,
155    arg1: SequenceType,
156    return_type: SequenceType,
157) -> FunctionSignature {
158    FunctionSignature::new(FN_NAMESPACE, local_name, vec![arg1], return_type)
159}
160
161/// Create a signature for fn:xxx($arg1, $arg2) with two arguments
162pub fn sig_2(
163    local_name: &'static str,
164    arg1: SequenceType,
165    arg2: SequenceType,
166    return_type: SequenceType,
167) -> FunctionSignature {
168    FunctionSignature::new(FN_NAMESPACE, local_name, vec![arg1, arg2], return_type)
169}
170
171/// Create a signature for fn:xxx($arg1, $arg2, $arg3) with three arguments
172pub fn sig_3(
173    local_name: &'static str,
174    arg1: SequenceType,
175    arg2: SequenceType,
176    arg3: SequenceType,
177    return_type: SequenceType,
178) -> FunctionSignature {
179    FunctionSignature::new(
180        FN_NAMESPACE,
181        local_name,
182        vec![arg1, arg2, arg3],
183        return_type,
184    )
185}
186
187/// Create common sequence types for function signatures
188pub mod types {
189    use super::*;
190
191    /// xs:string
192    pub fn string() -> SequenceType {
193        SequenceType::string()
194    }
195
196    /// xs:string?
197    pub fn string_opt() -> SequenceType {
198        SequenceType::string_optional()
199    }
200
201    /// xs:string*
202    pub fn string_star() -> SequenceType {
203        SequenceType::star(ItemType::AtomicType(crate::types::XmlTypeCode::String))
204    }
205
206    /// xs:boolean
207    pub fn boolean() -> SequenceType {
208        SequenceType::boolean()
209    }
210
211    /// xs:integer
212    pub fn integer() -> SequenceType {
213        SequenceType::integer()
214    }
215
216    /// xs:integer?
217    pub fn integer_opt() -> SequenceType {
218        SequenceType::integer_optional()
219    }
220
221    /// xs:integer*
222    pub fn integer_star() -> SequenceType {
223        SequenceType::star(ItemType::AtomicType(crate::types::XmlTypeCode::Integer))
224    }
225
226    /// xs:double
227    pub fn double() -> SequenceType {
228        SequenceType::double()
229    }
230
231    /// xs:double?
232    pub fn double_opt() -> SequenceType {
233        SequenceType::double_optional()
234    }
235
236    /// xs:anyAtomicType
237    pub fn any_atomic() -> SequenceType {
238        SequenceType::any_atomic()
239    }
240
241    /// xs:anyAtomicType?
242    pub fn any_atomic_opt() -> SequenceType {
243        SequenceType::any_atomic_optional()
244    }
245
246    /// xs:anyAtomicType*
247    pub fn any_atomic_star() -> SequenceType {
248        SequenceType::any_atomic_star()
249    }
250
251    /// node()
252    pub fn node() -> SequenceType {
253        SequenceType::node()
254    }
255
256    /// node()?
257    pub fn node_opt() -> SequenceType {
258        SequenceType::optional(ItemType::AnyNode)
259    }
260
261    /// node()*
262    pub fn nodes() -> SequenceType {
263        SequenceType::nodes()
264    }
265
266    /// item()
267    pub fn item() -> SequenceType {
268        SequenceType::item()
269    }
270
271    /// item()?
272    pub fn item_opt() -> SequenceType {
273        SequenceType::optional(ItemType::AnyItem)
274    }
275
276    /// item()*
277    pub fn any() -> SequenceType {
278        SequenceType::any()
279    }
280
281    /// empty-sequence()
282    pub fn empty() -> SequenceType {
283        SequenceType::empty()
284    }
285
286    /// xs:QName
287    pub fn qname() -> SequenceType {
288        SequenceType::qname()
289    }
290
291    /// xs:QName?
292    pub fn qname_opt() -> SequenceType {
293        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::QName))
294    }
295
296    /// xs:anyURI
297    pub fn any_uri() -> SequenceType {
298        SequenceType::any_uri()
299    }
300
301    /// xs:anyURI?
302    pub fn any_uri_opt() -> SequenceType {
303        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::AnyUri))
304    }
305
306    /// xs:dateTime
307    pub fn datetime() -> SequenceType {
308        SequenceType::datetime()
309    }
310
311    /// xs:dateTime?
312    pub fn datetime_opt() -> SequenceType {
313        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::DateTime))
314    }
315
316    /// xs:date
317    pub fn date() -> SequenceType {
318        SequenceType::date()
319    }
320
321    /// xs:date?
322    pub fn date_opt() -> SequenceType {
323        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Date))
324    }
325
326    /// xs:time
327    pub fn time() -> SequenceType {
328        SequenceType::time()
329    }
330
331    /// xs:time?
332    pub fn time_opt() -> SequenceType {
333        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Time))
334    }
335
336    /// xs:duration
337    pub fn duration() -> SequenceType {
338        SequenceType::duration()
339    }
340
341    /// xs:duration?
342    pub fn duration_opt() -> SequenceType {
343        SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Duration))
344    }
345
346    /// xs:dayTimeDuration
347    pub fn day_time_duration() -> SequenceType {
348        SequenceType::one(ItemType::AtomicType(
349            crate::types::XmlTypeCode::DayTimeDuration,
350        ))
351    }
352
353    /// xs:dayTimeDuration?
354    pub fn day_time_duration_opt() -> SequenceType {
355        SequenceType::optional(ItemType::AtomicType(
356            crate::types::XmlTypeCode::DayTimeDuration,
357        ))
358    }
359
360    /// element()
361    pub fn element() -> SequenceType {
362        SequenceType::one(ItemType::Element(None, None))
363    }
364
365    /// numeric (xs:double | xs:decimal | xs:float)
366    pub fn numeric() -> SequenceType {
367        // Use double as the general numeric type
368        SequenceType::double()
369    }
370
371    /// numeric?
372    pub fn numeric_opt() -> SequenceType {
373        SequenceType::double_optional()
374    }
375}
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380
381    #[test]
382    fn test_arity_exact() {
383        let arity = FunctionArity::Exact(2);
384        assert!(arity.matches(2));
385        assert!(!arity.matches(1));
386        assert!(!arity.matches(3));
387        assert_eq!(arity.min_args(), 2);
388        assert_eq!(arity.max_args(), Some(2));
389    }
390
391    #[test]
392    fn test_arity_variadic() {
393        let arity = FunctionArity::Variadic(2);
394        assert!(!arity.matches(1));
395        assert!(arity.matches(2));
396        assert!(arity.matches(10));
397        assert_eq!(arity.min_args(), 2);
398        assert_eq!(arity.max_args(), None);
399    }
400
401    #[test]
402    fn test_arity_range() {
403        let arity = FunctionArity::Range(1, 3);
404        assert!(!arity.matches(0));
405        assert!(arity.matches(1));
406        assert!(arity.matches(2));
407        assert!(arity.matches(3));
408        assert!(!arity.matches(4));
409        assert_eq!(arity.min_args(), 1);
410        assert_eq!(arity.max_args(), Some(3));
411    }
412
413    #[test]
414    fn test_signature_param_type() {
415        let sig = sig_2(
416            "substring",
417            types::string(),
418            types::double(),
419            types::string(),
420        );
421
422        assert_eq!(sig.param_type(0), types::string());
423        assert_eq!(sig.param_type(1), types::double());
424        // Out of bounds returns last type
425        assert_eq!(sig.param_type(2), types::double());
426    }
427
428    #[test]
429    fn test_signature_matches_arity() {
430        let sig = sig_2("test", types::string(), types::string(), types::string());
431        assert!(!sig.matches_arity(1));
432        assert!(sig.matches_arity(2));
433        assert!(!sig.matches_arity(3));
434    }
435}