ssi_status/impl/
any.rs

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    /// Returns the URL of the status list credential.
24    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}