Skip to main content

jsonld_syntax/
compact_iri.rs

1use iri_rs::{IriRef, IriRefBuf};
2
3/// Error raised when a string is not a compact IRI.
4pub struct InvalidCompactIri<T>(pub T);
5
6#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
7/// Compact IRI: a prefix and a suffix separated by a colon.
8// `repr(transparent)` guarantees the layout assumed by the `&str` transmute
9// in `new_unchecked`.
10#[repr(transparent)]
11pub struct CompactIri(str);
12
13impl CompactIri {
14    /// Borrows the given string as a compact IRI, checking that it is one.
15    ///
16    /// # Errors
17    ///
18    /// Returns an error when the input is not valid for this type.
19    pub fn new(s: &str) -> Result<&Self, InvalidCompactIri<&str>> {
20        match s.split_once(':') {
21            Some((prefix, suffix)) if prefix != "_" && !suffix.starts_with("//") => match IriRef::parse(s) {
22                Ok(_) => Ok(unsafe { Self::new_unchecked(s) }),
23                Err(_) => Err(InvalidCompactIri(s)),
24            },
25            _ => Err(InvalidCompactIri(s)),
26        }
27    }
28
29    /// Creates a new compact IRI without parsing it.
30    ///
31    /// # Safety
32    ///
33    /// `s` must be a valid compact IRI: it must contain a `:`, the segment
34    /// before `:` must not equal `_`, the segment after `:` must not start with
35    /// `//`, and `s` as a whole must parse as an `IriRef`.
36    #[must_use]
37    pub unsafe fn new_unchecked(s: &str) -> &Self {
38        // SAFETY: `Self` is `repr(transparent)` over `str`, so the pointee
39        // layout is identical; the caller guarantees `s` is a valid compact IRI.
40        unsafe { &*(std::ptr::from_ref::<str>(s) as *const Self) }
41    }
42
43    /// Returns this value as a string slice.
44    #[must_use]
45    pub fn as_str(&self) -> &str {
46        &self.0
47    }
48
49    /// Clones this `CompactIri` into an owned one.
50    #[must_use]
51    pub fn to_owned(&self) -> CompactIriBuf {
52        CompactIriBuf(self.0.to_owned())
53    }
54
55    /// Returns the part before the colon, which names a term to expand.
56    #[must_use]
57    pub fn prefix(&self) -> &str {
58        // SAFETY: a `CompactIri` always contains a `:` (enforced by `new` /
59        // `new_unchecked`).
60        let i = unsafe { self.find(':').unwrap_unchecked() };
61        &self[0..i]
62    }
63
64    /// Returns the part after the colon, appended to the expanded prefix.
65    #[must_use]
66    pub fn suffix(&self) -> &str {
67        // SAFETY: see `prefix`.
68        let i = unsafe { self.find(':').unwrap_unchecked() };
69        &self[i + 1..]
70    }
71
72    /// Returns this compact IRI as an IRI reference.
73    ///
74    /// Every compact IRI is also a syntactically valid IRI reference; that is
75    /// checked when the value is created.
76    #[must_use]
77    pub fn as_iri_ref(&self) -> IriRef<&str> {
78        // SAFETY: validated as an `IriRef` at construction.
79        unsafe { IriRef::parse(self.as_str()).unwrap_unchecked() }
80    }
81}
82
83impl std::ops::Deref for CompactIri {
84    type Target = str;
85
86    fn deref(&self) -> &str {
87        &self.0
88    }
89}
90
91impl std::borrow::Borrow<str> for CompactIri {
92    fn borrow(&self) -> &str {
93        &self.0
94    }
95}
96
97impl AsRef<str> for CompactIri {
98    fn as_ref(&self) -> &str {
99        &self.0
100    }
101}
102
103#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
104/// Owned compact IRI.
105pub struct CompactIriBuf(String);
106
107impl CompactIriBuf {
108    /// Takes ownership of the given string, checking that it is a compact IRI.
109    ///
110    /// # Errors
111    ///
112    /// Returns an error when the input is not valid for this type.
113    pub fn new(s: String) -> Result<Self, InvalidCompactIri<String>> {
114        match CompactIri::new(&s) {
115            Ok(_) => Ok(unsafe { Self::new_unchecked(s) }),
116            Err(_) => Err(InvalidCompactIri(s)),
117        }
118    }
119
120    /// Creates a new compact IRI without parsing it.
121    ///
122    /// # Safety
123    ///
124    /// `s` must satisfy the contract of [`CompactIri::new_unchecked`].
125    #[must_use]
126    pub unsafe fn new_unchecked(s: String) -> Self {
127        Self(s)
128    }
129
130    /// Borrows this value as a [`CompactIri`].
131    #[must_use]
132    pub fn as_compact_iri(&self) -> &CompactIri {
133        unsafe { CompactIri::new_unchecked(&self.0) }
134    }
135
136    /// Converts this compact IRI into an owned IRI reference, without
137    /// reallocating.
138    #[must_use]
139    pub fn into_iri_ref(self) -> IriRefBuf {
140        // SAFETY: validated as an `IriRef` at construction.
141        unsafe { IriRefBuf::new(self.0).unwrap_unchecked() }
142    }
143
144    /// Unwraps the underlying string.
145    #[must_use]
146    pub fn into_string(self) -> String {
147        self.0
148    }
149}
150
151impl std::borrow::Borrow<CompactIri> for CompactIriBuf {
152    fn borrow(&self) -> &CompactIri {
153        self.as_compact_iri()
154    }
155}
156
157impl std::ops::Deref for CompactIriBuf {
158    type Target = CompactIri;
159
160    fn deref(&self) -> &CompactIri {
161        self.as_compact_iri()
162    }
163}