Skip to main content

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}