Skip to main content

structured_xades/
identifier.rs

1//! The one original-source `ObjectIdentifierType` structural kernel.
2//!
3//! This reading does not establish URI/OID lexical validity, permanence or trust.
4//! Callers retain the original nodes for the XML owner's exact scalar validation.
5
6use crate::container::XADES;
7use crate::properties::{self, Error, PropertyResult};
8use crate::xml::{Access, Name, Xml};
9use structured_asn1::memory::Work;
10
11mod meaning;
12pub use meaning::{
13    CheckError, CheckResult, Meaning, MeaningError, MeaningResult, check, check_value,
14};
15
16/// Read one original auxiliary identifier and check every anyURI field with
17/// the XML owner's scalar kernel. Structure shares [`read`]; there is no second
18/// grammar or collected/normalized URI string. EN319132-1 §5.1.2 and Annex C
19/// assign anyURI to Identifier and every `DocumentationReference`.
20/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
21///
22/// This checks base-datatype syntax only. A written OID qualifier still needs
23/// its RFC3061/URI interpretation; permanent assignment and signed binding are
24/// not inferred from lexical validity. Optional absent documentation creates
25/// no invented field. All source, unsupported and resource causes survive.
26pub fn read_uri_syntax<X: crate::scalar::ScalarXml, W: Work>(
27    access: &Access<'_, X, W>,
28    parent: X::Id,
29) -> UriResult<X> {
30    use crate::reference::LexicalSource;
31    use crate::scalar::{Datatype, check};
32    read_with(access, parent, |node| {
33        check(access, node, LexicalSource::SimpleContent, Datatype::AnyUri)
34            .map_err(UriError::Scalar)
35    })
36}
37
38/// Original structure versus requested base-datatype refusal.
39#[derive(Debug)]
40pub enum UriError<E, N> {
41    /// The original identifier's structural/source/resource cause.
42    Structure(Error<E, N>),
43    /// Exact lexical or current-operation refusal at one original URI field.
44    Scalar(crate::scalar::Error<E, N>),
45}
46
47impl<E, N> From<Error<E, N>> for UriError<E, N> {
48    fn from(cause: Error<E, N>) -> Self {
49        Self::Structure(cause)
50    }
51}
52
53/// URI-syntax reading in this owner's original identity/error domain.
54pub type UriResult<X> =
55    Result<Reading<<X as Xml>::Id>, UriError<<X as Xml>::Error, <X as Xml>::Id>>;
56
57/// Explicit OID spelling selected by the schema's Qualifier enumeration.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum Qualifier {
60    /// OID carried in a URI which is not a URN.
61    Uri,
62    /// OID carried in an RFC 3061 URN.
63    Urn,
64}
65
66/// Original auxiliary structure, not an authenticated-property constructor.
67#[derive(Debug)]
68pub struct Reading<N> {
69    /// Original Identifier simple-content element.
70    pub identifier: N,
71    /// Written qualifier; absence means a URI identifier, not an implied OID.
72    pub qualifier: Option<Qualifier>,
73    /// Original optional textual description.
74    pub description: Option<N>,
75    /// Original optional nonempty `DocumentationReferences` container.
76    pub documentation: Option<N>,
77}
78
79/// Reads EN319132-1 V1.3.1 §5.1.2's auxiliary type without collecting text.
80/// A caller supplies the actual ObjectIdentifier/SigPolicyId/CommitmentTypeId
81/// element already selected by its enclosing schema. This function cannot
82/// promote an arbitrary node to signed or trusted evidence.
83///
84/// The official schema enumerates `OIDAsURN`; the prose's `OIDASURN` spelling
85/// does not add a third schema value. URI/OID content validation is separate.
86/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
87pub fn read<X: Xml, W: Work>(
88    access: &Access<'_, X, W>,
89    parent: X::Id,
90) -> PropertyResult<Reading<X::Id>, X> {
91    read_with(access, parent, |node| {
92        properties::simple_content(access, node)
93    })
94}
95
96// Both inspection and lexical qualification share the field grammar. The
97// lexical owner checks URI simple content itself: no second documentation
98// traversal, redundant text scan or collected normalized string is required.
99fn read_with<X: Xml, W: Work, E: From<Error<X::Error, X::Id>>>(
100    access: &Access<'_, X, W>,
101    parent: X::Id,
102    mut uri: impl FnMut(X::Id) -> Result<(), E>,
103) -> Result<Reading<X::Id>, E> {
104    let mut fields = [None; 3];
105    let mut previous = 0;
106    for child in access.children(parent) {
107        let node = child.map_err(Error::Child)?;
108        let mut selected = None;
109        for (rank, local) in ["Identifier", "Description", "DocumentationReferences"]
110            .iter()
111            .enumerate()
112        {
113            if access
114                .has_name(
115                    node,
116                    Name {
117                        namespace: XADES,
118                        local,
119                    },
120                )
121                .map_err(Error::Access)?
122            {
123                selected = Some(rank);
124                break;
125            }
126        }
127        let rank = selected.ok_or(Error::Unexpected { node })?;
128        if fields[rank].is_some() {
129            return Err(Error::Duplicate { node }.into());
130        }
131        if rank < previous {
132            return Err(Error::Order { node }.into());
133        }
134        fields[rank] = Some(node);
135        previous = rank;
136        if rank == 0 {
137            uri(node)?;
138        } else if rank == 1 {
139            properties::simple_content(access, node)?;
140        }
141    }
142    let identifier = fields[0].ok_or(Error::Missing {
143        parent,
144        local: "Identifier",
145    })?;
146    let qualifier = access
147        .attribute(
148            identifier,
149            Name {
150                namespace: "",
151                local: "Qualifier",
152            },
153            |value| {
154                let Some(value) = value else { return Ok(None) };
155                let refusal = |cause| Error::Resource {
156                    node: identifier,
157                    cause,
158                };
159                if access.equal(value, "OIDAsURI").map_err(refusal)? {
160                    return Ok(Some(Qualifier::Uri));
161                }
162                if access.equal(value, "OIDAsURN").map_err(refusal)? {
163                    return Ok(Some(Qualifier::Urn));
164                }
165                Err(Error::Unexpected { node: identifier })
166            },
167        )
168        .map_err(Error::Access)??;
169    documentation_with(access, fields[2], &mut uri)?;
170    Ok(Reading {
171        identifier,
172        qualifier,
173        description: fields[1],
174        documentation: fields[2],
175    })
176}
177
178// Every structural, base-syntax and complete identifier route uses this exact
179// documentation grammar; temporary conversion output drops if a late URI fails.
180fn documentation_with<X: Xml, W: Work, E: From<Error<X::Error, X::Id>>>(
181    access: &Access<'_, X, W>,
182    parent: Option<X::Id>,
183    mut uri: impl FnMut(X::Id) -> Result<(), E>,
184) -> Result<(), E> {
185    if let Some(parent) = parent {
186        let mut present = false;
187        for child in access.children(parent) {
188            let node = child.map_err(Error::Child)?;
189            if !access
190                .has_name(
191                    node,
192                    Name {
193                        namespace: XADES,
194                        local: "DocumentationReference",
195                    },
196                )
197                .map_err(Error::Access)?
198            {
199                return Err(Error::Unexpected { node }.into());
200            }
201            uri(node)?;
202            present = true;
203        }
204        if !present {
205            return Err(Error::Missing {
206                parent,
207                local: "DocumentationReference",
208            }
209            .into());
210        }
211    }
212    Ok(())
213}