rdf_types/
generator.rs

1//! Resource identifier generators.
2use crate::{
3	vocabulary::{BlankIdVocabulary, BlankIdVocabularyMut, IriVocabulary},
4	BlankIdBuf, Id, Vocabulary,
5};
6
7/// Subject identifier generator.
8pub trait Generator<V: IriVocabulary + BlankIdVocabulary = ()> {
9	/// Generates the next fresh node identifier in the given vocabulary.
10	fn next(&mut self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId>;
11}
12
13impl<V: IriVocabulary + BlankIdVocabulary, G: Generator<V>> Generator<V> for &mut G {
14	fn next(&mut self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId> {
15		(*self).next(vocabulary)
16	}
17}
18
19/// Generates numbered blank node identifiers,
20/// with an optional prefix.
21///
22/// This generator can create `usize::MAX` unique blank node identifiers.
23/// If [`Generator::next`] is called `usize::MAX + 1` times, it will panic.
24#[derive(Default)]
25pub struct Blank {
26	/// Prefix string.
27	prefix: String,
28
29	/// Number of already generated identifiers.
30	count: usize,
31}
32
33impl Blank {
34	/// Creates a new numbered generator with no prefix.
35	pub fn new() -> Self {
36		Self::new_full(String::new(), 0)
37	}
38
39	/// Creates a new numbered generator with no prefix,
40	/// starting with the given `offset` number.
41	///
42	/// The returned generator can create `usize::MAX - offset` unique blank node identifiers
43	/// before panicking.
44	pub fn new_with_offset(offset: usize) -> Self {
45		Self::new_full(String::new(), offset)
46	}
47
48	/// Creates a new numbered generator with the given prefix.
49	pub fn new_with_prefix(prefix: String) -> Self {
50		Self::new_full(prefix, 0)
51	}
52
53	/// Creates a new numbered generator with the given prefix,
54	/// starting with the given `offset` number.
55	///
56	/// The returned generator can create `usize::MAX - offset` unique blank node identifiers
57	/// before panicking.
58	pub fn new_full(prefix: String, offset: usize) -> Self {
59		Self {
60			prefix,
61			count: offset,
62		}
63	}
64
65	/// Returns the prefix of this generator.
66	pub fn prefix(&self) -> &str {
67		&self.prefix
68	}
69
70	/// Returns the number of already generated identifiers.
71	pub fn count(&self) -> usize {
72		self.count
73	}
74
75	pub fn next_blank_id(&mut self) -> BlankIdBuf {
76		let id = unsafe { BlankIdBuf::new_unchecked(format!("_:{}{}", self.prefix, self.count)) };
77		self.count += 1;
78		id
79	}
80}
81
82impl<V: Vocabulary + BlankIdVocabularyMut> Generator<V> for Blank {
83	fn next(&mut self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId> {
84		Id::Blank(vocabulary.insert_blank_id(&self.next_blank_id()))
85	}
86}
87
88/// Generates UUID blank node identifiers based on the [`uuid`](https://crates.io/crates/uuid) crate.
89///
90/// This is an enum type with different UUID versions supported
91/// by the `uuid` library, so you can choose which kind of UUID
92/// better fits your application.
93/// Version 1 is not supported.
94///
95/// You need to enable the `uuid-generator` feature to
96/// use this type.
97/// You also need to enable the features of each version you need
98/// in the `uuid` crate.
99pub enum Uuid {
100	/// UUIDv3.
101	///
102	/// You must provide a vocabulary UUID and a name.
103	/// See [uuid::Uuid::new_v3] for more information.
104	#[cfg(feature = "uuid-generator-v3")]
105	V3(uuid::Uuid, String),
106
107	/// UUIDv4.
108	///
109	/// See [uuid::Uuid::new_v4] for more information.
110	#[cfg(feature = "uuid-generator-v4")]
111	V4,
112
113	/// UUIDv5.
114	///
115	/// You must provide a vocabulary UUID and a name.
116	/// See [uuid::Uuid::new_v5] for more information.
117	#[cfg(feature = "uuid-generator-v5")]
118	V5(uuid::Uuid, String),
119}
120
121#[cfg(any(
122	feature = "uuid-generator-v3",
123	feature = "uuid-generator-v4",
124	feature = "uuid-generator-v5"
125))]
126impl Uuid {
127	pub fn next_uuid(&self) -> uuid::Uuid {
128		match self {
129			#[cfg(feature = "uuid-generator-v3")]
130			Self::V3(vocabulary, name) => uuid::Uuid::new_v3(vocabulary, name.as_bytes()),
131			#[cfg(feature = "uuid-generator-v4")]
132			Self::V4 => uuid::Uuid::new_v4(),
133			#[cfg(feature = "uuid-generator-v5")]
134			Self::V5(vocabulary, name) => uuid::Uuid::new_v5(vocabulary, name.as_bytes()),
135		}
136	}
137
138	#[cfg(feature = "meta")]
139	/// Generates identifiers annotated with the given metadata.
140	pub fn with_metadata<M>(self, metadata: M) -> WithMetadata<Self, M>
141	where
142		Self: Sized,
143	{
144		WithMetadata {
145			metadata,
146			generator: self,
147		}
148	}
149
150	#[cfg(feature = "meta")]
151	/// Generates identifiers annotated with the default value of type `M`.
152	pub fn with_default_metadata<M: Default>(self) -> WithMetadata<Self, M>
153	where
154		Self: Sized,
155	{
156		WithMetadata {
157			metadata: M::default(),
158			generator: self,
159		}
160	}
161}
162
163#[cfg(any(
164	feature = "uuid-generator-v3",
165	feature = "uuid-generator-v4",
166	feature = "uuid-generator-v5"
167))]
168impl<V: crate::Vocabulary + crate::vocabulary::IriVocabularyMut> Generator<V> for Uuid {
169	fn next(&mut self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId> {
170		let mut buffer: Vec<u8> = vec![0; uuid::adapter::Urn::LENGTH];
171		let uuid = self.next_uuid();
172		let len = uuid.to_urn().encode_lower(buffer.as_mut()).len();
173		buffer.truncate(len);
174
175		Id::Iri(vocabulary.insert_owned(unsafe {
176			iref::IriBuf::new_unchecked(String::from_utf8_unchecked(buffer))
177		}))
178	}
179}
180
181#[cfg(any(
182	feature = "uuid-generator-v3",
183	feature = "uuid-generator-v4",
184	feature = "uuid-generator-v5"
185))]
186#[cfg(test)]
187mod tests {
188	use super::*;
189
190	#[cfg(feature = "uuid-generator-v3")]
191	#[test]
192	fn uuidv3_iri() {
193		let mut uuid_gen = Uuid::V3(
194			uuid::Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(),
195			"test".to_string(),
196		);
197		for _ in 0..100 {
198			let reference: Id = uuid_gen.next(&mut ());
199			assert!(iref::Iri::new(reference.as_str()).is_ok())
200		}
201	}
202
203	#[cfg(feature = "uuid-generator-v4")]
204	#[test]
205	fn uuidv4_iri() {
206		let mut uuid_gen = Uuid::V4;
207		for _ in 0..100 {
208			let reference: Id = uuid_gen.next(&mut ());
209			assert!(iref::Iri::new(reference.as_str()).is_ok())
210		}
211	}
212
213	#[cfg(feature = "uuid-generator-v5")]
214	#[test]
215	fn uuidv5_iri() {
216		let mut uuid_gen = Uuid::V5(
217			uuid::Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(),
218			"test".to_string(),
219		);
220		for _ in 0..100 {
221			let reference: Id = uuid_gen.next(&mut ());
222			assert!(iref::Iri::new(reference.as_str()).is_ok())
223		}
224	}
225}