//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `rooms/owner/group/remove`. Version: `0.2`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
/**
A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.
Multihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.
This definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.
Restricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that "interoperability is not guaranteed between implementations using such values", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DigestMultibase",
/// "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
/// "examples": [
/// "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
/// ],
/// "type": "string",
/// "minLength": 16,
/// "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DigestMultibase(::std::string::String);
impl ::std::ops::Deref for DigestMultibase {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DigestMultibase> for ::std::string::String {
fn from(value: DigestMultibase) -> Self {
value.0
}
}
impl ::std::str::FromStr for DigestMultibase {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 16usize {
return Err("shorter than 16 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DigestMultibase {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A room's owner asks their own agent to remove a member: withdraw every credential the room gave them, at its host at once, and then remove their leaf from the room's MLS group and advance its epoch. 0.2 adds the withdrawal, made first.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/rooms/owner/group/remove/0.2",
/// "title": "Payload",
/// "description": "A room's owner asks their own agent to remove a member: withdraw every credential the room gave them, at its host at once, and then remove their leaf from the room's MLS group and advance its epoch. 0.2 adds the withdrawal, made first.",
/// "type": "object",
/// "required": [
/// "host",
/// "member",
/// "roomId",
/// "signingKeyId"
/// ],
/// "properties": {
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "host": {
/// "description": "The room's host, as a DID: where the withdrawal is delivered first and the room's epoch advances.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
/// },
/// "member": {
/// "description": "The DID whose leaf is removed, and whose credentials the room withdraws. Never the recipient's own leaf.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
/// },
/// "mirrors": {
/// "description": "Any other host serving the room, as DIDs. Each is delivered the withdrawal too: a host that does not hold it honours the member's credentials still.",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
/// },
/// "maxItems": 7,
/// "uniqueItems": true
/// },
/// "reason": {
/// "description": "Why, carried to the host's epoch advance. Untrusted and attributed to its author: it MUST NOT carry personal data or a credential. Never put into the revocation list.",
/// "type": "string",
/// "maxLength": 500,
/// "minLength": 1
/// },
/// "roomId": {
/// "description": "The room whose group loses a member. The recipient MUST hold its group and a signing key for it.",
/// "type": "string",
/// "maxLength": 512,
/// "minLength": 1
/// },
/// "signingKeyId": {
/// "description": "Which held key signs the room's revocation list as the room, named explicitly as for rooms/owner/revoke.",
/// "type": "string",
/// "maxLength": 256,
/// "minLength": 1
/// },
/// "until": {
/// "description": "OPTIONAL. When the last credential the room issued `member` expires, after which the withdrawal can be dropped from the room's list.",
/// "type": "string",
/// "format": "date-time"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The room's host, as a DID: where the withdrawal is delivered first and the room's epoch advances.
pub host: PayloadHost,
///The DID whose leaf is removed, and whose credentials the room withdraws. Never the recipient's own leaf.
pub member: PayloadMember,
///Any other host serving the room, as DIDs. Each is delivered the withdrawal too: a host that does not hold it honours the member's credentials still.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub mirrors: ::std::option::Option<Vec<PayloadMirrorsItem>>,
///Why, carried to the host's epoch advance. Untrusted and attributed to its author: it MUST NOT carry personal data or a credential. Never put into the revocation list.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<PayloadReason>,
///The room whose group loses a member. The recipient MUST hold its group and a signing key for it.
#[serde(rename = "roomId")]
pub room_id: PayloadRoomId,
///Which held key signs the room's revocation list as the room, named explicitly as for rooms/owner/revoke.
#[serde(rename = "signingKeyId")]
pub signing_key_id: PayloadSigningKeyId,
///OPTIONAL. When the last credential the room issued `member` expires, after which the withdrawal can be dropped from the room's list.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub until: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///The room's host, as a DID: where the withdrawal is delivered first and the room's epoch advances.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The room's host, as a DID: where the withdrawal is delivered first and the room's epoch advances.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadHost(::std::string::String);
impl ::std::ops::Deref for PayloadHost {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadHost> for ::std::string::String {
fn from(value: PayloadHost) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadHost {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^did:").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadHost {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadHost {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadHost {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadHost {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The DID whose leaf is removed, and whose credentials the room withdraws. Never the recipient's own leaf.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The DID whose leaf is removed, and whose credentials the room withdraws. Never the recipient's own leaf.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadMember(::std::string::String);
impl ::std::ops::Deref for PayloadMember {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadMember> for ::std::string::String {
fn from(value: PayloadMember) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadMember {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^did:").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadMember {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadMember {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadMember {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadMember {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`PayloadMirrorsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadMirrorsItem(::std::string::String);
impl ::std::ops::Deref for PayloadMirrorsItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadMirrorsItem> for ::std::string::String {
fn from(value: PayloadMirrorsItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadMirrorsItem {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^did:").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadMirrorsItem {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadMirrorsItem {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadMirrorsItem {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadMirrorsItem {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Why, carried to the host's epoch advance. Untrusted and attributed to its author: it MUST NOT carry personal data or a credential. Never put into the revocation list.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Why, carried to the host's epoch advance. Untrusted and attributed to its author: it MUST NOT carry personal data or a credential. Never put into the revocation list.",
/// "type": "string",
/// "maxLength": 500,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadReason(::std::string::String);
impl ::std::ops::Deref for PayloadReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadReason> for ::std::string::String {
fn from(value: PayloadReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 500usize {
return Err("longer than 500 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadReason {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The room whose group loses a member. The recipient MUST hold its group and a signing key for it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The room whose group loses a member. The recipient MUST hold its group and a signing key for it.",
/// "type": "string",
/// "maxLength": 512,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadRoomId(::std::string::String);
impl ::std::ops::Deref for PayloadRoomId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadRoomId> for ::std::string::String {
fn from(value: PayloadRoomId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadRoomId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 512usize {
return Err("longer than 512 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadRoomId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Which held key signs the room's revocation list as the room, named explicitly as for rooms/owner/revoke.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which held key signs the room's revocation list as the room, named explicitly as for rooms/owner/revoke.",
/// "type": "string",
/// "maxLength": 256,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadSigningKeyId(::std::string::String);
impl ::std::ops::Deref for PayloadSigningKeyId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadSigningKeyId> for ::std::string::String {
fn from(value: PayloadSigningKeyId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadSigningKeyId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 256usize {
return Err("longer than 256 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadSigningKeyId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadSigningKeyId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadSigningKeyId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadSigningKeyId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Success response to rooms/owner/group/remove. Type https://trusttasks.org/spec/rooms/owner/group/remove/0.2#response. The revocation list in force at the host, and the room's new epoch, which the removed member holds no key for.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Success response to rooms/owner/group/remove. Type https://trusttasks.org/spec/rooms/owner/group/remove/0.2#response. The revocation list in force at the host, and the room's new epoch, which the removed member holds no key for.",
/// "type": "object",
/// "required": [
/// "epoch",
/// "member",
/// "revocation",
/// "roomId"
/// ],
/// "properties": {
/// "epoch": {
/// "description": "The room epoch after the removal, as the host recorded it.",
/// "type": "integer",
/// "minimum": 2.0
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "member": {
/// "description": "Who was removed.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
/// },
/// "mirrorsPending": {
/// "description": "Mirrors that did not confirm the withdrawal. Each still honours the member's credentials until the list is delivered — with rooms/owner/revoke and no `withdraw`. Absent when every mirror confirmed.",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
/// },
/// "maxItems": 7
/// },
/// "revocation": {
/// "description": "The revocation list carrying the withdrawal, as `host` accepted it.",
/// "$ref": "#/definitions/RevocationHead"
/// },
/// "revokedAt": {
/// "description": "The withdrawal's `revokedAt`: credentials the room issued `member` before it are refused, and fresh ones issued after it are not.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "roomId": {
/// "description": "The room.",
/// "type": "string",
/// "maxLength": 512,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///The room epoch after the removal, as the host recorded it.
pub epoch: i64,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Who was removed.
pub member: ResponseMember,
///Mirrors that did not confirm the withdrawal. Each still honours the member's credentials until the list is delivered — with rooms/owner/revoke and no `withdraw`. Absent when every mirror confirmed.
#[serde(
rename = "mirrorsPending",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub mirrors_pending: ::std::vec::Vec<ResponseMirrorsPendingItem>,
///The revocation list carrying the withdrawal, as `host` accepted it.
pub revocation: RevocationHead,
///The withdrawal's `revokedAt`: credentials the room issued `member` before it are refused, and fresh ones issued after it are not.
#[serde(
rename = "revokedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub revoked_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///The room.
#[serde(rename = "roomId")]
pub room_id: ResponseRoomId,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///Who was removed.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Who was removed.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseMember(::std::string::String);
impl ::std::ops::Deref for ResponseMember {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseMember> for ::std::string::String {
fn from(value: ResponseMember) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseMember {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^did:").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseMember {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResponseMember {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResponseMember {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ResponseMember {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`ResponseMirrorsPendingItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseMirrorsPendingItem(::std::string::String);
impl ::std::ops::Deref for ResponseMirrorsPendingItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseMirrorsPendingItem> for ::std::string::String {
fn from(value: ResponseMirrorsPendingItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseMirrorsPendingItem {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^did:").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseMirrorsPendingItem {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResponseMirrorsPendingItem {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResponseMirrorsPendingItem {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ResponseMirrorsPendingItem {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The room.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The room.",
/// "type": "string",
/// "maxLength": 512,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseRoomId(::std::string::String);
impl ::std::ops::Deref for ResponseRoomId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseRoomId> for ::std::string::String {
fn from(value: ResponseRoomId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseRoomId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 512usize {
return Err("longer than 512 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseRoomId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResponseRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResponseRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ResponseRoomId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Which `RevocationList` a party holds, without its entries: comparable between the room's key holder and a host, and between two hosts, without disclosing whom the room withdrew.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RevocationHead",
/// "description": "Which `RevocationList` a party holds, without its entries: comparable between the room's key holder and a host, and between two hosts, without disclosing whom the room withdrew.",
/// "type": "object",
/// "required": [
/// "digest",
/// "sequence"
/// ],
/// "properties": {
/// "digest": {
/// "description": "SHA-256 multihash of the list's RFC 8785 (JCS) canonicalization, `proof` included. Two parties holding one sequence with different digests have been given two lists the room signed at one sequence.",
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "sequence": {
/// "description": "The list's `sequence`.",
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RevocationHead {
///SHA-256 multihash of the list's RFC 8785 (JCS) canonicalization, `proof` included. Two parties holding one sequence with different digests have been given two lists the room signed at one sequence.
pub digest: DigestMultibase,
///The list's `sequence`.
pub sequence: ::std::num::NonZeroU64,
}
impl RevocationHead {
pub fn builder() -> builder::RevocationHead {
Default::default()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct Payload {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
host: ::std::result::Result<super::PayloadHost, ::std::string::String>,
member: ::std::result::Result<super::PayloadMember, ::std::string::String>,
mirrors: ::std::result::Result<
::std::option::Option<Vec<super::PayloadMirrorsItem>>,
::std::string::String,
>,
reason: ::std::result::Result<
::std::option::Option<super::PayloadReason>,
::std::string::String,
>,
room_id: ::std::result::Result<super::PayloadRoomId, ::std::string::String>,
signing_key_id: ::std::result::Result<super::PayloadSigningKeyId, ::std::string::String>,
until: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
host: Err("no value supplied for host".to_string()),
member: Err("no value supplied for member".to_string()),
mirrors: Ok(Default::default()),
reason: Ok(Default::default()),
room_id: Err("no value supplied for room_id".to_string()),
signing_key_id: Err("no value supplied for signing_key_id".to_string()),
until: Ok(Default::default()),
}
}
}
impl Payload {
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn host<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadHost>,
T::Error: ::std::fmt::Display,
{
self.host = value
.try_into()
.map_err(|e| format!("error converting supplied value for host: {e}"));
self
}
pub fn member<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadMember>,
T::Error: ::std::fmt::Display,
{
self.member = value
.try_into()
.map_err(|e| format!("error converting supplied value for member: {e}"));
self
}
pub fn mirrors<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::PayloadMirrorsItem>>>,
T::Error: ::std::fmt::Display,
{
self.mirrors = value
.try_into()
.map_err(|e| format!("error converting supplied value for mirrors: {e}"));
self
}
pub fn reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadReason>>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn room_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadRoomId>,
T::Error: ::std::fmt::Display,
{
self.room_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for room_id: {e}"));
self
}
pub fn signing_key_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadSigningKeyId>,
T::Error: ::std::fmt::Display,
{
self.signing_key_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for signing_key_id: {e}"));
self
}
pub fn until<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.until = value
.try_into()
.map_err(|e| format!("error converting supplied value for until: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ext: value.ext?,
host: value.host?,
member: value.member?,
mirrors: value.mirrors?,
reason: value.reason?,
room_id: value.room_id?,
signing_key_id: value.signing_key_id?,
until: value.until?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
ext: Ok(value.ext),
host: Ok(value.host),
member: Ok(value.member),
mirrors: Ok(value.mirrors),
reason: Ok(value.reason),
room_id: Ok(value.room_id),
signing_key_id: Ok(value.signing_key_id),
until: Ok(value.until),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
epoch: ::std::result::Result<i64, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
member: ::std::result::Result<super::ResponseMember, ::std::string::String>,
mirrors_pending: ::std::result::Result<
::std::vec::Vec<super::ResponseMirrorsPendingItem>,
::std::string::String,
>,
revocation: ::std::result::Result<super::RevocationHead, ::std::string::String>,
revoked_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
room_id: ::std::result::Result<super::ResponseRoomId, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
epoch: Err("no value supplied for epoch".to_string()),
ext: Ok(Default::default()),
member: Err("no value supplied for member".to_string()),
mirrors_pending: Ok(Default::default()),
revocation: Err("no value supplied for revocation".to_string()),
revoked_at: Ok(Default::default()),
room_id: Err("no value supplied for room_id".to_string()),
}
}
}
impl Response {
pub fn epoch<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<i64>,
T::Error: ::std::fmt::Display,
{
self.epoch = value
.try_into()
.map_err(|e| format!("error converting supplied value for epoch: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn member<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseMember>,
T::Error: ::std::fmt::Display,
{
self.member = value
.try_into()
.map_err(|e| format!("error converting supplied value for member: {e}"));
self
}
pub fn mirrors_pending<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::ResponseMirrorsPendingItem>>,
T::Error: ::std::fmt::Display,
{
self.mirrors_pending = value
.try_into()
.map_err(|e| format!("error converting supplied value for mirrors_pending: {e}"));
self
}
pub fn revocation<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RevocationHead>,
T::Error: ::std::fmt::Display,
{
self.revocation = value
.try_into()
.map_err(|e| format!("error converting supplied value for revocation: {e}"));
self
}
pub fn revoked_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.revoked_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for revoked_at: {e}"));
self
}
pub fn room_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseRoomId>,
T::Error: ::std::fmt::Display,
{
self.room_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for room_id: {e}"));
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
epoch: value.epoch?,
ext: value.ext?,
member: value.member?,
mirrors_pending: value.mirrors_pending?,
revocation: value.revocation?,
revoked_at: value.revoked_at?,
room_id: value.room_id?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
epoch: Ok(value.epoch),
ext: Ok(value.ext),
member: Ok(value.member),
mirrors_pending: Ok(value.mirrors_pending),
revocation: Ok(value.revocation),
revoked_at: Ok(value.revoked_at),
room_id: Ok(value.room_id),
}
}
}
#[derive(Clone, Debug)]
pub struct RevocationHead {
digest: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
sequence: ::std::result::Result<::std::num::NonZeroU64, ::std::string::String>,
}
impl ::std::default::Default for RevocationHead {
fn default() -> Self {
Self {
digest: Err("no value supplied for digest".to_string()),
sequence: Err("no value supplied for sequence".to_string()),
}
}
}
impl RevocationHead {
pub fn digest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.digest = value
.try_into()
.map_err(|e| format!("error converting supplied value for digest: {e}"));
self
}
pub fn sequence<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::num::NonZeroU64>,
T::Error: ::std::fmt::Display,
{
self.sequence = value
.try_into()
.map_err(|e| format!("error converting supplied value for sequence: {e}"));
self
}
}
impl ::std::convert::TryFrom<RevocationHead> for super::RevocationHead {
type Error = super::error::ConversionError;
fn try_from(
value: RevocationHead,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
digest: value.digest?,
sequence: value.sequence?,
})
}
}
impl ::std::convert::From<super::RevocationHead> for RevocationHead {
fn from(value: super::RevocationHead) -> Self {
Self {
digest: Ok(value.digest),
sequence: Ok(value.sequence),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/rooms/owner/group/remove/0.2";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to rooms/owner/group/remove. Type https://trusttasks.org/spec/rooms/owner/group/remove/0.2#response. The revocation list in force at the host, and the room's new epoch, which the removed member holds no key for.\",\n \"properties\": {\n \"epoch\": {\n \"description\": \"The room epoch after the removal, as the host recorded it.\",\n \"minimum\": 2,\n \"type\": \"integer\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"member\": {\n \"description\": \"Who was removed.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"mirrorsPending\": {\n \"description\": \"Mirrors that did not confirm the withdrawal. Each still honours the member's credentials until the list is delivered — with rooms/owner/revoke and no `withdraw`. Absent when every mirror confirmed.\",\n \"items\": {\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"maxItems\": 7,\n \"type\": \"array\"\n },\n \"revocation\": {\n \"$ref\": \"#/$defs/RevocationHead\",\n \"description\": \"The revocation list carrying the withdrawal, as `host` accepted it.\"\n },\n \"revokedAt\": {\n \"description\": \"The withdrawal's `revokedAt`: credentials the room issued `member` before it are refused, and fresh ones issued after it are not.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"roomId\": {\n \"description\": \"The room.\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"roomId\",\n \"member\",\n \"revocation\",\n \"epoch\"\n ],\n \"title\": \"Rooms Owner Group Remove — response payload\",\n \"type\": \"object\"\n },\n \"RevocationHead\": {\n \"additionalProperties\": false,\n \"description\": \"Which `RevocationList` a party holds, without its entries: comparable between the room's key holder and a host, and between two hosts, without disclosing whom the room withdrew.\",\n \"properties\": {\n \"digest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"SHA-256 multihash of the list's RFC 8785 (JCS) canonicalization, `proof` included. Two parties holding one sequence with different digests have been given two lists the room signed at one sequence.\"\n },\n \"sequence\": {\n \"description\": \"The list's `sequence`.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"sequence\",\n \"digest\"\n ],\n \"title\": \"RevocationHead\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/rooms/owner/group/remove/0.2\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"A room's owner asks their own agent to remove a member: withdraw every credential the room gave them, at its host at once, and then remove their leaf from the room's MLS group and advance its epoch. 0.2 adds the withdrawal, made first.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"host\": {\n \"description\": \"The room's host, as a DID: where the withdrawal is delivered first and the room's epoch advances.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"member\": {\n \"description\": \"The DID whose leaf is removed, and whose credentials the room withdraws. Never the recipient's own leaf.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"mirrors\": {\n \"description\": \"Any other host serving the room, as DIDs. Each is delivered the withdrawal too: a host that does not hold it honours the member's credentials still.\",\n \"items\": {\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"maxItems\": 7,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"reason\": {\n \"description\": \"Why, carried to the host's epoch advance. Untrusted and attributed to its author: it MUST NOT carry personal data or a credential. Never put into the revocation list.\",\n \"maxLength\": 500,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"roomId\": {\n \"description\": \"The room whose group loses a member. The recipient MUST hold its group and a signing key for it.\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"signingKeyId\": {\n \"description\": \"Which held key signs the room's revocation list as the room, named explicitly as for rooms/owner/revoke.\",\n \"maxLength\": 256,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"until\": {\n \"description\": \"OPTIONAL. When the last credential the room issued `member` expires, after which the withdrawal can be dropped from the room's list.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"roomId\",\n \"host\",\n \"member\",\n \"signingKeyId\"\n ],\n \"title\": \"Rooms Owner Group Remove — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/rooms/owner/group/remove/0.2#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to rooms/owner/group/remove. Type https://trusttasks.org/spec/rooms/owner/group/remove/0.2#response. The revocation list in force at the host, and the room's new epoch, which the removed member holds no key for.\",\n \"properties\": {\n \"epoch\": {\n \"description\": \"The room epoch after the removal, as the host recorded it.\",\n \"minimum\": 2,\n \"type\": \"integer\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"member\": {\n \"description\": \"Who was removed.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"mirrorsPending\": {\n \"description\": \"Mirrors that did not confirm the withdrawal. Each still honours the member's credentials until the list is delivered — with rooms/owner/revoke and no `withdraw`. Absent when every mirror confirmed.\",\n \"items\": {\n \"maxLength\": 2048,\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"maxItems\": 7,\n \"type\": \"array\"\n },\n \"revocation\": {\n \"$ref\": \"#/$defs/RevocationHead\",\n \"description\": \"The revocation list carrying the withdrawal, as `host` accepted it.\"\n },\n \"revokedAt\": {\n \"description\": \"The withdrawal's `revokedAt`: credentials the room issued `member` before it are refused, and fresh ones issued after it are not.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"roomId\": {\n \"description\": \"The room.\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"roomId\",\n \"member\",\n \"revocation\",\n \"epoch\"\n ],\n \"title\": \"Rooms Owner Group Remove — response payload\",\n \"type\": \"object\"\n },\n \"RevocationHead\": {\n \"additionalProperties\": false,\n \"description\": \"Which `RevocationList` a party holds, without its entries: comparable between the room's key holder and a host, and between two hosts, without disclosing whom the room withdrew.\",\n \"properties\": {\n \"digest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"SHA-256 multihash of the list's RFC 8785 (JCS) canonicalization, `proof` included. Two parties holding one sequence with different digests have been given two lists the room signed at one sequence.\"\n },\n \"sequence\": {\n \"description\": \"The list's `sequence`.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"sequence\",\n \"digest\"\n ],\n \"title\": \"RevocationHead\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[
error_codes::NO_GROUP,
error_codes::NOT_MEMBER,
error_codes::OWN_LEAF,
error_codes::NO_SIGNING_KEY,
error_codes::NO_AUTHORITY,
error_codes::PRIVATE_ROOM,
error_codes::EPOCH_CONFLICT,
error_codes::HOST_UNREACHABLE,
error_codes::HOST_REFUSED,
];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `rooms/owner/group/remove:noGroup`
///
/// The recipient holds no group state for this room.
///
/// Declared `retryable: false`.
pub const NO_GROUP: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:noGroup",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:notMember`
///
/// `member` has no leaf in the group.
///
/// Declared `retryable: false`.
pub const NOT_MEMBER: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:notMember",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:ownLeaf`
///
/// `member` is the recipient's own leaf. A key holder does not remove itself; an owner leaving a room hands it on with rooms/owner/transfer.
///
/// Declared `retryable: false`.
pub const OWN_LEAF: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:ownLeaf",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:noSigningKey`
///
/// The recipient holds no key by `signingKeyId`, or the key is not one its caller may name.
///
/// Declared `retryable: false`.
pub const NO_SIGNING_KEY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:noSigningKey",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:noAuthority`
///
/// The recipient holds no authority credential conferring `admin` on this room for its caller, so it can neither deliver the withdrawal nor advance the room's epoch at the host.
///
/// Declared `retryable: false`.
pub const NO_AUTHORITY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:noAuthority",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:privateRoom`
///
/// The room is `private`. Its host cannot be told which party to refuse without being told who was a member, so a member cannot be withdrawn one at a time there. Nothing changed. Remove them with rooms/owner/group/remove 0.1, which withdraws nothing at the host, and reset every credential with rooms/owner/revoke if the room needs that too.
///
/// Declared `retryable: false`.
pub const PRIVATE_ROOM: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:privateRoom",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/owner/group/remove:epochConflict`
///
/// The host is at a different epoch than the recipient's group — another admin advanced the room. The withdrawal, if it was accepted, stays in force. Catch up (rooms/epoch/commits) and try again. The host's epoch is carried in `details.hostEpoch`.
///
/// Declared `retryable: true`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const EPOCH_CONFLICT: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:epochConflict",
retryable: true,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"hostEpoch\":{\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"hostEpoch\"],\"type\":\"object\"}",
),
};
/// `rooms/owner/group/remove:hostUnreachable`
///
/// The named host could not be resolved, advertises no transport this recipient speaks, or did not answer. The outcome is unknown, so the recipient keeps the pending removal and the next request to it settles it (item 8). `details.stage` says which step went unanswered.
///
/// Declared `retryable: true`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const HOST_UNREACHABLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:hostUnreachable",
retryable: true,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"stage\":{\"enum\":[\"revocation\",\"advance\"],\"type\":\"string\"}},\"required\":[\"stage\"],\"type\":\"object\"}",
),
};
/// `rooms/owner/group/remove:hostRefused`
///
/// The host answered and declined. `details.stage` says which step: at `revocation` nothing changed; at `advance` the withdrawal is in force at the host and the group is unchanged. The host's own code is in `details.code`.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const HOST_REFUSED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/owner/group/remove:hostRefused",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"code\":{\"maxLength\":128,\"type\":\"string\"},\"stage\":{\"enum\":[\"revocation\",\"advance\"],\"type\":\"string\"}},\"required\":[\"stage\"],\"type\":\"object\"}",
),
};
}