Skip to main content

jsonld_core/
indexed.rs

1use crate::object::{InvalidExpandedJson, TryFromJson, TryFromJsonObject};
2use jsonld_syntax::{IntoJson, IntoJsonWithContext};
3use rdfx::vocabulary::VocabularyMut;
4use std::{
5    convert::{TryFrom, TryInto},
6    ops::{Deref, DerefMut},
7};
8
9/// Indexed objects.
10///
11/// Nodes and value objects may be indexed by a string in JSON-LD.
12/// This type is a wrapper around any kind of indexable data.
13///
14/// It is a pointer type that `Deref` into the underlying value.
15#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
16pub struct Indexed<T> {
17    /// Index.
18    index: Option<String>,
19
20    /// Value.
21    value: T,
22}
23
24impl<T> Indexed<T> {
25    /// Creates a non-indexed value, without metadata.
26    pub fn none(value: T) -> Self {
27        Self::new(value, None)
28    }
29
30    /// Create a new (maybe) indexed value.
31    #[inline(always)]
32    pub fn new(value: T, index: Option<String>) -> Self {
33        Indexed { index, value }
34    }
35
36    /// Get a reference to the inner value.
37    #[inline(always)]
38    pub fn inner(&self) -> &T {
39        &self.value
40    }
41
42    /// Mutably borrows the indexed value.
43    pub fn inner_mut(&mut self) -> &mut T {
44        &mut self.value
45    }
46
47    /// Drops the index and returns the underlying value.
48    #[inline(always)]
49    pub fn into_inner(self) -> T {
50        self.value
51    }
52
53    /// Returns the `@index` of the value, if it has one.
54    #[inline(always)]
55    pub fn index(&self) -> Option<&str> {
56        match &self.index {
57            Some(index) => Some(index.as_str()),
58            None => None,
59        }
60    }
61
62    /// Sets the `@index` of the value, or removes it when given `None`.
63    #[inline(always)]
64    pub fn set_index(&mut self, index: Option<String>) {
65        self.index = index;
66    }
67
68    /// Consumes the indexed value, returning the inner value and its
69    /// `@index`.
70    #[inline(always)]
71    pub fn into_parts(self) -> (T, Option<String>) {
72        (self.value, self.index)
73    }
74
75    /// Cast the inner value.
76    #[inline(always)]
77    pub fn map_inner<U, F>(self, f: F) -> Indexed<U>
78    where
79        F: FnOnce(T) -> U,
80    {
81        Indexed::new(f(self.value), self.index)
82    }
83
84    /// Cast the inner value.
85    #[inline(always)]
86    pub fn cast<U: From<T>>(self) -> Indexed<U> {
87        Indexed::new(self.value.into(), self.index)
88    }
89
90    /// Try to cast the inner value.
91    ///
92    /// # Errors
93    ///
94    /// Returns the index and the conversion error when the inner value does not convert.
95    #[inline(always)]
96    pub fn try_cast<U: TryFrom<T>>(self) -> Result<Indexed<U>, Indexed<U::Error>> {
97        match self.value.try_into() {
98            Ok(value) => Ok(Indexed::new(value, self.index)),
99            Err(e) => Err(Indexed::new(e, self.index)),
100        }
101    }
102}
103
104impl<T, B, O: TryFromJsonObject<T, B>> TryFromJson<T, B> for Indexed<O> {
105    fn try_from_json_in(vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>, value: jstrict::Value) -> Result<Self, InvalidExpandedJson> {
106        match value {
107            jstrict::Value::Object(object) => Self::try_from_json_object_in(vocabulary, object),
108            _ => Err(InvalidExpandedJson::InvalidObject),
109        }
110    }
111}
112
113impl<T, B, O: TryFromJsonObject<T, B>> TryFromJsonObject<T, B> for Indexed<O> {
114    fn try_from_json_object_in(vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>, mut object: jstrict::Object) -> Result<Self, InvalidExpandedJson> {
115        let index = match object.remove_unique("@index").map_err(InvalidExpandedJson::duplicate_key)? {
116            Some(index_entry) => match index_entry.value {
117                jstrict::Value::String(index) => Some(index.to_string()),
118                _ => return Err(InvalidExpandedJson::InvalidIndex),
119            },
120            None => None,
121        };
122
123        let value = O::try_from_json_object_in(vocabulary, object)?;
124        Ok(Self::new(value, index))
125    }
126}
127
128impl<T> From<T> for Indexed<T> {
129    #[inline(always)]
130    fn from(value: T) -> Indexed<T> {
131        Indexed::new(value, None)
132    }
133}
134
135impl<T> Deref for Indexed<T> {
136    type Target = T;
137
138    #[inline(always)]
139    fn deref(&self) -> &T {
140        &self.value
141    }
142}
143
144impl<T> DerefMut for Indexed<T> {
145    #[inline(always)]
146    fn deref_mut(&mut self) -> &mut T {
147        &mut self.value
148    }
149}
150
151impl<T> AsRef<T> for Indexed<T> {
152    #[inline(always)]
153    fn as_ref(&self) -> &T {
154        &self.value
155    }
156}
157
158impl<T> AsMut<T> for Indexed<T> {
159    #[inline(always)]
160    fn as_mut(&mut self) -> &mut T {
161        &mut self.value
162    }
163}
164
165impl<T: IntoJsonWithContext<N>, N> IntoJsonWithContext<N> for Indexed<T> {
166    fn into_json_with(self, vocabulary: &N) -> jstrict::Value {
167        let mut result = self.value.into_json_with(vocabulary);
168
169        if let Some(obj) = result.as_object_mut()
170            && let Some(index) = self.index
171        {
172            obj.insert("@index".into(), index.into_json());
173        }
174
175        result
176    }
177}