1use iref::Uri;
2use ssi_claims_core::{DateTimeProvider, Eip712TypesLoaderProvider, ResolverProvider};
3use ssi_json_ld::JsonLdLoaderProvider;
4use ssi_jwk::JWKResolver;
5use ssi_verification_methods::{AnyMethod, VerificationMethodResolver};
6
7use crate::{
8 bitstring_status_list::{
9 self, BitstringStatusListCredential, BitstringStatusListEntry,
10 BitstringStatusListEntrySetCredential,
11 },
12 token_status_list::{self, StatusListToken},
13 EncodedStatusMap, FromBytes, FromBytesOptions, StatusMap, StatusMapEntry, StatusMapEntrySet,
14 StatusSizeError,
15};
16
17pub enum AnyStatusMap {
18 BitstringStatusList(Box<BitstringStatusListCredential>),
19 TokenStatusList(StatusListToken),
20}
21
22impl AnyStatusMap {
23 pub fn credential_url(&self) -> Option<&Uri> {
25 match self {
26 Self::BitstringStatusList(cred) => cred.id.as_deref(),
27 Self::TokenStatusList(_) => None,
28 }
29 }
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum FromBytesError {
34 #[error("unexpected media type `{0}`")]
35 UnexpectedMediaType(String),
36
37 #[error(transparent)]
38 BitstringStatusList(bitstring_status_list::FromBytesError),
39
40 #[error(transparent)]
41 TokenStatusList(token_status_list::FromBytesError),
42}
43
44impl<V> FromBytes<V> for AnyStatusMap
45where
46 V: ResolverProvider + DateTimeProvider + JsonLdLoaderProvider + Eip712TypesLoaderProvider,
47 V::Resolver: JWKResolver + VerificationMethodResolver<Method = AnyMethod>,
48{
49 type Error = FromBytesError;
50
51 async fn from_bytes_with(
52 bytes: &[u8],
53 media_type: &str,
54 verifier: &V,
55 options: FromBytesOptions,
56 ) -> Result<Self, Self::Error> {
57 match media_type {
58 "statuslist+jwt" | "statuslist+cwt" => {
59 StatusListToken::from_bytes_with(bytes, media_type, verifier, options)
60 .await
61 .map(AnyStatusMap::TokenStatusList)
62 .map_err(FromBytesError::TokenStatusList)
63 }
64 "application/vc+ld+json+jwt"
65 | "application/vc+ld+json+sd-jwt"
66 | "application/vc+ld+json+cose"
67 | "application/vc+ld+json" => {
68 BitstringStatusListCredential::from_bytes_with(bytes, media_type, verifier, options)
69 .await
70 .map(Box::new)
71 .map(AnyStatusMap::BitstringStatusList)
72 .map_err(FromBytesError::BitstringStatusList)
73 }
74 other => Err(FromBytesError::UnexpectedMediaType(other.to_owned())),
75 }
76 }
77}
78
79#[derive(Debug, thiserror::Error)]
80pub enum DecodeError {
81 #[error(transparent)]
82 BitstringStatusList(#[from] bitstring_status_list::DecodeError),
83
84 #[error(transparent)]
85 TokenStatusList(#[from] token_status_list::DecodeError),
86}
87
88impl EncodedStatusMap for AnyStatusMap {
89 type DecodeError = DecodeError;
90 type Decoded = AnyDecodedStatusMap;
91
92 fn decode(self) -> Result<Self::Decoded, Self::DecodeError> {
93 match self {
94 Self::BitstringStatusList(m) => m
95 .decode()
96 .map(AnyDecodedStatusMap::BitstringStatusList)
97 .map_err(Into::into),
98 Self::TokenStatusList(m) => m
99 .decode()
100 .map(AnyDecodedStatusMap::TokenStatusList)
101 .map_err(Into::into),
102 }
103 }
104}
105
106#[derive(Clone)]
107pub enum AnyDecodedStatusMap {
108 BitstringStatusList(bitstring_status_list::StatusList),
109 TokenStatusList(token_status_list::StatusList),
110}
111
112impl AnyDecodedStatusMap {
113 pub fn iter(
114 &self,
115 status_size: Option<u8>,
116 ) -> Result<AnyDecodedStatusMapIter, StatusSizeError> {
117 match self {
118 Self::BitstringStatusList(m) => Ok(AnyDecodedStatusMapIter::BitstringStatusList(
119 m.iter(status_size.ok_or(StatusSizeError::Missing)?.try_into()?),
120 )),
121 Self::TokenStatusList(m) => Ok(AnyDecodedStatusMapIter::TokenStatusList(m.iter())),
122 }
123 }
124}
125
126impl StatusMap for AnyDecodedStatusMap {
127 type Key = usize;
128 type StatusSize = u8;
129 type Status = u8;
130
131 fn time_to_live(&self) -> Option<std::time::Duration> {
132 match self {
133 Self::BitstringStatusList(m) => m.time_to_live(),
134 Self::TokenStatusList(m) => m.time_to_live(),
135 }
136 }
137
138 fn get_by_key(
139 &self,
140 status_size: Option<u8>,
141 key: Self::Key,
142 ) -> Result<Option<Self::Status>, StatusSizeError> {
143 match self {
144 Self::BitstringStatusList(m) => {
145 m.get_by_key(status_size.map(TryInto::try_into).transpose()?, key)
146 }
147 Self::TokenStatusList(m) => {
148 m.get_by_key(status_size.map(TryInto::try_into).transpose()?, key)
149 }
150 }
151 }
152}
153
154pub enum AnyDecodedStatusMapIter<'a> {
155 BitstringStatusList(bitstring_status_list::BitStringIter<'a>),
156 TokenStatusList(token_status_list::BitStringIter<'a>),
157}
158
159impl Iterator for AnyDecodedStatusMapIter<'_> {
160 type Item = u8;
161
162 fn next(&mut self) -> Option<Self::Item> {
163 match self {
164 Self::BitstringStatusList(i) => i.next(),
165 Self::TokenStatusList(i) => i.next(),
166 }
167 }
168}
169
170#[derive(Debug, thiserror::Error)]
171pub enum EntrySetFromBytesError {
172 #[error(transparent)]
173 TokenStatusList(#[from] token_status_list::EntrySetFromBytesError),
174
175 #[error(transparent)]
176 BitstringStatusList(#[from] bitstring_status_list::FromBytesError),
177
178 #[error("unexpected media type `{0}`")]
179 UnexpectedMediaType(String),
180}
181
182pub enum AnyEntrySet {
183 BitstringStatusList(Box<BitstringStatusListEntrySetCredential>),
184 TokenStatusList(token_status_list::AnyStatusListEntrySet),
185}
186
187impl<V> FromBytes<V> for AnyEntrySet
188where
189 V: ResolverProvider + DateTimeProvider + JsonLdLoaderProvider + Eip712TypesLoaderProvider,
190 V::Resolver: JWKResolver + VerificationMethodResolver<Method = AnyMethod>,
191{
192 type Error = EntrySetFromBytesError;
193
194 async fn from_bytes_with(
195 bytes: &[u8],
196 media_type: &str,
197 params: &V,
198 options: FromBytesOptions,
199 ) -> Result<Self, Self::Error> {
200 match media_type {
201 "application/json" | "application/jwt" | "application/cbor" | "application/cwt" => {
202 token_status_list::AnyStatusListEntrySet::from_bytes_with(
203 bytes, media_type, params, options,
204 )
205 .await
206 .map(Self::TokenStatusList)
207 .map_err(Into::into)
208 }
209 "application/vc+ld+json+jwt"
210 | "application/vc+ld+json+sd-jwt"
211 | "application/vc+ld+json+cose"
212 | "application/vc+ld+json" => {
213 bitstring_status_list::BitstringStatusListEntrySetCredential::from_bytes_with(
214 bytes, media_type, params, options,
215 )
216 .await
217 .map(Box::new)
218 .map(Self::BitstringStatusList)
219 .map_err(Into::into)
220 }
221 other => Err(EntrySetFromBytesError::UnexpectedMediaType(
222 other.to_owned(),
223 )),
224 }
225 }
226}
227
228impl StatusMapEntrySet for AnyEntrySet {
229 type Entry<'a>
230 = AnyStatusMapEntryRef<'a>
231 where
232 Self: 'a;
233
234 fn get_entry(&self, purpose: crate::StatusPurpose<&str>) -> Option<Self::Entry<'_>> {
235 match self {
236 Self::BitstringStatusList(s) => s
237 .get_entry(purpose)
238 .map(AnyStatusMapEntryRef::BitstringStatusList),
239 Self::TokenStatusList(s) => s
240 .get_entry(purpose)
241 .map(AnyStatusMapEntryRef::TokenStatusList),
242 }
243 }
244}
245
246pub enum AnyStatusMapEntryRef<'a> {
247 BitstringStatusList(&'a BitstringStatusListEntry),
248 TokenStatusList(token_status_list::AnyStatusListReference<'a>),
249}
250
251impl StatusMapEntry for AnyStatusMapEntryRef<'_> {
252 type Key = usize;
253 type StatusSize = u8;
254
255 fn status_list_url(&self) -> &Uri {
256 match self {
257 Self::BitstringStatusList(e) => e.status_list_url(),
258 Self::TokenStatusList(e) => e.status_list_url(),
259 }
260 }
261
262 fn key(&self) -> Self::Key {
263 match self {
264 Self::BitstringStatusList(e) => e.key(),
265 Self::TokenStatusList(e) => e.key(),
266 }
267 }
268
269 fn status_size(&self) -> Option<Self::StatusSize> {
270 match self {
271 Self::BitstringStatusList(e) => e.status_size().map(Into::into),
272 Self::TokenStatusList(e) => e.status_size().map(Into::into),
273 }
274 }
275}