ucan_capabilities_object/
nota_bene.rs1use serde::{
2 de::Deserializer,
3 ser::{SerializeSeq, Serializer},
4 Deserialize, Serialize,
5};
6use std::collections::BTreeMap;
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct NotaBeneCollection<T>(Vec<BTreeMap<String, T>>);
11
12impl<T> NotaBeneCollection<T> {
13 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 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 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 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}