json_ld_core/object/
list.rs

1use super::{Any, InvalidExpandedJson, MappedEq};
2use crate::{Id, IndexedObject, Relabel, TryFromJson};
3use contextual::WithContext;
4use educe::Educe;
5use json_ld_syntax::{IntoJson, IntoJsonWithContext};
6use rdf_types::{Generator, Subject, Vocabulary, VocabularyMut};
7use std::hash::Hash;
8
9#[allow(clippy::derived_hash_with_manual_eq)]
10#[derive(Educe, Debug, Clone, Hash)]
11#[educe(
12	PartialEq(bound = "T: Eq + Hash, B: Eq + Hash"),
13	Eq(bound = "T: Eq + Hash, B: Eq + Hash")
14)]
15/// List object.
16pub struct List<T, B> {
17	entry: Vec<IndexedObject<T, B>>,
18}
19
20impl<T, B> List<T, B> {
21	/// Creates a new list object.
22	pub fn new(objects: Vec<IndexedObject<T, B>>) -> Self {
23		Self { entry: objects }
24	}
25
26	pub fn len(&self) -> usize {
27		self.entry.len()
28	}
29
30	pub fn is_empty(&self) -> bool {
31		self.entry.is_empty()
32	}
33
34	/// Returns a reference to the "@list" entry of the list object.
35	///
36	/// Alias for `as_slice`.
37	pub fn entry(&self) -> &[IndexedObject<T, B>] {
38		&self.entry
39	}
40
41	pub fn entry_mut(&mut self) -> &mut Vec<IndexedObject<T, B>> {
42		&mut self.entry
43	}
44
45	pub fn as_slice(&self) -> &[IndexedObject<T, B>] {
46		self.entry.as_slice()
47	}
48
49	pub fn as_mut_slice(&mut self) -> &mut [IndexedObject<T, B>] {
50		self.entry.as_mut_slice()
51	}
52
53	pub fn into_entry(self) -> Vec<IndexedObject<T, B>> {
54		self.entry
55	}
56
57	pub fn push(&mut self, object: IndexedObject<T, B>) {
58		self.entry.push(object)
59	}
60
61	pub fn pop(&mut self) -> Option<IndexedObject<T, B>> {
62		self.entry.pop()
63	}
64
65	pub fn iter(&self) -> core::slice::Iter<IndexedObject<T, B>> {
66		self.entry.iter()
67	}
68
69	pub fn iter_mut(&mut self) -> core::slice::IterMut<IndexedObject<T, B>> {
70		self.entry.iter_mut()
71	}
72
73	/// Puts this list object literals into canonical form using the given
74	/// `buffer`.
75	///
76	/// The buffer is used to compute the canonical form of numbers.
77	pub fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer) {
78		for object in self {
79			object.canonicalize_with(buffer)
80		}
81	}
82
83	/// Puts this list object literals into canonical form.
84	pub fn canonicalize(&mut self) {
85		let mut buffer = ryu_js::Buffer::new();
86		self.canonicalize_with(&mut buffer)
87	}
88
89	/// Map the identifiers present in this list (recursively).
90	pub fn map_ids<U, C>(
91		self,
92		mut map_iri: impl FnMut(T) -> U,
93		mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>,
94	) -> List<U, C>
95	where
96		U: Eq + Hash,
97		C: Eq + Hash,
98	{
99		self.map_ids_with(&mut map_iri, &mut map_id)
100	}
101
102	pub(crate) fn map_ids_with<U, C>(
103		self,
104		map_iri: &mut impl FnMut(T) -> U,
105		map_id: &mut impl FnMut(Id<T, B>) -> Id<U, C>,
106	) -> List<U, C>
107	where
108		U: Eq + Hash,
109		C: Eq + Hash,
110	{
111		List::new(
112			self.entry
113				.into_iter()
114				.map(|indexed_object| {
115					indexed_object.map_inner(|object| object.map_ids_with(map_iri, map_id))
116				})
117				.collect(),
118		)
119	}
120}
121
122impl<T, B> Relabel<T, B> for List<T, B> {
123	fn relabel_with<N: Vocabulary<Iri = T, BlankId = B>, G: Generator<N>>(
124		&mut self,
125		vocabulary: &mut N,
126		generator: &mut G,
127		relabeling: &mut hashbrown::HashMap<B, Subject<T, B>>,
128	) where
129		T: Clone + Eq + Hash,
130		B: Clone + Eq + Hash,
131	{
132		for object in self {
133			object.relabel_with(vocabulary, generator, relabeling)
134		}
135	}
136}
137
138impl<T: Eq + Hash, B: Eq + Hash> List<T, B> {
139	pub(crate) fn try_from_json_object_in(
140		vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>,
141		object: json_syntax::Object,
142		list_entry: json_syntax::object::Entry,
143	) -> Result<Self, InvalidExpandedJson> {
144		let list = Vec::try_from_json_in(vocabulary, list_entry.value)?;
145
146		match object.into_iter().next() {
147			Some(_) => Err(InvalidExpandedJson::UnexpectedEntry),
148			None => Ok(Self::new(list)),
149		}
150	}
151}
152
153impl<T, B> Any<T, B> for List<T, B> {
154	fn as_ref(&self) -> super::Ref<T, B> {
155		super::Ref::List(self)
156	}
157}
158
159impl<T: Eq + Hash, B: Eq + Hash> MappedEq for List<T, B> {
160	type BlankId = B;
161
162	fn mapped_eq<'a, 'b, F: Clone + Fn(&'a B) -> &'b B>(&'a self, other: &Self, f: F) -> bool
163	where
164		B: 'a + 'b,
165	{
166		self.entry.mapped_eq(&other.entry, f)
167	}
168}
169
170impl<'a, T, B> IntoIterator for &'a List<T, B> {
171	type Item = &'a IndexedObject<T, B>;
172	type IntoIter = core::slice::Iter<'a, IndexedObject<T, B>>;
173
174	fn into_iter(self) -> Self::IntoIter {
175		self.iter()
176	}
177}
178
179impl<'a, T, B> IntoIterator for &'a mut List<T, B> {
180	type Item = &'a mut IndexedObject<T, B>;
181	type IntoIter = core::slice::IterMut<'a, IndexedObject<T, B>>;
182
183	fn into_iter(self) -> Self::IntoIter {
184		self.iter_mut()
185	}
186}
187
188impl<T, B> IntoIterator for List<T, B> {
189	type Item = IndexedObject<T, B>;
190	type IntoIter = std::vec::IntoIter<IndexedObject<T, B>>;
191
192	fn into_iter(self) -> Self::IntoIter {
193		self.entry.into_iter()
194	}
195}
196
197/// List object fragment.
198pub enum FragmentRef<'a, T, B> {
199	/// "@list" entry.
200	Entry(&'a [IndexedObject<T, B>]),
201
202	/// "@list" entry key.
203	Key,
204
205	/// "@list" value.
206	Value(&'a [IndexedObject<T, B>]),
207}
208
209impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> IntoJsonWithContext<N> for List<T, B> {
210	fn into_json_with(self, vocabulary: &N) -> json_syntax::Value {
211		let mut obj = json_syntax::Object::new();
212
213		obj.insert("@list".into(), self.entry.into_with(vocabulary).into_json());
214
215		obj.into()
216	}
217}