structured_xades/identifier/meaning.rs
1//! Qualifier interpretation over the XML owner's complete normalized URI.
2
3use super::{Qualifier, Reading};
4use crate::reference::{LexicalSource, VisitError};
5use crate::scalar::{UriValue, UriXml};
6use crate::xml::{Access, AccessError, Xml};
7use structured_asn1::memory::{MemoryError, Work};
8use structured_asn1::value::OwnedOid;
9
10/// Established syntax/qualifier interpretation, not permanent assignment,
11/// authenticated coverage or trust. URI spelling stays in its original source.
12#[derive(Debug)]
13pub enum Meaning {
14 /// Generic URI, or `OIDAsURI`'s explicitly non-URN URI. No universal OID-to-URI
15 /// resolver is specified here; no encoded OID is invented from such a URI.
16 Uri,
17 /// RFC3061 OID with independent original-account storage after the XML
18 /// visitor's temporary joined/collapsed string has been released.
19 Oid(OwnedOid),
20}
21
22/// Original XML, base syntax, qualifier and OID failures retain separate causes.
23#[derive(Debug)]
24pub enum MeaningError<E, N> {
25 /// Original source/generation/current-operation refusal.
26 Access(AccessError<E, N>),
27 /// No valid complete original anyURI was established.
28 Value {
29 /// Original Identifier element.
30 node: N,
31 /// Requested base-datatype outcome, never a fabricated malformed fault.
32 fact: crate::scalar::Fact,
33 },
34 /// `OIDAsURN` did not use the RFC3061 namespace, or `OIDAsURI` used a URN.
35 Qualifier {
36 /// Original Identifier element.
37 node: N,
38 },
39 /// Original arbitrary-width ASN.1 OID conversion or resource refusal.
40 Oid {
41 /// Original Identifier element containing the normalized URI.
42 node: N,
43 /// Offset is relative to the normalized dotted-decimal suffix, NOT an
44 /// invented physical XML byte coordinate. Original node stays attached.
45 cause: structured_asn1::Error,
46 },
47}
48
49/// Complete interpretation in this XML owner's original identity/error domain.
50pub type MeaningResult<X> = Result<Meaning, MeaningError<<X as Xml>::Error, <X as Xml>::Id>>;
51
52/// Complete original identifier and documentation check, before trust/policy.
53#[derive(Debug)]
54pub enum CheckError<E, N> {
55 /// Original documentation grammar/source refusal.
56 Structure(crate::properties::Error<E, N>),
57 /// Identifier qualifier/OID/original URI interpretation refusal.
58 Meaning(MeaningError<E, N>),
59 /// A documentation reference's original base-datatype refusal.
60 Scalar(crate::scalar::Error<E, N>),
61}
62
63impl<E, N> From<crate::properties::Error<E, N>> for CheckError<E, N> {
64 fn from(cause: crate::properties::Error<E, N>) -> Self {
65 Self::Structure(cause)
66 }
67}
68
69/// Complete identifier result using original source and error types.
70pub type CheckResult<X> = Result<Meaning, CheckError<<X as Xml>::Error, <X as Xml>::Id>>;
71
72/// Check qualifier meaning AND every original documentation URI using the same
73/// documentation grammar as structural reading. Failure drops a partially
74/// retained numeric OID; original XML and prior capacity owners remain intact.
75pub fn check<X: UriXml + crate::scalar::ScalarXml, W: Work>(
76 access: &Access<'_, X, W>,
77 reading: &Reading<X::Id>,
78) -> CheckResult<X> {
79 let meaning = check_value(access, reading).map_err(CheckError::Meaning)?;
80 super::documentation_with(access, reading.documentation, |node| {
81 crate::scalar::check(
82 access,
83 node,
84 LexicalSource::SimpleContent,
85 crate::scalar::Datatype::AnyUri,
86 )
87 .map_err(CheckError::Scalar)
88 })?;
89 Ok(meaning)
90}
91
92/// Interpret an already structurally selected original Identifier without
93/// reparsing XML or retaining a temporary normalized string. Documentation URI
94/// validation is the enclosing reader's separate obligation.
95///
96/// EN319132-1 V1.3.1 5.1.2 selects RFC3061 for `OIDAsURN` and explicitly excludes
97/// URNs from `OIDAsURI`. RFC3061 2 permits ONLY unpadded decimal arcs; 3 includes
98/// uppercase URN:OID. The existing ASN.1 numericOID kernel enforces those arc
99/// rules and ASN.1 roots, including widths beyond machine integers. No percent
100/// decoding, URI canonicalization, network lookup or OID assignment is implied.
101/// <https://www.rfc-editor.org/rfc/rfc3061.html#section-2>
102/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
103pub fn check_value<X: UriXml, W: Work>(
104 access: &Access<'_, X, W>,
105 reading: &Reading<X::Id>,
106) -> MeaningResult<X> {
107 let node = reading.identifier;
108 let result = access.query_fallible(node, |xml, snapshot, work| {
109 xml.with_uri(
110 snapshot,
111 node,
112 LexicalSource::SimpleContent,
113 work,
114 |value| {
115 use crate::scalar::Fact;
116 let text = match value {
117 UriValue::Valid(text) => text,
118 value => {
119 let fact = match value {
120 UriValue::Absent => Fact::Absent,
121 UriValue::ComplexContent => Fact::ComplexContent,
122 UriValue::Malformed => Fact::Malformed,
123 UriValue::Unsupported => Fact::Unsupported,
124 UriValue::Valid(_) => unreachable!("successful URI selected above"),
125 };
126 return Err(MeaningError::Value { node, fact });
127 }
128 };
129 match reading.qualifier {
130 None => Ok(Meaning::Uri),
131 Some(Qualifier::Uri) => {
132 if prefix(text, "urn:", work).map_err(|cause| resource(node, cause))? {
133 return Err(MeaningError::Qualifier { node });
134 }
135 Ok(Meaning::Uri)
136 }
137 Some(Qualifier::Urn) => {
138 if !prefix(text, "urn:oid:", work).map_err(|cause| resource(node, cause))? {
139 return Err(MeaningError::Qualifier { node });
140 }
141 OwnedOid::parse_in(&text[8..], work)
142 .map(Meaning::Oid)
143 .map_err(|cause| MeaningError::Oid { node, cause })
144 }
145 }
146 },
147 )
148 });
149 match result {
150 Ok(value) => Ok(value),
151 Err(AccessError::Xml(VisitError::Visitor(cause))) => Err(cause),
152 Err(AccessError::Xml(VisitError::Processing(cause))) => {
153 Err(MeaningError::Access(AccessError::Xml(cause)))
154 }
155 Err(AccessError::Changed { node }) => {
156 Err(MeaningError::Access(AccessError::Changed { node }))
157 }
158 Err(AccessError::Resource { node, cause }) => Err(resource(node, cause)),
159 }
160}
161
162fn prefix<W: Work>(text: &str, expected: &str, work: &W) -> Result<bool, MemoryError> {
163 if text.len() < expected.len() {
164 return Ok(false);
165 }
166 work.charge_work(expected.len() as u64)?;
167 Ok(text.as_bytes()[..expected.len()].eq_ignore_ascii_case(expected.as_bytes()))
168}
169
170fn resource<E, N>(node: N, cause: MemoryError) -> MeaningError<E, N> {
171 MeaningError::Access(AccessError::Resource { node, cause })
172}