Skip to main content

json_ld/syntax/
compact_iri.rs

1use std::borrow::Cow;
2
3use rdf_syntax::{InvalidIri, Iri, IriBuf, IriRef, IriRefBuf};
4
5#[derive(Debug, thiserror::Error)]
6#[error("invalid compact IRI")]
7pub struct InvalidJsonLdCompactIri<T>(pub T);
8
9#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
10pub struct JsonLdCompactIri(str);
11
12impl JsonLdCompactIri {
13	/// Checks that `s` has the syntactic shape of a compact IRI, namely
14	/// `prefix:suffix`, where `prefix` is not `_` (which denotes a blank
15	/// node identifier instead) and `suffix` does not start with `//`
16	/// (which would make it ambiguous with an absolute IRI with an
17	/// authority).
18	///
19	/// A compact IRI's `prefix` is a term name, resolved against the active
20	/// context, so it is **not** required to also be a syntactically valid
21	/// IRI on its own: term names may use characters, such as `_`, that are
22	/// not permitted in an IRI scheme. For instance `my_term:value` is a
23	/// valid compact IRI but not a valid absolute IRI. See
24	/// [ยง 4.1.5 Compact IRIs](https://www.w3.org/TR/json-ld11/#compact-iris)
25	/// of the JSON-LD 1.1 specification.
26	pub fn new(s: &str) -> Result<&Self, InvalidJsonLdCompactIri<&str>> {
27		match s.split_once(':') {
28			Some((prefix, suffix)) if prefix != "_" && !suffix.starts_with("//") => {
29				match IriRef::new(s) {
30					Ok(_) => Ok(unsafe { Self::new_unchecked(s) }),
31					Err(_) => Err(InvalidJsonLdCompactIri(s)),
32				}
33			}
34			_ => Err(InvalidJsonLdCompactIri(s)),
35		}
36	}
37
38	/// Creates a new compact IRI without parsing it.
39	///
40	/// # Safety
41	///
42	/// The input string must be a compact IRI.
43	pub unsafe fn new_unchecked(s: &str) -> &Self {
44		unsafe { std::mem::transmute(s) }
45	}
46
47	pub fn as_str(&self) -> &str {
48		&self.0
49	}
50
51	pub fn to_owned(&self) -> JsonLdCompactIriBuf {
52		JsonLdCompactIriBuf(self.0.to_owned())
53	}
54
55	pub fn prefix(&self) -> &str {
56		let i = self.find(':').unwrap();
57		&self[0..i]
58	}
59
60	pub fn suffix(&self) -> &str {
61		let i = self.find(':').unwrap();
62		&self[i + 1..]
63	}
64
65	/// Returns this compact IRI as an absolute IRI, if it happens to also be
66	/// a syntactically valid one.
67	///
68	/// This is `None` whenever `prefix` is not itself a valid IRI scheme
69	/// (e.g. because it contains a `_`). To properly turn a compact IRI into
70	/// an absolute IRI, resolve its prefix against the active context and
71	/// use [`Self::resolve`] instead.
72	pub fn as_iri(&self) -> Option<&Iri> {
73		Iri::new(self.as_str()).ok()
74	}
75
76	/// Returns this compact IRI as an IRI reference, if it happens to also
77	/// be a syntactically valid one. See [`Self::as_iri`] for why this may
78	/// be `None`.
79	pub fn as_iri_ref(&self) -> Option<&IriRef> {
80		IriRef::new(self.as_str()).ok()
81	}
82
83	pub fn resolve<'a>(
84		&self,
85		resolver: impl FnOnce(&str) -> Option<Cow<'a, Iri>>,
86	) -> Result<IriBuf, InvalidIri<String>> {
87		match resolver(self.prefix()) {
88			Some(prefix) => IriBuf::from_string(format!("{}{}", prefix, self.suffix())),
89			None => IriBuf::from_string(self.as_str().to_owned()),
90		}
91	}
92}
93
94impl std::ops::Deref for JsonLdCompactIri {
95	type Target = str;
96
97	fn deref(&self) -> &str {
98		&self.0
99	}
100}
101
102impl std::borrow::Borrow<str> for JsonLdCompactIri {
103	fn borrow(&self) -> &str {
104		&self.0
105	}
106}
107
108impl AsRef<str> for JsonLdCompactIri {
109	fn as_ref(&self) -> &str {
110		&self.0
111	}
112}
113
114#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
115pub struct JsonLdCompactIriBuf(String);
116
117impl JsonLdCompactIriBuf {
118	pub fn from_string(s: String) -> Result<Self, InvalidJsonLdCompactIri<String>> {
119		match JsonLdCompactIri::new(&s) {
120			Ok(_) => Ok(unsafe { Self::new_unchecked(s) }),
121			Err(_) => Err(InvalidJsonLdCompactIri(s)),
122		}
123	}
124
125	/// Creates a new compact IRI without parsing it.
126	///
127	/// # Safety
128	///
129	/// The input string must be a compact IRI.
130	pub unsafe fn new_unchecked(s: String) -> Self {
131		Self(s)
132	}
133
134	pub fn as_compact_iri(&self) -> &JsonLdCompactIri {
135		unsafe { JsonLdCompactIri::new_unchecked(&self.0) }
136	}
137
138	pub fn into_iri_ref(self) -> IriRefBuf {
139		IriRefBuf::new(self.0).unwrap()
140	}
141
142	pub fn into_string(self) -> String {
143		self.0
144	}
145}
146
147impl std::borrow::Borrow<JsonLdCompactIri> for JsonLdCompactIriBuf {
148	fn borrow(&self) -> &JsonLdCompactIri {
149		self.as_compact_iri()
150	}
151}
152
153impl std::ops::Deref for JsonLdCompactIriBuf {
154	type Target = JsonLdCompactIri;
155
156	fn deref(&self) -> &JsonLdCompactIri {
157		self.as_compact_iri()
158	}
159}