jsonld_syntax/context/definition/
key.rs1use crate::{CompactIri, Keyword};
2use iri_rs::Iri;
3use rdfx::BlankId;
4use smol_str::SmolStr;
5use std::{borrow::Borrow, fmt, hash::Hash};
6
7#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct Key(SmolStr);
21
22impl Key {
23 #[must_use]
25 pub fn as_iri(&self) -> Option<Iri<&str>> {
26 Iri::parse(self.0.as_str()).ok()
27 }
28
29 #[must_use]
31 pub fn as_compact_iri(&self) -> Option<&CompactIri> {
32 CompactIri::new(self.0.as_str()).ok()
33 }
34
35 #[must_use]
38 pub fn as_blank_id(&self) -> Option<&BlankId> {
39 BlankId::new(self.0.as_str()).ok()
40 }
41
42 #[must_use]
44 pub fn as_str(&self) -> &str {
45 self.0.as_str()
46 }
47
48 #[must_use]
50 pub fn len(&self) -> usize {
51 self.0.len()
52 }
53
54 #[must_use]
56 pub fn is_empty(&self) -> bool {
57 self.0.is_empty()
58 }
59
60 #[must_use]
62 pub fn into_string(self) -> String {
63 String::from(self.0)
64 }
65
66 #[must_use]
69 pub fn is_keyword_like(&self) -> bool {
70 crate::is_keyword_like(self.0.as_str())
71 }
72}
73
74impl From<jstrict::object::Key> for Key {
75 fn from(k: jstrict::object::Key) -> Self {
76 Self::from(k.into_string())
77 }
78}
79
80impl From<String> for Key {
81 fn from(k: String) -> Self {
82 Self(SmolStr::from(k))
83 }
84}
85
86impl<'a> From<&'a str> for Key {
87 fn from(value: &'a str) -> Self {
88 Self(SmolStr::new(value))
89 }
90}
91
92impl fmt::Display for Key {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 self.as_str().fmt(f)
95 }
96}
97
98impl Borrow<str> for Key {
99 fn borrow(&self) -> &str {
100 self.as_str()
101 }
102}
103
104#[cfg(feature = "serde")]
105impl serde::Serialize for Key {
106 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107 where
108 S: serde::Serializer,
109 {
110 serializer.serialize_str(self.as_str())
111 }
112}
113
114#[cfg(feature = "serde")]
115impl<'de> serde::Deserialize<'de> for Key {
116 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117 where
118 D: serde::Deserializer<'de>,
119 {
120 struct Visitor;
121
122 impl serde::de::Visitor<'_> for Visitor {
123 type Value = Key;
124
125 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
126 f.write_str("a string")
127 }
128
129 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Key, E> {
135 Ok(Key::from(v))
136 }
137 }
138
139 deserializer.deserialize_str(Visitor)
140 }
141}
142
143#[derive(Clone, Copy, PartialEq, Eq, Debug)]
144pub struct KeyRef<'a>(&'a str);
146
147impl<'a> KeyRef<'a> {
148 #[must_use]
150 pub fn is_empty(&self) -> bool {
151 self.0.is_empty()
152 }
153
154 #[must_use]
157 pub fn is_keyword_like(&self) -> bool {
158 crate::is_keyword_like(self.as_str())
159 }
160
161 #[must_use]
163 pub fn as_str(&self) -> &'a str {
164 self.0
165 }
166
167 #[must_use]
169 pub fn to_owned(self) -> Key {
170 Key::from(self.0)
171 }
172}
173
174impl<'a> From<&'a str> for KeyRef<'a> {
175 fn from(s: &'a str) -> Self {
176 Self(s)
177 }
178}
179
180impl<'a> From<&'a Key> for KeyRef<'a> {
181 fn from(k: &'a Key) -> Self {
182 Self(k.as_str())
183 }
184}
185
186impl fmt::Display for KeyRef<'_> {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 self.0.fmt(f)
189 }
190}
191
192#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
193pub enum KeyOrKeyword {
195 Keyword(Keyword),
197 Key(Key),
199}
200
201impl KeyOrKeyword {
202 #[must_use]
204 pub fn is_empty(&self) -> bool {
205 match self {
206 Self::Keyword(_) => false,
207 Self::Key(k) => k.is_empty(),
208 }
209 }
210
211 #[must_use]
213 pub fn into_keyword(self) -> Option<Keyword> {
214 match self {
215 Self::Keyword(k) => Some(k),
216 Self::Key(_) => None,
217 }
218 }
219
220 #[must_use]
222 pub fn into_key(self) -> Option<Key> {
223 match self {
224 Self::Keyword(_) => None,
225 Self::Key(k) => Some(k),
226 }
227 }
228
229 #[must_use]
231 pub fn as_keyword(&self) -> Option<Keyword> {
232 match self {
233 Self::Keyword(k) => Some(*k),
234 Self::Key(_) => None,
235 }
236 }
237
238 #[must_use]
240 pub fn as_key(&self) -> Option<&Key> {
241 match self {
242 Self::Keyword(_) => None,
243 Self::Key(k) => Some(k),
244 }
245 }
246
247 #[must_use]
249 pub fn as_str(&self) -> &str {
250 match self {
251 Self::Keyword(k) => k.into_str(),
252 Self::Key(k) => k.as_str(),
253 }
254 }
255}
256
257#[allow(clippy::derived_hash_with_manual_eq)]
258impl Hash for KeyOrKeyword {
259 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
260 self.as_str().hash(state);
261 }
262}
263
264impl fmt::Display for KeyOrKeyword {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match self {
267 Self::Key(k) => k.fmt(f),
268 Self::Keyword(k) => k.fmt(f),
269 }
270 }
271}
272
273#[derive(Clone, Copy, PartialEq, Eq)]
274pub enum KeyOrKeywordRef<'a> {
276 Keyword(Keyword),
278 Key(KeyRef<'a>),
280}
281
282impl<'a> KeyOrKeywordRef<'a> {
283 #[must_use]
285 pub fn to_owned(self) -> KeyOrKeyword {
286 match self {
287 Self::Keyword(k) => KeyOrKeyword::Keyword(k),
288 Self::Key(k) => KeyOrKeyword::Key(k.to_owned()),
289 }
290 }
291
292 #[must_use]
294 pub fn as_str(&self) -> &'a str {
295 match self {
296 Self::Keyword(k) => k.into_str(),
297 Self::Key(k) => k.as_str(),
298 }
299 }
300}
301
302impl<'a> From<&'a str> for KeyOrKeywordRef<'a> {
303 fn from(s: &'a str) -> Self {
304 match Keyword::try_from(s) {
305 Ok(k) => Self::Keyword(k),
306 Err(_) => Self::Key(s.into()),
307 }
308 }
309}
310
311impl<'a> From<&'a KeyOrKeyword> for KeyOrKeywordRef<'a> {
312 fn from(k: &'a KeyOrKeyword) -> Self {
313 match k {
314 KeyOrKeyword::Keyword(k) => Self::Keyword(*k),
315 KeyOrKeyword::Key(k) => Self::Key(k.into()),
316 }
317 }
318}
319
320impl<'a> From<KeyRef<'a>> for KeyOrKeywordRef<'a> {
321 fn from(k: KeyRef<'a>) -> Self {
322 Self::Key(k)
323 }
324}
325
326impl<'a> From<&'a Key> for KeyOrKeywordRef<'a> {
327 fn from(k: &'a Key) -> Self {
328 Self::Key(k.into())
329 }
330}
331
332pub enum KeyOrType {
334 Key(Key),
336 Type,
338}
339
340impl KeyOrType {
341 #[must_use]
343 pub fn as_str(&self) -> &str {
344 match self {
345 Self::Key(k) => k.as_str(),
346 Self::Type => "@type",
347 }
348 }
349}
350
351impl fmt::Display for KeyOrType {
352 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353 self.as_str().fmt(f)
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 #![allow(clippy::unwrap_used)]
360 use super::*;
361 use crate::{TryFromJson, context::Context};
362 use jstrict::Parse;
363 use std::{
364 collections::HashMap,
365 hash::{BuildHasher, RandomState},
366 };
367
368 fn parse_definition(text: &str) -> crate::context::Definition {
370 let (value, _) = jstrict::Value::parse_str(text).unwrap();
371 match Context::try_from_json(&value).unwrap() {
372 Context::One(crate::ContextEntry::Definition(definition)) => Some(definition),
373 Context::One(_) | Context::Many(_) => None,
374 }
375 .unwrap()
376 }
377
378 #[test]
382 fn keys_from_borrowed_and_owned_strings_agree() {
383 let borrowed = Key::from("speed");
384 let owned = Key::from(String::from("speed"));
385 assert_eq!(borrowed, owned);
386
387 let hasher = RandomState::new();
388 assert_eq!(hasher.hash_one(&borrowed), hasher.hash_one(&owned));
389 assert_eq!(hasher.hash_one(&borrowed), hasher.hash_one("speed"));
390
391 let map = HashMap::from([(borrowed, 7_u8)]);
392 assert_eq!(map.get("speed"), Some(&7));
393 }
394
395 #[test]
398 fn keys_of_separately_parsed_contexts_are_equal() {
399 let first = parse_definition(r#"{"speed": "https://example.org/speed"}"#);
400 let second = parse_definition(r#"{"speed": "https://example.org/velocity"}"#);
401 let first_key = first.bindings.get_key("speed").unwrap();
402 let second_key = second.bindings.get_key("speed").unwrap();
403 assert_eq!(first_key, second_key);
404 assert_eq!(first_key.cmp(second_key), std::cmp::Ordering::Equal);
405 assert!(!first_key.0.is_heap_allocated());
406 assert_eq!(*first_key, first_key.clone());
407 }
408
409 #[cfg(feature = "serde")]
414 #[test]
415 fn key_deserializes_through_serde_content_buffer() {
416 let (value, _) = jstrict::Value::parse_str(r#"{"@vocab": "http://example.com/", "term": "http://example.com/term"}"#).unwrap();
417 let definition: crate::context::Definition = crate::from_value(value).unwrap();
418 assert!(definition.bindings.get(&Key::from("term")).is_some());
419 }
420
421 #[cfg(feature = "serde")]
424 #[test]
425 fn key_deserializes_from_owned_string() {
426 let (value, _) = jstrict::Value::parse_str(r#""api""#).unwrap();
427 let key: Key = crate::from_value(value).unwrap();
428 assert_eq!(key.as_str(), "api");
429 }
430}