1mod context;
2mod credential;
3mod non_empty_object;
4mod non_empty_vec;
5mod presentation;
6mod types;
7
8use std::collections::BTreeMap;
9
10pub use context::*;
11pub use credential::*;
12use iref::{Uri, UriBuf};
13pub use non_empty_object::*;
14pub use non_empty_vec::*;
15pub use presentation::*;
16use serde::{Deserialize, Serialize};
17pub use types::*;
18
19use crate::Identified;
20
21#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
22#[serde(untagged)]
23pub enum IdOr<T> {
24 Id(UriBuf),
25 NotId(T),
26}
27
28impl<T: Identified> IdOr<T> {
29 pub fn id(&self) -> &Uri {
30 match self {
31 Self::Id(id) => id,
32 Self::NotId(t) => t.id(),
33 }
34 }
35}
36
37impl<T: Identified> Identified for IdOr<T> {
38 fn id(&self) -> &Uri {
39 self.id()
40 }
41}
42
43impl<T> From<UriBuf> for IdOr<T> {
44 fn from(value: UriBuf) -> Self {
45 Self::Id(value)
46 }
47}
48
49#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
50#[serde(rename_all = "camelCase")]
51pub struct IdentifiedObject {
52 pub id: UriBuf,
53
54 #[serde(flatten)]
55 pub extra_properties: BTreeMap<String, json_syntax::Value>,
56}
57
58#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
59#[serde(rename_all = "camelCase")]
60pub struct MaybeIdentifiedObject {
61 #[serde(
62 default,
63 deserialize_with = "not_null",
64 skip_serializing_if = "Option::is_none"
65 )]
66 pub id: Option<UriBuf>,
67
68 #[serde(flatten)]
69 pub extra_properties: BTreeMap<String, json_syntax::Value>,
70}
71
72#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
73#[serde(rename_all = "camelCase")]
74pub struct IdentifiedTypedObject {
75 pub id: UriBuf,
76
77 #[serde(rename = "type", with = "value_or_array")]
78 pub types: Vec<String>,
79
80 #[serde(flatten)]
81 pub extra_properties: BTreeMap<String, json_syntax::Value>,
82}
83
84#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
85#[serde(rename_all = "camelCase")]
86pub struct MaybeIdentifiedTypedObject {
87 #[serde(
88 default,
89 deserialize_with = "not_null",
90 skip_serializing_if = "Option::is_none"
91 )]
92 pub id: Option<UriBuf>,
93
94 #[serde(rename = "type", with = "value_or_array")]
95 pub types: Vec<String>,
96
97 #[serde(flatten)]
98 pub extra_properties: BTreeMap<String, json_syntax::Value>,
99}
100
101#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
102#[serde(rename_all = "camelCase")]
103pub struct TypedObject {
104 #[serde(rename = "type", with = "value_or_array")]
105 pub types: Vec<String>,
106
107 #[serde(flatten)]
108 pub extra_properties: BTreeMap<String, json_syntax::Value>,
109}
110
111pub(crate) mod value_or_array {
112 use serde::{Deserialize, Serialize};
113 use ssi_core::OneOrMany;
114
115 pub fn serialize<T: Serialize, S>(value: &[T], serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: serde::Serializer,
118 {
119 match value.split_first() {
120 Some((first, [])) => first.serialize(serializer),
121 _ => value.serialize(serializer),
122 }
123 }
124
125 pub fn deserialize<'de, T: Deserialize<'de>, D>(deserializer: D) -> Result<Vec<T>, D::Error>
126 where
127 D: serde::Deserializer<'de>,
128 {
129 Ok(OneOrMany::deserialize(deserializer)?.into_vec())
130 }
131}
132
133pub(crate) mod non_empty_value_or_array {
134 use serde::{Deserialize, Serialize};
135 use ssi_core::OneOrMany;
136
137 use super::NonEmptyVec;
138
139 pub fn serialize<T: Serialize, S>(value: &[T], serializer: S) -> Result<S::Ok, S::Error>
140 where
141 S: serde::Serializer,
142 {
143 match value.split_first() {
144 Some((first, [])) => first.serialize(serializer),
145 _ => value.serialize(serializer),
146 }
147 }
148
149 pub fn deserialize<'de, T: Deserialize<'de>, D>(
150 deserializer: D,
151 ) -> Result<NonEmptyVec<T>, D::Error>
152 where
153 D: serde::Deserializer<'de>,
154 {
155 OneOrMany::deserialize(deserializer)?
156 .into_vec()
157 .try_into()
158 .map_err(serde::de::Error::custom)
159 }
160}
161
162pub(crate) fn not_null<'de, T: Deserialize<'de>, D>(deserializer: D) -> Result<Option<T>, D::Error>
168where
169 D: serde::Deserializer<'de>,
170{
171 T::deserialize(deserializer).map(Some)
172}
173
174#[cfg(test)]
175mod tests {
176 use super::MaybeIdentifiedObject;
177 use serde_json::json;
178
179 #[test]
180 fn deserialize_id_not_null_1() {
181 assert!(serde_json::from_value::<MaybeIdentifiedObject>(json!({
182 "id": null
183 }))
184 .is_err())
185 }
186
187 #[test]
188 fn deserialize_id_not_null_2() {
189 assert!(serde_json::from_value::<MaybeIdentifiedObject>(json!({})).is_ok())
190 }
191
192 #[test]
193 fn deserialize_id_not_null_3() {
194 assert!(serde_json::from_value::<MaybeIdentifiedObject>(json!({
195 "id": "http://example.org/#id"
196 }))
197 .is_ok())
198 }
199}