diff --git a/crates/status/examples/status_list.rs b/crates/status/examples/status_list.rs index f1e4f52c1..c613b1a95 100644 --- a/crates/status/examples/status_list.rs +++ b/crates/status/examples/status_list.rs @@ -21,7 +21,7 @@ use ssi_data_integrity::{AnySuite, ProofOptions}; use ssi_dids::{VerificationMethodDIDResolver, DIDJWK}; use ssi_jwk::JWK; use ssi_status::{ - any::AnyStatusMap, bitstream_status_list, EncodedStatusMap, FromBytes, FromBytesOptions, + any::AnyStatusMap, bitstring_status_list, EncodedStatusMap, FromBytes, FromBytesOptions, }; use ssi_verification_methods::{ReferenceOrOwned, SingleSecretSigner}; use std::{ @@ -139,7 +139,7 @@ impl Command { Ok(()) } Self::Create { id, list, key } => { - let data = create_bitstream_status_list(id.clone(), list, key).await?; + let data = create_bitstring_status_list(id.clone(), list, key).await?; stdout().write_all(&data).unwrap(); Ok(()) } @@ -147,14 +147,14 @@ impl Command { } } -async fn create_bitstream_status_list( +async fn create_bitstring_status_list( id: UriBuf, list: Vec, key: Option, ) -> Result, Error> { - let mut status_list = bitstream_status_list::StatusList::new( - bitstream_status_list::StatusSize::default(), - bitstream_status_list::TimeToLive::default(), + let mut status_list = bitstring_status_list::StatusList::new( + bitstring_status_list::StatusSize::default(), + bitstring_status_list::TimeToLive::default(), // list.into_iter().map(|v| v.0).collect(), ); @@ -162,11 +162,11 @@ async fn create_bitstream_status_list( status_list.push(v.0).unwrap(); } - let credential = bitstream_status_list::BitstringStatusListCredential::new( + let credential = bitstring_status_list::BitstringStatusListCredential::new( Some(id), status_list.to_credential_subject( None, - bitstream_status_list::StatusPurpose::Revocation, + bitstring_status_list::StatusPurpose::Revocation, Vec::new(), ), ); diff --git a/crates/status/src/impl/any.rs b/crates/status/src/impl/any.rs index bb4d644c8..1cdaeb923 100644 --- a/crates/status/src/impl/any.rs +++ b/crates/status/src/impl/any.rs @@ -5,7 +5,7 @@ use ssi_jwk::JWKResolver; use ssi_verification_methods::{AnyMethod, VerificationMethodResolver}; use crate::{ - bitstream_status_list::{ + bitstring_status_list::{ self, BitstringStatusListCredential, BitstringStatusListEntry, BitstringStatusListEntrySetCredential, }, @@ -34,7 +34,7 @@ pub enum FromBytesError { UnexpectedMediaType(String), #[error(transparent)] - BitstringStatusList(bitstream_status_list::FromBytesError), + BitstringStatusList(bitstring_status_list::FromBytesError), #[error(transparent)] TokenStatusList(token_status_list::FromBytesError), @@ -77,7 +77,7 @@ where #[derive(Debug, thiserror::Error)] pub enum DecodeError { #[error(transparent)] - BitstringStatusList(#[from] bitstream_status_list::DecodeError), + BitstringStatusList(#[from] bitstring_status_list::DecodeError), #[error(transparent)] TokenStatusList(#[from] token_status_list::DecodeError), @@ -103,7 +103,7 @@ impl EncodedStatusMap for AnyStatusMap { #[derive(Clone)] pub enum AnyDecodedStatusMap { - BitstringStatusList(bitstream_status_list::StatusList), + BitstringStatusList(bitstring_status_list::StatusList), TokenStatusList(token_status_list::StatusList), } @@ -145,7 +145,7 @@ impl<'a> IntoIterator for &'a AnyDecodedStatusMap { } pub enum AnyDecodedStatusMapIter<'a> { - BitstringStatusList(bitstream_status_list::BitStringIter<'a>), + BitstringStatusList(bitstring_status_list::BitStringIter<'a>), TokenStatusList(token_status_list::BitStringIter<'a>), } @@ -166,7 +166,7 @@ pub enum EntrySetFromBytesError { TokenStatusList(#[from] token_status_list::EntrySetFromBytesError), #[error(transparent)] - BitstringStatusList(#[from] bitstream_status_list::FromBytesError), + BitstringStatusList(#[from] bitstring_status_list::FromBytesError), #[error("unexpected media type `{0}`")] UnexpectedMediaType(String), @@ -203,7 +203,7 @@ where | "application/vc+ld+json+sd-jwt" | "application/vc+ld+json+cose" | "application/vc+ld+json" => { - bitstream_status_list::BitstringStatusListEntrySetCredential::from_bytes_with( + bitstring_status_list::BitstringStatusListEntrySetCredential::from_bytes_with( bytes, media_type, params, options, ) .await diff --git a/crates/status/src/impl/bitstream_status_list/mod.rs b/crates/status/src/impl/bitstring_status_list/mod.rs similarity index 90% rename from crates/status/src/impl/bitstream_status_list/mod.rs rename to crates/status/src/impl/bitstring_status_list/mod.rs index 6f2a68500..a7f777c19 100644 --- a/crates/status/src/impl/bitstream_status_list/mod.rs +++ b/crates/status/src/impl/bitstring_status_list/mod.rs @@ -5,9 +5,10 @@ //! Credentials through use of bitstrings. //! //! See: +use core::fmt; use iref::UriBuf; use serde::{Deserialize, Serialize}; -use std::{hash::Hash, time::Duration}; +use std::{hash::Hash, str::FromStr, time::Duration}; use crate::{Overflow, StatusMap}; @@ -127,7 +128,7 @@ impl From for Duration { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum StatusPurpose { /// Cancel the validity of a verifiable credential. @@ -142,9 +143,54 @@ pub enum StatusPurpose { /// Convey an arbitrary message related to the status of the verifiable /// credential. + /// + /// The actual message is stored in the status list credential, in + /// [`BitstringStatusList::status_message`]. Message, } +impl StatusPurpose { + /// Creates a new status purpose from its name. + pub fn from_name(name: &str) -> Option { + match name { + "revocation" => Some(Self::Revocation), + "suspension" => Some(Self::Suspension), + "message" => Some(Self::Message), + _ => None, + } + } + + /// Returns the name of this status purpose. + pub fn name(&self) -> &'static str { + match self { + Self::Revocation => "revocation", + Self::Suspension => "suspension", + Self::Message => "message", + } + } + + /// Returns the string representation of this status purpose. + /// + /// Same as [`Self::name`]. + pub fn as_str(&self) -> &'static str { + self.name() + } + + /// Turns this status purpose into its name. + /// + /// Same as [`Self::name`]. + pub fn into_name(self) -> &'static str { + self.name() + } + + /// Turns this status purpose into its string representation. + /// + /// Same as [`Self::name`]. + pub fn into_str(self) -> &'static str { + self.name() + } +} + impl<'a> From<&'a StatusPurpose> for crate::StatusPurpose<&'a str> { fn from(value: &'a StatusPurpose) -> Self { match value { @@ -166,6 +212,25 @@ impl<'a> PartialEq> for StatusPurpose { } } +impl fmt::Display for StatusPurpose { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name().fmt(f) + } +} + +/// Error raised when converting a string into a [`StatusPurpose`] fails. +#[derive(Debug, Clone, thiserror::Error)] +#[error("invalid status purpose: {0}")] +pub struct InvalidStatusPurpose(pub String); + +impl FromStr for StatusPurpose { + type Err = InvalidStatusPurpose; + + fn from_str(s: &str) -> Result { + Self::from_name(s).ok_or_else(|| InvalidStatusPurpose(s.to_owned())) + } +} + /// Bit-string as defined by the W3C Bitstring Status List specification. /// /// Bits are indexed from most significant to least significant. diff --git a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/credential.rs b/crates/status/src/impl/bitstring_status_list/syntax/entry_set/credential.rs similarity index 98% rename from crates/status/src/impl/bitstream_status_list/syntax/entry_set/credential.rs rename to crates/status/src/impl/bitstring_status_list/syntax/entry_set/credential.rs index b77feb6d5..25d105da9 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/credential.rs +++ b/crates/status/src/impl/bitstring_status_list/syntax/entry_set/credential.rs @@ -20,7 +20,7 @@ use ssi_vc::v2::{syntax::JsonCredentialTypes, Context}; use ssi_verification_methods::{ssi_core::OneOrMany, AnyMethod, VerificationMethodResolver}; use crate::{ - bitstream_status_list::FromBytesError, FromBytes, FromBytesOptions, StatusMapEntrySet, + bitstring_status_list::FromBytesError, FromBytes, FromBytesOptions, StatusMapEntrySet, }; use super::BitstringStatusListEntry; diff --git a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs b/crates/status/src/impl/bitstring_status_list/syntax/entry_set/mod.rs similarity index 60% rename from crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs rename to crates/status/src/impl/bitstring_status_list/syntax/entry_set/mod.rs index 82476fea8..59a12906f 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs +++ b/crates/status/src/impl/bitstring_status_list/syntax/entry_set/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; mod credential; pub use credential::*; -use crate::{bitstream_status_list::StatusPurpose, StatusMapEntry}; +use crate::{bitstring_status_list::StatusPurpose, StatusMapEntry}; pub const BITSTRING_STATUS_LIST_ENTRY_TYPE: &str = "BitstringStatusListEntry"; @@ -16,28 +16,41 @@ pub const BITSTRING_STATUS_LIST_ENTRY_TYPE: &str = "BitstringStatusListEntry"; /// /// See: #[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(tag = "type", rename_all = "camelCase")] pub struct BitstringStatusListEntry { /// Optional identifier for the status list entry. /// /// Identifies the status information associated with the verifiable /// credential. Must *not* be the URL of the status list. - id: Option, - - /// `BitstringStatusListEntry` type. - #[serde(rename = "type")] - type_: BitstringStatusListEntryType, + pub id: Option, /// Purpose of the status entry. - status_purpose: StatusPurpose, + pub status_purpose: StatusPurpose, /// URL to a `BitstringStatusListCredential` verifiable credential. - status_list_credential: UriBuf, + pub status_list_credential: UriBuf, /// Arbitrary size integer greater than or equal to 0, encoded as a string /// in base 10. #[serde(with = "base10_nat_string")] - status_list_index: usize, + pub status_list_index: usize, +} + +impl BitstringStatusListEntry { + /// Creates a new bit-string status list entry. + pub fn new( + id: Option, + status_purpose: StatusPurpose, + status_list_credential: UriBuf, + status_list_index: usize, + ) -> Self { + Self { + id, + status_purpose, + status_list_credential, + status_list_index, + } + } } impl StatusMapEntry for BitstringStatusListEntry { @@ -52,34 +65,6 @@ impl StatusMapEntry for BitstringStatusListEntry { } } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BitstringStatusListEntryType; - -impl Serialize for BitstringStatusListEntryType { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - BITSTRING_STATUS_LIST_ENTRY_TYPE.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for BitstringStatusListEntryType { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let type_ = String::deserialize(deserializer)?; - if type_ == BITSTRING_STATUS_LIST_ENTRY_TYPE { - Ok(Self) - } else { - Err(serde::de::Error::custom( - "expected `BitstringStatusListEntry` type", - )) - } - } -} - mod base10_nat_string { use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/crates/status/src/impl/bitstream_status_list/syntax/mod.rs b/crates/status/src/impl/bitstring_status_list/syntax/mod.rs similarity index 100% rename from crates/status/src/impl/bitstream_status_list/syntax/mod.rs rename to crates/status/src/impl/bitstring_status_list/syntax/mod.rs diff --git a/crates/status/src/impl/bitstream_status_list/syntax/status_list/credential.rs b/crates/status/src/impl/bitstring_status_list/syntax/status_list/credential.rs similarity index 100% rename from crates/status/src/impl/bitstream_status_list/syntax/status_list/credential.rs rename to crates/status/src/impl/bitstring_status_list/syntax/status_list/credential.rs diff --git a/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs b/crates/status/src/impl/bitstring_status_list/syntax/status_list/mod.rs similarity index 62% rename from crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs rename to crates/status/src/impl/bitstring_status_list/syntax/status_list/mod.rs index 1aaa9a635..3bddbacd9 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs +++ b/crates/status/src/impl/bitstring_status_list/syntax/status_list/mod.rs @@ -4,22 +4,18 @@ use serde::{Deserialize, Serialize}; mod credential; pub use credential::*; -use crate::bitstream_status_list::{ +use crate::bitstring_status_list::{ EncodedList, StatusList, StatusMessage, StatusPurpose, StatusSize, TimeToLive, }; pub const BITSTRING_STATUS_LIST_TYPE: &str = "BitstringStatusList"; #[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(tag = "type", rename_all = "camelCase")] pub struct BitstringStatusList { #[serde(default, skip_serializing_if = "Option::is_none")] pub id: Option, - /// `BitstringStatusList` type. - #[serde(rename = "type")] - pub type_: BitstringStatusListType, - /// Status purpose. pub status_purpose: StatusPurpose, @@ -52,7 +48,6 @@ impl BitstringStatusList { ) -> Self { Self { id, - type_: BitstringStatusListType, status_purpose, status_size, encoded_list, @@ -67,31 +62,3 @@ impl BitstringStatusList { Ok(StatusList::from_bytes(self.status_size, bytes, self.ttl)) } } - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BitstringStatusListType; - -impl Serialize for BitstringStatusListType { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - BITSTRING_STATUS_LIST_TYPE.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for BitstringStatusListType { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let type_ = String::deserialize(deserializer)?; - if type_ == BITSTRING_STATUS_LIST_TYPE { - Ok(Self) - } else { - Err(serde::de::Error::custom( - "expected `BitstringStatusList` type", - )) - } - } -} diff --git a/crates/status/src/impl/mod.rs b/crates/status/src/impl/mod.rs index 99a98a18c..082de2dbb 100644 --- a/crates/status/src/impl/mod.rs +++ b/crates/status/src/impl/mod.rs @@ -1,5 +1,5 @@ pub mod any; -pub mod bitstream_status_list; +pub mod bitstring_status_list; pub mod token_status_list; pub use flate2::Compression;