Skip to main content

json_ld/core/document/
expanded.rs

1use btree_indexmap::BTreeIndexSet;
2
3use crate::{
4	JsonLdIndexed, JsonLdIndexedObject, JsonLdNode, JsonLdObject, JsonLdVisit,
5	object::{JsonLdObjectMut, JsonLdObjectRef},
6};
7
8/// Result of the document expansion algorithm.
9///
10/// It is just an alias for a set of (indexed) objects.
11#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct JsonLdExpandedDocument(BTreeIndexSet<JsonLdIndexedObject>);
14
15impl JsonLdExpandedDocument {
16	#[inline(always)]
17	pub fn new() -> Self {
18		Self::default()
19	}
20
21	#[inline(always)]
22	pub fn len(&self) -> usize {
23		self.0.len()
24	}
25
26	#[inline(always)]
27	pub fn is_empty(&self) -> bool {
28		self.0.is_empty()
29	}
30
31	#[inline(always)]
32	pub fn objects(&self) -> &BTreeIndexSet<JsonLdIndexedObject> {
33		&self.0
34	}
35
36	#[inline(always)]
37	pub fn into_objects(self) -> BTreeIndexSet<JsonLdIndexedObject> {
38		self.0
39	}
40
41	#[inline(always)]
42	pub fn iter(&self) -> btree_indexmap::set::Iter<'_, JsonLdIndexedObject> {
43		self.0.iter()
44	}
45
46	/// Returns the main node object of the document, if any.
47	///
48	/// The main node is the unique top level (root) node object. If multiple
49	/// node objects are on the root, `None` is returned.
50	pub fn main_node(&self) -> Option<&JsonLdNode> {
51		let mut result = None;
52
53		for object in self {
54			if let JsonLdObject::Node(node) = object.inner() {
55				if result.is_some() {
56					return None;
57				}
58
59				result = Some(&**node)
60			}
61		}
62
63		result
64	}
65
66	/// Consumes the document and returns its main node object, if any.
67	///
68	/// The main node is the unique top level (root) node object. If multiple
69	/// node objects are on the root, `None` is returned.
70	pub fn into_main_node(self) -> Option<JsonLdNode> {
71		let mut result = None;
72
73		for object in self {
74			if let JsonLdObject::Node(node) = object.into_inner() {
75				if result.is_some() {
76					return None;
77				}
78
79				result = Some(*node)
80			}
81		}
82
83		result
84	}
85
86	#[inline(always)]
87	pub fn insert(&mut self, object: JsonLdIndexedObject) -> bool {
88		self.0.insert(object)
89	}
90}
91
92#[cfg(feature = "serde")]
93impl JsonLdExpandedDocument {
94	/// Returns the JSON representation of this expanded document, that is a
95	/// JSON array of its expanded objects.
96	pub fn to_json(&self) -> json_syntax::JsonValue {
97		// An expanded document is, by definition, a valid JSON value: it only
98		// contains JSON-LD objects, which are made of strings, numbers,
99		// booleans, arrays and objects with string keys. Serializing it to a
100		// JSON value therefore cannot fail.
101		json_syntax::to_value(self).expect("an expanded document is a valid JSON value")
102	}
103}
104
105#[cfg(feature = "serde")]
106impl From<JsonLdExpandedDocument> for json_syntax::JsonValue {
107	fn from(document: JsonLdExpandedDocument) -> Self {
108		document.to_json()
109	}
110}
111
112impl JsonLdVisit for JsonLdExpandedDocument {
113	fn visit_with(&self, f: &mut impl FnMut(JsonLdObjectRef)) {
114		self.0.visit_with(f);
115	}
116
117	fn visit_mut_with(&mut self, f: &mut impl FnMut(JsonLdObjectMut)) {
118		self.0.visit_mut_with(f);
119	}
120}
121
122impl From<JsonLdIndexed<JsonLdNode>> for JsonLdExpandedDocument {
123	fn from(value: JsonLdIndexed<JsonLdNode>) -> Self {
124		let mut result = Self::default();
125
126		result.insert(value.map_inner(JsonLdObject::node));
127
128		result
129	}
130}
131
132impl IntoIterator for JsonLdExpandedDocument {
133	type IntoIter = IntoIter;
134	type Item = JsonLdIndexedObject;
135
136	#[inline(always)]
137	fn into_iter(self) -> Self::IntoIter {
138		IntoIter(self.0.into_iter())
139	}
140}
141
142impl<'a> IntoIterator for &'a JsonLdExpandedDocument {
143	type IntoIter = btree_indexmap::set::Iter<'a, JsonLdIndexedObject>;
144	type Item = &'a JsonLdIndexedObject;
145
146	#[inline(always)]
147	fn into_iter(self) -> Self::IntoIter {
148		self.iter()
149	}
150}
151pub struct IntoIter(btree_indexmap::set::IntoIter<JsonLdIndexedObject>);
152
153impl Iterator for IntoIter {
154	type Item = JsonLdIndexedObject;
155
156	fn next(&mut self) -> Option<Self::Item> {
157		self.0.next()
158	}
159}
160
161impl FromIterator<JsonLdIndexedObject> for JsonLdExpandedDocument {
162	fn from_iter<I: IntoIterator<Item = JsonLdIndexedObject>>(iter: I) -> Self {
163		Self(iter.into_iter().collect())
164	}
165}
166
167impl Extend<JsonLdIndexedObject> for JsonLdExpandedDocument {
168	fn extend<I: IntoIterator<Item = JsonLdIndexedObject>>(&mut self, iter: I) {
169		self.0.extend(iter)
170	}
171}
172
173impl From<BTreeIndexSet<JsonLdIndexedObject>> for JsonLdExpandedDocument {
174	fn from(set: BTreeIndexSet<JsonLdIndexedObject>) -> Self {
175		Self(set)
176	}
177}
178
179#[cfg(all(test, feature = "serde"))]
180mod tests {
181	use json_syntax::JsonValue;
182	use rdf_syntax::{Id, iri};
183
184	use super::*;
185	use crate::JsonLdLenient;
186
187	#[test]
188	fn empty_document_to_json() {
189		assert_eq!(
190			JsonLdExpandedDocument::new().to_json(),
191			JsonValue::Array(Vec::new())
192		);
193	}
194
195	#[test]
196	fn node_document_to_json() {
197		let id = JsonLdLenient::Valid(Id::Iri(iri!("https://example.org/a").to_owned()));
198		let document = JsonLdExpandedDocument::from(JsonLdIndexed::new(
199			JsonLdNode::new_with_id(Some(id)),
200			None,
201		));
202
203		let json = document.to_json();
204		let JsonValue::Array(objects) = &json else {
205			panic!("an expanded document must be a JSON array")
206		};
207		assert_eq!(objects.len(), 1);
208
209		assert_eq!(JsonValue::from(document), json);
210	}
211}