1use super::{ArchiveError, Graph, MemberId, Role, resource};
4use crate::layout::{Form, Inspection, Principal};
5use structured_asn1::{
6 memory::{Execution, OwnedMap, OwnedVec, Work},
7 range::RangeSource,
8};
9use structured_ltv::timestamp::Purpose;
10
11type SourceResult<T, S> = Result<T, ArchiveError<<S as RangeSource>::Error>>;
12type Associations<S> =
13 OwnedMap<usize, InputAssociation<<S as RangeSource>::Document, <S as RangeSource>::Generation>>;
14type MemberOf<S> = MemberId<<S as RangeSource>::Document, <S as RangeSource>::Generation>;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum AssertionScope {
20 Declared,
22 Current,
24 Outside,
26 Unavailable,
28}
29#[derive(Debug, Clone, Copy)]
31pub enum InputAssociation<D, G> {
32 Known {
34 member: MemberId<D, G>,
36 purpose: Purpose,
38 },
39 Missing,
41 Ambiguous,
43}
44#[derive(Debug, Clone, Copy)]
47pub struct AssertionInput<D, G> {
48 pub token: MemberId<D, G>,
50 pub scope: AssertionScope,
52 pub association: InputAssociation<D, G>,
54}
55pub struct TimestampInputs<D, G> {
58 entries: OwnedVec<AssertionInput<D, G>>,
59 complete: bool,
60}
61impl<D, G> TimestampInputs<D, G> {
62 #[must_use]
64 pub fn entries(&self) -> &[AssertionInput<D, G>] {
65 &self.entries
66 }
67 #[must_use]
69 pub fn complete(&self) -> bool {
70 self.complete
71 }
72}
73impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Graph<'v, 's, N, D, G, S> {
74 fn input_associations(
75 &self,
76 layout: &Inspection<'_, 'v, 's, S>,
77 execution: &Execution<'_>,
78 ) -> SourceResult<Associations<S>, S> {
79 let refusal = |cause| resource(self.archive.directory_span(), cause);
80 let mut associations = execution.map();
81 for fact in &self.facts {
82 execution.charge_work(1).map_err(refusal)?;
83 let catalogue = fact.catalogue();
84 let Some(token) = catalogue.signature_member(execution)? else {
85 continue;
86 };
87 let role = layout
88 .census()
89 .occurrence(token.id().ordinal(), execution)?
90 .map(|(_, role)| role);
91 if role != Some(Role::Timestamp) {
92 continue;
93 }
94 let source = catalogue.base();
95 let purpose = match layout
96 .census()
97 .occurrence(source.id().ordinal(), execution)?
98 .map(|(_, role)| role)
99 {
100 Some(Role::ArchiveManifest) => Purpose::Archive,
101 Some(Role::Manifest) => Purpose::Content,
102 _ => continue,
103 };
104 if associations
105 .try_insert(
106 token.id().ordinal(),
107 InputAssociation::Known {
108 member: source.id(),
109 purpose,
110 },
111 )
112 .map_err(refusal)?
113 .is_some()
114 {
115 associations
116 .try_insert(token.id().ordinal(), InputAssociation::Ambiguous)
117 .map_err(refusal)?;
118 }
119 }
120 Ok(associations.into_owned())
121 }
122 fn simple_data(
123 layout: &Inspection<'_, 'v, 's, S>,
124 execution: &Execution<'_>,
125 ) -> SourceResult<Option<MemberOf<S>>, S> {
126 if layout.form() != Form::Simple(Principal::Timestamp) {
127 return Ok(None);
128 }
129 let mut simple = None;
130 for ordinal in 0..layout.census().len() {
131 let Some((member, role)) = layout.census().occurrence(ordinal, execution)? else {
132 continue;
133 };
134 if role != Role::Data {
135 continue;
136 }
137 if simple.is_some() {
138 return Ok(None);
139 }
140 simple = Some(member.id());
141 }
142 Ok(simple)
143 }
144 pub fn timestamp_inputs<W: Work>(
148 &self,
149 index: usize,
150 layout: &Inspection<'_, 'v, 's, S>,
151 work: &W,
152 ) -> SourceResult<TimestampInputs<S::Document, S::Generation>, S> {
153 if !core::ptr::eq(layout.census().archive(), self.archive) {
154 return Err(ArchiveError::Foreign {
155 range: self.archive.directory_span(),
156 });
157 }
158 let execution = self.archive.session().query(work);
159 self.archive
160 .admit(&execution, self.archive.directory_span())?;
161 let refusal = |cause| resource(self.archive.directory_span(), cause);
162 let current = self.nodes.get(index).ok_or(ArchiveError::Foreign {
163 range: self.archive.directory_span(),
164 })?;
165 let associations = self.input_associations(layout, &execution)?;
166 let mut declared = execution.map();
167 for member in current.objects().iter().flatten() {
168 declared.try_insert(member.ordinal(), ()).map_err(refusal)?;
169 }
170 let simple = Self::simple_data(layout, &execution)?;
171 let geometry = self.geometry_complete();
172 let mut complete = geometry;
173 let mut entries = execution.vector();
174 for ordinal in 0..layout.census().len() {
175 let Some((member, role)) = layout.census().occurrence(ordinal, &execution)? else {
176 continue;
177 };
178 if role != Role::Timestamp {
179 continue;
180 }
181 let mut association = match associations.get(&ordinal, &execution).map_err(refusal)? {
182 Some(value) => *value,
183 None => InputAssociation::Missing,
184 };
185 if let Some(data) = simple
186 && matches!(association, InputAssociation::Missing)
187 {
188 execution
189 .charge_work(member.name().len() as u64)
190 .map_err(refusal)?;
191 if member.name() == b"META-INF/timestamp.tst" {
192 association = InputAssociation::Known {
193 member: data,
194 purpose: Purpose::Content,
195 };
196 }
197 }
198 let scope = if current.timestamp == Some(member.id()) {
199 AssertionScope::Current
200 } else if !geometry {
201 AssertionScope::Unavailable
202 } else if declared
203 .get(&ordinal, &execution)
204 .map_err(refusal)?
205 .is_some()
206 {
207 AssertionScope::Declared
208 } else {
209 AssertionScope::Outside
210 };
211 if scope == AssertionScope::Declared
212 && !matches!(association, InputAssociation::Known { .. })
213 {
214 complete = false;
215 }
216 entries
217 .try_push(AssertionInput {
218 token: member.id(),
219 scope,
220 association,
221 })
222 .map_err(refusal)?;
223 }
224 self.archive
225 .admit(&execution, self.archive.directory_span())?;
226 Ok(TimestampInputs {
227 entries: entries.into_owned(),
228 complete,
229 })
230 }
231}