From 9c1a2c5656036f68b32a37034d2422ce1b603351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 3 Jul 2024 17:54:13 +0200 Subject: [PATCH 1/6] Remove `BitstringStatusListType`. --- .../syntax/status_list/mod.rs | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs b/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs index 1aaa9a635..aaa47d7fc 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs +++ b/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs @@ -11,15 +11,11 @@ use crate::bitstream_status_list::{ 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, @@ -66,32 +61,4 @@ impl BitstringStatusList { let bytes = self.encoded_list.decode(None)?; 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", - )) - } - } -} +} \ No newline at end of file From d91f404a5ccf56003aab662d28004d64fb24afde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Jul 2024 12:22:59 +0200 Subject: [PATCH 2/6] Remove `BitstringStatusListEntryType`. Make `BitstringStatusListEntry` fields public. --- .../syntax/entry_set/mod.rs | 42 +++---------------- .../syntax/status_list/mod.rs | 2 +- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs b/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs index 82476fea8..b8bd7fd2a 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs +++ b/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs @@ -16,28 +16,24 @@ 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 StatusMapEntry for BitstringStatusListEntry { @@ -52,34 +48,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/status_list/mod.rs b/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs index aaa47d7fc..36d8bb6ae 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs +++ b/crates/status/src/impl/bitstream_status_list/syntax/status_list/mod.rs @@ -61,4 +61,4 @@ impl BitstringStatusList { let bytes = self.encoded_list.decode(None)?; Ok(StatusList::from_bytes(self.status_size, bytes, self.ttl)) } -} \ No newline at end of file +} From ad466655a3beaa51654e085878b87f1b7fa2c32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Jul 2024 12:52:36 +0200 Subject: [PATCH 3/6] Add constructor for `BitstringStatusListEntry`. --- .../syntax/entry_set/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs b/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs index b8bd7fd2a..38f0fa5c6 100644 --- a/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs +++ b/crates/status/src/impl/bitstream_status_list/syntax/entry_set/mod.rs @@ -36,6 +36,23 @@ pub struct BitstringStatusListEntry { 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 { type Key = usize; From 5a93f572bbb7cb6c0cbd6e433527cb4d41ce061b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Jul 2024 13:03:45 +0200 Subject: [PATCH 4/6] Add constructors and accessors for `bitstring_status_list::StatusPurpose`. --- .../src/impl/bitstream_status_list/mod.rs | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/crates/status/src/impl/bitstream_status_list/mod.rs b/crates/status/src/impl/bitstream_status_list/mod.rs index 6f2a68500..02a22dbb7 100644 --- a/crates/status/src/impl/bitstream_status_list/mod.rs +++ b/crates/status/src/impl/bitstream_status_list/mod.rs @@ -7,7 +7,7 @@ //! See: 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 +127,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 +142,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 +211,19 @@ impl<'a> PartialEq> for StatusPurpose { } } +/// 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. From 81fb5a22ea48b2181147bbff0b40a2ff9db94f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Jul 2024 13:10:45 +0200 Subject: [PATCH 5/6] Rename `bitstream_status_list` into `bitstring_status_list`. --- crates/status/examples/status_list.rs | 16 ++++++++-------- crates/status/src/impl/any.rs | 14 +++++++------- .../mod.rs | 0 .../syntax/entry_set/credential.rs | 2 +- .../syntax/entry_set/mod.rs | 2 +- .../syntax/mod.rs | 0 .../syntax/status_list/credential.rs | 0 .../syntax/status_list/mod.rs | 2 +- crates/status/src/impl/mod.rs | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/mod.rs (100%) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/syntax/entry_set/credential.rs (98%) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/syntax/entry_set/mod.rs (97%) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/syntax/mod.rs (100%) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/syntax/status_list/credential.rs (100%) rename crates/status/src/impl/{bitstream_status_list => bitstring_status_list}/syntax/status_list/mod.rs (97%) 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 100% rename from crates/status/src/impl/bitstream_status_list/mod.rs rename to crates/status/src/impl/bitstring_status_list/mod.rs 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 97% 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 38f0fa5c6..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"; 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 97% 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 36d8bb6ae..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,7 +4,7 @@ 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, }; 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; From 4f0a0451f7bd4ddf6c10760e5da11bf7c7b40fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Jul 2024 13:13:20 +0200 Subject: [PATCH 6/6] Impl `Display` for `bitstring_status_list::StatusPurpose`. --- crates/status/src/impl/bitstring_status_list/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/status/src/impl/bitstring_status_list/mod.rs b/crates/status/src/impl/bitstring_status_list/mod.rs index 02a22dbb7..a7f777c19 100644 --- a/crates/status/src/impl/bitstring_status_list/mod.rs +++ b/crates/status/src/impl/bitstring_status_list/mod.rs @@ -5,6 +5,7 @@ //! Credentials through use of bitstrings. //! //! See: +use core::fmt; use iref::UriBuf; use serde::{Deserialize, Serialize}; use std::{hash::Hash, str::FromStr, time::Duration}; @@ -211,6 +212,12 @@ 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}")]