ucan_capabilities_object/
nota_bene.rs

1use serde::{
2    de::Deserializer,
3    ser::{SerializeSeq, Serializer},
4    Deserialize, Serialize,
5};
6use std::collections::BTreeMap;
7
8/// A collection of UCAN Nota Bene information.
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct NotaBeneCollection<T>(Vec<BTreeMap<String, T>>);
11
12impl<T> NotaBeneCollection<T> {
13    /// Create a new empty set of Nota Bene information.
14    pub fn new() -> Self {
15        Self::default()
16    }
17    pub fn into_inner(self) -> Vec<BTreeMap<String, T>> {
18        self.0
19    }
20}
21
22impl<T> Default for NotaBeneCollection<T> {
23    fn default() -> Self {
24        Self(Vec::new())
25    }
26}
27
28impl<T> Serialize for NotaBeneCollection<T>
29where
30    T: Serialize,
31{
32    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33    where
34        S: Serializer,
35    {
36        let seq = if self.0.is_empty() {
37            let mut seq = serializer.serialize_seq(Some(1))?;
38            // the ucan spec basically says that an empty NotaBeneCollection is not
39            // allowed, so if this exists we just serialize an empty map
40            seq.serialize_element(&BTreeMap::<String, T>::new())?;
41            seq
42        } else {
43            let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
44            for item in &self.0 {
45                seq.serialize_element(item)?;
46            }
47            seq
48        };
49        seq.end()
50    }
51}
52
53impl<'de, T> Deserialize<'de> for NotaBeneCollection<T>
54where
55    T: Deserialize<'de>,
56{
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: Deserializer<'de>,
60    {
61        let vec: Vec<BTreeMap<String, T>> = Vec::deserialize(deserializer)?;
62        if vec.is_empty() {
63            // can't have an empty vec here
64            Err(serde::de::Error::custom(
65                "empty NotaBeneCollection is not allowed",
66            ))
67        } else if vec.len() == 1 && vec.first().map(|m| m.is_empty()).unwrap_or(false) {
68            // if the only entry is an empty map, then we just return an empty vec
69            Ok(Self::default())
70        } else {
71            Ok(Self(vec))
72        }
73    }
74}
75
76impl std::ops::DerefMut for NotaBeneCollection<String> {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        &mut self.0
79    }
80}
81
82impl std::ops::Deref for NotaBeneCollection<String> {
83    type Target = Vec<BTreeMap<String, String>>;
84
85    fn deref(&self) -> &Self::Target {
86        &self.0
87    }
88}
89
90impl<T> AsRef<[BTreeMap<String, T>]> for NotaBeneCollection<T> {
91    fn as_ref(&self) -> &[BTreeMap<String, T>] {
92        &self.0
93    }
94}
95
96impl<T> IntoIterator for NotaBeneCollection<T> {
97    type Item = BTreeMap<String, T>;
98    type IntoIter = std::vec::IntoIter<BTreeMap<String, T>>;
99
100    fn into_iter(self) -> Self::IntoIter {
101        self.0.into_iter()
102    }
103}
104
105impl<T> Extend<BTreeMap<String, T>> for NotaBeneCollection<T> {
106    fn extend<I: IntoIterator<Item = BTreeMap<String, T>>>(&mut self, iter: I) {
107        self.0.extend(iter);
108    }
109}
110
111impl<T1, T2> From<Vec<BTreeMap<String, T1>>> for NotaBeneCollection<T2>
112where
113    T1: Into<T2>,
114{
115    fn from(iter: Vec<BTreeMap<String, T1>>) -> Self {
116        Self(
117            iter.into_iter()
118                .map(|nb| nb.into_iter().map(|(s, v)| (s, v.into())).collect())
119                .collect(),
120        )
121    }
122}
123
124pub fn try_convert<T1, T2>(
125    nb1: NotaBeneCollection<T1>,
126) -> Result<NotaBeneCollection<T2>, <T2 as TryFrom<T1>>::Error>
127where
128    T2: TryFrom<T1>,
129{
130    Ok(NotaBeneCollection(
131        nb1.into_iter()
132            .map(|nb| {
133                nb.into_iter()
134                    .map(|(s, v)| Ok((s, v.try_into()?)))
135                    .collect::<Result<BTreeMap<String, T2>, <T2 as TryFrom<T1>>::Error>>()
136            })
137            .collect::<Result<Vec<BTreeMap<String, T2>>, <T2 as TryFrom<T1>>::Error>>()?,
138    ))
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn serde() {
147        let mut nb = NotaBeneCollection::<String>::new();
148        assert_eq!(nb.len(), 0);
149        assert_eq!(
150            serde_json::from_str::<NotaBeneCollection<String>>(r#"[{}]"#).unwrap(),
151            nb
152        );
153        assert_eq!(serde_json::to_string(&nb).unwrap(), r#"[{}]"#);
154
155        nb.push(
156            [("foo".to_string(), "bar".to_string())]
157                .into_iter()
158                .collect(),
159        );
160        assert_eq!(nb.len(), 1);
161        assert_eq!(serde_json::to_string(&nb).unwrap(), r#"[{"foo":"bar"}]"#);
162    }
163}