Skip to main content

schematron_engine/
schema.rs

1//! Stage-1 Schematron schema model: `schema`/`ns`/`pattern`/`rule`/
2//! `assert`/`report`, as produced by [`crate::parser::parse`].
3//!
4//! `context` and `test` are kept as raw, unparsed XPath strings — parsing
5//! them is a later phase, gated on the sister crate `xpath-eval` (see
6//! `plan/02-schema-parser.md`, "Voraussetzungen").
7
8/// A parsed Schematron schema (Stage-1 subset, plus Stage-2 `<let>`).
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct Schema {
11    pub ns: Vec<NamespaceBinding>,
12    pub lets: Vec<LetBinding>,
13    pub default_phase: Option<String>,
14    pub phases: Vec<Phase>,
15    pub patterns: Vec<Pattern>,
16    pub diagnostics: Vec<Diagnostic>,
17}
18
19/// A `<diagnostic id="..." role="...">` — referenced by a [`Check`]'s
20/// `diagnostics` IDREFS list. `message` uses the same rich-content model
21/// as a check's own message (see `plan/08-diagnostics.md`).
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct Diagnostic {
24    pub id: String,
25    pub role: Option<String>,
26    pub message: Vec<MessagePart>,
27}
28
29/// A `<phase id="..."><active pattern="..."/>...</phase>` — a named,
30/// selectable subset of patterns. See `plan/05-phase-selection.md` for how
31/// `Schema.default_phase`/`Schema.phases` feed into
32/// [`crate::engine::evaluate_with_phase`].
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct Phase {
35    pub id: String,
36    pub lets: Vec<LetBinding>,
37    /// The `Pattern::id`s this phase activates, in document order (from
38    /// this phase's `<active pattern="...">` children).
39    pub active: Vec<String>,
40}
41
42/// A `<let name="..." value="...">` variable binding. `value` is the raw,
43/// unparsed XPath expression (see [`Rule::context`]/[`Check::test`] for the
44/// same treatment). See `plan/04-let-bindings.md` for scope (only the
45/// `value`-attribute form is supported, not the `foreign-element+` literal-
46/// XML-content form) and evaluation semantics (schema/pattern-level `<let>`
47/// evaluate against the document root, rule-level against the matched
48/// context node, per [`crate::engine::evaluate`]).
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct LetBinding {
51    pub name: String,
52    pub value: String,
53}
54
55/// An `<ns prefix="..." uri="..."/>` binding, used to resolve prefixes in
56/// `context`/`test` XPath expressions (not evaluated in this phase).
57#[derive(Clone, Debug, Eq, PartialEq)]
58pub struct NamespaceBinding {
59    pub prefix: String,
60    pub uri: String,
61}
62
63/// A `<pattern>` — a named or anonymous group of rules.
64#[derive(Clone, Debug, Eq, PartialEq)]
65pub struct Pattern {
66    pub id: Option<String>,
67    pub lets: Vec<LetBinding>,
68    pub rules: Vec<Rule>,
69}
70
71/// A `<rule context="...">`. `context` is the raw, unparsed XPath
72/// expression selecting the nodes this rule applies to.
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct Rule {
75    pub context: String,
76    pub lets: Vec<LetBinding>,
77    pub checks: Vec<Check>,
78}
79
80/// An `<assert>` or `<report>` element. `test` is the raw, unparsed XPath
81/// expression. `message` is the element's rich content, rendered to a
82/// final string only when the check fires — see
83/// [`crate::engine::evaluate`] and `plan/06-value-of-interpolation.md`.
84#[derive(Clone, Debug, Eq, PartialEq)]
85pub struct Check {
86    pub kind: CheckKind,
87    pub test: String,
88    pub id: Option<String>,
89    pub role: Option<String>,
90    pub message: Vec<MessagePart>,
91    /// The `diagnostics="id1 id2"` IDREFS list, raw (not cross-checked
92    /// against `Schema.diagnostics` here — see `plan/08-diagnostics.md`,
93    /// same accepted simplification as `Phase.active`).
94    pub diagnostics: Vec<String>,
95}
96
97/// One piece of a [`Check`]'s message content, in document order.
98///
99/// The ISO Schematron content model for `assert`/`report` also allows
100/// `name`/`emph`/`dir`/`span` markup (and arbitrary foreign-namespace
101/// elements) — only `<value-of select="...">` is evaluated (Stage 2, see
102/// `plan/06-value-of-interpolation.md`); text nested inside the other,
103/// unsupported wrapper elements still contributes via `Text` (the wrapper
104/// tag itself carries no meaning here, same as Stage 1's plain-text
105/// handling), but those elements' own semantics (e.g. `<name/>` resolving
106/// to the context node's name) are not implemented.
107#[derive(Clone, Debug, Eq, PartialEq)]
108pub enum MessagePart {
109    Text(String),
110    /// A `<value-of select="...">` — `select` is the raw, unparsed XPath
111    /// expression, evaluated against the firing check's context node.
112    ValueOf(String),
113}
114
115/// Whether a [`Check`] is an `<assert>` (fails when `test` evaluates to
116/// false) or a `<report>` (fires when `test` evaluates to true).
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub enum CheckKind {
119    Assert,
120    Report,
121}