Skip to main content

structured_asic/xml/
producer.rs

1//! Closed association of an actual complete ZIP input with its produced XML.
2
3use crate::{
4    manifest, references,
5    zip::{Member, ReadFailure},
6};
7use core::future::Future;
8use structured_asn1::{
9    memory::{HashWork, OwnedVec, Work},
10    range::RangeSource,
11};
12use structured_xades::{
13    attributes::AttributeXml,
14    binary::OwnedBinaryXml,
15    scalar::{ScalarXml, UriXml},
16    xml::{Access, AccessError, Snapshot, Xml},
17};
18
19pub mod signed;
20
21/// Stable XML owner types, independent of temporary parsed-document borrows.
22pub trait Types {
23    /// Stable original node identity, not a phase-local position.
24    type Id: Copy + Eq + HashWork;
25    /// Immutable original document identity.
26    type Document: Copy + Eq;
27    /// Original generation, checked before and after scoped operations.
28    type Generation: Copy + Eq;
29    /// Original XML processing/resource refusal.
30    type Error;
31}
32
33/// Actual owner of the moved input and admitted AST/arena storage. A borrowed
34/// view can remain alive across await without pinning or extending a lifetime.
35pub trait Parsed: Types {
36    /// Structural capabilities over this actual document. Signature verification
37    /// is separate and must retain this same original snapshot, root and bytes.
38    type View<'a>: Xml<
39            Id = Self::Id,
40            Document = Self::Document,
41            Generation = Self::Generation,
42            Error = Self::Error,
43        > + ScalarXml
44        + AttributeXml
45        + UriXml
46        + OwnedBinaryXml
47    where
48        Self: 'a;
49    /// Borrows this document without decoding, copying input or reparsing it.
50    fn view(&self) -> Self::View<'_>;
51    /// Original whole-document root, not a detached/reparented subtree.
52    fn root(&self) -> Self::Id;
53}
54
55/// Existing XML parser's funded ownership boundary. Parsing must consume the
56/// COMPLETE supplied bytes as one XML document. Retain their actual capacity
57/// charge; transfer validated text without a second buffer where supported.
58/// Admit every decode, arena growth, traversal and callback through current
59/// Work and the actual source limits. No default-budget parser substitutes it.
60pub trait Factory: Types {
61    /// Storage may borrow this operation's arena/account. The common driver
62    /// keeps it alive until the entire asynchronous visitor has completed.
63    type Parsed<'a, W: Work + 'a>: Parsed<
64            Id = Self::Id,
65            Document = Self::Document,
66            Generation = Self::Generation,
67            Error = Self::Error,
68        >
69    where
70        Self: 'a;
71    /// Accepts the actual complete CRC-checked allocation by ownership.
72    fn parse<'a, W: Work + 'a>(
73        &'a self,
74        input: OwnedVec<u8>,
75        execution: &'a W,
76    ) -> impl Future<Output = Result<Self::Parsed<'a, W>, Self::Error>> + 'a;
77}
78
79/// Fixed-output continuation. Its result cannot borrow the temporary document
80/// or view; independently owned findings can retain original scalar identities.
81pub trait Visitor<'v, 's, F: Types + ?Sized, S: RangeSource> {
82    /// Completed independently retained result, never a temporary XML borrow.
83    type Output;
84    /// Visitor's original cause, preserved over any later snapshot check.
85    type Error;
86    /// Executes while the actual parsed owner, source and query are alive.
87    fn visit<X, W: Work>(
88        self,
89        source: Source<'_, 'v, 's, X, S, W>,
90    ) -> impl Future<Output = Result<Self::Output, Self::Error>>
91    where
92        X: Xml<Id = F::Id, Document = F::Document, Generation = F::Generation, Error = F::Error>
93            + ScalarXml
94            + AttributeXml
95            + UriXml
96            + OwnedBinaryXml;
97}
98
99/// The actual original member and the view produced from its complete bytes.
100/// Only the closed driver constructs it. A caller cannot attach an independently
101/// parsed snapshot, assert a name match, or replace the narrowed current Work.
102pub struct Source<'q, 'v, 's, X: Xml, S: RangeSource, W: Work> {
103    member: &'q Member<'v, 's, S>,
104    xml: &'q X,
105    root: X::Id,
106    snapshot: Snapshot<X::Document, X::Generation>,
107    execution: &'q W,
108}
109impl<'v, 's, X: Xml, S: RangeSource, W: Work> Source<'_, 'v, 's, X, S, W> {
110    /// Actual complete original ZIP occurrence supplied to the XML owner.
111    #[must_use]
112    pub fn member(&self) -> &Member<'v, 's, S> {
113        self.member
114    }
115    /// Original produced view, preserving namespace and parent context.
116    #[must_use]
117    pub fn xml(&self) -> &X {
118        self.xml
119    }
120    /// Actual original root.
121    #[must_use]
122    pub fn root(&self) -> X::Id {
123        self.root
124    }
125    /// Snapshot observed from the actual produced view.
126    #[must_use]
127    pub fn snapshot(&self) -> Snapshot<X::Document, X::Generation> {
128        self.snapshot
129    }
130    /// Same source/current operation used for acquisition and XML production.
131    #[must_use]
132    pub fn execution(&self) -> &W {
133        self.execution
134    }
135}
136
137/// Source, producer and continuation failures retain independent original roles.
138#[derive(Debug)]
139pub enum Error<SE, PE, VE, XE> {
140    /// Complete input/CRC, snapshot or source/current budget refused.
141    Input(SE),
142    /// Actual XML decoding/parsing/arena refused.
143    Produce(PE),
144    /// Original callback failure, including its resource/processing class.
145    Visit(VE),
146    /// Produced root or generation/current query refused.
147    Xml(XE),
148}
149
150impl<'v, 's, S: RangeSource> Member<'v, 's, S> {
151    /// Acquires exactly this original member, validates EOF/CRC and moves its
152    /// funded allocation to the selected XML owner. The association is minted
153    /// only around that actual owner's scoped view. Cancellation drops all
154    /// driver-owned temporary storage; external visitor effects are not rolled
155    /// back. No successful partial result is returned after a late refusal.
156    pub async fn with_xml<F: Factory, V: Visitor<'v, 's, F, S>, W: Work>(
157        &self,
158        factory: &F,
159        visitor: V,
160        work: &W,
161    ) -> Result<
162        V::Output,
163        Error<
164            ReadFailure<S::Error, S::Document, S::Generation>,
165            F::Error,
166            V::Error,
167            AccessError<F::Error, F::Id>,
168        >,
169    > {
170        self.content(work)
171            .await
172            .map_err(Error::Input)?
173            .with_xml(factory, visitor, work)
174            .await
175    }
176}
177
178impl<'v, 's, S: RangeSource> crate::content::Content<'v, 's, S> {
179    /// Moves the already complete allocation into XML production. Raw manifest
180    /// signatures/time assertions can consume this same retained input first,
181    /// avoiding a second ZIP acquisition, decode, CRC pass or buffer copy.
182    pub async fn with_xml<F: Factory, V: Visitor<'v, 's, F, S>, W: Work>(
183        self,
184        factory: &F,
185        visitor: V,
186        work: &W,
187    ) -> Result<
188        V::Output,
189        Error<
190            ReadFailure<S::Error, S::Document, S::Generation>,
191            F::Error,
192            V::Error,
193            AccessError<F::Error, F::Id>,
194        >,
195    > {
196        let archive = self.member().archive();
197        let execution = archive.session().query(work);
198        let (member, input) = self.into_parts();
199        archive
200            .admit(&execution, member.central_span())
201            .map_err(|cause| {
202                Error::Input(ReadFailure::Processing {
203                    member: member.id(),
204                    cause,
205                })
206            })?;
207        let document = factory
208            .parse(input, &execution)
209            .await
210            .map_err(Error::Produce)?;
211        let root = document.root();
212        let xml = document.view();
213        let snapshot = xml.snapshot();
214        let access = Access::new(&xml, snapshot, &execution);
215        access.inspect(root, |_| ()).map_err(Error::Xml)?;
216        member
217            .archive()
218            .admit(&execution, member.central_span())
219            .map_err(|cause| {
220                Error::Input(ReadFailure::Processing {
221                    member: member.id(),
222                    cause,
223                })
224            })?;
225        let result = visitor
226            .visit(Source {
227                member: &member,
228                xml: &xml,
229                root,
230                snapshot,
231                execution: &execution,
232            })
233            .await
234            .map_err(Error::Visit)?;
235        access.inspect(root, |_| ()).map_err(Error::Xml)?;
236        archive
237            .admit(&execution, member.central_span())
238            .map_err(|cause| {
239                Error::Input(ReadFailure::Processing {
240                    member: member.id(),
241                    cause,
242                })
243            })?;
244        Ok(result)
245    }
246}
247
248/// Whole original manifest, resolved inside its producing scope. This binding
249/// cannot escape the owner borrow or be constructed from independent snapshots.
250pub struct Manifest<'q, 'v, 's, X: Xml, S: RangeSource, W: Work> {
251    xml: &'q X,
252    execution: &'q W,
253    catalogue: references::Catalogue<'v, 's, X::Id, X::Document, X::Generation, S>,
254}
255/// Original manifest syntax and exact member resolution retain their causes.
256#[derive(Debug)]
257pub enum ManifestError<ME, RE> {
258    /// Actual original XML structure/scalar inspection failed.
259    Inspect(ME),
260    /// Exact original reference or source/current admission failed.
261    Resolve(RE),
262}
263/// Original source-produced manifest and unchanged grammar/resolution errors.
264pub type ManifestResult<'q, 'v, 's, X, S, W> = Result<
265    Manifest<'q, 'v, 's, X, S, W>,
266    ManifestError<
267        manifest::Error<<X as Xml>::Error, <X as Xml>::Id>,
268        references::ResolveError<<X as Xml>::Error, <X as Xml>::Id, <S as RangeSource>::Error>,
269    >,
270>;
271impl<'q, 'v, 's, X, S, W> Source<'q, 'v, 's, X, S, W>
272where
273    X: Xml + AttributeXml + ScalarXml + UriXml,
274    S: RangeSource,
275    W: Work,
276{
277    /// Inspects package inventory only from this actual produced document.
278    /// Filename association remains distinct from signature authentication.
279    pub fn package(
280        &self,
281        format: crate::package::Format,
282    ) -> crate::package::InspectionResult<'v, 's, X, S> {
283        let access = Access::new(self.xml, self.snapshot, self.execution);
284        let base = self
285            .member
286            .archive()
287            .original(self.member.id(), self.execution)
288            .map_err(crate::package::Error::Archive)?;
289        crate::package::inspect(base, &access, self.root, format)
290    }
291    /// Inspects and resolves the actual produced root, never an external node.
292    pub fn manifest(&self) -> ManifestResult<'q, 'v, 's, X, S, W> {
293        let access = Access::new(self.xml, self.snapshot, self.execution);
294        let reading = manifest::inspect(&access, self.root).map_err(ManifestError::Inspect)?;
295        let base = self
296            .member
297            .archive()
298            .original(self.member.id(), self.execution)
299            .map_err(|cause| ManifestError::Resolve(references::ResolveError::Archive(cause)))?;
300        let catalogue = references::resolve_manifest(base, reading, self.xml, self.execution)
301            .map_err(ManifestError::Resolve)?;
302        Ok(Manifest {
303            xml: self.xml,
304            execution: self.execution,
305            catalogue,
306        })
307    }
308}
309impl<'v, 's, X: Xml, S: RangeSource, W: Work> Manifest<'_, 'v, 's, X, S, W> {
310    /// Original source association, still inside its actual producing scope.
311    #[must_use]
312    pub fn source(&self) -> Source<'_, 'v, 's, X, S, W> {
313        Source {
314            member: self.catalogue.base(),
315            xml: self.xml,
316            root: self.catalogue.reading().root(),
317            snapshot: self.catalogue.reading().snapshot(),
318            execution: self.execution,
319        }
320    }
321    /// Complete syntax/reference census, independently of crypto and trust.
322    #[must_use]
323    pub fn catalogue(
324        &self,
325    ) -> &references::Catalogue<'v, 's, X::Id, X::Document, X::Generation, S> {
326        &self.catalogue
327    }
328    /// Moves independently retained source-derived instructions without keeping
329    /// the temporary AST alive. This is inspection evidence, not replay authority
330    /// or authenticated manifest coverage. No catalogue or string is copied.
331    pub fn into_facts(self) -> Facts<'v, 's, X::Id, X::Document, X::Generation, S> {
332        Facts {
333            catalogue: self.catalogue,
334            digests: None,
335        }
336    }
337}
338
339/// Complete instructions derived inside the actual producing XML scope. Only
340/// that closed scope can construct these retained facts; no public attach or
341/// arbitrary member-plus-snapshot constructor exists.
342pub struct Facts<'v, 's, N, D, G, S: RangeSource> {
343    catalogue: references::Catalogue<'v, 's, N, D, G, S>,
344    digests: Option<OwnedVec<references::DigestReading>>,
345}
346impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Facts<'v, 's, N, D, G, S> {
347    /// Every original syntax fault, extension, rootfile and reference outcome.
348    #[must_use]
349    pub fn catalogue(&self) -> &references::Catalogue<'v, 's, N, D, G, S> {
350        &self.catalogue
351    }
352    /// Every actual original object hash result. None means not executed,
353    /// independently of syntax, manifest authentication and archive geometry.
354    #[must_use]
355    pub fn object_digests(&self) -> Option<&[references::DigestReading]> {
356        self.digests.as_deref()
357    }
358}
359
360impl<'v, 's, X: OwnedBinaryXml, S: RangeSource, W: Work> Manifest<'_, 'v, 's, X, S, W> {
361    /// Checks every actual source instruction before retaining the source-bound
362    /// facts. Repeated references share one complete member pass; the result
363    /// survives AST drop without gaining XML replay authority or trust.
364    pub async fn check_objects<'p, P: structured_xades::certificate::DigestProvider<'p, X>>(
365        self,
366        provider: &'p P,
367    ) -> Result<
368        Facts<'v, 's, X::Id, X::Document, X::Generation, S>,
369        references::CheckError<
370            X::Error,
371            P::Error,
372            X::Id,
373            X::Document,
374            X::Generation,
375            S::Error,
376            S::Document,
377            S::Generation,
378        >,
379    > {
380        let answers = self
381            .catalogue
382            .check_objects(self.xml, provider, self.execution)
383            .await?
384            .into_answers();
385        Ok(Facts {
386            catalogue: self.catalogue,
387            digests: Some(answers),
388        })
389    }
390}