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
//! User-supplied extension functions for XSLT / XPath.
//!
//! Stylesheets can reference functions in non-XSLT namespaces (e.g.
//! `<xsl:value-of select="my:lookup($id)"/>` where `xmlns:my="urn:app"`).
//! The XPath engine consults the [`ExtensionFunctions`] trait when
//! it encounters such a call; if the trait's `call` returns `Some`,
//! the engine uses that result. Returning `None` signals "I don't
//! handle this function" — the engine continues its fallback chain
//! (native EXSLT, then "unknown function" error).
//!
//! Two integration paths:
//!
//! 1. **`Extensions` builder** — register individual functions by
//! `(namespace, name)`. Best for the common case of a few
//! short helpers:
//!
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use sup_xml_xslt::{Extensions, XPathValue, Stylesheet};
//!
//! let mut exts = Extensions::new();
//! exts.register("urn:app", "double", |args| {
//! let n = match args.first() {
//! Some(XPathValue::Number(n)) => n.as_f64(),
//! Some(XPathValue::String(s)) => s.parse().unwrap_or(0.0),
//! _ => 0.0,
//! };
//! Ok(XPathValue::Number((n * 2.0).into()))
//! });
//! # let style = Stylesheet::compile_str(
//! # r#"<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"/>"#)?;
//! # let source_doc =
//! # sup_xml_core::parse_str("<r/>", &sup_xml_core::ParseOptions::default())?;
//! let result = style.apply_with_extensions(&source_doc, &exts)?;
//! # let _ = result;
//! # Ok(())
//! # }
//! ```
//!
//! 2. **Custom [`ExtensionFunctions`] impl** — for cases that need
//! state, dynamic dispatch over a wide function set, or
//! integration with an existing function table. Implement the
//! trait directly on your own type.
//!
//! Both paths reach the same hook inside the XPath evaluator.
use HashMap;
use XmlError;
use XPathValue;
/// Caller-supplied lookup for non-XSLT XPath functions.
///
/// Implement on your own type to integrate with an existing function
/// registry, or use the [`Extensions`] builder for per-call closure
/// registration.
type ExtensionFn = ;
/// Ergonomic builder for the common case of registering a handful of
/// closure-based extension functions keyed by `(namespace, name)`.
///
/// Lookups are O(1) and don't allocate per call (the &str argument is
/// matched directly against the stored String keys via `Borrow`).