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>>;
120pub type VisitResult<T, X, E> =
122 Result<T, AccessError<crate::reference::VisitError<<X as Xml>::Error, E>, <X as Xml>::Id>>;
123
124#[derive(Debug)]
126pub enum ChildError<E, N> {
127 Access(AccessError<E, N>),
129 Text {
131 node: N,
133 },
134}
135
136pub struct Access<'a, X: Xml, W: Work> {
143 xml: &'a X,
144 snapshot: Snapshot<X::Document, X::Generation>,
145 execution: &'a W,
146}
147
148impl<'a, X: Xml, W: Work> Access<'a, X, W> {
149 #[must_use]
151 pub fn new(
152 xml: &'a X,
153 snapshot: Snapshot<X::Document, X::Generation>,
154 execution: &'a W,
155 ) -> Self {
156 Self {
157 xml,
158 snapshot,
159 execution,
160 }
161 }
162
163 #[must_use]
165 pub fn snapshot(&self) -> Snapshot<X::Document, X::Generation> {
166 self.snapshot
167 }
168
169 #[must_use]
175 pub fn execution(&self) -> &W {
176 self.execution
177 }
178
179 pub(crate) fn owner(&self) -> &'a X {
180 self.xml
181 }
182
183 #[must_use]
190 pub fn children(&self, parent: X::Id) -> Children<'_, 'a, X, W> {
191 Children {
192 access: self,
193 parent,
194 previous: None,
195 started: false,
196 done: false,
197 }
198 }
199
200 fn unchanged<E>(&self, node: X::Id) -> Result<(), AccessError<E, X::Id>> {
201 if self.xml.snapshot() != self.snapshot {
202 return Err(AccessError::Changed { node });
203 }
204 Ok(())
205 }
206
207 fn call<R, E>(
208 &self,
209 node: X::Id,
210 callback: impl FnOnce() -> Result<R, E>,
211 ) -> Result<R, AccessError<E, X::Id>> {
212 self.unchanged(node)?;
213 self.execution
214 .charge_work(1)
215 .map_err(|cause| AccessError::Resource { node, cause })?;
216 let result = callback().map_err(AccessError::Xml)?;
217 self.unchanged(node)?;
218 Ok(result)
219 }
220
221 pub(crate) fn query<R>(
222 &self,
223 node: X::Id,
224 callback: impl FnOnce(&X, Snapshot<X::Document, X::Generation>, &W) -> Result<R, X::Error>,
225 ) -> AccessResult<R, X> {
226 self.call(node, || callback(self.xml, self.snapshot, self.execution))
227 }
228
229 pub(crate) fn query_fallible<R, E>(
232 &self,
233 node: X::Id,
234 callback: impl FnOnce(&X, Snapshot<X::Document, X::Generation>, &W) -> Result<R, E>,
235 ) -> Result<R, AccessError<E, X::Id>> {
236 self.call(node, || callback(self.xml, self.snapshot, self.execution))
237 }
238
239 pub(crate) fn equal(&self, left: &str, right: &str) -> Result<bool, MemoryError> {
240 equal_text(left, right, self.execution)
241 }
242
243 pub(crate) fn equal_id(&self, left: X::Id, right: X::Id) -> Result<bool, MemoryError> {
244 equal_id(left, right, self.execution)
245 }
246
247 pub(crate) fn trim_xml_whitespace<'value>(
248 &self,
249 value: &'value str,
250 ) -> Result<&'value str, MemoryError> {
251 self.execution
252 .charge_work(u64::try_from(value.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
253 Ok(value.trim_matches([' ', '\t', '\r', '\n']))
254 }
255
256 pub fn inspect<R>(
271 &self,
272 node: X::Id,
273 visitor: impl for<'view> FnOnce(Node<'view>) -> R,
274 ) -> AccessResult<R, X> {
275 self.call(node, || {
276 self.xml
277 .inspect(self.snapshot, node, self.execution, visitor)
278 })
279 }
280
281 pub fn has_name(&self, node: X::Id, expected: Name<'_>) -> AccessResult<bool, X> {
286 let compared = self.inspect(node, |value| {
287 let Node::Element(actual) = value else {
288 return Ok(false);
289 };
290 if !equal_text(actual.namespace, expected.namespace, self.execution)? {
294 return Ok(false);
295 }
296 equal_text(actual.local, expected.local, self.execution)
297 })?;
298 compared.map_err(|cause| AccessError::Resource { node, cause })
299 }
300
301 pub fn attribute<R>(
303 &self,
304 node: X::Id,
305 name: Name<'_>,
306 visitor: impl for<'view> FnOnce(Option<&'view str>) -> R,
307 ) -> AccessResult<R, X> {
308 self.call(node, || {
309 self.xml
310 .attribute(self.snapshot, node, name, self.execution, visitor)
311 })
312 }
313
314 pub fn first_child(&self, parent: X::Id) -> AccessResult<Option<X::Id>, X> {
316 self.call(parent, || {
317 self.xml.first_child(self.snapshot, parent, self.execution)
318 })
319 }
320
321 pub fn next_sibling(&self, parent: X::Id, node: X::Id) -> AccessResult<Option<X::Id>, X> {
323 self.call(node, || {
324 self.xml
325 .next_sibling(self.snapshot, parent, node, self.execution)
326 })
327 }
328}
329
330impl<X: Xml, W: Work> Query for Access<'_, X, W> {
333 type Id = X::Id;
334 type Document = X::Document;
335 type Generation = X::Generation;
336 type Work = W;
337
338 fn snapshot(&self) -> Snapshot<X::Document, X::Generation> {
339 self.snapshot
340 }
341
342 fn execution(&self) -> &W {
343 self.execution
344 }
345
346 fn admit(&self, node: X::Id, units: u64) -> Result<(), ScopeError<X::Id>> {
347 let changed = |_: AccessError<(), X::Id>| ScopeError::Changed { node };
348 self.unchanged(node).map_err(changed)?;
349 self.execution
350 .charge_work(units)
351 .map_err(|cause| ScopeError::Resource { node, cause })?;
352 self.unchanged(node).map_err(changed)
353 }
354}
355
356impl<E, N> From<ScopeError<N>> for AccessError<E, N> {
357 fn from(cause: ScopeError<N>) -> Self {
358 match cause {
359 ScopeError::Changed { node } => Self::Changed { node },
360 ScopeError::Resource { node, cause } => Self::Resource { node, cause },
361 }
362 }
363}
364
365pub(crate) fn equal_id<N: Copy + HashWork, W: Work>(
366 left: N,
367 right: N,
368 execution: &W,
369) -> Result<bool, MemoryError> {
370 let cost = left
371 .hash_work()?
372 .checked_add(right.hash_work()?)
373 .ok_or(MemoryError::WorkExceeded)?;
374 execution.charge_work(cost)?;
375 Ok(left == right)
376}
377
378pub struct Children<'a, 'xml, X: Xml, W: Work> {
380 access: &'a Access<'xml, X, W>,
381 parent: X::Id,
382 previous: Option<X::Id>,
383 started: bool,
384 done: bool,
385}
386
387impl<X: Xml, W: Work> Iterator for Children<'_, '_, X, W> {
388 type Item = Result<X::Id, ChildError<X::Error, X::Id>>;
389
390 fn next(&mut self) -> Option<Self::Item> {
391 if self.done {
392 return None;
393 }
394 loop {
395 let advanced = if self.started {
396 let Some(previous) = self.previous else {
397 self.done = true;
398 return None;
399 };
400 self.access.next_sibling(self.parent, previous)
401 } else {
402 self.started = true;
403 self.access.first_child(self.parent)
404 };
405 let node = match advanced {
406 Ok(Some(node)) => node,
407 Ok(None) => {
408 self.done = true;
409 return None;
410 }
411 Err(error) => {
412 self.done = true;
413 return Some(Err(ChildError::Access(error)));
414 }
415 };
416 self.previous = Some(node);
417 let inspected = self.access.inspect(node, |value| match value {
418 Node::Element(_) => Ok(Some(true)),
419 Node::Other => Ok(None),
420 Node::Text(text) => {
421 let length =
422 u64::try_from(text.len()).map_err(|_| MemoryError::WorkExceeded)?;
423 self.access.execution.charge_work(length)?;
424 for byte in text.bytes() {
428 if !matches!(byte, b' ' | b'\t' | b'\r' | b'\n') {
429 return Ok(Some(false));
430 }
431 }
432 Ok(None)
433 }
434 });
435 match inspected {
436 Ok(Ok(Some(true))) => return Some(Ok(node)),
437 Ok(Ok(None)) => {}
438 Ok(Ok(Some(false))) => {
439 self.done = true;
440 return Some(Err(ChildError::Text { node }));
441 }
442 Ok(Err(cause)) => {
443 self.done = true;
444 return Some(Err(ChildError::Access(AccessError::Resource {
445 node,
446 cause,
447 })));
448 }
449 Err(error) => {
450 self.done = true;
451 return Some(Err(ChildError::Access(error)));
452 }
453 }
454 }
455 }
456}
457
458impl<X: Xml, W: Work> core::iter::FusedIterator for Children<'_, '_, X, W> {}
459
460fn equal_text<W: Work>(left: &str, right: &str, execution: &W) -> Result<bool, MemoryError> {
461 if left.len() != right.len() {
462 return Ok(false);
463 }
464 let length = u64::try_from(left.len()).map_err(|_| MemoryError::WorkExceeded)?;
465 execution.charge_work(length)?;
466 Ok(left == right)
467}