//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/members/update`. Version: `0.1`.
#[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())
}
}
}
///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())
})
}
}
///One community member as the maintainer sees it: the membership record joined with its ACL role.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "MemberResponse",
/// "description": "One community member as the maintainer sees it: the membership record joined with its ACL role.",
/// "type": "object",
/// "required": [
/// "departurePreference",
/// "did",
/// "extensions",
/// "joinedAt",
/// "publishConsent",
/// "role"
/// ],
/// "properties": {
/// "currentRoleVecId": {
/// "description": "Id of the member's current role Verifiable Endorsement Credential, if issued.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "currentVmcId": {
/// "description": "Id of the member's current Verifiable Membership Credential, if issued.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "departurePreference": {
/// "description": "How the member's record is handled on departure.",
/// "type": "string",
/// "enum": [
/// "purge",
/// "tombstone",
/// "historical",
/// "policydefault"
/// ]
/// },
/// "did": {
/// "description": "The member's DID.",
/// "type": "string",
/// "minLength": 1
/// },
/// "extensions": {
/// "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).",
/// "type": "object"
/// },
/// "joinedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "joinedViaInvitation": {
/// "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.",
/// "type": "boolean"
/// },
/// "label": {
/// "description": "Optional human-readable label.",
/// "type": [
/// "string",
/// "null"
/// ],
/// "maxLength": 256
/// },
/// "memberVmcId": {
/// "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "memberVmcReceivedAt": {
/// "description": "When that reciprocal credential was received. Paired with `memberVmcId`.",
/// "type": [
/// "string",
/// "null"
/// ],
/// "format": "date-time"
/// },
/// "personhood": {
/// "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.",
/// "type": "boolean"
/// },
/// "personhoodAssertedAt": {
/// "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.",
/// "type": [
/// "string",
/// "null"
/// ],
/// "format": "date-time"
/// },
/// "publishConsent": {
/// "description": "Whether the member consented to being published in the community directory.",
/// "type": "boolean"
/// },
/// "role": {
/// "description": "The member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).",
/// "type": "string",
/// "minLength": 1
/// },
/// "statusListIndex": {
/// "description": "The member's slot on the community's membership status list, when allocated.",
/// "type": [
/// "integer",
/// "null"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "memberResponse"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct MemberResponse {
///Id of the member's current role Verifiable Endorsement Credential, if issued.
#[serde(
rename = "currentRoleVecId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub current_role_vec_id: ::std::option::Option<::std::string::String>,
///Id of the member's current Verifiable Membership Credential, if issued.
#[serde(
rename = "currentVmcId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub current_vmc_id: ::std::option::Option<::std::string::String>,
///How the member's record is handled on departure.
#[serde(rename = "departurePreference")]
pub departure_preference: MemberResponseDeparturePreference,
///The member's DID.
pub did: MemberResponseDid,
///Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).
pub extensions: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
#[serde(rename = "joinedAt")]
pub joined_at: ::chrono::DateTime<::chrono::offset::Utc>,
///Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.
#[serde(
rename = "joinedViaInvitation",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub joined_via_invitation: ::std::option::Option<bool>,
///Optional human-readable label.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub label: ::std::option::Option<MemberResponseLabel>,
///`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.
#[serde(
rename = "memberVmcId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub member_vmc_id: ::std::option::Option<::std::string::String>,
///When that reciprocal credential was received. Paired with `memberVmcId`.
#[serde(
rename = "memberVmcReceivedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub member_vmc_received_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub personhood: ::std::option::Option<bool>,
///When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.
#[serde(
rename = "personhoodAssertedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub personhood_asserted_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Whether the member consented to being published in the community directory.
#[serde(rename = "publishConsent")]
pub publish_consent: bool,
///The member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).
pub role: MemberResponseRole,
///The member's slot on the community's membership status list, when allocated.
#[serde(
rename = "statusListIndex",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub status_list_index: ::std::option::Option<i64>,
}
impl MemberResponse {
pub fn builder() -> builder::MemberResponse {
Default::default()
}
}
///How the member's record is handled on departure.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "How the member's record is handled on departure.",
/// "type": "string",
/// "enum": [
/// "purge",
/// "tombstone",
/// "historical",
/// "policydefault"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum MemberResponseDeparturePreference {
#[serde(rename = "purge")]
Purge,
#[serde(rename = "tombstone")]
Tombstone,
#[serde(rename = "historical")]
Historical,
#[serde(rename = "policydefault")]
Policydefault,
}
impl ::std::fmt::Display for MemberResponseDeparturePreference {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Purge => f.write_str("purge"),
Self::Tombstone => f.write_str("tombstone"),
Self::Historical => f.write_str("historical"),
Self::Policydefault => f.write_str("policydefault"),
}
}
}
impl ::std::str::FromStr for MemberResponseDeparturePreference {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"purge" => Ok(Self::Purge),
"tombstone" => Ok(Self::Tombstone),
"historical" => Ok(Self::Historical),
"policydefault" => Ok(Self::Policydefault),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for MemberResponseDeparturePreference {
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 MemberResponseDeparturePreference {
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 MemberResponseDeparturePreference {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The member's DID.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The member's DID.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct MemberResponseDid(::std::string::String);
impl ::std::ops::Deref for MemberResponseDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<MemberResponseDid> for ::std::string::String {
fn from(value: MemberResponseDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for MemberResponseDid {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for MemberResponseDid {
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 MemberResponseDid {
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 MemberResponseDid {
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 MemberResponseDid {
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())
})
}
}
///Optional human-readable label.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional human-readable label.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct MemberResponseLabel(::std::string::String);
impl ::std::ops::Deref for MemberResponseLabel {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<MemberResponseLabel> for ::std::string::String {
fn from(value: MemberResponseLabel) -> Self {
value.0
}
}
impl ::std::str::FromStr for MemberResponseLabel {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for MemberResponseLabel {
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 MemberResponseLabel {
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 MemberResponseLabel {
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 MemberResponseLabel {
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 member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct MemberResponseRole(::std::string::String);
impl ::std::ops::Deref for MemberResponseRole {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<MemberResponseRole> for ::std::string::String {
fn from(value: MemberResponseRole) -> Self {
value.0
}
}
impl ::std::str::FromStr for MemberResponseRole {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for MemberResponseRole {
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 MemberResponseRole {
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 MemberResponseRole {
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 MemberResponseRole {
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())
})
}
}
///`Payload`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vtc/members/update/0.1",
/// "title": "Payload",
/// "type": "object",
/// "required": [
/// "did"
/// ],
/// "properties": {
/// "departurePreference": {
/// "type": "string",
/// "enum": [
/// "purge",
/// "tombstone",
/// "historical",
/// "policydefault"
/// ]
/// },
/// "did": {
/// "description": "The member to update.",
/// "type": "string",
/// "minLength": 1
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "extensions": {
/// "description": "Replacement extension bag (maintainer-capped, 16 KiB in the reference implementation).",
/// "type": "object"
/// },
/// "label": {
/// "description": "Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.",
/// "type": [
/// "string",
/// "null"
/// ],
/// "maxLength": 256
/// },
/// "publishConsent": {
/// "description": "New directory-publish consent.",
/// "type": "boolean"
/// },
/// "role": {
/// "description": "New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.",
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
#[serde(
rename = "departurePreference",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub departure_preference: ::std::option::Option<PayloadDeparturePreference>,
///The member to update.
pub did: PayloadDid,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Replacement extension bag (maintainer-capped, 16 KiB in the reference implementation).
#[serde(default, skip_serializing_if = "::serde_json::Map::is_empty")]
pub extensions: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
///Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub label: ::std::option::Option<PayloadLabel>,
///New directory-publish consent.
#[serde(
rename = "publishConsent",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub publish_consent: ::std::option::Option<bool>,
///New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub role: ::std::option::Option<PayloadRole>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///`PayloadDeparturePreference`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "purge",
/// "tombstone",
/// "historical",
/// "policydefault"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum PayloadDeparturePreference {
#[serde(rename = "purge")]
Purge,
#[serde(rename = "tombstone")]
Tombstone,
#[serde(rename = "historical")]
Historical,
#[serde(rename = "policydefault")]
Policydefault,
}
impl ::std::fmt::Display for PayloadDeparturePreference {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Purge => f.write_str("purge"),
Self::Tombstone => f.write_str("tombstone"),
Self::Historical => f.write_str("historical"),
Self::Policydefault => f.write_str("policydefault"),
}
}
}
impl ::std::str::FromStr for PayloadDeparturePreference {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"purge" => Ok(Self::Purge),
"tombstone" => Ok(Self::Tombstone),
"historical" => Ok(Self::Historical),
"policydefault" => Ok(Self::Policydefault),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PayloadDeparturePreference {
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 PayloadDeparturePreference {
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 PayloadDeparturePreference {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The member to update.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The member to update.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadDid(::std::string::String);
impl ::std::ops::Deref for PayloadDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadDid> for ::std::string::String {
fn from(value: PayloadDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadDid {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadDid {
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 PayloadDid {
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 PayloadDid {
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 PayloadDid {
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())
})
}
}
///Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadLabel(::std::string::String);
impl ::std::ops::Deref for PayloadLabel {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadLabel> for ::std::string::String {
fn from(value: PayloadLabel) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadLabel {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadLabel {
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 PayloadLabel {
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 PayloadLabel {
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 PayloadLabel {
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())
})
}
}
///New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadRole(::std::string::String);
impl ::std::ops::Deref for PayloadRole {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadRole> for ::std::string::String {
fn from(value: PayloadRole) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadRole {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadRole {
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 PayloadRole {
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 PayloadRole {
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 PayloadRole {
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())
})
}
}
///`Response`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "type": "object",
/// "required": [
/// "member"
/// ],
/// "properties": {
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "member": {
/// "$ref": "#/definitions/MemberResponse"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
pub member: MemberResponse,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct MemberResponse {
current_role_vec_id: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
current_vmc_id: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
departure_preference:
::std::result::Result<super::MemberResponseDeparturePreference, ::std::string::String>,
did: ::std::result::Result<super::MemberResponseDid, ::std::string::String>,
extensions: ::std::result::Result<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
::std::string::String,
>,
joined_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
joined_via_invitation:
::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
label: ::std::result::Result<
::std::option::Option<super::MemberResponseLabel>,
::std::string::String,
>,
member_vmc_id: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
member_vmc_received_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
personhood: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
personhood_asserted_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
publish_consent: ::std::result::Result<bool, ::std::string::String>,
role: ::std::result::Result<super::MemberResponseRole, ::std::string::String>,
status_list_index: ::std::result::Result<::std::option::Option<i64>, ::std::string::String>,
}
impl ::std::default::Default for MemberResponse {
fn default() -> Self {
Self {
current_role_vec_id: Ok(Default::default()),
current_vmc_id: Ok(Default::default()),
departure_preference: Err("no value supplied for departure_preference".to_string()),
did: Err("no value supplied for did".to_string()),
extensions: Err("no value supplied for extensions".to_string()),
joined_at: Err("no value supplied for joined_at".to_string()),
joined_via_invitation: Ok(Default::default()),
label: Ok(Default::default()),
member_vmc_id: Ok(Default::default()),
member_vmc_received_at: Ok(Default::default()),
personhood: Ok(Default::default()),
personhood_asserted_at: Ok(Default::default()),
publish_consent: Err("no value supplied for publish_consent".to_string()),
role: Err("no value supplied for role".to_string()),
status_list_index: Ok(Default::default()),
}
}
}
impl MemberResponse {
pub fn current_role_vec_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.current_role_vec_id = value.try_into().map_err(|e| {
format!("error converting supplied value for current_role_vec_id: {e}")
});
self
}
pub fn current_vmc_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.current_vmc_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for current_vmc_id: {e}"));
self
}
pub fn departure_preference<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::MemberResponseDeparturePreference>,
T::Error: ::std::fmt::Display,
{
self.departure_preference = value.try_into().map_err(|e| {
format!("error converting supplied value for departure_preference: {e}")
});
self
}
pub fn did<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::MemberResponseDid>,
T::Error: ::std::fmt::Display,
{
self.did = value
.try_into()
.map_err(|e| format!("error converting supplied value for did: {e}"));
self
}
pub fn extensions<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
>,
T::Error: ::std::fmt::Display,
{
self.extensions = value
.try_into()
.map_err(|e| format!("error converting supplied value for extensions: {e}"));
self
}
pub fn joined_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.joined_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for joined_at: {e}"));
self
}
pub fn joined_via_invitation<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.joined_via_invitation = value.try_into().map_err(|e| {
format!("error converting supplied value for joined_via_invitation: {e}")
});
self
}
pub fn label<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::MemberResponseLabel>>,
T::Error: ::std::fmt::Display,
{
self.label = value
.try_into()
.map_err(|e| format!("error converting supplied value for label: {e}"));
self
}
pub fn member_vmc_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.member_vmc_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for member_vmc_id: {e}"));
self
}
pub fn member_vmc_received_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.member_vmc_received_at = value.try_into().map_err(|e| {
format!("error converting supplied value for member_vmc_received_at: {e}")
});
self
}
pub fn personhood<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.personhood = value
.try_into()
.map_err(|e| format!("error converting supplied value for personhood: {e}"));
self
}
pub fn personhood_asserted_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.personhood_asserted_at = value.try_into().map_err(|e| {
format!("error converting supplied value for personhood_asserted_at: {e}")
});
self
}
pub fn publish_consent<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.publish_consent = value
.try_into()
.map_err(|e| format!("error converting supplied value for publish_consent: {e}"));
self
}
pub fn role<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::MemberResponseRole>,
T::Error: ::std::fmt::Display,
{
self.role = value
.try_into()
.map_err(|e| format!("error converting supplied value for role: {e}"));
self
}
pub fn status_list_index<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<i64>>,
T::Error: ::std::fmt::Display,
{
self.status_list_index = value
.try_into()
.map_err(|e| format!("error converting supplied value for status_list_index: {e}"));
self
}
}
impl ::std::convert::TryFrom<MemberResponse> for super::MemberResponse {
type Error = super::error::ConversionError;
fn try_from(
value: MemberResponse,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
current_role_vec_id: value.current_role_vec_id?,
current_vmc_id: value.current_vmc_id?,
departure_preference: value.departure_preference?,
did: value.did?,
extensions: value.extensions?,
joined_at: value.joined_at?,
joined_via_invitation: value.joined_via_invitation?,
label: value.label?,
member_vmc_id: value.member_vmc_id?,
member_vmc_received_at: value.member_vmc_received_at?,
personhood: value.personhood?,
personhood_asserted_at: value.personhood_asserted_at?,
publish_consent: value.publish_consent?,
role: value.role?,
status_list_index: value.status_list_index?,
})
}
}
impl ::std::convert::From<super::MemberResponse> for MemberResponse {
fn from(value: super::MemberResponse) -> Self {
Self {
current_role_vec_id: Ok(value.current_role_vec_id),
current_vmc_id: Ok(value.current_vmc_id),
departure_preference: Ok(value.departure_preference),
did: Ok(value.did),
extensions: Ok(value.extensions),
joined_at: Ok(value.joined_at),
joined_via_invitation: Ok(value.joined_via_invitation),
label: Ok(value.label),
member_vmc_id: Ok(value.member_vmc_id),
member_vmc_received_at: Ok(value.member_vmc_received_at),
personhood: Ok(value.personhood),
personhood_asserted_at: Ok(value.personhood_asserted_at),
publish_consent: Ok(value.publish_consent),
role: Ok(value.role),
status_list_index: Ok(value.status_list_index),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
departure_preference: ::std::result::Result<
::std::option::Option<super::PayloadDeparturePreference>,
::std::string::String,
>,
did: ::std::result::Result<super::PayloadDid, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
extensions: ::std::result::Result<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
::std::string::String,
>,
label: ::std::result::Result<
::std::option::Option<super::PayloadLabel>,
::std::string::String,
>,
publish_consent: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
role:
::std::result::Result<::std::option::Option<super::PayloadRole>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
departure_preference: Ok(Default::default()),
did: Err("no value supplied for did".to_string()),
ext: Ok(Default::default()),
extensions: Ok(Default::default()),
label: Ok(Default::default()),
publish_consent: Ok(Default::default()),
role: Ok(Default::default()),
}
}
}
impl Payload {
pub fn departure_preference<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadDeparturePreference>>,
T::Error: ::std::fmt::Display,
{
self.departure_preference = value.try_into().map_err(|e| {
format!("error converting supplied value for departure_preference: {e}")
});
self
}
pub fn did<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadDid>,
T::Error: ::std::fmt::Display,
{
self.did = value
.try_into()
.map_err(|e| format!("error converting supplied value for did: {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 extensions<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
>,
T::Error: ::std::fmt::Display,
{
self.extensions = value
.try_into()
.map_err(|e| format!("error converting supplied value for extensions: {e}"));
self
}
pub fn label<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadLabel>>,
T::Error: ::std::fmt::Display,
{
self.label = value
.try_into()
.map_err(|e| format!("error converting supplied value for label: {e}"));
self
}
pub fn publish_consent<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.publish_consent = value
.try_into()
.map_err(|e| format!("error converting supplied value for publish_consent: {e}"));
self
}
pub fn role<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadRole>>,
T::Error: ::std::fmt::Display,
{
self.role = value
.try_into()
.map_err(|e| format!("error converting supplied value for role: {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 {
departure_preference: value.departure_preference?,
did: value.did?,
ext: value.ext?,
extensions: value.extensions?,
label: value.label?,
publish_consent: value.publish_consent?,
role: value.role?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
departure_preference: Ok(value.departure_preference),
did: Ok(value.did),
ext: Ok(value.ext),
extensions: Ok(value.extensions),
label: Ok(value.label),
publish_consent: Ok(value.publish_consent),
role: Ok(value.role),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
member: ::std::result::Result<super::MemberResponse, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
member: Err("no value supplied for member".to_string()),
}
}
}
impl Response {
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::MemberResponse>,
T::Error: ::std::fmt::Display,
{
self.member = value
.try_into()
.map_err(|e| format!("error converting supplied value for member: {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 {
ext: value.ext?,
member: value.member?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
ext: Ok(value.ext),
member: Ok(value.member),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/members/update/0.1";
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 \"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 \"MemberResponse\": {\n \"$anchor\": \"memberResponse\",\n \"additionalProperties\": false,\n \"description\": \"One community member as the maintainer sees it: the membership record joined with its ACL role.\",\n \"properties\": {\n \"currentRoleVecId\": {\n \"description\": \"Id of the member's current role Verifiable Endorsement Credential, if issued.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"currentVmcId\": {\n \"description\": \"Id of the member's current Verifiable Membership Credential, if issued.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"departurePreference\": {\n \"description\": \"How the member's record is handled on departure.\",\n \"enum\": [\n \"purge\",\n \"tombstone\",\n \"historical\",\n \"policydefault\"\n ],\n \"type\": \"string\"\n },\n \"did\": {\n \"description\": \"The member's DID.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"extensions\": {\n \"description\": \"Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).\",\n \"type\": \"object\"\n },\n \"joinedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"joinedViaInvitation\": {\n \"description\": \"Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.\",\n \"type\": \"boolean\"\n },\n \"label\": {\n \"description\": \"Optional human-readable label.\",\n \"maxLength\": 256,\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"memberVmcId\": {\n \"description\": \"`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"memberVmcReceivedAt\": {\n \"description\": \"When that reciprocal credential was received. Paired with `memberVmcId`.\",\n \"format\": \"date-time\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"personhood\": {\n \"description\": \"Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.\",\n \"type\": \"boolean\"\n },\n \"personhoodAssertedAt\": {\n \"description\": \"When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.\",\n \"format\": \"date-time\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"publishConsent\": {\n \"description\": \"Whether the member consented to being published in the community directory.\",\n \"type\": \"boolean\"\n },\n \"role\": {\n \"description\": \"The member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"statusListIndex\": {\n \"description\": \"The member's slot on the community's membership status list, when allocated.\",\n \"type\": [\n \"integer\",\n \"null\"\n ]\n }\n },\n \"required\": [\n \"did\",\n \"role\",\n \"joinedAt\",\n \"publishConsent\",\n \"departurePreference\",\n \"extensions\"\n ],\n \"title\": \"MemberResponse\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"member\": {\n \"$ref\": \"#/$defs/MemberResponse\"\n }\n },\n \"required\": [\n \"member\"\n ],\n \"title\": \"VTC Members Update — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vtc/members/update/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"properties\": {\n \"departurePreference\": {\n \"enum\": [\n \"purge\",\n \"tombstone\",\n \"historical\",\n \"policydefault\"\n ],\n \"type\": \"string\"\n },\n \"did\": {\n \"description\": \"The member to update.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"extensions\": {\n \"description\": \"Replacement extension bag (maintainer-capped, 16 KiB in the reference implementation).\",\n \"type\": \"object\"\n },\n \"label\": {\n \"description\": \"Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.\",\n \"maxLength\": 256,\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"publishConsent\": {\n \"description\": \"New directory-publish consent.\",\n \"type\": \"boolean\"\n },\n \"role\": {\n \"description\": \"New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.\",\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"did\"\n ],\n \"title\": \"VTC Members Update — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/members/update/0.1#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 \"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 \"MemberResponse\": {\n \"$anchor\": \"memberResponse\",\n \"additionalProperties\": false,\n \"description\": \"One community member as the maintainer sees it: the membership record joined with its ACL role.\",\n \"properties\": {\n \"currentRoleVecId\": {\n \"description\": \"Id of the member's current role Verifiable Endorsement Credential, if issued.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"currentVmcId\": {\n \"description\": \"Id of the member's current Verifiable Membership Credential, if issued.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"departurePreference\": {\n \"description\": \"How the member's record is handled on departure.\",\n \"enum\": [\n \"purge\",\n \"tombstone\",\n \"historical\",\n \"policydefault\"\n ],\n \"type\": \"string\"\n },\n \"did\": {\n \"description\": \"The member's DID.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"extensions\": {\n \"description\": \"Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).\",\n \"type\": \"object\"\n },\n \"joinedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"joinedViaInvitation\": {\n \"description\": \"Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.\",\n \"type\": \"boolean\"\n },\n \"label\": {\n \"description\": \"Optional human-readable label.\",\n \"maxLength\": 256,\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"memberVmcId\": {\n \"description\": \"`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"memberVmcReceivedAt\": {\n \"description\": \"When that reciprocal credential was received. Paired with `memberVmcId`.\",\n \"format\": \"date-time\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"personhood\": {\n \"description\": \"Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.\",\n \"type\": \"boolean\"\n },\n \"personhoodAssertedAt\": {\n \"description\": \"When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.\",\n \"format\": \"date-time\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"publishConsent\": {\n \"description\": \"Whether the member consented to being published in the community directory.\",\n \"type\": \"boolean\"\n },\n \"role\": {\n \"description\": \"The member's role in wire form (e.g. `admin`, `moderator`, `custom:editor`).\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"statusListIndex\": {\n \"description\": \"The member's slot on the community's membership status list, when allocated.\",\n \"type\": [\n \"integer\",\n \"null\"\n ]\n }\n },\n \"required\": [\n \"did\",\n \"role\",\n \"joinedAt\",\n \"publishConsent\",\n \"departurePreference\",\n \"extensions\"\n ],\n \"title\": \"MemberResponse\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"member\": {\n \"$ref\": \"#/$defs/MemberResponse\"\n }\n },\n \"required\": [\n \"member\"\n ],\n \"title\": \"VTC Members Update — response payload\",\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::NOT_FOUND, error_codes::ADMIN_ROLE_FORBIDDEN];
/// 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 {
/// `vtc/members/update:notFound`
///
/// No member with the supplied `did` exists.
///
/// Declared `retryable: false`.
pub const NOT_FOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/members/update:notFound",
retryable: false,
};
/// `vtc/members/update:adminRoleForbidden`
///
/// `role` was `admin`. Promotion to admin is a separate, gated flow, not a metadata update.
///
/// Declared `retryable: false`.
pub const ADMIN_ROLE_FORBIDDEN: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/members/update:adminRoleForbidden",
retryable: false,
};
}
#[cfg(test)]
mod conformance {
//! Round-trip tests harvested from the spec's `spec.md`,
//! plus a `rejects_invalid_examples` test for any fixtures
//! in `payload.invalid-examples.json` (validate feature).
/// Each fixture in `payload.invalid-examples.json` MUST be
/// rejected by at least one of: serde deserialization, or
/// JSON-Schema validation under the `validate` feature. The
/// fixture file documents the producer-side bug class that
/// each payload exemplifies; this generated test pins it.
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
(
"Unknown top-level member is rejected.",
"{\n \"__x__\": true,\n \"did\": \"did:key:z6Mk\"\n}",
),
("`did` is required.", "{\n \"role\": \"moderator\"\n}"),
(
"`departurePreference` must be one of the enum values.",
"{\n \"departurePreference\": \"delete\",\n \"did\": \"did:key:z6Mk\"\n}",
),
];
for (i, (note, raw)) in fixtures.iter().enumerate() {
let value: serde_json::Value = match serde_json::from_str(raw) {
Ok(v) => v,
Err(_) => continue,
};
let serde_ok = serde_json::from_value::<super::Payload>(value.clone()).is_ok();
let schema_ok = super::Payload::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid-example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}