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}