1use structured_asn1::memory::{HashWork, MemoryError, Work};
4use structured_ltv::source::{Query, ScopeError};
5
6pub use structured_ltv::source::Snapshot;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct Name<'a> {
12 pub namespace: &'a str,
14 pub local: &'a str,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Node<'a> {
21 Element(Name<'a>),
23 Text(&'a str),
25 Other,
27}
28
29pub trait Xml {
41 type Id: Copy + Eq + HashWork;
45 type Document: Copy + Eq;
47 type Generation: Copy + Eq;
49 type Error;
51
52 fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
54
55 fn inspect<R, W: Work>(
57 &self,
58 snapshot: Snapshot<Self::Document, Self::Generation>,
59 node: Self::Id,
60 execution: &W,
61 visitor: impl for<'view> FnOnce(Node<'view>) -> R,
62 ) -> Result<R, Self::Error>;
63
64 fn attribute<R, W: Work>(
70 &self,
71 snapshot: Snapshot<Self::Document, Self::Generation>,
72 node: Self::Id,
73 name: Name<'_>,
74 execution: &W,
75 visitor: impl for<'view> FnOnce(Option<&'view str>) -> R,
76 ) -> Result<R, Self::Error>;
77
78 fn first_child<W: Work>(
80 &self,
81 snapshot: Snapshot<Self::Document, Self::Generation>,
82 parent: Self::Id,
83 execution: &W,
84 ) -> Result<Option<Self::Id>, Self::Error>;
85
86 fn next_sibling<W: Work>(
91 &self,
92 snapshot: Snapshot<Self::Document, Self::Generation>,
93 parent: Self::Id,
94 node: Self::Id,
95 execution: &W,
96 ) -> Result<Option<Self::Id>, Self::Error>;
97}
98
99#[derive(Debug)]
101pub enum AccessError<E, N> {
102 Xml(E),
104 Resource {
106 node: N,
108 cause: MemoryError,
110 },
111 Changed {
113 node: N,
115 },
116}
117
118pub type AccessResult<T, X> = Result<T, AccessError<<X as Xml>::Error, <X as Xml>::Id>>;
120
121#[derive(Debug)]
123pub enum ChildError<E, N> {
124 Access(AccessError<E, N>),
126 Text {
128 node: N,
130 },
131}
132
133pub struct Access<'a, X: Xml, W: Work> {
140 xml: &'a X,
141 snapshot: Snapshot<X::Document, X::Generation>,
142 execution: &'a W,
143}
144
145impl<'a, X: Xml, W: Work> Access<'a, X, W> {
146 #[must_use]
148 pub fn new(
149 xml: &'a X,
150 snapshot: Snapshot<X::Document, X::Generation>,
151 execution: &'a W,
152 ) -> Self {
153 Self {
154 xml,
155 snapshot,
156 execution,
157 }
158 }
159
160 #[must_use]
162 pub fn snapshot(&self) -> Snapshot<X::Document, X::Generation> {
163 self.snapshot
164 }
165
166 #[must_use]
172 pub fn execution(&self) -> &W {
173 self.execution
174 }
175
176 pub(crate) fn owner(&self) -> &'a X {
177 self.xml
178 }
179
180 #[must_use]
187 pub fn children(&self, parent: X::Id) -> Children<'_, 'a, X, W> {
188 Children {
189 access: self,
190 parent,
191 previous: None,
192 started: false,
193 done: false,
194 }
195 }
196
197 fn unchanged<E>(&self, node: X::Id) -> Result<(), AccessError<E, X::Id>> {
198 if self.xml.snapshot() != self.snapshot {
199 return Err(AccessError::Changed { node });
200 }
201 Ok(())
202 }
203
204 fn call<R, E>(
205 &self,
206 node: X::Id,
207 callback: impl FnOnce() -> Result<R, E>,
208 ) -> Result<R, AccessError<E, X::Id>> {
209 self.unchanged(node)?;
210 self.execution
211 .charge_work(1)
212 .map_err(|cause| AccessError::Resource { node, cause })?;
213 let result = callback().map_err(AccessError::Xml)?;
214 self.unchanged(node)?;
215 Ok(result)
216 }
217
218 pub(crate) fn query<R>(
219 &self,
220 node: X::Id,
221 callback: impl FnOnce(&X, Snapshot<X::Document, X::Generation>, &W) -> Result<R, X::Error>,
222 ) -> AccessResult<R, X> {
223 self.call(node, || callback(self.xml, self.snapshot, self.execution))
224 }
225
226 pub(crate) fn query_fallible<R, E>(
229 &self,
230 node: X::Id,
231 callback: impl FnOnce(&X, Snapshot<X::Document, X::Generation>, &W) -> Result<R, E>,
232 ) -> Result<R, AccessError<E, X::Id>> {
233 self.call(node, || callback(self.xml, self.snapshot, self.execution))
234 }
235
236 pub(crate) fn equal(&self, left: &str, right: &str) -> Result<bool, MemoryError> {
237 equal_text(left, right, self.execution)
238 }
239
240 pub(crate) fn equal_id(&self, left: X::Id, right: X::Id) -> Result<bool, MemoryError> {
241 equal_id(left, right, self.execution)
242 }
243
244 pub(crate) fn trim_xml_whitespace<'value>(
245 &self,
246 value: &'value str,
247 ) -> Result<&'value str, MemoryError> {
248 self.execution
249 .charge_work(u64::try_from(value.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
250 Ok(value.trim_matches([' ', '\t', '\r', '\n']))
251 }
252
253 pub fn inspect<R>(
268 &self,
269 node: X::Id,
270 visitor: impl for<'view> FnOnce(Node<'view>) -> R,
271 ) -> AccessResult<R, X> {
272 self.call(node, || {
273 self.xml
274 .inspect(self.snapshot, node, self.execution, visitor)
275 })
276 }
277
278 pub fn has_name(&self, node: X::Id, expected: Name<'_>) -> AccessResult<bool, X> {
283 let compared = self.inspect(node, |value| {
284 let Node::Element(actual) = value else {
285 return Ok(false);
286 };
287 if !equal_text(actual.namespace, expected.namespace, self.execution)? {
291 return Ok(false);
292 }
293 equal_text(actual.local, expected.local, self.execution)
294 })?;
295 compared.map_err(|cause| AccessError::Resource { node, cause })
296 }
297
298 pub fn attribute<R>(
300 &self,
301 node: X::Id,
302 name: Name<'_>,
303 visitor: impl for<'view> FnOnce(Option<&'view str>) -> R,
304 ) -> AccessResult<R, X> {
305 self.call(node, || {
306 self.xml
307 .attribute(self.snapshot, node, name, self.execution, visitor)
308 })
309 }
310
311 pub fn first_child(&self, parent: X::Id) -> AccessResult<Option<X::Id>, X> {
313 self.call(parent, || {
314 self.xml.first_child(self.snapshot, parent, self.execution)
315 })
316 }
317
318 pub fn next_sibling(&self, parent: X::Id, node: X::Id) -> AccessResult<Option<X::Id>, X> {
320 self.call(node, || {
321 self.xml
322 .next_sibling(self.snapshot, parent, node, self.execution)
323 })
324 }
325}
326
327impl<X: Xml, W: Work> Query for Access<'_, X, W> {
330 type Id = X::Id;
331 type Document = X::Document;
332 type Generation = X::Generation;
333 type Work = W;
334
335 fn snapshot(&self) -> Snapshot<X::Document, X::Generation> {
336 self.snapshot
337 }
338
339 fn execution(&self) -> &W {
340 self.execution
341 }
342
343 fn admit(&self, node: X::Id, units: u64) -> Result<(), ScopeError<X::Id>> {
344 let changed = |_: AccessError<(), X::Id>| ScopeError::Changed { node };
345 self.unchanged(node).map_err(changed)?;
346 self.execution
347 .charge_work(units)
348 .map_err(|cause| ScopeError::Resource { node, cause })?;
349 self.unchanged(node).map_err(changed)
350 }
351}
352
353impl<E, N> From<ScopeError<N>> for AccessError<E, N> {
354 fn from(cause: ScopeError<N>) -> Self {
355 match cause {
356 ScopeError::Changed { node } => Self::Changed { node },
357 ScopeError::Resource { node, cause } => Self::Resource { node, cause },
358 }
359 }
360}
361
362pub(crate) fn equal_id<N: Copy + HashWork, W: Work>(
363 left: N,
364 right: N,
365 execution: &W,
366) -> Result<bool, MemoryError> {
367 let cost = left
368 .hash_work()?
369 .checked_add(right.hash_work()?)
370 .ok_or(MemoryError::WorkExceeded)?;
371 execution.charge_work(cost)?;
372 Ok(left == right)
373}
374
375pub struct Children<'a, 'xml, X: Xml, W: Work> {
377 access: &'a Access<'xml, X, W>,
378 parent: X::Id,
379 previous: Option<X::Id>,
380 started: bool,
381 done: bool,
382}
383
384impl<X: Xml, W: Work> Iterator for Children<'_, '_, X, W> {
385 type Item = Result<X::Id, ChildError<X::Error, X::Id>>;
386
387 fn next(&mut self) -> Option<Self::Item> {
388 if self.done {
389 return None;
390 }
391 loop {
392 let advanced = if self.started {
393 let Some(previous) = self.previous else {
394 self.done = true;
395 return None;
396 };
397 self.access.next_sibling(self.parent, previous)
398 } else {
399 self.started = true;
400 self.access.first_child(self.parent)
401 };
402 let node = match advanced {
403 Ok(Some(node)) => node,
404 Ok(None) => {
405 self.done = true;
406 return None;
407 }
408 Err(error) => {
409 self.done = true;
410 return Some(Err(ChildError::Access(error)));
411 }
412 };
413 self.previous = Some(node);
414 let inspected = self.access.inspect(node, |value| match value {
415 Node::Element(_) => Ok(Some(true)),
416 Node::Other => Ok(None),
417 Node::Text(text) => {
418 let length =
419 u64::try_from(text.len()).map_err(|_| MemoryError::WorkExceeded)?;
420 self.access.execution.charge_work(length)?;
421 for byte in text.bytes() {
425 if !matches!(byte, b' ' | b'\t' | b'\r' | b'\n') {
426 return Ok(Some(false));
427 }
428 }
429 Ok(None)
430 }
431 });
432 match inspected {
433 Ok(Ok(Some(true))) => return Some(Ok(node)),
434 Ok(Ok(None)) => {}
435 Ok(Ok(Some(false))) => {
436 self.done = true;
437 return Some(Err(ChildError::Text { node }));
438 }
439 Ok(Err(cause)) => {
440 self.done = true;
441 return Some(Err(ChildError::Access(AccessError::Resource {
442 node,
443 cause,
444 })));
445 }
446 Err(error) => {
447 self.done = true;
448 return Some(Err(ChildError::Access(error)));
449 }
450 }
451 }
452 }
453}
454
455impl<X: Xml, W: Work> core::iter::FusedIterator for Children<'_, '_, X, W> {}
456
457fn equal_text<W: Work>(left: &str, right: &str, execution: &W) -> Result<bool, MemoryError> {
458 if left.len() != right.len() {
459 return Ok(false);
460 }
461 let length = u64::try_from(left.len()).map_err(|_| MemoryError::WorkExceeded)?;
462 execution.charge_work(length)?;
463 Ok(left == right)
464}