structured_xades/scalar.rs
1//! Original XML base-datatype syntax, separate from authenticated properties.
2
3use crate::reference::LexicalSource;
4use crate::xml::{Access, AccessError, Snapshot, Xml};
5use structured_asn1::memory::Work;
6
7/// Complete XML-normalized text, before schema whitespace conversion. This is
8/// a lexical source view, not successful interpretation of a requested schema.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum TextValue<'view> {
11 /// The requested original attribute was not written.
12 Absent,
13 /// Element children prevent reading simple content.
14 ComplexContent,
15 /// Entire original value, including significant spaces and empty text.
16 Present(&'view str),
17}
18
19/// Scoped original text for a format-owned interpretation such as RFC4514.
20/// Reuse the XML owner's existing scalar/simple-content reader. Do not collapse
21/// spaces, flatten element children, serialize/reparse or retain callback-local
22/// joined text. Current Work admits all traversal/joined storage before use.
23pub trait TextXml: Xml {
24 /// Visit one original simple-content or attribute source exactly once.
25 /// Preserve the first visitor refusal and the owner's original processing
26 /// cause. A foreign/stale source is rejected before text access.
27 fn with_text<R, VE, W: Work>(
28 &self,
29 snapshot: Snapshot<Self::Document, Self::Generation>,
30 node: Self::Id,
31 source: LexicalSource<'_>,
32 execution: &W,
33 visitor: impl for<'view> FnOnce(TextValue<'view>) -> Result<R, VE>,
34 ) -> Result<R, crate::reference::VisitError<Self::Error, VE>>;
35}
36
37/// Original `QName` value resolved by the XML owner, not by a copied prefix map.
38/// Namespace strings and local names can borrow callback-local joined storage.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct QNameValue<'view> {
41 /// In-scope namespace, including the default namespace for unprefixed values.
42 /// None means no binding; it does not mean a missing original value.
43 pub namespace: Option<&'view str>,
44 /// Complete local name, preserving case and Unicode without normalization.
45 pub local_name: &'view str,
46}
47
48/// `QName` syntax/resolution outcome, independent of schema type admissibility.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum QNameFact<'view> {
51 /// Original requested attribute absent.
52 Absent,
53 /// Element children prevent simple content.
54 ComplexContent,
55 /// Original lexical value is not an XSD1.0 `QName`.
56 Malformed,
57 /// Valid lexical `QName` whose prefix has no original in-scope binding.
58 UnboundPrefix(&'view str),
59 /// Owner does not implement this datatype.
60 Unsupported,
61 /// Resolved original value; not proof of a declared or derived schema type.
62 Value(QNameValue<'view>),
63}
64
65/// Original XML text and its separate `QName` interpretation.
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub struct QNameReading<'view> {
68 /// Complete XML-normalized text before schema whitespace collapse.
69 pub lexical: Option<&'view str>,
70 /// Exact lexical/resolution result under the original owner's namespace scope.
71 pub fact: QNameFact<'view>,
72}
73
74/// XML-owned `QName` projection for xsi:type's format-owned type interpretation.
75/// The owner admits text/namespace scans, joining and resolution with current
76/// Work, rejects foreign/stale source before access and preserves callback errors.
77/// No declaration lookup, schema fetch or signature verification follows.
78pub trait QNameXml: Xml {
79 /// Visit the original reading once. Borrowed text/names cannot leave this
80 /// scope; copied results must be admitted through the current execution.
81 ///
82 /// ```compile_fail
83 /// use structured_xades::{scalar::QNameXml, reference::{LexicalSource, VisitError}, xml::{Snapshot, Xml}};
84 /// use structured_asn1::memory::Work;
85 /// fn escape<'a, X: QNameXml, W: Work>(xml: &'a X, snapshot: Snapshot<X::Document, X::Generation>, node: X::Id, work: &W)
86 /// -> Result<Option<&'a str>, VisitError<X::Error, ()>> {
87 /// xml.with_qname(snapshot, node, LexicalSource::SimpleContent, work, |read| Ok(read.lexical))
88 /// }
89 /// ```
90 fn with_qname<R, VE, W: Work>(
91 &self,
92 snapshot: Snapshot<Self::Document, Self::Generation>,
93 node: Self::Id,
94 source: LexicalSource<'_>,
95 execution: &W,
96 visitor: impl for<'view> FnOnce(QNameReading<'view>) -> Result<R, VE>,
97 ) -> Result<R, crate::reference::VisitError<Self::Error, VE>>;
98}
99
100/// Scoped complete schema-normalized anyURI, without acquisition or resolution.
101/// A successful value may borrow temporary joined/collapsed storage ONLY while
102/// the visitor executes. It never claims permanence or OID interpretation.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum UriValue<'view> {
105 /// The selected original attribute was absent.
106 Absent,
107 /// Element children prevent reading simple content.
108 ComplexContent,
109 /// The present value violates the requested base datatype.
110 Malformed,
111 /// The XML owner cannot establish this base datatype.
112 Unsupported,
113 /// Entire XSD1.0 anyURI value after schema whitespace processing.
114 Valid(&'view str),
115}
116
117/// XML-owned scoped URI value for format-specific interpretation. Implementors
118/// reuse the SAME anyURI kernel as [`ScalarXml`], including original identity,
119/// joining/collapse capacity and current-operation admission before all work.
120/// No second XML parser, string copy, resolver or signature verification follows.
121pub trait UriXml: Xml {
122 /// Visit the complete value once; preserve the first caller refusal without
123 /// replacing it with a later source/generation/resource failure. The result
124 /// cannot borrow the callback-local normalized value.
125 ///
126 /// ```compile_fail
127 /// use structured_xades::{scalar::{UriXml, UriValue}, reference::{LexicalSource, VisitError}, xml::{Snapshot, Xml}};
128 /// use structured_asn1::memory::Work;
129 /// fn escape<'a, X: UriXml, W: Work>(xml: &'a X, snapshot: Snapshot<X::Document, X::Generation>, node: X::Id, work: &W)
130 /// -> Result<&'a str, VisitError<X::Error, ()>> {
131 /// xml.with_uri(snapshot, node, LexicalSource::SimpleContent, work, |value| {
132 /// match value { UriValue::Valid(text) => Ok(text), _ => Err(()) }
133 /// })
134 /// }
135 /// ```
136 fn with_uri<R, VE, W: Work>(
137 &self,
138 snapshot: Snapshot<Self::Document, Self::Generation>,
139 node: Self::Id,
140 source: LexicalSource<'_>,
141 execution: &W,
142 visitor: impl for<'view> FnOnce(UriValue<'view>) -> Result<R, VE>,
143 ) -> Result<R, crate::reference::VisitError<Self::Error, VE>>;
144}
145
146/// Exact base datatype requested by an `XAdES` field's normative schema.
147/// This is not an application policy, URI resolver or whole-schema validator.
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum Datatype {
150 /// XSD1.0 ID lexical syntax; no registration, uniqueness or authentication.
151 Id,
152 /// XSD1.0 anyURI, including its schema whitespace processing. No URL policy,
153 /// absolute-URI requirement, escaping, dereferencing or acquisition follows.
154 AnyUri,
155 /// XSD1.0 integer lexical syntax without a finite machine-integer bound.
156 Integer,
157}
158
159/// Outcome for one requested original base datatype, not whole-schema validity.
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum Fact {
162 /// The selected original attribute was not written.
163 Absent,
164 /// Element children prevent reading the requested simple content.
165 ComplexContent,
166 /// Present lexical data do not satisfy the requested base datatype.
167 Malformed,
168 /// The owner has not implemented this base datatype; never invalid syntax.
169 Unsupported,
170 /// The complete original value satisfies only the requested base datatype.
171 Valid,
172}
173
174/// Narrow XML-owned lexical projection over the original immutable source.
175///
176/// Implementors reuse their scalar kernel, including exact XSD1.0 whitespace,
177/// Unicode and arbitrary-precision rules. Admit scans, joining/collapse and
178/// temporary capacity under the supplied current Work before performing them.
179/// An error keeps its original processing/resource cause; it is not a lexical
180/// fact. No second parser, default value or silently narrowed integer is needed.
181/// Signature authentication and compiled XML operation policy are separate;
182/// this projection does not reverify a signature or confer authenticated status.
183/// <https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/>
184pub trait ScalarXml: Xml {
185 /// Inspect exactly this original element or expanded-name attribute.
186 /// Reject a foreign/stale source before access. Missing, malformed, complex
187 /// and unsupported remain distinct, even when no normalized value is kept.
188 fn scalar<W: Work>(
189 &self,
190 snapshot: Snapshot<Self::Document, Self::Generation>,
191 node: Self::Id,
192 source: LexicalSource<'_>,
193 datatype: Datatype,
194 execution: &W,
195 ) -> Result<Fact, Self::Error>;
196}
197
198/// A present schema field failed its requested base-datatype check.
199/// Caller/source/resource failures stay separate from these completed facts.
200#[derive(Debug)]
201pub enum Error<E, N> {
202 /// Original current-operation, XML or generation refusal.
203 Access(AccessError<E, N>),
204 /// The original field did not establish the requested lexical value.
205 Value {
206 /// Original XML owner, not a copied-string byte coordinate.
207 node: N,
208 /// Requested schema base datatype.
209 datatype: Datatype,
210 /// Exact failed/incomplete outcome; Unsupported is not Malformed.
211 fact: Fact,
212 },
213}
214
215/// Lexical check result preserving this owner's original source/error types.
216pub type ScalarResult<X> = Result<(), Error<<X as Xml>::Error, <X as Xml>::Id>>;
217
218/// Require a PRESENT field's complete original lexical syntax under current
219/// Work. Optional absence is decided by the owning field schema before calling
220/// this function; an absent requested attribute is never fabricated as valid.
221///
222/// Successful return proves only this requested base datatype. It does not
223/// validate other attributes, xsi:type derivation, URI/OID meaning, ID uniqueness,
224/// signed coverage, permanent identifier assignment or a baseline level.
225pub fn check<X: ScalarXml, W: Work>(
226 access: &Access<'_, X, W>,
227 node: X::Id,
228 source: LexicalSource<'_>,
229 datatype: Datatype,
230) -> ScalarResult<X> {
231 let fact = access
232 .query(node, |xml, snapshot, work| {
233 xml.scalar(snapshot, node, source, datatype, work)
234 })
235 .map_err(Error::Access)?;
236 match fact {
237 Fact::Valid => Ok(()),
238 fact => Err(Error::Value {
239 node,
240 datatype,
241 fact,
242 }),
243 }
244}