//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/join-requests/manifest`. Version: `0.4`.
/// Why this specification version is deprecated (SPEC "Superseded and
/// Withdrawn Versions"), or `None` while it is current.
pub const DEPRECATION: Option<&str> = None;
#[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())
}
}
}
/**
Who may see a directory field, ordered from narrowest to widest. `admins`: holders of `vtc.directory.admin` (and, for derived fields, `vtc.members.manage`). `groups`: members who share at least one community group with the subject (narrowed to the groups the subject names in `groupAudience`, where it names any). `connections`: members holding an attributed, in-force verified relationship with the subject at the community. `members`: every active member. `public`: anyone, and what the member's signed public card carries outside the community.
`groups` and `connections` are not a chain: a viewer sees a field when they satisfy the audience the subject chose for it or any wider audience. Every audience includes the administrators, and the subject always sees their own whole card. There is no `onlyMe`: a value nobody else should see is not directory data.*/
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum Audience {
#[serde(rename = "admins")]
Admins,
#[serde(rename = "groups")]
Groups,
#[serde(rename = "connections")]
Connections,
#[serde(rename = "members")]
Members,
#[serde(rename = "public")]
Public,
}
impl ::std::fmt::Display for Audience {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Admins => f.write_str("admins"),
Self::Groups => f.write_str("groups"),
Self::Connections => f.write_str("connections"),
Self::Members => f.write_str("members"),
Self::Public => f.write_str("public"),
}
}
}
impl ::std::str::FromStr for Audience {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"admins" => Ok(Self::Admins),
"groups" => Ok(Self::Groups),
"connections" => Ok(Self::Connections),
"members" => Ok(Self::Members),
"public" => Ok(Self::Public),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Audience {
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 Audience {
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 Audience {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
/**
The vocabulary token naming what a value IS — `name.legal`, `phone.mobile`, `address.postal`, `person.birthDate`. Dotted, most-general segment first, so that a consumer with no knowledge of the specific token can still group by its prefix.
The token is the maintainer's own; no external vocabulary is primary. External vocabularies (vCard/jCard, OIDC standard claims, schema.org) are mappings applied at PRESENTATION by a renderer, not at rest, so that a query written in any of them can be matched without the store having to live inside any one of them.
The `x:` prefix is an open extension namespace and is not decoration. The closest prior art — Windows CardSpace's self-issued card — supported exactly fifteen predefined claim types with no extensibility, and that is the specific way it failed the requirement a holder actually has. An `x:` attribute stores, composes, binds and discloses exactly like a known one; it renders generically and matches only an explicit query.*/
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ClaimType(::std::string::String);
impl ::std::ops::Deref for ClaimType {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ClaimType> for ::std::string::String {
fn from(value: ClaimType) -> Self {
value.0
}
}
impl ::std::str::FromStr for ClaimType {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(x:)?[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(x:)?[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ClaimType {
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 ClaimType {
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 ClaimType {
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 ClaimType {
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. How the community asks to be shown to a prospective applicant: a name, an accent colour and a logo. Presentation only, self-asserted and unverified — `communityDid` identifies the community, never `branding`. Not part of any criterion, so not covered by a `requirementsDigest`. Every member is optional.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CommunityBranding {
///An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.
#[serde(
rename = "accentColor",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub accent_color: ::std::option::Option<CommunityBrandingAccentColor>,
///The community's name as it asks to be shown.
#[serde(
rename = "displayName",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub display_name: ::std::option::Option<CommunityBrandingDisplayName>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.
#[serde(
rename = "logoUrl",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub logo_url: ::std::option::Option<::std::string::String>,
}
impl ::std::default::Default for CommunityBranding {
fn default() -> Self {
Self {
accent_color: Default::default(),
display_name: Default::default(),
ext: Default::default(),
logo_url: Default::default(),
}
}
}
impl CommunityBranding {
pub fn builder() -> builder::CommunityBranding {
Default::default()
}
}
///An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct CommunityBrandingAccentColor(::std::string::String);
impl ::std::ops::Deref for CommunityBrandingAccentColor {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CommunityBrandingAccentColor> for ::std::string::String {
fn from(value: CommunityBrandingAccentColor) -> Self {
value.0
}
}
impl ::std::str::FromStr for CommunityBrandingAccentColor {
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("^#[0-9a-fA-F]{6}$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^#[0-9a-fA-F]{6}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for CommunityBrandingAccentColor {
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 CommunityBrandingAccentColor {
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 CommunityBrandingAccentColor {
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 CommunityBrandingAccentColor {
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 community's name as it asks to be shown.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct CommunityBrandingDisplayName(::std::string::String);
impl ::std::ops::Deref for CommunityBrandingDisplayName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CommunityBrandingDisplayName> for ::std::string::String {
fn from(value: CommunityBrandingDisplayName) -> Self {
value.0
}
}
impl ::std::str::FromStr for CommunityBrandingDisplayName {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 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 CommunityBrandingDisplayName {
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 CommunityBrandingDisplayName {
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 CommunityBrandingDisplayName {
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 CommunityBrandingDisplayName {
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())
})
}
}
///`Criterion`
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Criterion {
///How the community decides a submission that meets this criterion. `automatic`: it admits the applicant without a person deciding. `review`: it refers the submission to an administrator, who decides.
pub admission: CriterionAdmission,
///Whose credentials meet `presentationDefinition`: `community` (this community's own), `recognised` (this community's, or a community it recognises), or `any` (any issuer whose credential verifies). Present exactly when `presentationDefinition` is.
#[serde(
rename = "credentialIssuers",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub credential_issuers: ::std::option::Option<CriterionCredentialIssuers>,
///Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<CriterionDescription>,
pub id: CriterionId,
///True: the criterion is met only by a submission carrying a valid, unconsumed invitation this community issued to the applicant.
#[serde(
rename = "invitationRequired",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub invitation_required: ::std::option::Option<bool>,
///The credentials an applicant must present for this criterion (opaque here). Absent: the criterion requires no credential.
#[serde(
rename = "presentationDefinition",
default,
skip_serializing_if = "::serde_json::Map::is_empty"
)]
pub presentation_definition: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
///Digest over the RFC 8785 (JCS) canonicalization of this criterion object with the `requirementsDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Names one version of the criterion: an applicant records it when starting an application and echoes it to vetters and at submission.
#[serde(rename = "requirementsDigest")]
pub requirements_digest: DigestMultibase,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub vetting: ::std::option::Option<VettingRequirements>,
}
impl Criterion {
pub fn builder() -> builder::Criterion {
Default::default()
}
}
///How the community decides a submission that meets this criterion. `automatic`: it admits the applicant without a person deciding. `review`: it refers the submission to an administrator, who decides.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum CriterionAdmission {
#[serde(rename = "automatic")]
Automatic,
#[serde(rename = "review")]
Review,
}
impl ::std::fmt::Display for CriterionAdmission {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Automatic => f.write_str("automatic"),
Self::Review => f.write_str("review"),
}
}
}
impl ::std::str::FromStr for CriterionAdmission {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"automatic" => Ok(Self::Automatic),
"review" => Ok(Self::Review),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CriterionAdmission {
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 CriterionAdmission {
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 CriterionAdmission {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Whose credentials meet `presentationDefinition`: `community` (this community's own), `recognised` (this community's, or a community it recognises), or `any` (any issuer whose credential verifies). Present exactly when `presentationDefinition` is.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum CriterionCredentialIssuers {
#[serde(rename = "any")]
Any,
#[serde(rename = "community")]
Community,
#[serde(rename = "recognised")]
Recognised,
}
impl ::std::fmt::Display for CriterionCredentialIssuers {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Any => f.write_str("any"),
Self::Community => f.write_str("community"),
Self::Recognised => f.write_str("recognised"),
}
}
}
impl ::std::str::FromStr for CriterionCredentialIssuers {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"any" => Ok(Self::Any),
"community" => Ok(Self::Community),
"recognised" => Ok(Self::Recognised),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CriterionCredentialIssuers {
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 CriterionCredentialIssuers {
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 CriterionCredentialIssuers {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct CriterionDescription(::std::string::String);
impl ::std::ops::Deref for CriterionDescription {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CriterionDescription> for ::std::string::String {
fn from(value: CriterionDescription) -> Self {
value.0
}
}
impl ::std::str::FromStr for CriterionDescription {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for CriterionDescription {
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 CriterionDescription {
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 CriterionDescription {
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 CriterionDescription {
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())
})
}
}
///`CriterionId`
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct CriterionId(::std::string::String);
impl ::std::ops::Deref for CriterionId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CriterionId> for ::std::string::String {
fn from(value: CriterionId) -> Self {
value.0
}
}
impl ::std::str::FromStr for CriterionId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 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 CriterionId {
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 CriterionId {
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 CriterionId {
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 CriterionId {
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 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`.*/
///
#[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())
})
}
}
///The statement itself. Every member is generated from the community's configuration when the statement is read. `statementDigest` names this exact statement, so an applicant can record which one it was shown.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct DirectoryDisclosure {
///Whether administrators keep notes about members. Notes are never on a card and never reach another member; a member may ask for the notes about them.
#[serde(rename = "adminNotes")]
pub admin_notes: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Whether a member may publish a card beyond the community: their `public` values, signed by them, offered through the community's trust registry. Always the member's own choice, and off on a new card. False where the community cannot publish external cards.
#[serde(rename = "externalCards")]
pub external_cards: bool,
///Every active field, base and community-defined, in display order. A base field the community has disabled is absent, which is how a reader learns it is not collected.
pub fields: ::std::vec::Vec<DisclosedField>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub groups: ::std::option::Option<GroupDisclosure>,
///The widest audience a member's membership identifier — the DID they joined with — can reach through the directory: the ceiling of the base field `did`, or `admins` where that field is disabled. `members` or `public` means a member may choose to show it that widely; a new card never shows it beyond `admins` (VTI-DIR-042). Administrators always see it.
#[serde(rename = "membershipIdentifiers")]
pub membership_identifiers: Audience,
#[serde(rename = "newCard")]
pub new_card: NewCardDefaults,
///Digest over the RFC 8785 (JCS) canonicalization of this statement with the `statementDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Changes exactly when what the statement says changes.
#[serde(rename = "statementDigest")]
pub statement_digest: DigestMultibase,
}
impl DirectoryDisclosure {
pub fn builder() -> builder::DirectoryDisclosure {
Default::default()
}
}
///One active field of the community's directory, as an applicant needs to see it: what it is, who writes its value, the widest audience it may ever reach and the audience a new card gives it. Disabled and retired fields are not listed: no projection shows them.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct DisclosedField {
///The widest audience the field may ever reach, whatever the member chooses.
pub ceiling: Audience,
///The audience a new card gives the field until its member chooses. Never wider than `ceiling`.
#[serde(rename = "defaultVisibility")]
pub default_visibility: Audience,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<FieldDescription>,
///Whether members may filter on the field and see counts by its values. Absent: false.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub facet: ::std::option::Option<bool>,
pub key: FieldKey,
pub label: FieldLabel,
///Whether a member's search may match the field, within what the searcher may see.
pub searchable: bool,
///Who writes the value. `admin` and `credential` matter most to an applicant: an `admin` field holds what the community asserts about the member, which the member reads and cannot change.
pub source: FieldSource,
#[serde(rename = "type")]
pub type_: FieldType,
}
impl DisclosedField {
pub fn builder() -> builder::DisclosedField {
Default::default()
}
}
///An ISO 8601 duration in weeks, days, hours, minutes and seconds only (e.g. `P120D`, `P2W`, `P1DT12H`, `PT15M`). Years and months are refused: their length depends on the calendar, and an age limit that means different things on different days is not a limit.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Duration(::std::string::String);
impl ::std::ops::Deref for Duration {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Duration> for ::std::string::String {
fn from(value: Duration) -> Self {
value.0
}
}
impl ::std::str::FromStr for Duration {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 32usize {
return Err("longer than 32 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Duration {
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 Duration {
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 Duration {
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 Duration {
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.
///
#[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)
}
}
/**
Names the `ext` namespaces a consumer MUST understand or refuse, per SPEC.md §4.5.1.
Every entry MUST be an immediate key of the sibling `ext` object at the same level; an entry naming an absent namespace is non-conforming and the consumer rejects the document with `malformedRequest`. A consumer that does not recognize a namespace named here MUST NOT process the document as though the namespace were absent, and rejects it with `unsupportedExtension` — the exception to the rule that unrecognized namespaces are ignored.
A producer marks a namespace only where the document's meaning depends on it. Marking one that merely carries a hint or an annotation turns every consumer that has not implemented it into a failure where it would otherwise have interoperated. JSON Schema cannot check either of those rules: that an entry names a present namespace is checkable only against the sibling `ext`, and whether a namespace is load-bearing is not a schema question at all. Both are consumer-side checks.*/
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ExtCritical(pub Vec<ExtCriticalItem>);
impl ::std::ops::Deref for ExtCritical {
type Target = Vec<ExtCriticalItem>;
fn deref(&self) -> &Vec<ExtCriticalItem> {
&self.0
}
}
impl ::std::convert::From<ExtCritical> for Vec<ExtCriticalItem> {
fn from(value: ExtCritical) -> Self {
value.0
}
}
impl ::std::convert::From<Vec<ExtCriticalItem>> for ExtCritical {
fn from(value: Vec<ExtCriticalItem>) -> Self {
Self(value)
}
}
///`ExtCriticalItem`
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtCriticalItem(::std::string::String);
impl ::std::ops::Deref for ExtCriticalItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtCriticalItem> for ::std::string::String {
fn from(value: ExtCriticalItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtCriticalItem {
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 ExtCriticalItem {
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 ExtCriticalItem {
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 ExtCriticalItem {
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 ExtCriticalItem {
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())
})
}
}
///`ExtKey`
///
#[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())
})
}
}
///Help text shown to a member filling the field in. Plain text; a reader MUST NOT treat it as instructions.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct FieldDescription(::std::string::String);
impl ::std::ops::Deref for FieldDescription {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<FieldDescription> for ::std::string::String {
fn from(value: FieldDescription) -> Self {
value.0
}
}
impl ::std::str::FromStr for FieldDescription {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 280usize {
return Err("longer than 280 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 FieldDescription {
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 FieldDescription {
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 FieldDescription {
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 FieldDescription {
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())
})
}
}
///Identifier of a directory field. A key with no `.` is a BASE field, the same in every community: `displayName`, `nickname`, `avatar`, `pronouns`, `bio`, `links` (written by the member) and `role`, `joinedAt`, `did`, `personhood` (derived from the community's own records, never stored on a card). A community-defined field MUST use a reverse-DNS key (`org.example.employer`), so a base key never collides with one, and a key is never reused with a different meaning once defined.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct FieldKey(::std::string::String);
impl ::std::ops::Deref for FieldKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<FieldKey> for ::std::string::String {
fn from(value: FieldKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for FieldKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^([a-z][a-z0-9-]*\\.)*[a-z][a-zA-Z0-9]{0,63}$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^([a-z][a-z0-9-]*\\.)*[a-z][a-zA-Z0-9]{0,63}$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for FieldKey {
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 FieldKey {
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 FieldKey {
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 FieldKey {
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())
})
}
}
///What readers see as the field's name. Plain text.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct FieldLabel(::std::string::String);
impl ::std::ops::Deref for FieldLabel {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<FieldLabel> for ::std::string::String {
fn from(value: FieldLabel) -> Self {
value.0
}
}
impl ::std::str::FromStr for FieldLabel {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 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 FieldLabel {
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 FieldLabel {
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 FieldLabel {
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 FieldLabel {
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())
})
}
}
///Who writes the value. `member`: self-asserted, through vtc/directory/card/update. `admin`: asserted by the community through vtc/directory/card/assert; the member reads it and cannot change it. `credential`: a claim the member discloses from a credential through vtc/directory/card/present, badged with its issuer. `derived`: a base field read from the community's own records (`role`, `joinedAt`, `did`, `personhood`) and never stored on a card. Each source is written only through its own path.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum FieldSource {
#[serde(rename = "member")]
Member,
#[serde(rename = "admin")]
Admin,
#[serde(rename = "credential")]
Credential,
#[serde(rename = "derived")]
Derived,
}
impl ::std::fmt::Display for FieldSource {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Member => f.write_str("member"),
Self::Admin => f.write_str("admin"),
Self::Credential => f.write_str("credential"),
Self::Derived => f.write_str("derived"),
}
}
}
impl ::std::str::FromStr for FieldSource {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"member" => Ok(Self::Member),
"admin" => Ok(Self::Admin),
"credential" => Ok(Self::Credential),
"derived" => Ok(Self::Derived),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for FieldSource {
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 FieldSource {
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 FieldSource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The kind of value a field holds, which fixes its `FieldValue` shape. `did` holds a DID the member chooses to show (never their membership DID, which is the derived base field `did`). `claim` holds a `VerifiedValue` and requires `source: credential`.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum FieldType {
#[serde(rename = "text")]
Text,
#[serde(rename = "longText")]
LongText,
#[serde(rename = "url")]
Url,
#[serde(rename = "email")]
Email,
#[serde(rename = "phone")]
Phone,
#[serde(rename = "enum")]
Enum,
#[serde(rename = "multiEnum")]
MultiEnum,
#[serde(rename = "date")]
Date,
#[serde(rename = "country")]
Country,
#[serde(rename = "image")]
Image,
#[serde(rename = "did")]
Did,
#[serde(rename = "claim")]
Claim,
}
impl ::std::fmt::Display for FieldType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Text => f.write_str("text"),
Self::LongText => f.write_str("longText"),
Self::Url => f.write_str("url"),
Self::Email => f.write_str("email"),
Self::Phone => f.write_str("phone"),
Self::Enum => f.write_str("enum"),
Self::MultiEnum => f.write_str("multiEnum"),
Self::Date => f.write_str("date"),
Self::Country => f.write_str("country"),
Self::Image => f.write_str("image"),
Self::Did => f.write_str("did"),
Self::Claim => f.write_str("claim"),
}
}
}
impl ::std::str::FromStr for FieldType {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"text" => Ok(Self::Text),
"longText" => Ok(Self::LongText),
"url" => Ok(Self::Url),
"email" => Ok(Self::Email),
"phone" => Ok(Self::Phone),
"enum" => Ok(Self::Enum),
"multiEnum" => Ok(Self::MultiEnum),
"date" => Ok(Self::Date),
"country" => Ok(Self::Country),
"image" => Ok(Self::Image),
"did" => Ok(Self::Did),
"claim" => Ok(Self::Claim),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for FieldType {
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 FieldType {
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 FieldType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Present when the community has groups. A member of a group holds a credential, issued by the group's own identifier, that evidences their membership, and can present it outside the community — which is why an applicant is told.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct GroupDisclosure {
///How long each group membership credential is valid for, as an ISO 8601 duration in weeks, days, hours, minutes and seconds (`P30D`). It is renewed while the member stays in the group, and a credential already issued stays valid until it expires even after its holder leaves.
#[serde(rename = "credentialValidity")]
pub credential_validity: GroupDisclosureCredentialValidity,
}
impl GroupDisclosure {
pub fn builder() -> builder::GroupDisclosure {
Default::default()
}
}
///How long each group membership credential is valid for, as an ISO 8601 duration in weeks, days, hours, minutes and seconds (`P30D`). It is renewed while the member stays in the group, and a credential already issued stays valid until it expires even after its holder leaves.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct GroupDisclosureCredentialValidity(::std::string::String);
impl ::std::ops::Deref for GroupDisclosureCredentialValidity {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<GroupDisclosureCredentialValidity> for ::std::string::String {
fn from(value: GroupDisclosureCredentialValidity) -> Self {
value.0
}
}
impl ::std::str::FromStr for GroupDisclosureCredentialValidity {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 32usize {
return Err("longer than 32 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for GroupDisclosureCredentialValidity {
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 GroupDisclosureCredentialValidity {
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 GroupDisclosureCredentialValidity {
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 GroupDisclosureCredentialValidity {
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())
})
}
}
///Who may ask the community to introduce them to this member. `anyone`: any active member who can find the entry. `groups`: only members who share a community group with them. `connections`: only members holding an attributed, in-force verified relationship with them. `off`: nobody. A refused introduction is never reported to its sender.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum Introductions {
#[serde(rename = "anyone")]
Anyone,
#[serde(rename = "groups")]
Groups,
#[serde(rename = "connections")]
Connections,
#[serde(rename = "off")]
Off,
}
impl ::std::fmt::Display for Introductions {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Anyone => f.write_str("anyone"),
Self::Groups => f.write_str("groups"),
Self::Connections => f.write_str("connections"),
Self::Off => f.write_str("off"),
}
}
}
impl ::std::str::FromStr for Introductions {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"anyone" => Ok(Self::Anyone),
"groups" => Ok(Self::Groups),
"connections" => Ok(Self::Connections),
"off" => Ok(Self::Off),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Introductions {
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 Introductions {
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 Introductions {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Whether members can find the entry. `listed`: it appears when members browse. `findable`: it does not appear when browsing, but a search that matches it under `search` returns it. `hidden`: no member finds it by browsing or searching; administrators still see it, and members who share a group that shows its members see it in that group's member list.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum Listing {
#[serde(rename = "listed")]
Listed,
#[serde(rename = "findable")]
Findable,
#[serde(rename = "hidden")]
Hidden,
}
impl ::std::fmt::Display for Listing {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Listed => f.write_str("listed"),
Self::Findable => f.write_str("findable"),
Self::Hidden => f.write_str("hidden"),
}
}
}
impl ::std::str::FromStr for Listing {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"listed" => Ok(Self::Listed),
"findable" => Ok(Self::Findable),
"hidden" => Ok(Self::Hidden),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Listing {
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 Listing {
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 Listing {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The discoverability a card has when the community creates it, before its member chooses. A new card is never published beyond the community (VTI-DIR-042), so there is no member for that here: `externalCards` says whether the member may turn it on.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct NewCardDefaults {
///From whom a new card accepts introductions. Absent where the community does not deliver introductions.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub introductions: ::std::option::Option<Introductions>,
///The listing a new card starts with: the community's `defaultListing` (vtc/directory/fields/list). Every member may always choose `hidden`.
pub listing: Listing,
///What a member's search may match, as the card's `discoverability.search`.
pub search: NewCardDefaultsSearch,
}
impl NewCardDefaults {
pub fn builder() -> builder::NewCardDefaults {
Default::default()
}
}
///What a member's search may match, as the card's `discoverability.search`.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum NewCardDefaultsSearch {
#[serde(rename = "byName")]
ByName,
#[serde(rename = "exactNickname")]
ExactNickname,
#[serde(rename = "off")]
Off,
}
impl ::std::fmt::Display for NewCardDefaultsSearch {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::ByName => f.write_str("byName"),
Self::ExactNickname => f.write_str("exactNickname"),
Self::Off => f.write_str("off"),
}
}
}
impl ::std::str::FromStr for NewCardDefaultsSearch {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"byName" => Ok(Self::ByName),
"exactNickname" => Ok(Self::ExactNickname),
"off" => Ok(Self::Off),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for NewCardDefaultsSearch {
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 NewCardDefaultsSearch {
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 NewCardDefaultsSearch {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Discover a community's join criteria before applying. The request carries nothing. Version 0.2 adds, per criterion, an optional `vetting` requirements object — how many identity-vetting statements a community needs, by which methods, from whom — and a `requirementsDigest` that names the exact version of the criterion an applicant started under. The response may also carry `branding`, how the community asks to be shown. Version 0.4 adds `directoryDisclosure`: where the community has a member directory, the statement of what it would disclose about a member and to whom, so an applicant reads it before applying.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
ext: Default::default(),
}
}
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///`Response`
///
#[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 branding: ::std::option::Option<CommunityBranding>,
#[serde(rename = "communityDid")]
pub community_did: ResponseCommunityDid,
pub criteria: ::std::vec::Vec<Criterion>,
///Present exactly where the community offers a member directory: the statement, generated from its configuration, of what the directory would disclose about a member and to whom — the same statement vtc/directory/disclosure returns. Outside every criterion, so no `requirementsDigest` covers it; it names itself with its own `statementDigest`.
#[serde(
rename = "directoryDisclosure",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub directory_disclosure: ::std::option::Option<DirectoryDisclosure>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///What the community asks an applicant to tell it about themselves, as claim types — never values. Answered in persona terms: the applicant's agent discloses those attributes from the face the applicant chooses, and they arrive on vtc/join-requests/submit as `attributes`. SELF-ASSERTED: a community MUST NOT describe an answer as verified, and MUST NOT make a decision that assumes it is. Outside every criterion, so no `requirementsDigest` covers it. Absent when the community asks nothing.
#[serde(
rename = "requestedAttributes",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub requested_attributes: ::std::vec::Vec<ResponseRequestedAttributesItem>,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///`ResponseCommunityDid`
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseCommunityDid(::std::string::String);
impl ::std::ops::Deref for ResponseCommunityDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseCommunityDid> for ::std::string::String {
fn from(value: ResponseCommunityDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseCommunityDid {
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("^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 ResponseCommunityDid {
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 ResponseCommunityDid {
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 ResponseCommunityDid {
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 ResponseCommunityDid {
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())
})
}
}
///`ResponseRequestedAttributesItem`
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ResponseRequestedAttributesItem {
///Why the community asks, in words shown to the applicant before they disclose.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub purpose: ::std::option::Option<ResponseRequestedAttributesItemPurpose>,
///False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.
#[serde(default = "defaults::default_bool::<true>")]
pub required: bool,
///A claim-type token from the persona claim-type registry (persona/_shared/0.1/CLAIM-TYPES.md) — `name.display`, `address.country` — or an `x:` extension token.
#[serde(rename = "type")]
pub type_: ResponseRequestedAttributesItemType,
}
impl ResponseRequestedAttributesItem {
pub fn builder() -> builder::ResponseRequestedAttributesItem {
Default::default()
}
}
///Why the community asks, in words shown to the applicant before they disclose.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseRequestedAttributesItemPurpose(::std::string::String);
impl ::std::ops::Deref for ResponseRequestedAttributesItemPurpose {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseRequestedAttributesItemPurpose> for ::std::string::String {
fn from(value: ResponseRequestedAttributesItemPurpose) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseRequestedAttributesItemPurpose {
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 ResponseRequestedAttributesItemPurpose {
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 ResponseRequestedAttributesItemPurpose {
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 ResponseRequestedAttributesItemPurpose {
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 ResponseRequestedAttributesItemPurpose {
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 claim-type token from the persona claim-type registry (persona/_shared/0.1/CLAIM-TYPES.md) — `name.display`, `address.country` — or an `x:` extension token.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseRequestedAttributesItemType(::std::string::String);
impl ::std::ops::Deref for ResponseRequestedAttributesItemType {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseRequestedAttributesItemType> for ::std::string::String {
fn from(value: ResponseRequestedAttributesItemType) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseRequestedAttributesItemType {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(x:)?[a-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*)*$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(x:)?[a-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*)*$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseRequestedAttributesItemType {
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 ResponseRequestedAttributesItemType {
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 ResponseRequestedAttributesItemType {
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 ResponseRequestedAttributesItemType {
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 class of documentation, named in lowerCamelCase. Open rather than enumerated, because what documentation a vetter accepts is each vetter's own choice. Well-known values: `passport`, `nationalId`, `driverLicence`, and `none` — the vetter will attest without a document, which is the `priorAcquaintance` case. Only the class ever travels — never a document number, an image, an issuing authority or an expiry date. `none` states a policy (what a vetter accepts); a record of what was relied on expresses 'no document' as an empty list instead.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VettingDocumentation(::std::string::String);
impl ::std::ops::Deref for VettingDocumentation {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VettingDocumentation> for ::std::string::String {
fn from(value: VettingDocumentation) -> Self {
value.0
}
}
impl ::std::str::FromStr for VettingDocumentation {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[a-z][a-zA-Z0-9]*$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-zA-Z0-9]*$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VettingDocumentation {
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 VettingDocumentation {
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 VettingDocumentation {
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 VettingDocumentation {
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())
})
}
}
///How the vetter established that the person they checked is the person controlling the applicant's DID. `inPerson` — both people were physically together. `video` — a live, two-way video call. `priorAcquaintance` — the vetter has known or worked with this person over a period, and attests from that knowledge rather than from a document. A method is a description of what happened, not an assurance level: which methods count, and how many of each, is community policy.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum VettingMethod {
#[serde(rename = "inPerson")]
InPerson,
#[serde(rename = "video")]
Video,
#[serde(rename = "priorAcquaintance")]
PriorAcquaintance,
}
impl ::std::fmt::Display for VettingMethod {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::InPerson => f.write_str("inPerson"),
Self::Video => f.write_str("video"),
Self::PriorAcquaintance => f.write_str("priorAcquaintance"),
}
}
}
impl ::std::str::FromStr for VettingMethod {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"inPerson" => Ok(Self::InPerson),
"video" => Ok(Self::Video),
"priorAcquaintance" => Ok(Self::PriorAcquaintance),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for VettingMethod {
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 VettingMethod {
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 VettingMethod {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The vetter's own declaration of how they relate to the applicant. Declared, not verified: it exists so community policy can cap how much evidence comes from people close to the applicant, and a false declaration is the vetter's attributable act.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum VettingRelationship {
#[serde(rename = "none")]
None,
#[serde(rename = "communityColleague")]
CommunityColleague,
#[serde(rename = "sameEmployer")]
SameEmployer,
#[serde(rename = "family")]
Family,
#[serde(rename = "otherPersonal")]
OtherPersonal,
}
impl ::std::fmt::Display for VettingRelationship {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::None => f.write_str("none"),
Self::CommunityColleague => f.write_str("communityColleague"),
Self::SameEmployer => f.write_str("sameEmployer"),
Self::Family => f.write_str("family"),
Self::OtherPersonal => f.write_str("otherPersonal"),
}
}
}
impl ::std::str::FromStr for VettingRelationship {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"none" => Ok(Self::None),
"communityColleague" => Ok(Self::CommunityColleague),
"sameEmployer" => Ok(Self::SameEmployer),
"family" => Ok(Self::Family),
"otherPersonal" => Ok(Self::OtherPersonal),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for VettingRelationship {
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 VettingRelationship {
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 VettingRelationship {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///What identity-vetting evidence a criterion needs, beyond what a presentation-definition can express: distinct eligible vetters, per-method floors, independence caps. Every number is the community's own policy. This schema supplies no defaults — an absent optional member means the community imposes no constraint of that kind, never that some protocol value applies. Deliberately open: a consumer MUST ignore members it does not recognise, so a community publishing a newer shape does not make an older client unable to read the rest. Durations: `maxStatementAge` — a statement older than this at decision time does not count (absent: no limit beyond the statement's own validity); `decisionSla` — how long after submission the community undertakes to decide, including on a referred application; `requirementsGrace` — how long an application started under an earlier `requirementsDigest` is still evaluated under that version.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[non_exhaustive]
pub struct VettingRequirements {
///Documentation a statement must have relied on in order to count. Absent — the expected case — means each vetter decides what documentation they accept, including none for prior acquaintance, and the community counts what they attest.
#[serde(
rename = "acceptedDocumentClasses",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub accepted_document_classes: ::std::option::Option<Vec<VettingDocumentation>>,
///Methods whose statements count at all.
#[serde(rename = "acceptedMethods")]
pub accepted_methods: Vec<VettingMethod>,
#[serde(
rename = "decisionSla",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub decision_sla: ::std::option::Option<Duration>,
#[serde(rename = "eligibleVetters")]
pub eligible_vetters: VettingRequirementsEligibleVetters,
/**
Ecosystem-defined extension members per SPEC.md §4.5.1, for vetting parameters this version does not enumerate. A community publishing an admission mode the base members cannot express — the zero-knowledge parameters an applicant proves against, say — puts them under a namespace it controls, rather than waiting for a version that names them.
This object is already `additionalProperties: true`, so an unknown member here has always validated. That is not the same as reaching a consumer: a generated type names the members this schema declares and drops the rest, so an undeclared member is parsed and discarded, and it is discarded again on the re-serialization `requirementsDigest` is computed over. `ext` is a declared member, so it survives both.*/
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
/**
Namespaces of `ext` an applicant MUST understand or refuse, per SPEC.md §4.5.1.
A community marks a namespace here when applying under this criterion without understanding it would mean something other than what the community requires — an applicant that ignored a hidden-vetting namespace, for instance, would submit named statements to a criterion whose whole point was that it does not receive them. An applicant that does not recognize a marked namespace stops rather than downgrading silently.
Absent, which is the common case, every namespace under `ext` is advisory and ignorable.*/
#[serde(
rename = "extCritical",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub ext_critical: ::std::option::Option<ExtCritical>,
///Where the community's vetting governance — including the attestation text vetters sign — is published.
#[serde(
rename = "governanceFrameworkUrl",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub governance_framework_url: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub independence: ::std::option::Option<VettingRequirementsIndependence>,
///Whether an invitation credential must accompany the statements at submission (`required`), may (`optional`), or plays no part (`none`). Absent: the presentation-definition alone governs.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub invitation: ::std::option::Option<VettingRequirementsInvitation>,
#[serde(
rename = "maxStatementAge",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub max_statement_age: ::std::option::Option<Duration>,
///Per-method floors within `minStatements` — e.g. `{ "inPerson": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.
#[serde(
rename = "minByMethod",
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub min_by_method: ::std::collections::HashMap<VettingMethod, u64>,
///How many counted statements are needed, counting each vetter once however many DIDs they hold.
#[serde(rename = "minStatements")]
pub min_statements: ::std::num::NonZeroU64,
///Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.
#[serde(
rename = "optionalClaims",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub optional_claims: ::std::option::Option<Vec<ClaimType>>,
///Claim types the applicant's Vetting Card must carry, which the identity commitment is computed over, and which a counted statement must list as verified. Absent: none.
#[serde(
rename = "requiredClaims",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub required_claims: ::std::option::Option<Vec<ClaimType>>,
#[serde(
rename = "requirementsGrace",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub requirements_grace: ::std::option::Option<Duration>,
///The predicate IRI a counted vetting statement carries in `credentialSubject.predicate` — `https://registry.trustoverip.org/dtg/vsc/vetted/1`, the DTG VSC registry's identity-vetting predicate, or a predicate in a namespace the community controls — registered as one the community accepts via vtc/endorsement-types/register. A statement under any other predicate does not count.
#[serde(rename = "statementType")]
pub statement_type: ::std::string::String,
///Version of this requirements object's shape. `0.1` for the members defined here.
pub version: VettingRequirementsVersion,
}
impl VettingRequirements {
pub fn builder() -> builder::VettingRequirements {
Default::default()
}
}
///How a vetter's eligibility is established.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct VettingRequirementsEligibleVetters {
///The role a statement's issuer must hold, matched as the action `role:<role>` in a Verifiable Authority Credential (`AuthorityCredential`) the community issued to that issuer, whose `authority.scope` is the community's DID (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds such a credential.
pub role: VettingRequirementsEligibleVettersRole,
}
impl VettingRequirementsEligibleVetters {
pub fn builder() -> builder::VettingRequirementsEligibleVetters {
Default::default()
}
}
///The role a statement's issuer must hold, matched as the action `role:<role>` in a Verifiable Authority Credential (`AuthorityCredential`) the community issued to that issuer, whose `authority.scope` is the community's DID (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds such a credential.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VettingRequirementsEligibleVettersRole(::std::string::String);
impl ::std::ops::Deref for VettingRequirementsEligibleVettersRole {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VettingRequirementsEligibleVettersRole> for ::std::string::String {
fn from(value: VettingRequirementsEligibleVettersRole) -> Self {
value.0
}
}
impl ::std::str::FromStr for VettingRequirementsEligibleVettersRole {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-zA-Z][a-zA-Z0-9_-]*$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-zA-Z][a-zA-Z0-9_-]*$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VettingRequirementsEligibleVettersRole {
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 VettingRequirementsEligibleVettersRole {
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 VettingRequirementsEligibleVettersRole {
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 VettingRequirementsEligibleVettersRole {
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())
})
}
}
///Caps on how much evidence may come from people close to the applicant. Absent: no caps.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct VettingRequirementsIndependence {
///The most counted statements that may come from vetters declaring each relationship — e.g. `{ "family": 0 }`.
#[serde(
rename = "maxByDeclaredRelationship",
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub max_by_declared_relationship: ::std::collections::HashMap<VettingRelationship, u64>,
///When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.
#[serde(
rename = "requireConsistentIdentityCommitment",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub require_consistent_identity_commitment: ::std::option::Option<bool>,
}
impl ::std::default::Default for VettingRequirementsIndependence {
fn default() -> Self {
Self {
max_by_declared_relationship: Default::default(),
require_consistent_identity_commitment: Default::default(),
}
}
}
impl VettingRequirementsIndependence {
pub fn builder() -> builder::VettingRequirementsIndependence {
Default::default()
}
}
///Whether an invitation credential must accompany the statements at submission (`required`), may (`optional`), or plays no part (`none`). Absent: the presentation-definition alone governs.
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum VettingRequirementsInvitation {
#[serde(rename = "required")]
Required,
#[serde(rename = "optional")]
Optional,
#[serde(rename = "none")]
None,
}
impl ::std::fmt::Display for VettingRequirementsInvitation {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Required => f.write_str("required"),
Self::Optional => f.write_str("optional"),
Self::None => f.write_str("none"),
}
}
}
impl ::std::str::FromStr for VettingRequirementsInvitation {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"required" => Ok(Self::Required),
"optional" => Ok(Self::Optional),
"none" => Ok(Self::None),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for VettingRequirementsInvitation {
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 VettingRequirementsInvitation {
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 VettingRequirementsInvitation {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Version of this requirements object's shape. `0.1` for the members defined here.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VettingRequirementsVersion(::std::string::String);
impl ::std::ops::Deref for VettingRequirementsVersion {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VettingRequirementsVersion> for ::std::string::String {
fn from(value: VettingRequirementsVersion) -> Self {
value.0
}
}
impl ::std::str::FromStr for VettingRequirementsVersion {
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("^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VettingRequirementsVersion {
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 VettingRequirementsVersion {
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 VettingRequirementsVersion {
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 VettingRequirementsVersion {
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())
})
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct CommunityBranding {
accent_color: ::std::result::Result<
::std::option::Option<super::CommunityBrandingAccentColor>,
::std::string::String,
>,
display_name: ::std::result::Result<
::std::option::Option<super::CommunityBrandingDisplayName>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
logo_url: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
}
impl ::std::default::Default for CommunityBranding {
fn default() -> Self {
Self {
accent_color: Ok(Default::default()),
display_name: Ok(Default::default()),
ext: Ok(Default::default()),
logo_url: Ok(Default::default()),
}
}
}
impl CommunityBranding {
pub fn accent_color<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CommunityBrandingAccentColor>>,
T::Error: ::std::fmt::Display,
{
self.accent_color = value
.try_into()
.map_err(|e| format!("error converting supplied value for accent_color: {e}"));
self
}
pub fn display_name<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CommunityBrandingDisplayName>>,
T::Error: ::std::fmt::Display,
{
self.display_name = value
.try_into()
.map_err(|e| format!("error converting supplied value for display_name: {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 logo_url<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.logo_url = value
.try_into()
.map_err(|e| format!("error converting supplied value for logo_url: {e}"));
self
}
}
impl ::std::convert::TryFrom<CommunityBranding> for super::CommunityBranding {
type Error = super::error::ConversionError;
fn try_from(
value: CommunityBranding,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
accent_color: value.accent_color?,
display_name: value.display_name?,
ext: value.ext?,
logo_url: value.logo_url?,
})
}
}
impl ::std::convert::From<super::CommunityBranding> for CommunityBranding {
fn from(value: super::CommunityBranding) -> Self {
Self {
accent_color: Ok(value.accent_color),
display_name: Ok(value.display_name),
ext: Ok(value.ext),
logo_url: Ok(value.logo_url),
}
}
}
#[derive(Clone, Debug)]
pub struct Criterion {
admission: ::std::result::Result<super::CriterionAdmission, ::std::string::String>,
credential_issuers: ::std::result::Result<
::std::option::Option<super::CriterionCredentialIssuers>,
::std::string::String,
>,
description: ::std::result::Result<
::std::option::Option<super::CriterionDescription>,
::std::string::String,
>,
id: ::std::result::Result<super::CriterionId, ::std::string::String>,
invitation_required:
::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
presentation_definition: ::std::result::Result<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
::std::string::String,
>,
requirements_digest: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
vetting: ::std::result::Result<
::std::option::Option<super::VettingRequirements>,
::std::string::String,
>,
}
impl ::std::default::Default for Criterion {
fn default() -> Self {
Self {
admission: Err("no value supplied for admission".to_string()),
credential_issuers: Ok(Default::default()),
description: Ok(Default::default()),
id: Err("no value supplied for id".to_string()),
invitation_required: Ok(Default::default()),
presentation_definition: Ok(Default::default()),
requirements_digest: Err("no value supplied for requirements_digest".to_string()),
vetting: Ok(Default::default()),
}
}
}
impl Criterion {
pub fn admission<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::CriterionAdmission>,
T::Error: ::std::fmt::Display,
{
self.admission = value
.try_into()
.map_err(|e| format!("error converting supplied value for admission: {e}"));
self
}
pub fn credential_issuers<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CriterionCredentialIssuers>>,
T::Error: ::std::fmt::Display,
{
self.credential_issuers = value.try_into().map_err(|e| {
format!("error converting supplied value for credential_issuers: {e}")
});
self
}
pub fn description<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CriterionDescription>>,
T::Error: ::std::fmt::Display,
{
self.description = value
.try_into()
.map_err(|e| format!("error converting supplied value for description: {e}"));
self
}
pub fn id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::CriterionId>,
T::Error: ::std::fmt::Display,
{
self.id = value
.try_into()
.map_err(|e| format!("error converting supplied value for id: {e}"));
self
}
pub fn invitation_required<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.invitation_required = value.try_into().map_err(|e| {
format!("error converting supplied value for invitation_required: {e}")
});
self
}
pub fn presentation_definition<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.presentation_definition = value.try_into().map_err(|e| {
format!("error converting supplied value for presentation_definition: {e}")
});
self
}
pub fn requirements_digest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.requirements_digest = value.try_into().map_err(|e| {
format!("error converting supplied value for requirements_digest: {e}")
});
self
}
pub fn vetting<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::VettingRequirements>>,
T::Error: ::std::fmt::Display,
{
self.vetting = value
.try_into()
.map_err(|e| format!("error converting supplied value for vetting: {e}"));
self
}
}
impl ::std::convert::TryFrom<Criterion> for super::Criterion {
type Error = super::error::ConversionError;
fn try_from(
value: Criterion,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
admission: value.admission?,
credential_issuers: value.credential_issuers?,
description: value.description?,
id: value.id?,
invitation_required: value.invitation_required?,
presentation_definition: value.presentation_definition?,
requirements_digest: value.requirements_digest?,
vetting: value.vetting?,
})
}
}
impl ::std::convert::From<super::Criterion> for Criterion {
fn from(value: super::Criterion) -> Self {
Self {
admission: Ok(value.admission),
credential_issuers: Ok(value.credential_issuers),
description: Ok(value.description),
id: Ok(value.id),
invitation_required: Ok(value.invitation_required),
presentation_definition: Ok(value.presentation_definition),
requirements_digest: Ok(value.requirements_digest),
vetting: Ok(value.vetting),
}
}
}
#[derive(Clone, Debug)]
pub struct DirectoryDisclosure {
admin_notes: ::std::result::Result<bool, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
external_cards: ::std::result::Result<bool, ::std::string::String>,
fields:
::std::result::Result<::std::vec::Vec<super::DisclosedField>, ::std::string::String>,
groups: ::std::result::Result<
::std::option::Option<super::GroupDisclosure>,
::std::string::String,
>,
membership_identifiers: ::std::result::Result<super::Audience, ::std::string::String>,
new_card: ::std::result::Result<super::NewCardDefaults, ::std::string::String>,
statement_digest: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
}
impl ::std::default::Default for DirectoryDisclosure {
fn default() -> Self {
Self {
admin_notes: Err("no value supplied for admin_notes".to_string()),
ext: Ok(Default::default()),
external_cards: Err("no value supplied for external_cards".to_string()),
fields: Err("no value supplied for fields".to_string()),
groups: Ok(Default::default()),
membership_identifiers: Err(
"no value supplied for membership_identifiers".to_string()
),
new_card: Err("no value supplied for new_card".to_string()),
statement_digest: Err("no value supplied for statement_digest".to_string()),
}
}
}
impl DirectoryDisclosure {
pub fn admin_notes<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.admin_notes = value
.try_into()
.map_err(|e| format!("error converting supplied value for admin_notes: {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 external_cards<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.external_cards = value
.try_into()
.map_err(|e| format!("error converting supplied value for external_cards: {e}"));
self
}
pub fn fields<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::DisclosedField>>,
T::Error: ::std::fmt::Display,
{
self.fields = value
.try_into()
.map_err(|e| format!("error converting supplied value for fields: {e}"));
self
}
pub fn groups<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::GroupDisclosure>>,
T::Error: ::std::fmt::Display,
{
self.groups = value
.try_into()
.map_err(|e| format!("error converting supplied value for groups: {e}"));
self
}
pub fn membership_identifiers<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Audience>,
T::Error: ::std::fmt::Display,
{
self.membership_identifiers = value.try_into().map_err(|e| {
format!("error converting supplied value for membership_identifiers: {e}")
});
self
}
pub fn new_card<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::NewCardDefaults>,
T::Error: ::std::fmt::Display,
{
self.new_card = value
.try_into()
.map_err(|e| format!("error converting supplied value for new_card: {e}"));
self
}
pub fn statement_digest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.statement_digest = value
.try_into()
.map_err(|e| format!("error converting supplied value for statement_digest: {e}"));
self
}
}
impl ::std::convert::TryFrom<DirectoryDisclosure> for super::DirectoryDisclosure {
type Error = super::error::ConversionError;
fn try_from(
value: DirectoryDisclosure,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
admin_notes: value.admin_notes?,
ext: value.ext?,
external_cards: value.external_cards?,
fields: value.fields?,
groups: value.groups?,
membership_identifiers: value.membership_identifiers?,
new_card: value.new_card?,
statement_digest: value.statement_digest?,
})
}
}
impl ::std::convert::From<super::DirectoryDisclosure> for DirectoryDisclosure {
fn from(value: super::DirectoryDisclosure) -> Self {
Self {
admin_notes: Ok(value.admin_notes),
ext: Ok(value.ext),
external_cards: Ok(value.external_cards),
fields: Ok(value.fields),
groups: Ok(value.groups),
membership_identifiers: Ok(value.membership_identifiers),
new_card: Ok(value.new_card),
statement_digest: Ok(value.statement_digest),
}
}
}
#[derive(Clone, Debug)]
pub struct DisclosedField {
ceiling: ::std::result::Result<super::Audience, ::std::string::String>,
default_visibility: ::std::result::Result<super::Audience, ::std::string::String>,
description: ::std::result::Result<
::std::option::Option<super::FieldDescription>,
::std::string::String,
>,
facet: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
key: ::std::result::Result<super::FieldKey, ::std::string::String>,
label: ::std::result::Result<super::FieldLabel, ::std::string::String>,
searchable: ::std::result::Result<bool, ::std::string::String>,
source: ::std::result::Result<super::FieldSource, ::std::string::String>,
type_: ::std::result::Result<super::FieldType, ::std::string::String>,
}
impl ::std::default::Default for DisclosedField {
fn default() -> Self {
Self {
ceiling: Err("no value supplied for ceiling".to_string()),
default_visibility: Err("no value supplied for default_visibility".to_string()),
description: Ok(Default::default()),
facet: Ok(Default::default()),
key: Err("no value supplied for key".to_string()),
label: Err("no value supplied for label".to_string()),
searchable: Err("no value supplied for searchable".to_string()),
source: Err("no value supplied for source".to_string()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl DisclosedField {
pub fn ceiling<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Audience>,
T::Error: ::std::fmt::Display,
{
self.ceiling = value
.try_into()
.map_err(|e| format!("error converting supplied value for ceiling: {e}"));
self
}
pub fn default_visibility<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Audience>,
T::Error: ::std::fmt::Display,
{
self.default_visibility = value.try_into().map_err(|e| {
format!("error converting supplied value for default_visibility: {e}")
});
self
}
pub fn description<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::FieldDescription>>,
T::Error: ::std::fmt::Display,
{
self.description = value
.try_into()
.map_err(|e| format!("error converting supplied value for description: {e}"));
self
}
pub fn facet<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.facet = value
.try_into()
.map_err(|e| format!("error converting supplied value for facet: {e}"));
self
}
pub fn key<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::FieldKey>,
T::Error: ::std::fmt::Display,
{
self.key = value
.try_into()
.map_err(|e| format!("error converting supplied value for key: {e}"));
self
}
pub fn label<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::FieldLabel>,
T::Error: ::std::fmt::Display,
{
self.label = value
.try_into()
.map_err(|e| format!("error converting supplied value for label: {e}"));
self
}
pub fn searchable<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.searchable = value
.try_into()
.map_err(|e| format!("error converting supplied value for searchable: {e}"));
self
}
pub fn source<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::FieldSource>,
T::Error: ::std::fmt::Display,
{
self.source = value
.try_into()
.map_err(|e| format!("error converting supplied value for source: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::FieldType>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<DisclosedField> for super::DisclosedField {
type Error = super::error::ConversionError;
fn try_from(
value: DisclosedField,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ceiling: value.ceiling?,
default_visibility: value.default_visibility?,
description: value.description?,
facet: value.facet?,
key: value.key?,
label: value.label?,
searchable: value.searchable?,
source: value.source?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::DisclosedField> for DisclosedField {
fn from(value: super::DisclosedField) -> Self {
Self {
ceiling: Ok(value.ceiling),
default_visibility: Ok(value.default_visibility),
description: Ok(value.description),
facet: Ok(value.facet),
key: Ok(value.key),
label: Ok(value.label),
searchable: Ok(value.searchable),
source: Ok(value.source),
type_: Ok(value.type_),
}
}
}
#[derive(Clone, Debug)]
pub struct GroupDisclosure {
credential_validity:
::std::result::Result<super::GroupDisclosureCredentialValidity, ::std::string::String>,
}
impl ::std::default::Default for GroupDisclosure {
fn default() -> Self {
Self {
credential_validity: Err("no value supplied for credential_validity".to_string()),
}
}
}
impl GroupDisclosure {
pub fn credential_validity<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::GroupDisclosureCredentialValidity>,
T::Error: ::std::fmt::Display,
{
self.credential_validity = value.try_into().map_err(|e| {
format!("error converting supplied value for credential_validity: {e}")
});
self
}
}
impl ::std::convert::TryFrom<GroupDisclosure> for super::GroupDisclosure {
type Error = super::error::ConversionError;
fn try_from(
value: GroupDisclosure,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
credential_validity: value.credential_validity?,
})
}
}
impl ::std::convert::From<super::GroupDisclosure> for GroupDisclosure {
fn from(value: super::GroupDisclosure) -> Self {
Self {
credential_validity: Ok(value.credential_validity),
}
}
}
#[derive(Clone, Debug)]
pub struct NewCardDefaults {
introductions: ::std::result::Result<
::std::option::Option<super::Introductions>,
::std::string::String,
>,
listing: ::std::result::Result<super::Listing, ::std::string::String>,
search: ::std::result::Result<super::NewCardDefaultsSearch, ::std::string::String>,
}
impl ::std::default::Default for NewCardDefaults {
fn default() -> Self {
Self {
introductions: Ok(Default::default()),
listing: Err("no value supplied for listing".to_string()),
search: Err("no value supplied for search".to_string()),
}
}
}
impl NewCardDefaults {
pub fn introductions<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Introductions>>,
T::Error: ::std::fmt::Display,
{
self.introductions = value
.try_into()
.map_err(|e| format!("error converting supplied value for introductions: {e}"));
self
}
pub fn listing<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Listing>,
T::Error: ::std::fmt::Display,
{
self.listing = value
.try_into()
.map_err(|e| format!("error converting supplied value for listing: {e}"));
self
}
pub fn search<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::NewCardDefaultsSearch>,
T::Error: ::std::fmt::Display,
{
self.search = value
.try_into()
.map_err(|e| format!("error converting supplied value for search: {e}"));
self
}
}
impl ::std::convert::TryFrom<NewCardDefaults> for super::NewCardDefaults {
type Error = super::error::ConversionError;
fn try_from(
value: NewCardDefaults,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
introductions: value.introductions?,
listing: value.listing?,
search: value.search?,
})
}
}
impl ::std::convert::From<super::NewCardDefaults> for NewCardDefaults {
fn from(value: super::NewCardDefaults) -> Self {
Self {
introductions: Ok(value.introductions),
listing: Ok(value.listing),
search: Ok(value.search),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
ext: 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
}
}
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? })
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self { ext: Ok(value.ext) }
}
}
#[derive(Clone, Debug)]
pub struct Response {
branding: ::std::result::Result<
::std::option::Option<super::CommunityBranding>,
::std::string::String,
>,
community_did: ::std::result::Result<super::ResponseCommunityDid, ::std::string::String>,
criteria: ::std::result::Result<::std::vec::Vec<super::Criterion>, ::std::string::String>,
directory_disclosure: ::std::result::Result<
::std::option::Option<super::DirectoryDisclosure>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
requested_attributes: ::std::result::Result<
::std::vec::Vec<super::ResponseRequestedAttributesItem>,
::std::string::String,
>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
branding: Ok(Default::default()),
community_did: Err("no value supplied for community_did".to_string()),
criteria: Err("no value supplied for criteria".to_string()),
directory_disclosure: Ok(Default::default()),
ext: Ok(Default::default()),
requested_attributes: Ok(Default::default()),
}
}
}
impl Response {
pub fn branding<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CommunityBranding>>,
T::Error: ::std::fmt::Display,
{
self.branding = value
.try_into()
.map_err(|e| format!("error converting supplied value for branding: {e}"));
self
}
pub fn community_did<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseCommunityDid>,
T::Error: ::std::fmt::Display,
{
self.community_did = value
.try_into()
.map_err(|e| format!("error converting supplied value for community_did: {e}"));
self
}
pub fn criteria<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::Criterion>>,
T::Error: ::std::fmt::Display,
{
self.criteria = value
.try_into()
.map_err(|e| format!("error converting supplied value for criteria: {e}"));
self
}
pub fn directory_disclosure<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::DirectoryDisclosure>>,
T::Error: ::std::fmt::Display,
{
self.directory_disclosure = value.try_into().map_err(|e| {
format!("error converting supplied value for directory_disclosure: {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 requested_attributes<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::ResponseRequestedAttributesItem>>,
T::Error: ::std::fmt::Display,
{
self.requested_attributes = value.try_into().map_err(|e| {
format!("error converting supplied value for requested_attributes: {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 {
branding: value.branding?,
community_did: value.community_did?,
criteria: value.criteria?,
directory_disclosure: value.directory_disclosure?,
ext: value.ext?,
requested_attributes: value.requested_attributes?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
branding: Ok(value.branding),
community_did: Ok(value.community_did),
criteria: Ok(value.criteria),
directory_disclosure: Ok(value.directory_disclosure),
ext: Ok(value.ext),
requested_attributes: Ok(value.requested_attributes),
}
}
}
#[derive(Clone, Debug)]
pub struct ResponseRequestedAttributesItem {
purpose: ::std::result::Result<
::std::option::Option<super::ResponseRequestedAttributesItemPurpose>,
::std::string::String,
>,
required: ::std::result::Result<bool, ::std::string::String>,
type_: ::std::result::Result<
super::ResponseRequestedAttributesItemType,
::std::string::String,
>,
}
impl ::std::default::Default for ResponseRequestedAttributesItem {
fn default() -> Self {
Self {
purpose: Ok(Default::default()),
required: Ok(super::defaults::default_bool::<true>()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl ResponseRequestedAttributesItem {
pub fn purpose<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<super::ResponseRequestedAttributesItemPurpose>,
>,
T::Error: ::std::fmt::Display,
{
self.purpose = value
.try_into()
.map_err(|e| format!("error converting supplied value for purpose: {e}"));
self
}
pub fn required<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.required = value
.try_into()
.map_err(|e| format!("error converting supplied value for required: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseRequestedAttributesItemType>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<ResponseRequestedAttributesItem>
for super::ResponseRequestedAttributesItem
{
type Error = super::error::ConversionError;
fn try_from(
value: ResponseRequestedAttributesItem,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
purpose: value.purpose?,
required: value.required?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::ResponseRequestedAttributesItem>
for ResponseRequestedAttributesItem
{
fn from(value: super::ResponseRequestedAttributesItem) -> Self {
Self {
purpose: Ok(value.purpose),
required: Ok(value.required),
type_: Ok(value.type_),
}
}
}
#[derive(Clone, Debug)]
pub struct VettingRequirements {
accepted_document_classes: ::std::result::Result<
::std::option::Option<Vec<super::VettingDocumentation>>,
::std::string::String,
>,
accepted_methods: ::std::result::Result<Vec<super::VettingMethod>, ::std::string::String>,
decision_sla:
::std::result::Result<::std::option::Option<super::Duration>, ::std::string::String>,
eligible_vetters:
::std::result::Result<super::VettingRequirementsEligibleVetters, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
ext_critical:
::std::result::Result<::std::option::Option<super::ExtCritical>, ::std::string::String>,
governance_framework_url: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
independence: ::std::result::Result<
::std::option::Option<super::VettingRequirementsIndependence>,
::std::string::String,
>,
invitation: ::std::result::Result<
::std::option::Option<super::VettingRequirementsInvitation>,
::std::string::String,
>,
max_statement_age:
::std::result::Result<::std::option::Option<super::Duration>, ::std::string::String>,
min_by_method: ::std::result::Result<
::std::collections::HashMap<super::VettingMethod, u64>,
::std::string::String,
>,
min_statements: ::std::result::Result<::std::num::NonZeroU64, ::std::string::String>,
optional_claims: ::std::result::Result<
::std::option::Option<Vec<super::ClaimType>>,
::std::string::String,
>,
required_claims: ::std::result::Result<
::std::option::Option<Vec<super::ClaimType>>,
::std::string::String,
>,
requirements_grace:
::std::result::Result<::std::option::Option<super::Duration>, ::std::string::String>,
statement_type: ::std::result::Result<::std::string::String, ::std::string::String>,
version: ::std::result::Result<super::VettingRequirementsVersion, ::std::string::String>,
}
impl ::std::default::Default for VettingRequirements {
fn default() -> Self {
Self {
accepted_document_classes: Ok(Default::default()),
accepted_methods: Err("no value supplied for accepted_methods".to_string()),
decision_sla: Ok(Default::default()),
eligible_vetters: Err("no value supplied for eligible_vetters".to_string()),
ext: Ok(Default::default()),
ext_critical: Ok(Default::default()),
governance_framework_url: Ok(Default::default()),
independence: Ok(Default::default()),
invitation: Ok(Default::default()),
max_statement_age: Ok(Default::default()),
min_by_method: Ok(Default::default()),
min_statements: Err("no value supplied for min_statements".to_string()),
optional_claims: Ok(Default::default()),
required_claims: Ok(Default::default()),
requirements_grace: Ok(Default::default()),
statement_type: Err("no value supplied for statement_type".to_string()),
version: Err("no value supplied for version".to_string()),
}
}
}
impl VettingRequirements {
pub fn accepted_document_classes<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::VettingDocumentation>>>,
T::Error: ::std::fmt::Display,
{
self.accepted_document_classes = value.try_into().map_err(|e| {
format!("error converting supplied value for accepted_document_classes: {e}")
});
self
}
pub fn accepted_methods<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<Vec<super::VettingMethod>>,
T::Error: ::std::fmt::Display,
{
self.accepted_methods = value
.try_into()
.map_err(|e| format!("error converting supplied value for accepted_methods: {e}"));
self
}
pub fn decision_sla<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Duration>>,
T::Error: ::std::fmt::Display,
{
self.decision_sla = value
.try_into()
.map_err(|e| format!("error converting supplied value for decision_sla: {e}"));
self
}
pub fn eligible_vetters<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::VettingRequirementsEligibleVetters>,
T::Error: ::std::fmt::Display,
{
self.eligible_vetters = value
.try_into()
.map_err(|e| format!("error converting supplied value for eligible_vetters: {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 ext_critical<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ExtCritical>>,
T::Error: ::std::fmt::Display,
{
self.ext_critical = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext_critical: {e}"));
self
}
pub fn governance_framework_url<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.governance_framework_url = value.try_into().map_err(|e| {
format!("error converting supplied value for governance_framework_url: {e}")
});
self
}
pub fn independence<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<super::VettingRequirementsIndependence>,
>,
T::Error: ::std::fmt::Display,
{
self.independence = value
.try_into()
.map_err(|e| format!("error converting supplied value for independence: {e}"));
self
}
pub fn invitation<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::VettingRequirementsInvitation>>,
T::Error: ::std::fmt::Display,
{
self.invitation = value
.try_into()
.map_err(|e| format!("error converting supplied value for invitation: {e}"));
self
}
pub fn max_statement_age<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Duration>>,
T::Error: ::std::fmt::Display,
{
self.max_statement_age = value
.try_into()
.map_err(|e| format!("error converting supplied value for max_statement_age: {e}"));
self
}
pub fn min_by_method<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::collections::HashMap<super::VettingMethod, u64>>,
T::Error: ::std::fmt::Display,
{
self.min_by_method = value
.try_into()
.map_err(|e| format!("error converting supplied value for min_by_method: {e}"));
self
}
pub fn min_statements<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::num::NonZeroU64>,
T::Error: ::std::fmt::Display,
{
self.min_statements = value
.try_into()
.map_err(|e| format!("error converting supplied value for min_statements: {e}"));
self
}
pub fn optional_claims<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::ClaimType>>>,
T::Error: ::std::fmt::Display,
{
self.optional_claims = value
.try_into()
.map_err(|e| format!("error converting supplied value for optional_claims: {e}"));
self
}
pub fn required_claims<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::ClaimType>>>,
T::Error: ::std::fmt::Display,
{
self.required_claims = value
.try_into()
.map_err(|e| format!("error converting supplied value for required_claims: {e}"));
self
}
pub fn requirements_grace<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Duration>>,
T::Error: ::std::fmt::Display,
{
self.requirements_grace = value.try_into().map_err(|e| {
format!("error converting supplied value for requirements_grace: {e}")
});
self
}
pub fn statement_type<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.statement_type = value
.try_into()
.map_err(|e| format!("error converting supplied value for statement_type: {e}"));
self
}
pub fn version<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::VettingRequirementsVersion>,
T::Error: ::std::fmt::Display,
{
self.version = value
.try_into()
.map_err(|e| format!("error converting supplied value for version: {e}"));
self
}
}
impl ::std::convert::TryFrom<VettingRequirements> for super::VettingRequirements {
type Error = super::error::ConversionError;
fn try_from(
value: VettingRequirements,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
accepted_document_classes: value.accepted_document_classes?,
accepted_methods: value.accepted_methods?,
decision_sla: value.decision_sla?,
eligible_vetters: value.eligible_vetters?,
ext: value.ext?,
ext_critical: value.ext_critical?,
governance_framework_url: value.governance_framework_url?,
independence: value.independence?,
invitation: value.invitation?,
max_statement_age: value.max_statement_age?,
min_by_method: value.min_by_method?,
min_statements: value.min_statements?,
optional_claims: value.optional_claims?,
required_claims: value.required_claims?,
requirements_grace: value.requirements_grace?,
statement_type: value.statement_type?,
version: value.version?,
})
}
}
impl ::std::convert::From<super::VettingRequirements> for VettingRequirements {
fn from(value: super::VettingRequirements) -> Self {
Self {
accepted_document_classes: Ok(value.accepted_document_classes),
accepted_methods: Ok(value.accepted_methods),
decision_sla: Ok(value.decision_sla),
eligible_vetters: Ok(value.eligible_vetters),
ext: Ok(value.ext),
ext_critical: Ok(value.ext_critical),
governance_framework_url: Ok(value.governance_framework_url),
independence: Ok(value.independence),
invitation: Ok(value.invitation),
max_statement_age: Ok(value.max_statement_age),
min_by_method: Ok(value.min_by_method),
min_statements: Ok(value.min_statements),
optional_claims: Ok(value.optional_claims),
required_claims: Ok(value.required_claims),
requirements_grace: Ok(value.requirements_grace),
statement_type: Ok(value.statement_type),
version: Ok(value.version),
}
}
}
#[derive(Clone, Debug)]
pub struct VettingRequirementsEligibleVetters {
role: ::std::result::Result<
super::VettingRequirementsEligibleVettersRole,
::std::string::String,
>,
}
impl ::std::default::Default for VettingRequirementsEligibleVetters {
fn default() -> Self {
Self {
role: Err("no value supplied for role".to_string()),
}
}
}
impl VettingRequirementsEligibleVetters {
pub fn role<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::VettingRequirementsEligibleVettersRole>,
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<VettingRequirementsEligibleVetters>
for super::VettingRequirementsEligibleVetters
{
type Error = super::error::ConversionError;
fn try_from(
value: VettingRequirementsEligibleVetters,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self { role: value.role? })
}
}
impl ::std::convert::From<super::VettingRequirementsEligibleVetters>
for VettingRequirementsEligibleVetters
{
fn from(value: super::VettingRequirementsEligibleVetters) -> Self {
Self {
role: Ok(value.role),
}
}
}
#[derive(Clone, Debug)]
pub struct VettingRequirementsIndependence {
max_by_declared_relationship: ::std::result::Result<
::std::collections::HashMap<super::VettingRelationship, u64>,
::std::string::String,
>,
require_consistent_identity_commitment:
::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
}
impl ::std::default::Default for VettingRequirementsIndependence {
fn default() -> Self {
Self {
max_by_declared_relationship: Ok(Default::default()),
require_consistent_identity_commitment: Ok(Default::default()),
}
}
}
impl VettingRequirementsIndependence {
pub fn max_by_declared_relationship<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::collections::HashMap<super::VettingRelationship, u64>,
>,
T::Error: ::std::fmt::Display,
{
self.max_by_declared_relationship = value.try_into().map_err(|e| {
format!("error converting supplied value for max_by_declared_relationship: {e}")
});
self
}
pub fn require_consistent_identity_commitment<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.require_consistent_identity_commitment = value
.try_into()
.map_err(|e| {
format!(
"error converting supplied value for require_consistent_identity_commitment: {e}"
)
});
self
}
}
impl ::std::convert::TryFrom<VettingRequirementsIndependence>
for super::VettingRequirementsIndependence
{
type Error = super::error::ConversionError;
fn try_from(
value: VettingRequirementsIndependence,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
max_by_declared_relationship: value.max_by_declared_relationship?,
require_consistent_identity_commitment: value
.require_consistent_identity_commitment?,
})
}
}
impl ::std::convert::From<super::VettingRequirementsIndependence>
for VettingRequirementsIndependence
{
fn from(value: super::VettingRequirementsIndependence) -> Self {
Self {
max_by_declared_relationship: Ok(value.max_by_declared_relationship),
require_consistent_identity_commitment: Ok(
value.require_consistent_identity_commitment
),
}
}
}
}
/// Generation of default values for serde.
pub mod defaults {
pub(super) fn default_bool<const V: bool>() -> bool {
V
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/join-requests/manifest/0.4";
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\"$defs\":{\"Audience\":{\"description\":\"Who may see a directory field, ordered from narrowest to widest. `admins`: holders of `vtc.directory.admin` (and, for derived fields, `vtc.members.manage`). `groups`: members who share at least one community group with the subject (narrowed to the groups the subject names in `groupAudience`, where it names any). `connections`: members holding an attributed, in-force verified relationship with the subject at the community. `members`: every active member. `public`: anyone, and what the member's signed public card carries outside the community.\\n\\n`groups` and `connections` are not a chain: a viewer sees a field when they satisfy the audience the subject chose for it or any wider audience. Every audience includes the administrators, and the subject always sees their own whole card. There is no `onlyMe`: a value nobody else should see is not directory data.\",\"enum\":[\"admins\",\"groups\",\"connections\",\"members\",\"public\"],\"title\":\"Audience\",\"type\":\"string\"},\"ClaimType\":{\"description\":\"The vocabulary token naming what a value IS — `name.legal`, `phone.mobile`, `address.postal`, `person.birthDate`. Dotted, most-general segment first, so that a consumer with no knowledge of the specific token can still group by its prefix.\\n\\nThe token is the maintainer's own; no external vocabulary is primary. External vocabularies (vCard/jCard, OIDC standard claims, schema.org) are mappings applied at PRESENTATION by a renderer, not at rest, so that a query written in any of them can be matched without the store having to live inside any one of them.\\n\\nThe `x:` prefix is an open extension namespace and is not decoration. The closest prior art — Windows CardSpace's self-issued card — supported exactly fifteen predefined claim types with no extensibility, and that is the specific way it failed the requirement a holder actually has. An `x:` attribute stores, composes, binds and discloses exactly like a known one; it renders generically and matches only an explicit query.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^(x:)?[a-z][a-zA-Z0-9]*(\\\\.[a-z][a-zA-Z0-9]*)*$\",\"title\":\"ClaimType\",\"type\":\"string\"},\"CommunityBranding\":{\"additionalProperties\":false,\"description\":\"OPTIONAL. How the community asks to be shown to a prospective applicant: a name, an accent colour and a logo. Presentation only, self-asserted and unverified — `communityDid` identifies the community, never `branding`. Not part of any criterion, so not covered by a `requirementsDigest`. Every member is optional.\",\"properties\":{\"accentColor\":{\"description\":\"An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.\",\"pattern\":\"^#[0-9a-fA-F]{6}$\",\"type\":\"string\"},\"displayName\":{\"description\":\"The community's name as it asks to be shown.\",\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"logoUrl\":{\"description\":\"An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.\",\"format\":\"uri\",\"maxLength\":2048,\"pattern\":\"^https://\",\"type\":\"string\"}},\"title\":\"CommunityBranding\",\"type\":\"object\"},\"Criterion\":{\"additionalProperties\":false,\"dependentRequired\":{\"credentialIssuers\":[\"presentationDefinition\"],\"presentationDefinition\":[\"credentialIssuers\"]},\"properties\":{\"admission\":{\"description\":\"How the community decides a submission that meets this criterion. `automatic`: it admits the applicant without a person deciding. `review`: it refers the submission to an administrator, who decides.\",\"enum\":[\"automatic\",\"review\"],\"type\":\"string\"},\"credentialIssuers\":{\"description\":\"Whose credentials meet `presentationDefinition`: `community` (this community's own), `recognised` (this community's, or a community it recognises), or `any` (any issuer whose credential verifies). Present exactly when `presentationDefinition` is.\",\"enum\":[\"any\",\"community\",\"recognised\"],\"type\":\"string\"},\"description\":{\"description\":\"Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.\",\"maxLength\":1024,\"type\":\"string\"},\"id\":{\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\"invitationRequired\":{\"description\":\"True: the criterion is met only by a submission carrying a valid, unconsumed invitation this community issued to the applicant.\",\"type\":\"boolean\"},\"presentationDefinition\":{\"description\":\"The credentials an applicant must present for this criterion (opaque here). Absent: the criterion requires no credential.\",\"type\":\"object\"},\"requirementsDigest\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Digest over the RFC 8785 (JCS) canonicalization of this criterion object with the `requirementsDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Names one version of the criterion: an applicant records it when starting an application and echoes it to vetters and at submission.\"},\"vetting\":{\"$ref\":\"#/$defs/VettingRequirements\"}},\"required\":[\"id\",\"admission\",\"requirementsDigest\"],\"title\":\"Criterion\",\"type\":\"object\"},\"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\"],\"minLength\":16,\"pattern\":\"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\"title\":\"DigestMultibase\",\"type\":\"string\"},\"DirectoryDisclosure\":{\"additionalProperties\":false,\"description\":\"The statement itself. Every member is generated from the community's configuration when the statement is read. `statementDigest` names this exact statement, so an applicant can record which one it was shown.\",\"properties\":{\"adminNotes\":{\"description\":\"Whether administrators keep notes about members. Notes are never on a card and never reach another member; a member may ask for the notes about them.\",\"type\":\"boolean\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"externalCards\":{\"description\":\"Whether a member may publish a card beyond the community: their `public` values, signed by them, offered through the community's trust registry. Always the member's own choice, and off on a new card. False where the community cannot publish external cards.\",\"type\":\"boolean\"},\"fields\":{\"description\":\"Every active field, base and community-defined, in display order. A base field the community has disabled is absent, which is how a reader learns it is not collected.\",\"items\":{\"$ref\":\"#/$defs/DisclosedField\"},\"maxItems\":128,\"type\":\"array\"},\"groups\":{\"$ref\":\"#/$defs/GroupDisclosure\"},\"membershipIdentifiers\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The widest audience a member's membership identifier — the DID they joined with — can reach through the directory: the ceiling of the base field `did`, or `admins` where that field is disabled. `members` or `public` means a member may choose to show it that widely; a new card never shows it beyond `admins` (VTI-DIR-042). Administrators always see it.\"},\"newCard\":{\"$ref\":\"#/$defs/NewCardDefaults\"},\"statementDigest\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Digest over the RFC 8785 (JCS) canonicalization of this statement with the `statementDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Changes exactly when what the statement says changes.\"}},\"required\":[\"fields\",\"newCard\",\"membershipIdentifiers\",\"adminNotes\",\"externalCards\",\"statementDigest\"],\"title\":\"DirectoryDisclosure\",\"type\":\"object\"},\"DisclosedField\":{\"additionalProperties\":false,\"description\":\"One active field of the community's directory, as an applicant needs to see it: what it is, who writes its value, the widest audience it may ever reach and the audience a new card gives it. Disabled and retired fields are not listed: no projection shows them.\",\"properties\":{\"ceiling\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The widest audience the field may ever reach, whatever the member chooses.\"},\"defaultVisibility\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The audience a new card gives the field until its member chooses. Never wider than `ceiling`.\"},\"description\":{\"$ref\":\"#/$defs/FieldDescription\"},\"facet\":{\"description\":\"Whether members may filter on the field and see counts by its values. Absent: false.\",\"type\":\"boolean\"},\"key\":{\"$ref\":\"#/$defs/FieldKey\"},\"label\":{\"$ref\":\"#/$defs/FieldLabel\"},\"searchable\":{\"description\":\"Whether a member's search may match the field, within what the searcher may see.\",\"type\":\"boolean\"},\"source\":{\"$ref\":\"#/$defs/FieldSource\",\"description\":\"Who writes the value. `admin` and `credential` matter most to an applicant: an `admin` field holds what the community asserts about the member, which the member reads and cannot change.\"},\"type\":{\"$ref\":\"#/$defs/FieldType\"}},\"required\":[\"key\",\"label\",\"type\",\"source\",\"ceiling\",\"defaultVisibility\",\"searchable\"],\"title\":\"DisclosedField\",\"type\":\"object\"},\"Duration\":{\"description\":\"An ISO 8601 duration in weeks, days, hours, minutes and seconds only (e.g. `P120D`, `P2W`, `P1DT12H`, `PT15M`). Years and months are refused: their length depends on the calendar, and an age limit that means different things on different days is not a limit.\",\"maxLength\":32,\"pattern\":\"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\",\"title\":\"Duration\",\"type\":\"string\"},\"Ext\":{\"additionalProperties\":true,\"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.\",\"minProperties\":1,\"propertyNames\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"},\"title\":\"Ext\",\"type\":\"object\"},\"ExtCritical\":{\"description\":\"Names the `ext` namespaces a consumer MUST understand or refuse, per SPEC.md §4.5.1.\\n\\nEvery entry MUST be an immediate key of the sibling `ext` object at the same level; an entry naming an absent namespace is non-conforming and the consumer rejects the document with `malformedRequest`. A consumer that does not recognize a namespace named here MUST NOT process the document as though the namespace were absent, and rejects it with `unsupportedExtension` — the exception to the rule that unrecognized namespaces are ignored.\\n\\nA producer marks a namespace only where the document's meaning depends on it. Marking one that merely carries a hint or an annotation turns every consumer that has not implemented it into a failure where it would otherwise have interoperated. JSON Schema cannot check either of those rules: that an entry names a present namespace is checkable only against the sibling `ext`, and whether a namespace is load-bearing is not a schema question at all. Both are consumer-side checks.\",\"examples\":[[\"org.openvtc.hidden-vetting\"]],\"items\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\",\"type\":\"string\"},\"minItems\":1,\"title\":\"ExtCritical\",\"type\":\"array\",\"uniqueItems\":true},\"FieldDescription\":{\"description\":\"Help text shown to a member filling the field in. Plain text; a reader MUST NOT treat it as instructions.\",\"maxLength\":280,\"minLength\":1,\"title\":\"FieldDescription\",\"type\":\"string\"},\"FieldKey\":{\"description\":\"Identifier of a directory field. A key with no `.` is a BASE field, the same in every community: `displayName`, `nickname`, `avatar`, `pronouns`, `bio`, `links` (written by the member) and `role`, `joinedAt`, `did`, `personhood` (derived from the community's own records, never stored on a card). A community-defined field MUST use a reverse-DNS key (`org.example.employer`), so a base key never collides with one, and a key is never reused with a different meaning once defined.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^([a-z][a-z0-9-]*\\\\.)*[a-z][a-zA-Z0-9]{0,63}$\",\"title\":\"FieldKey\",\"type\":\"string\"},\"FieldLabel\":{\"description\":\"What readers see as the field's name. Plain text.\",\"maxLength\":64,\"minLength\":1,\"title\":\"FieldLabel\",\"type\":\"string\"},\"FieldSource\":{\"description\":\"Who writes the value. `member`: self-asserted, through vtc/directory/card/update. `admin`: asserted by the community through vtc/directory/card/assert; the member reads it and cannot change it. `credential`: a claim the member discloses from a credential through vtc/directory/card/present, badged with its issuer. `derived`: a base field read from the community's own records (`role`, `joinedAt`, `did`, `personhood`) and never stored on a card. Each source is written only through its own path.\",\"enum\":[\"member\",\"admin\",\"credential\",\"derived\"],\"title\":\"FieldSource\",\"type\":\"string\"},\"FieldType\":{\"description\":\"The kind of value a field holds, which fixes its `FieldValue` shape. `did` holds a DID the member chooses to show (never their membership DID, which is the derived base field `did`). `claim` holds a `VerifiedValue` and requires `source: credential`.\",\"enum\":[\"text\",\"longText\",\"url\",\"email\",\"phone\",\"enum\",\"multiEnum\",\"date\",\"country\",\"image\",\"did\",\"claim\"],\"title\":\"FieldType\",\"type\":\"string\"},\"GroupDisclosure\":{\"additionalProperties\":false,\"description\":\"Present when the community has groups. A member of a group holds a credential, issued by the group's own identifier, that evidences their membership, and can present it outside the community — which is why an applicant is told.\",\"properties\":{\"credentialValidity\":{\"description\":\"How long each group membership credential is valid for, as an ISO 8601 duration in weeks, days, hours, minutes and seconds (`P30D`). It is renewed while the member stays in the group, and a credential already issued stays valid until it expires even after its holder leaves.\",\"maxLength\":32,\"pattern\":\"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\",\"type\":\"string\"}},\"required\":[\"credentialValidity\"],\"title\":\"GroupDisclosure\",\"type\":\"object\"},\"Introductions\":{\"description\":\"Who may ask the community to introduce them to this member. `anyone`: any active member who can find the entry. `groups`: only members who share a community group with them. `connections`: only members holding an attributed, in-force verified relationship with them. `off`: nobody. A refused introduction is never reported to its sender.\",\"enum\":[\"anyone\",\"groups\",\"connections\",\"off\"],\"title\":\"Introductions\",\"type\":\"string\"},\"Listing\":{\"description\":\"Whether members can find the entry. `listed`: it appears when members browse. `findable`: it does not appear when browsing, but a search that matches it under `search` returns it. `hidden`: no member finds it by browsing or searching; administrators still see it, and members who share a group that shows its members see it in that group's member list.\",\"enum\":[\"listed\",\"findable\",\"hidden\"],\"title\":\"Listing\",\"type\":\"string\"},\"NewCardDefaults\":{\"additionalProperties\":false,\"description\":\"The discoverability a card has when the community creates it, before its member chooses. A new card is never published beyond the community (VTI-DIR-042), so there is no member for that here: `externalCards` says whether the member may turn it on.\",\"properties\":{\"introductions\":{\"$ref\":\"#/$defs/Introductions\",\"description\":\"From whom a new card accepts introductions. Absent where the community does not deliver introductions.\"},\"listing\":{\"$ref\":\"#/$defs/Listing\",\"description\":\"The listing a new card starts with: the community's `defaultListing` (vtc/directory/fields/list). Every member may always choose `hidden`.\"},\"search\":{\"description\":\"What a member's search may match, as the card's `discoverability.search`.\",\"enum\":[\"byName\",\"exactNickname\",\"off\"],\"type\":\"string\"}},\"required\":[\"listing\",\"search\"],\"title\":\"NewCardDefaults\",\"type\":\"object\"},\"Response\":{\"$anchor\":\"response\",\"additionalProperties\":false,\"properties\":{\"branding\":{\"$ref\":\"#/$defs/CommunityBranding\"},\"communityDid\":{\"pattern\":\"^did:\",\"type\":\"string\"},\"criteria\":{\"items\":{\"$ref\":\"#/$defs/Criterion\"},\"type\":\"array\"},\"directoryDisclosure\":{\"$ref\":\"#/$defs/DirectoryDisclosure\",\"description\":\"Present exactly where the community offers a member directory: the statement, generated from its configuration, of what the directory would disclose about a member and to whom — the same statement vtc/directory/disclosure returns. Outside every criterion, so no `requirementsDigest` covers it; it names itself with its own `statementDigest`.\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"requestedAttributes\":{\"description\":\"What the community asks an applicant to tell it about themselves, as claim types — never values. Answered in persona terms: the applicant's agent discloses those attributes from the face the applicant chooses, and they arrive on vtc/join-requests/submit as `attributes`. SELF-ASSERTED: a community MUST NOT describe an answer as verified, and MUST NOT make a decision that assumes it is. Outside every criterion, so no `requirementsDigest` covers it. Absent when the community asks nothing.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"purpose\":{\"description\":\"Why the community asks, in words shown to the applicant before they disclose.\",\"maxLength\":256,\"type\":\"string\"},\"required\":{\"default\":true,\"description\":\"False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.\",\"type\":\"boolean\"},\"type\":{\"description\":\"A claim-type token from the persona claim-type registry (persona/_shared/0.1/CLAIM-TYPES.md) — `name.display`, `address.country` — or an `x:` extension token.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^(x:)?[a-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*)*$\",\"type\":\"string\"}},\"required\":[\"type\"],\"type\":\"object\"},\"maxItems\":32,\"type\":\"array\"}},\"required\":[\"communityDid\",\"criteria\"],\"title\":\"VTC Join-Requests Manifest — response payload\",\"type\":\"object\"},\"VettingDocumentation\":{\"description\":\"A class of documentation, named in lowerCamelCase. Open rather than enumerated, because what documentation a vetter accepts is each vetter's own choice. Well-known values: `passport`, `nationalId`, `driverLicence`, and `none` — the vetter will attest without a document, which is the `priorAcquaintance` case. Only the class ever travels — never a document number, an image, an issuing authority or an expiry date. `none` states a policy (what a vetter accepts); a record of what was relied on expresses 'no document' as an empty list instead.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z][a-zA-Z0-9]*$\",\"title\":\"VettingDocumentation\",\"type\":\"string\"},\"VettingMethod\":{\"description\":\"How the vetter established that the person they checked is the person controlling the applicant's DID. `inPerson` — both people were physically together. `video` — a live, two-way video call. `priorAcquaintance` — the vetter has known or worked with this person over a period, and attests from that knowledge rather than from a document. A method is a description of what happened, not an assurance level: which methods count, and how many of each, is community policy.\",\"enum\":[\"inPerson\",\"video\",\"priorAcquaintance\"],\"title\":\"VettingMethod\",\"type\":\"string\"},\"VettingRelationship\":{\"description\":\"The vetter's own declaration of how they relate to the applicant. Declared, not verified: it exists so community policy can cap how much evidence comes from people close to the applicant, and a false declaration is the vetter's attributable act.\",\"enum\":[\"none\",\"communityColleague\",\"sameEmployer\",\"family\",\"otherPersonal\"],\"title\":\"VettingRelationship\",\"type\":\"string\"},\"VettingRequirements\":{\"additionalProperties\":true,\"description\":\"What identity-vetting evidence a criterion needs, beyond what a presentation-definition can express: distinct eligible vetters, per-method floors, independence caps. Every number is the community's own policy. This schema supplies no defaults — an absent optional member means the community imposes no constraint of that kind, never that some protocol value applies. Deliberately open: a consumer MUST ignore members it does not recognise, so a community publishing a newer shape does not make an older client unable to read the rest. Durations: `maxStatementAge` — a statement older than this at decision time does not count (absent: no limit beyond the statement's own validity); `decisionSla` — how long after submission the community undertakes to decide, including on a referred application; `requirementsGrace` — how long an application started under an earlier `requirementsDigest` is still evaluated under that version.\",\"properties\":{\"acceptedDocumentClasses\":{\"description\":\"Documentation a statement must have relied on in order to count. Absent — the expected case — means each vetter decides what documentation they accept, including none for prior acquaintance, and the community counts what they attest.\",\"items\":{\"$ref\":\"#/$defs/VettingDocumentation\"},\"minItems\":1,\"type\":\"array\",\"uniqueItems\":true},\"acceptedMethods\":{\"description\":\"Methods whose statements count at all.\",\"items\":{\"$ref\":\"#/$defs/VettingMethod\"},\"minItems\":1,\"type\":\"array\",\"uniqueItems\":true},\"decisionSla\":{\"$ref\":\"#/$defs/Duration\"},\"eligibleVetters\":{\"additionalProperties\":false,\"description\":\"How a vetter's eligibility is established.\",\"properties\":{\"role\":{\"description\":\"The role a statement's issuer must hold, matched as the action `role:<role>` in a Verifiable Authority Credential (`AuthorityCredential`) the community issued to that issuer, whose `authority.scope` is the community's DID (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds such a credential.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9_-]*$\",\"type\":\"string\"}},\"required\":[\"role\"],\"type\":\"object\"},\"ext\":{\"$ref\":\"#/$defs/Ext\",\"description\":\"Ecosystem-defined extension members per SPEC.md §4.5.1, for vetting parameters this version does not enumerate. A community publishing an admission mode the base members cannot express — the zero-knowledge parameters an applicant proves against, say — puts them under a namespace it controls, rather than waiting for a version that names them.\\n\\nThis object is already `additionalProperties: true`, so an unknown member here has always validated. That is not the same as reaching a consumer: a generated type names the members this schema declares and drops the rest, so an undeclared member is parsed and discarded, and it is discarded again on the re-serialization `requirementsDigest` is computed over. `ext` is a declared member, so it survives both.\"},\"extCritical\":{\"$ref\":\"#/$defs/ExtCritical\",\"description\":\"Namespaces of `ext` an applicant MUST understand or refuse, per SPEC.md §4.5.1.\\n\\nA community marks a namespace here when applying under this criterion without understanding it would mean something other than what the community requires — an applicant that ignored a hidden-vetting namespace, for instance, would submit named statements to a criterion whose whole point was that it does not receive them. An applicant that does not recognize a marked namespace stops rather than downgrading silently.\\n\\nAbsent, which is the common case, every namespace under `ext` is advisory and ignorable.\"},\"governanceFrameworkUrl\":{\"description\":\"Where the community's vetting governance — including the attestation text vetters sign — is published.\",\"format\":\"uri\",\"maxLength\":2048,\"pattern\":\"^https://\",\"type\":\"string\"},\"independence\":{\"additionalProperties\":false,\"description\":\"Caps on how much evidence may come from people close to the applicant. Absent: no caps.\",\"properties\":{\"maxByDeclaredRelationship\":{\"additionalProperties\":{\"minimum\":0,\"type\":\"integer\"},\"description\":\"The most counted statements that may come from vetters declaring each relationship — e.g. `{ \\\"family\\\": 0 }`.\",\"propertyNames\":{\"$ref\":\"#/$defs/VettingRelationship\"},\"type\":\"object\"},\"requireConsistentIdentityCommitment\":{\"description\":\"When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"invitation\":{\"description\":\"Whether an invitation credential must accompany the statements at submission (`required`), may (`optional`), or plays no part (`none`). Absent: the presentation-definition alone governs.\",\"enum\":[\"required\",\"optional\",\"none\"],\"type\":\"string\"},\"maxStatementAge\":{\"$ref\":\"#/$defs/Duration\"},\"minByMethod\":{\"additionalProperties\":{\"minimum\":0,\"type\":\"integer\"},\"description\":\"Per-method floors within `minStatements` — e.g. `{ \\\"inPerson\\\": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.\",\"propertyNames\":{\"$ref\":\"#/$defs/VettingMethod\"},\"type\":\"object\"},\"minStatements\":{\"description\":\"How many counted statements are needed, counting each vetter once however many DIDs they hold.\",\"minimum\":1,\"type\":\"integer\"},\"optionalClaims\":{\"description\":\"Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.\",\"items\":{\"$ref\":\"#/$defs/ClaimType\"},\"type\":\"array\",\"uniqueItems\":true},\"requiredClaims\":{\"description\":\"Claim types the applicant's Vetting Card must carry, which the identity commitment is computed over, and which a counted statement must list as verified. Absent: none.\",\"items\":{\"$ref\":\"#/$defs/ClaimType\"},\"type\":\"array\",\"uniqueItems\":true},\"requirementsGrace\":{\"$ref\":\"#/$defs/Duration\"},\"statementType\":{\"description\":\"The predicate IRI a counted vetting statement carries in `credentialSubject.predicate` — `https://registry.trustoverip.org/dtg/vsc/vetted/1`, the DTG VSC registry's identity-vetting predicate, or a predicate in a namespace the community controls — registered as one the community accepts via vtc/endorsement-types/register. A statement under any other predicate does not count.\",\"format\":\"uri\",\"maxLength\":512,\"minLength\":1,\"type\":\"string\"},\"version\":{\"description\":\"Version of this requirements object's shape. `0.1` for the members defined here.\",\"pattern\":\"^(0|[1-9][0-9]*)\\\\.(0|[1-9][0-9]*)$\",\"type\":\"string\"}},\"required\":[\"version\",\"statementType\",\"minStatements\",\"acceptedMethods\",\"eligibleVetters\"],\"title\":\"VettingRequirements\",\"type\":\"object\"}},\"$id\":\"https://trusttasks.org/spec/vtc/join-requests/manifest/0.4\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"additionalProperties\":false,\"description\":\"Discover a community's join criteria before applying. The request carries nothing. Version 0.2 adds, per criterion, an optional `vetting` requirements object — how many identity-vetting statements a community needs, by which methods, from whom — and a `requirementsDigest` that names the exact version of the criterion an applicant started under. The response may also carry `branding`, how the community asks to be shown. Version 0.4 adds `directoryDisclosure`: where the community has a member directory, the statement of what it would disclose about a member and to whom, so an applicant reads it before applying.\",\"properties\":{\"ext\":{\"$ref\":\"#/$defs/Ext\"}},\"title\":\"VTC Join-Requests Manifest — payload\",\"type\":\"object\"}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/vtc/join-requests/manifest/0.4#response";
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\"$defs\":{\"Audience\":{\"description\":\"Who may see a directory field, ordered from narrowest to widest. `admins`: holders of `vtc.directory.admin` (and, for derived fields, `vtc.members.manage`). `groups`: members who share at least one community group with the subject (narrowed to the groups the subject names in `groupAudience`, where it names any). `connections`: members holding an attributed, in-force verified relationship with the subject at the community. `members`: every active member. `public`: anyone, and what the member's signed public card carries outside the community.\\n\\n`groups` and `connections` are not a chain: a viewer sees a field when they satisfy the audience the subject chose for it or any wider audience. Every audience includes the administrators, and the subject always sees their own whole card. There is no `onlyMe`: a value nobody else should see is not directory data.\",\"enum\":[\"admins\",\"groups\",\"connections\",\"members\",\"public\"],\"title\":\"Audience\",\"type\":\"string\"},\"ClaimType\":{\"description\":\"The vocabulary token naming what a value IS — `name.legal`, `phone.mobile`, `address.postal`, `person.birthDate`. Dotted, most-general segment first, so that a consumer with no knowledge of the specific token can still group by its prefix.\\n\\nThe token is the maintainer's own; no external vocabulary is primary. External vocabularies (vCard/jCard, OIDC standard claims, schema.org) are mappings applied at PRESENTATION by a renderer, not at rest, so that a query written in any of them can be matched without the store having to live inside any one of them.\\n\\nThe `x:` prefix is an open extension namespace and is not decoration. The closest prior art — Windows CardSpace's self-issued card — supported exactly fifteen predefined claim types with no extensibility, and that is the specific way it failed the requirement a holder actually has. An `x:` attribute stores, composes, binds and discloses exactly like a known one; it renders generically and matches only an explicit query.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^(x:)?[a-z][a-zA-Z0-9]*(\\\\.[a-z][a-zA-Z0-9]*)*$\",\"title\":\"ClaimType\",\"type\":\"string\"},\"CommunityBranding\":{\"additionalProperties\":false,\"description\":\"OPTIONAL. How the community asks to be shown to a prospective applicant: a name, an accent colour and a logo. Presentation only, self-asserted and unverified — `communityDid` identifies the community, never `branding`. Not part of any criterion, so not covered by a `requirementsDigest`. Every member is optional.\",\"properties\":{\"accentColor\":{\"description\":\"An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.\",\"pattern\":\"^#[0-9a-fA-F]{6}$\",\"type\":\"string\"},\"displayName\":{\"description\":\"The community's name as it asks to be shown.\",\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"logoUrl\":{\"description\":\"An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.\",\"format\":\"uri\",\"maxLength\":2048,\"pattern\":\"^https://\",\"type\":\"string\"}},\"title\":\"CommunityBranding\",\"type\":\"object\"},\"Criterion\":{\"additionalProperties\":false,\"dependentRequired\":{\"credentialIssuers\":[\"presentationDefinition\"],\"presentationDefinition\":[\"credentialIssuers\"]},\"properties\":{\"admission\":{\"description\":\"How the community decides a submission that meets this criterion. `automatic`: it admits the applicant without a person deciding. `review`: it refers the submission to an administrator, who decides.\",\"enum\":[\"automatic\",\"review\"],\"type\":\"string\"},\"credentialIssuers\":{\"description\":\"Whose credentials meet `presentationDefinition`: `community` (this community's own), `recognised` (this community's, or a community it recognises), or `any` (any issuer whose credential verifies). Present exactly when `presentationDefinition` is.\",\"enum\":[\"any\",\"community\",\"recognised\"],\"type\":\"string\"},\"description\":{\"description\":\"Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.\",\"maxLength\":1024,\"type\":\"string\"},\"id\":{\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\"invitationRequired\":{\"description\":\"True: the criterion is met only by a submission carrying a valid, unconsumed invitation this community issued to the applicant.\",\"type\":\"boolean\"},\"presentationDefinition\":{\"description\":\"The credentials an applicant must present for this criterion (opaque here). Absent: the criterion requires no credential.\",\"type\":\"object\"},\"requirementsDigest\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Digest over the RFC 8785 (JCS) canonicalization of this criterion object with the `requirementsDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Names one version of the criterion: an applicant records it when starting an application and echoes it to vetters and at submission.\"},\"vetting\":{\"$ref\":\"#/$defs/VettingRequirements\"}},\"required\":[\"id\",\"admission\",\"requirementsDigest\"],\"title\":\"Criterion\",\"type\":\"object\"},\"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\"],\"minLength\":16,\"pattern\":\"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\"title\":\"DigestMultibase\",\"type\":\"string\"},\"DirectoryDisclosure\":{\"additionalProperties\":false,\"description\":\"The statement itself. Every member is generated from the community's configuration when the statement is read. `statementDigest` names this exact statement, so an applicant can record which one it was shown.\",\"properties\":{\"adminNotes\":{\"description\":\"Whether administrators keep notes about members. Notes are never on a card and never reach another member; a member may ask for the notes about them.\",\"type\":\"boolean\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"externalCards\":{\"description\":\"Whether a member may publish a card beyond the community: their `public` values, signed by them, offered through the community's trust registry. Always the member's own choice, and off on a new card. False where the community cannot publish external cards.\",\"type\":\"boolean\"},\"fields\":{\"description\":\"Every active field, base and community-defined, in display order. A base field the community has disabled is absent, which is how a reader learns it is not collected.\",\"items\":{\"$ref\":\"#/$defs/DisclosedField\"},\"maxItems\":128,\"type\":\"array\"},\"groups\":{\"$ref\":\"#/$defs/GroupDisclosure\"},\"membershipIdentifiers\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The widest audience a member's membership identifier — the DID they joined with — can reach through the directory: the ceiling of the base field `did`, or `admins` where that field is disabled. `members` or `public` means a member may choose to show it that widely; a new card never shows it beyond `admins` (VTI-DIR-042). Administrators always see it.\"},\"newCard\":{\"$ref\":\"#/$defs/NewCardDefaults\"},\"statementDigest\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Digest over the RFC 8785 (JCS) canonicalization of this statement with the `statementDigest` member removed, as a multibase-encoded multihash (SHA-256 and base58btc RECOMMENDED). Changes exactly when what the statement says changes.\"}},\"required\":[\"fields\",\"newCard\",\"membershipIdentifiers\",\"adminNotes\",\"externalCards\",\"statementDigest\"],\"title\":\"DirectoryDisclosure\",\"type\":\"object\"},\"DisclosedField\":{\"additionalProperties\":false,\"description\":\"One active field of the community's directory, as an applicant needs to see it: what it is, who writes its value, the widest audience it may ever reach and the audience a new card gives it. Disabled and retired fields are not listed: no projection shows them.\",\"properties\":{\"ceiling\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The widest audience the field may ever reach, whatever the member chooses.\"},\"defaultVisibility\":{\"$ref\":\"#/$defs/Audience\",\"description\":\"The audience a new card gives the field until its member chooses. Never wider than `ceiling`.\"},\"description\":{\"$ref\":\"#/$defs/FieldDescription\"},\"facet\":{\"description\":\"Whether members may filter on the field and see counts by its values. Absent: false.\",\"type\":\"boolean\"},\"key\":{\"$ref\":\"#/$defs/FieldKey\"},\"label\":{\"$ref\":\"#/$defs/FieldLabel\"},\"searchable\":{\"description\":\"Whether a member's search may match the field, within what the searcher may see.\",\"type\":\"boolean\"},\"source\":{\"$ref\":\"#/$defs/FieldSource\",\"description\":\"Who writes the value. `admin` and `credential` matter most to an applicant: an `admin` field holds what the community asserts about the member, which the member reads and cannot change.\"},\"type\":{\"$ref\":\"#/$defs/FieldType\"}},\"required\":[\"key\",\"label\",\"type\",\"source\",\"ceiling\",\"defaultVisibility\",\"searchable\"],\"title\":\"DisclosedField\",\"type\":\"object\"},\"Duration\":{\"description\":\"An ISO 8601 duration in weeks, days, hours, minutes and seconds only (e.g. `P120D`, `P2W`, `P1DT12H`, `PT15M`). Years and months are refused: their length depends on the calendar, and an age limit that means different things on different days is not a limit.\",\"maxLength\":32,\"pattern\":\"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\",\"title\":\"Duration\",\"type\":\"string\"},\"Ext\":{\"additionalProperties\":true,\"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.\",\"minProperties\":1,\"propertyNames\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"},\"title\":\"Ext\",\"type\":\"object\"},\"ExtCritical\":{\"description\":\"Names the `ext` namespaces a consumer MUST understand or refuse, per SPEC.md §4.5.1.\\n\\nEvery entry MUST be an immediate key of the sibling `ext` object at the same level; an entry naming an absent namespace is non-conforming and the consumer rejects the document with `malformedRequest`. A consumer that does not recognize a namespace named here MUST NOT process the document as though the namespace were absent, and rejects it with `unsupportedExtension` — the exception to the rule that unrecognized namespaces are ignored.\\n\\nA producer marks a namespace only where the document's meaning depends on it. Marking one that merely carries a hint or an annotation turns every consumer that has not implemented it into a failure where it would otherwise have interoperated. JSON Schema cannot check either of those rules: that an entry names a present namespace is checkable only against the sibling `ext`, and whether a namespace is load-bearing is not a schema question at all. Both are consumer-side checks.\",\"examples\":[[\"org.openvtc.hidden-vetting\"]],\"items\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\",\"type\":\"string\"},\"minItems\":1,\"title\":\"ExtCritical\",\"type\":\"array\",\"uniqueItems\":true},\"FieldDescription\":{\"description\":\"Help text shown to a member filling the field in. Plain text; a reader MUST NOT treat it as instructions.\",\"maxLength\":280,\"minLength\":1,\"title\":\"FieldDescription\",\"type\":\"string\"},\"FieldKey\":{\"description\":\"Identifier of a directory field. A key with no `.` is a BASE field, the same in every community: `displayName`, `nickname`, `avatar`, `pronouns`, `bio`, `links` (written by the member) and `role`, `joinedAt`, `did`, `personhood` (derived from the community's own records, never stored on a card). A community-defined field MUST use a reverse-DNS key (`org.example.employer`), so a base key never collides with one, and a key is never reused with a different meaning once defined.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^([a-z][a-z0-9-]*\\\\.)*[a-z][a-zA-Z0-9]{0,63}$\",\"title\":\"FieldKey\",\"type\":\"string\"},\"FieldLabel\":{\"description\":\"What readers see as the field's name. Plain text.\",\"maxLength\":64,\"minLength\":1,\"title\":\"FieldLabel\",\"type\":\"string\"},\"FieldSource\":{\"description\":\"Who writes the value. `member`: self-asserted, through vtc/directory/card/update. `admin`: asserted by the community through vtc/directory/card/assert; the member reads it and cannot change it. `credential`: a claim the member discloses from a credential through vtc/directory/card/present, badged with its issuer. `derived`: a base field read from the community's own records (`role`, `joinedAt`, `did`, `personhood`) and never stored on a card. Each source is written only through its own path.\",\"enum\":[\"member\",\"admin\",\"credential\",\"derived\"],\"title\":\"FieldSource\",\"type\":\"string\"},\"FieldType\":{\"description\":\"The kind of value a field holds, which fixes its `FieldValue` shape. `did` holds a DID the member chooses to show (never their membership DID, which is the derived base field `did`). `claim` holds a `VerifiedValue` and requires `source: credential`.\",\"enum\":[\"text\",\"longText\",\"url\",\"email\",\"phone\",\"enum\",\"multiEnum\",\"date\",\"country\",\"image\",\"did\",\"claim\"],\"title\":\"FieldType\",\"type\":\"string\"},\"GroupDisclosure\":{\"additionalProperties\":false,\"description\":\"Present when the community has groups. A member of a group holds a credential, issued by the group's own identifier, that evidences their membership, and can present it outside the community — which is why an applicant is told.\",\"properties\":{\"credentialValidity\":{\"description\":\"How long each group membership credential is valid for, as an ISO 8601 duration in weeks, days, hours, minutes and seconds (`P30D`). It is renewed while the member stays in the group, and a credential already issued stays valid until it expires even after its holder leaves.\",\"maxLength\":32,\"pattern\":\"^P(([0-9]+W)?([0-9]+D)?T([0-9]+H([0-9]+M)?([0-9]+S)?|[0-9]+M([0-9]+S)?|[0-9]+S)|[0-9]+W([0-9]+D)?|[0-9]+D)$\",\"type\":\"string\"}},\"required\":[\"credentialValidity\"],\"title\":\"GroupDisclosure\",\"type\":\"object\"},\"Introductions\":{\"description\":\"Who may ask the community to introduce them to this member. `anyone`: any active member who can find the entry. `groups`: only members who share a community group with them. `connections`: only members holding an attributed, in-force verified relationship with them. `off`: nobody. A refused introduction is never reported to its sender.\",\"enum\":[\"anyone\",\"groups\",\"connections\",\"off\"],\"title\":\"Introductions\",\"type\":\"string\"},\"Listing\":{\"description\":\"Whether members can find the entry. `listed`: it appears when members browse. `findable`: it does not appear when browsing, but a search that matches it under `search` returns it. `hidden`: no member finds it by browsing or searching; administrators still see it, and members who share a group that shows its members see it in that group's member list.\",\"enum\":[\"listed\",\"findable\",\"hidden\"],\"title\":\"Listing\",\"type\":\"string\"},\"NewCardDefaults\":{\"additionalProperties\":false,\"description\":\"The discoverability a card has when the community creates it, before its member chooses. A new card is never published beyond the community (VTI-DIR-042), so there is no member for that here: `externalCards` says whether the member may turn it on.\",\"properties\":{\"introductions\":{\"$ref\":\"#/$defs/Introductions\",\"description\":\"From whom a new card accepts introductions. Absent where the community does not deliver introductions.\"},\"listing\":{\"$ref\":\"#/$defs/Listing\",\"description\":\"The listing a new card starts with: the community's `defaultListing` (vtc/directory/fields/list). Every member may always choose `hidden`.\"},\"search\":{\"description\":\"What a member's search may match, as the card's `discoverability.search`.\",\"enum\":[\"byName\",\"exactNickname\",\"off\"],\"type\":\"string\"}},\"required\":[\"listing\",\"search\"],\"title\":\"NewCardDefaults\",\"type\":\"object\"},\"Response\":{\"$anchor\":\"response\",\"additionalProperties\":false,\"properties\":{\"branding\":{\"$ref\":\"#/$defs/CommunityBranding\"},\"communityDid\":{\"pattern\":\"^did:\",\"type\":\"string\"},\"criteria\":{\"items\":{\"$ref\":\"#/$defs/Criterion\"},\"type\":\"array\"},\"directoryDisclosure\":{\"$ref\":\"#/$defs/DirectoryDisclosure\",\"description\":\"Present exactly where the community offers a member directory: the statement, generated from its configuration, of what the directory would disclose about a member and to whom — the same statement vtc/directory/disclosure returns. Outside every criterion, so no `requirementsDigest` covers it; it names itself with its own `statementDigest`.\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"requestedAttributes\":{\"description\":\"What the community asks an applicant to tell it about themselves, as claim types — never values. Answered in persona terms: the applicant's agent discloses those attributes from the face the applicant chooses, and they arrive on vtc/join-requests/submit as `attributes`. SELF-ASSERTED: a community MUST NOT describe an answer as verified, and MUST NOT make a decision that assumes it is. Outside every criterion, so no `requirementsDigest` covers it. Absent when the community asks nothing.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"purpose\":{\"description\":\"Why the community asks, in words shown to the applicant before they disclose.\",\"maxLength\":256,\"type\":\"string\"},\"required\":{\"default\":true,\"description\":\"False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.\",\"type\":\"boolean\"},\"type\":{\"description\":\"A claim-type token from the persona claim-type registry (persona/_shared/0.1/CLAIM-TYPES.md) — `name.display`, `address.country` — or an `x:` extension token.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^(x:)?[a-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*)*$\",\"type\":\"string\"}},\"required\":[\"type\"],\"type\":\"object\"},\"maxItems\":32,\"type\":\"array\"}},\"required\":[\"communityDid\",\"criteria\"],\"title\":\"VTC Join-Requests Manifest — response payload\",\"type\":\"object\"},\"VettingDocumentation\":{\"description\":\"A class of documentation, named in lowerCamelCase. Open rather than enumerated, because what documentation a vetter accepts is each vetter's own choice. Well-known values: `passport`, `nationalId`, `driverLicence`, and `none` — the vetter will attest without a document, which is the `priorAcquaintance` case. Only the class ever travels — never a document number, an image, an issuing authority or an expiry date. `none` states a policy (what a vetter accepts); a record of what was relied on expresses 'no document' as an empty list instead.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z][a-zA-Z0-9]*$\",\"title\":\"VettingDocumentation\",\"type\":\"string\"},\"VettingMethod\":{\"description\":\"How the vetter established that the person they checked is the person controlling the applicant's DID. `inPerson` — both people were physically together. `video` — a live, two-way video call. `priorAcquaintance` — the vetter has known or worked with this person over a period, and attests from that knowledge rather than from a document. A method is a description of what happened, not an assurance level: which methods count, and how many of each, is community policy.\",\"enum\":[\"inPerson\",\"video\",\"priorAcquaintance\"],\"title\":\"VettingMethod\",\"type\":\"string\"},\"VettingRelationship\":{\"description\":\"The vetter's own declaration of how they relate to the applicant. Declared, not verified: it exists so community policy can cap how much evidence comes from people close to the applicant, and a false declaration is the vetter's attributable act.\",\"enum\":[\"none\",\"communityColleague\",\"sameEmployer\",\"family\",\"otherPersonal\"],\"title\":\"VettingRelationship\",\"type\":\"string\"},\"VettingRequirements\":{\"additionalProperties\":true,\"description\":\"What identity-vetting evidence a criterion needs, beyond what a presentation-definition can express: distinct eligible vetters, per-method floors, independence caps. Every number is the community's own policy. This schema supplies no defaults — an absent optional member means the community imposes no constraint of that kind, never that some protocol value applies. Deliberately open: a consumer MUST ignore members it does not recognise, so a community publishing a newer shape does not make an older client unable to read the rest. Durations: `maxStatementAge` — a statement older than this at decision time does not count (absent: no limit beyond the statement's own validity); `decisionSla` — how long after submission the community undertakes to decide, including on a referred application; `requirementsGrace` — how long an application started under an earlier `requirementsDigest` is still evaluated under that version.\",\"properties\":{\"acceptedDocumentClasses\":{\"description\":\"Documentation a statement must have relied on in order to count. Absent — the expected case — means each vetter decides what documentation they accept, including none for prior acquaintance, and the community counts what they attest.\",\"items\":{\"$ref\":\"#/$defs/VettingDocumentation\"},\"minItems\":1,\"type\":\"array\",\"uniqueItems\":true},\"acceptedMethods\":{\"description\":\"Methods whose statements count at all.\",\"items\":{\"$ref\":\"#/$defs/VettingMethod\"},\"minItems\":1,\"type\":\"array\",\"uniqueItems\":true},\"decisionSla\":{\"$ref\":\"#/$defs/Duration\"},\"eligibleVetters\":{\"additionalProperties\":false,\"description\":\"How a vetter's eligibility is established.\",\"properties\":{\"role\":{\"description\":\"The role a statement's issuer must hold, matched as the action `role:<role>` in a Verifiable Authority Credential (`AuthorityCredential`) the community issued to that issuer, whose `authority.scope` is the community's DID (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds such a credential.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9_-]*$\",\"type\":\"string\"}},\"required\":[\"role\"],\"type\":\"object\"},\"ext\":{\"$ref\":\"#/$defs/Ext\",\"description\":\"Ecosystem-defined extension members per SPEC.md §4.5.1, for vetting parameters this version does not enumerate. A community publishing an admission mode the base members cannot express — the zero-knowledge parameters an applicant proves against, say — puts them under a namespace it controls, rather than waiting for a version that names them.\\n\\nThis object is already `additionalProperties: true`, so an unknown member here has always validated. That is not the same as reaching a consumer: a generated type names the members this schema declares and drops the rest, so an undeclared member is parsed and discarded, and it is discarded again on the re-serialization `requirementsDigest` is computed over. `ext` is a declared member, so it survives both.\"},\"extCritical\":{\"$ref\":\"#/$defs/ExtCritical\",\"description\":\"Namespaces of `ext` an applicant MUST understand or refuse, per SPEC.md §4.5.1.\\n\\nA community marks a namespace here when applying under this criterion without understanding it would mean something other than what the community requires — an applicant that ignored a hidden-vetting namespace, for instance, would submit named statements to a criterion whose whole point was that it does not receive them. An applicant that does not recognize a marked namespace stops rather than downgrading silently.\\n\\nAbsent, which is the common case, every namespace under `ext` is advisory and ignorable.\"},\"governanceFrameworkUrl\":{\"description\":\"Where the community's vetting governance — including the attestation text vetters sign — is published.\",\"format\":\"uri\",\"maxLength\":2048,\"pattern\":\"^https://\",\"type\":\"string\"},\"independence\":{\"additionalProperties\":false,\"description\":\"Caps on how much evidence may come from people close to the applicant. Absent: no caps.\",\"properties\":{\"maxByDeclaredRelationship\":{\"additionalProperties\":{\"minimum\":0,\"type\":\"integer\"},\"description\":\"The most counted statements that may come from vetters declaring each relationship — e.g. `{ \\\"family\\\": 0 }`.\",\"propertyNames\":{\"$ref\":\"#/$defs/VettingRelationship\"},\"type\":\"object\"},\"requireConsistentIdentityCommitment\":{\"description\":\"When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.\",\"type\":\"boolean\"}},\"type\":\"object\"},\"invitation\":{\"description\":\"Whether an invitation credential must accompany the statements at submission (`required`), may (`optional`), or plays no part (`none`). Absent: the presentation-definition alone governs.\",\"enum\":[\"required\",\"optional\",\"none\"],\"type\":\"string\"},\"maxStatementAge\":{\"$ref\":\"#/$defs/Duration\"},\"minByMethod\":{\"additionalProperties\":{\"minimum\":0,\"type\":\"integer\"},\"description\":\"Per-method floors within `minStatements` — e.g. `{ \\\"inPerson\\\": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.\",\"propertyNames\":{\"$ref\":\"#/$defs/VettingMethod\"},\"type\":\"object\"},\"minStatements\":{\"description\":\"How many counted statements are needed, counting each vetter once however many DIDs they hold.\",\"minimum\":1,\"type\":\"integer\"},\"optionalClaims\":{\"description\":\"Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.\",\"items\":{\"$ref\":\"#/$defs/ClaimType\"},\"type\":\"array\",\"uniqueItems\":true},\"requiredClaims\":{\"description\":\"Claim types the applicant's Vetting Card must carry, which the identity commitment is computed over, and which a counted statement must list as verified. Absent: none.\",\"items\":{\"$ref\":\"#/$defs/ClaimType\"},\"type\":\"array\",\"uniqueItems\":true},\"requirementsGrace\":{\"$ref\":\"#/$defs/Duration\"},\"statementType\":{\"description\":\"The predicate IRI a counted vetting statement carries in `credentialSubject.predicate` — `https://registry.trustoverip.org/dtg/vsc/vetted/1`, the DTG VSC registry's identity-vetting predicate, or a predicate in a namespace the community controls — registered as one the community accepts via vtc/endorsement-types/register. A statement under any other predicate does not count.\",\"format\":\"uri\",\"maxLength\":512,\"minLength\":1,\"type\":\"string\"},\"version\":{\"description\":\"Version of this requirements object's shape. `0.1` for the members defined here.\",\"pattern\":\"^(0|[1-9][0-9]*)\\\\.(0|[1-9][0-9]*)$\",\"type\":\"string\"}},\"required\":[\"version\",\"statementType\",\"minStatements\",\"acceptedMethods\",\"eligibleVetters\"],\"title\":\"VettingRequirements\",\"type\":\"object\"}},\"$ref\":\"#/$defs/Response\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\"}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The earlier `MAJOR.MINOR` of this slug that this version declares it
/// extends (front matter `extends`, SPEC §5.2 "Declared extension"), or
/// `None`. Only the direct declaration; walk a chain with
/// [`crate::extension::extends_for`].
pub const EXTENDS: Option<&str> = None;
/// 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] = &[];