json_ld_context_processing/
stack.rs

1use std::sync::Arc;
2
3/// Single frame of the context processing stack.
4struct StackNode<I> {
5	/// Previous frame.
6	previous: Option<Arc<StackNode<I>>>,
7
8	/// URL of the last loaded context.
9	url: I,
10}
11
12impl<I> StackNode<I> {
13	/// Create a new stack frame registering the load of the given context URL.
14	fn new(previous: Option<Arc<StackNode<I>>>, url: I) -> StackNode<I> {
15		StackNode { previous, url }
16	}
17
18	/// Checks if this frame or any parent holds the given URL.
19	fn contains(&self, url: &I) -> bool
20	where
21		I: PartialEq,
22	{
23		if self.url == *url {
24			true
25		} else {
26			match &self.previous {
27				Some(prev) => prev.contains(url),
28				None => false,
29			}
30		}
31	}
32}
33
34/// Context processing stack.
35///
36/// Contains the list of the loaded contexts to detect loops.
37#[derive(Clone)]
38pub struct ProcessingStack<I> {
39	head: Option<Arc<StackNode<I>>>,
40}
41
42impl<I> ProcessingStack<I> {
43	/// Creates a new empty processing stack.
44	pub fn new() -> Self {
45		Self { head: None }
46	}
47
48	/// Checks if the stack is empty.
49	pub fn is_empty(&self) -> bool {
50		self.head.is_none()
51	}
52
53	/// Checks if the given URL is already in the stack.
54	///
55	/// This is used for loop detection.
56	pub fn cycle(&self, url: &I) -> bool
57	where
58		I: PartialEq,
59	{
60		match &self.head {
61			Some(head) => head.contains(url),
62			None => false,
63		}
64	}
65
66	/// Push a new URL to the stack, unless it is already in the stack.
67	///
68	/// Returns `true` if the URL was successfully added or
69	/// `false` if a loop has been detected.
70	pub fn push(&mut self, url: I) -> bool
71	where
72		I: PartialEq,
73	{
74		if self.cycle(&url) {
75			false
76		} else {
77			let mut head = None;
78			std::mem::swap(&mut head, &mut self.head);
79			self.head = Some(Arc::new(StackNode::new(head, url)));
80			true
81		}
82	}
83}
84
85impl<I> Default for ProcessingStack<I> {
86	fn default() -> Self {
87		Self::new()
88	}
89}