//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/join-requests/manifest`. Version: `0.2`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
/**
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.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ClaimType",
/// "description": "\nThe 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.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^(x:)?[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "CommunityBranding",
/// "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.",
/// "type": "object",
/// "properties": {
/// "accentColor": {
/// "description": "An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.",
/// "type": "string",
/// "pattern": "^#[0-9a-fA-F]{6}$"
/// },
/// "displayName": {
/// "description": "The community's name as it asks to be shown.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "logoUrl": {
/// "description": "An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.",
/// "type": "string",
/// "format": "uri",
/// "maxLength": 2048,
/// "pattern": "^https://"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.",
/// "type": "string",
/// "pattern": "^#[0-9a-fA-F]{6}$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The community's name as it asks to be shown.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Criterion",
/// "type": "object",
/// "required": [
/// "id",
/// "presentationDefinition"
/// ],
/// "properties": {
/// "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.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "id": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "presentationDefinition": {
/// "description": "The presentation-definition an applicant must satisfy for this criterion (opaque here).",
/// "type": "object"
/// },
/// "requirementsDigest": {
/// "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. REQUIRED when `vetting` is present.",
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "vetting": {
/// "$ref": "#/definitions/VettingRequirements"
/// }
/// },
/// "additionalProperties": false,
/// "dependentRequired": {
/// "vetting": [
/// "requirementsDigest"
/// ]
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Criterion {
///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,
///The presentation-definition an applicant must satisfy for this criterion (opaque here).
#[serde(rename = "presentationDefinition")]
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. REQUIRED when `vetting` is present.
#[serde(
rename = "requirementsDigest",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub requirements_digest: ::std::option::Option<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()
}
}
///Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[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`.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DigestMultibase",
/// "description": "\nA cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
/// "examples": [
/// "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
/// ],
/// "type": "string",
/// "minLength": 16,
/// "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DigestMultibase(::std::string::String);
impl ::std::ops::Deref for DigestMultibase {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DigestMultibase> for ::std::string::String {
fn from(value: DigestMultibase) -> Self {
value.0
}
}
impl ::std::str::FromStr for DigestMultibase {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 16usize {
return Err("shorter than 16 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DigestMultibase {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "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.",
/// "type": "string",
/// "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)$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
/**
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.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ExtCritical",
/// "description": "\nNames 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"
/// ]
/// ],
/// "type": "array",
/// "items": {
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// },
/// "minItems": 1,
/// "uniqueItems": true
///}
/// ```
/// </details>
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct 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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vtc/join-requests/manifest/0.2",
/// "title": "Payload",
/// "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.",
/// "type": "object",
/// "properties": {
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "type": "object",
/// "required": [
/// "communityDid",
/// "criteria"
/// ],
/// "properties": {
/// "branding": {
/// "$ref": "#/definitions/CommunityBranding"
/// },
/// "communityDid": {
/// "type": "string",
/// "pattern": "^did:"
/// },
/// "criteria": {
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/Criterion"
/// }
/// },
/// "ext": {
/// "$ref": "#/definitions/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.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "type"
/// ],
/// "properties": {
/// "purpose": {
/// "description": "Why the community asks, in words shown to the applicant before they disclose.",
/// "type": "string",
/// "maxLength": 256
/// },
/// "required": {
/// "description": "False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.",
/// "default": true,
/// "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.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^(x:)?[a-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*)*$"
/// }
/// },
/// "additionalProperties": false
/// },
/// "maxItems": 32
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub branding: ::std::option::Option<CommunityBranding>,
#[serde(rename = "communityDid")]
pub community_did: ResponseCommunityDid,
pub criteria: ::std::vec::Vec<Criterion>,
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^did:"
///}
/// ```
/// </details>
#[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`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "type"
/// ],
/// "properties": {
/// "purpose": {
/// "description": "Why the community asks, in words shown to the applicant before they disclose.",
/// "type": "string",
/// "maxLength": 256
/// },
/// "required": {
/// "description": "False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.",
/// "default": true,
/// "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.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^(x:)?[a-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*)*$"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Why the community asks, in words shown to the applicant before they disclose.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "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.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^(x:)?[a-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*)*$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "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.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1,
/// "pattern": "^[a-z][a-zA-Z0-9]*$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "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.",
/// "type": "string",
/// "enum": [
/// "inPerson",
/// "video",
/// "priorAcquaintance"
/// ]
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "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.",
/// "type": "string",
/// "enum": [
/// "none",
/// "communityColleague",
/// "sameEmployer",
/// "family",
/// "otherPersonal"
/// ]
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "VettingRequirements",
/// "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.",
/// "type": "object",
/// "required": [
/// "acceptedMethods",
/// "eligibleVetters",
/// "minStatements",
/// "statementType",
/// "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.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/VettingDocumentation"
/// },
/// "minItems": 1,
/// "uniqueItems": true
/// },
/// "acceptedMethods": {
/// "description": "Methods whose statements count at all.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/VettingMethod"
/// },
/// "minItems": 1,
/// "uniqueItems": true
/// },
/// "decisionSla": {
/// "$ref": "#/definitions/Duration"
/// },
/// "eligibleVetters": {
/// "description": "How a vetter's eligibility is established.",
/// "type": "object",
/// "required": [
/// "role"
/// ],
/// "properties": {
/// "role": {
/// "description": "The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$"
/// }
/// },
/// "additionalProperties": false
/// },
/// "ext": {
/// "description": "\nEcosystem-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.",
/// "$ref": "#/definitions/Ext"
/// },
/// "extCritical": {
/// "description": "\nNamespaces 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.",
/// "$ref": "#/definitions/ExtCritical"
/// },
/// "governanceFrameworkUrl": {
/// "description": "Where the community's vetting governance — including the attestation text vetters sign — is published.",
/// "type": "string",
/// "format": "uri",
/// "maxLength": 2048,
/// "pattern": "^https://"
/// },
/// "independence": {
/// "description": "Caps on how much evidence may come from people close to the applicant. Absent: no caps.",
/// "type": "object",
/// "properties": {
/// "maxByDeclaredRelationship": {
/// "description": "The most counted statements that may come from vetters declaring each relationship — e.g. `{ \"family\": 0 }`.",
/// "type": "object",
/// "additionalProperties": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/VettingRelationship"
/// }
/// },
/// "requireConsistentIdentityCommitment": {
/// "description": "When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false
/// },
/// "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.",
/// "type": "string",
/// "enum": [
/// "required",
/// "optional",
/// "none"
/// ]
/// },
/// "maxStatementAge": {
/// "$ref": "#/definitions/Duration"
/// },
/// "minByMethod": {
/// "description": "Per-method floors within `minStatements` — e.g. `{ \"inPerson\": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.",
/// "type": "object",
/// "additionalProperties": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/VettingMethod"
/// }
/// },
/// "minStatements": {
/// "description": "How many counted statements are needed, counting each vetter once however many DIDs they hold.",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "optionalClaims": {
/// "description": "Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/ClaimType"
/// },
/// "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.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/ClaimType"
/// },
/// "uniqueItems": true
/// },
/// "requirementsGrace": {
/// "$ref": "#/definitions/Duration"
/// },
/// "statementType": {
/// "description": "The endorsement type URI a counted vetting statement carries as `credentialSubject.endorsement.type`, as registered with the community via vtc/endorsement-types/register.",
/// "type": "string",
/// "format": "uri",
/// "maxLength": 512,
/// "minLength": 1
/// },
/// "version": {
/// "description": "Version of this requirements object's shape. `0.1` for the members defined here.",
/// "type": "string",
/// "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
/// }
/// },
/// "additionalProperties": true
///}
/// ```
/// </details>
#[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 endorsement type URI a counted vetting statement carries as `credentialSubject.endorsement.type`, as registered with the community via vtc/endorsement-types/register.
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "How a vetter's eligibility is established.",
/// "type": "object",
/// "required": [
/// "role"
/// ],
/// "properties": {
/// "role": {
/// "description": "The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct VettingRequirementsEligibleVetters {
///The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.
pub role: VettingRequirementsEligibleVettersRole,
}
impl VettingRequirementsEligibleVetters {
pub fn builder() -> builder::VettingRequirementsEligibleVetters {
Default::default()
}
}
///The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$"
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Caps on how much evidence may come from people close to the applicant. Absent: no caps.",
/// "type": "object",
/// "properties": {
/// "maxByDeclaredRelationship": {
/// "description": "The most counted statements that may come from vetters declaring each relationship — e.g. `{ \"family\": 0 }`.",
/// "type": "object",
/// "additionalProperties": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "propertyNames": {
/// "$ref": "#/definitions/VettingRelationship"
/// }
/// },
/// "requireConsistentIdentityCommitment": {
/// "description": "When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "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.",
/// "type": "string",
/// "enum": [
/// "required",
/// "optional",
/// "none"
/// ]
///}
/// ```
/// </details>
#[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.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Version of this requirements object's shape. `0.1` for the members defined here.",
/// "type": "string",
/// "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
///}
/// ```
/// </details>
#[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 {
description: ::std::result::Result<
::std::option::Option<super::CriterionDescription>,
::std::string::String,
>,
id: ::std::result::Result<super::CriterionId, ::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<
::std::option::Option<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 {
description: Ok(Default::default()),
id: Err("no value supplied for id".to_string()),
presentation_definition: Err(
"no value supplied for presentation_definition".to_string()
),
requirements_digest: Ok(Default::default()),
vetting: Ok(Default::default()),
}
}
}
impl Criterion {
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 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<::std::option::Option<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 {
description: value.description?,
id: value.id?,
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 {
description: Ok(value.description),
id: Ok(value.id),
presentation_definition: Ok(value.presentation_definition),
requirements_digest: Ok(value.requirements_digest),
vetting: Ok(value.vetting),
}
}
}
#[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>,
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()),
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 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?,
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),
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.2";
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"ClaimType\": {\n \"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.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^(x:)?[a-z][a-zA-Z0-9]*(\\\\.[a-z][a-zA-Z0-9]*)*$\",\n \"title\": \"ClaimType\",\n \"type\": \"string\"\n },\n \"CommunityBranding\": {\n \"additionalProperties\": false,\n \"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.\",\n \"properties\": {\n \"accentColor\": {\n \"description\": \"An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.\",\n \"pattern\": \"^#[0-9a-fA-F]{6}$\",\n \"type\": \"string\"\n },\n \"displayName\": {\n \"description\": \"The community's name as it asks to be shown.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"logoUrl\": {\n \"description\": \"An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.\",\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n }\n },\n \"title\": \"CommunityBranding\",\n \"type\": \"object\"\n },\n \"Criterion\": {\n \"additionalProperties\": false,\n \"dependentRequired\": {\n \"vetting\": [\n \"requirementsDigest\"\n ]\n },\n \"properties\": {\n \"description\": {\n \"description\": \"Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"id\": {\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"presentationDefinition\": {\n \"description\": \"The presentation-definition an applicant must satisfy for this criterion (opaque here).\",\n \"type\": \"object\"\n },\n \"requirementsDigest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"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. REQUIRED when `vetting` is present.\"\n },\n \"vetting\": {\n \"$ref\": \"#/$defs/VettingRequirements\"\n }\n },\n \"required\": [\n \"id\",\n \"presentationDefinition\"\n ],\n \"title\": \"Criterion\",\n \"type\": \"object\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"Duration\": {\n \"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.\",\n \"maxLength\": 32,\n \"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)$\",\n \"title\": \"Duration\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"ExtCritical\": {\n \"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.\",\n \"examples\": [\n [\n \"org.openvtc.hidden-vetting\"\n ]\n ],\n \"items\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\",\n \"type\": \"string\"\n },\n \"minItems\": 1,\n \"title\": \"ExtCritical\",\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"properties\": {\n \"branding\": {\n \"$ref\": \"#/$defs/CommunityBranding\"\n },\n \"communityDid\": {\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"criteria\": {\n \"items\": {\n \"$ref\": \"#/$defs/Criterion\"\n },\n \"type\": \"array\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"requestedAttributes\": {\n \"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.\",\n \"items\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"purpose\": {\n \"description\": \"Why the community asks, in words shown to the applicant before they disclose.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"required\": {\n \"default\": true,\n \"description\": \"False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.\",\n \"type\": \"boolean\"\n },\n \"type\": {\n \"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.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^(x:)?[a-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*)*$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"type\"\n ],\n \"type\": \"object\"\n },\n \"maxItems\": 32,\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"communityDid\",\n \"criteria\"\n ],\n \"title\": \"VTC Join-Requests Manifest — response payload\",\n \"type\": \"object\"\n },\n \"VettingDocumentation\": {\n \"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.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][a-zA-Z0-9]*$\",\n \"title\": \"VettingDocumentation\",\n \"type\": \"string\"\n },\n \"VettingMethod\": {\n \"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.\",\n \"enum\": [\n \"inPerson\",\n \"video\",\n \"priorAcquaintance\"\n ],\n \"title\": \"VettingMethod\",\n \"type\": \"string\"\n },\n \"VettingRelationship\": {\n \"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.\",\n \"enum\": [\n \"none\",\n \"communityColleague\",\n \"sameEmployer\",\n \"family\",\n \"otherPersonal\"\n ],\n \"title\": \"VettingRelationship\",\n \"type\": \"string\"\n },\n \"VettingRequirements\": {\n \"additionalProperties\": true,\n \"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.\",\n \"properties\": {\n \"acceptedDocumentClasses\": {\n \"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.\",\n \"items\": {\n \"$ref\": \"#/$defs/VettingDocumentation\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"acceptedMethods\": {\n \"description\": \"Methods whose statements count at all.\",\n \"items\": {\n \"$ref\": \"#/$defs/VettingMethod\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"decisionSla\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"eligibleVetters\": {\n \"additionalProperties\": false,\n \"description\": \"How a vetter's eligibility is established.\",\n \"properties\": {\n \"role\": {\n \"description\": \"The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_-]*$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"role\"\n ],\n \"type\": \"object\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"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.\"\n },\n \"extCritical\": {\n \"$ref\": \"#/$defs/ExtCritical\",\n \"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.\"\n },\n \"governanceFrameworkUrl\": {\n \"description\": \"Where the community's vetting governance — including the attestation text vetters sign — is published.\",\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n },\n \"independence\": {\n \"additionalProperties\": false,\n \"description\": \"Caps on how much evidence may come from people close to the applicant. Absent: no caps.\",\n \"properties\": {\n \"maxByDeclaredRelationship\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"description\": \"The most counted statements that may come from vetters declaring each relationship — e.g. `{ \\\"family\\\": 0 }`.\",\n \"propertyNames\": {\n \"$ref\": \"#/$defs/VettingRelationship\"\n },\n \"type\": \"object\"\n },\n \"requireConsistentIdentityCommitment\": {\n \"description\": \"When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.\",\n \"type\": \"boolean\"\n }\n },\n \"type\": \"object\"\n },\n \"invitation\": {\n \"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.\",\n \"enum\": [\n \"required\",\n \"optional\",\n \"none\"\n ],\n \"type\": \"string\"\n },\n \"maxStatementAge\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"minByMethod\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"description\": \"Per-method floors within `minStatements` — e.g. `{ \\\"inPerson\\\": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.\",\n \"propertyNames\": {\n \"$ref\": \"#/$defs/VettingMethod\"\n },\n \"type\": \"object\"\n },\n \"minStatements\": {\n \"description\": \"How many counted statements are needed, counting each vetter once however many DIDs they hold.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"optionalClaims\": {\n \"description\": \"Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.\",\n \"items\": {\n \"$ref\": \"#/$defs/ClaimType\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"requiredClaims\": {\n \"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.\",\n \"items\": {\n \"$ref\": \"#/$defs/ClaimType\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"requirementsGrace\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"statementType\": {\n \"description\": \"The endorsement type URI a counted vetting statement carries as `credentialSubject.endorsement.type`, as registered with the community via vtc/endorsement-types/register.\",\n \"format\": \"uri\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"version\": {\n \"description\": \"Version of this requirements object's shape. `0.1` for the members defined here.\",\n \"pattern\": \"^(0|[1-9][0-9]*)\\\\.(0|[1-9][0-9]*)$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"version\",\n \"statementType\",\n \"minStatements\",\n \"acceptedMethods\",\n \"eligibleVetters\"\n ],\n \"title\": \"VettingRequirements\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"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.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n }\n },\n \"title\": \"VTC Join-Requests Manifest — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2#response";
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"ClaimType\": {\n \"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.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^(x:)?[a-z][a-zA-Z0-9]*(\\\\.[a-z][a-zA-Z0-9]*)*$\",\n \"title\": \"ClaimType\",\n \"type\": \"string\"\n },\n \"CommunityBranding\": {\n \"additionalProperties\": false,\n \"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.\",\n \"properties\": {\n \"accentColor\": {\n \"description\": \"An sRGB colour as `#rrggbb`, compared case-insensitively. A community SHOULD write it in lower case.\",\n \"pattern\": \"^#[0-9a-fA-F]{6}$\",\n \"type\": \"string\"\n },\n \"displayName\": {\n \"description\": \"The community's name as it asks to be shown.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"logoUrl\": {\n \"description\": \"An https URL of the community's logo. Fetched by the client, so an untrusted image from wherever it points.\",\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n }\n },\n \"title\": \"CommunityBranding\",\n \"type\": \"object\"\n },\n \"Criterion\": {\n \"additionalProperties\": false,\n \"dependentRequired\": {\n \"vetting\": [\n \"requirementsDigest\"\n ]\n },\n \"properties\": {\n \"description\": {\n \"description\": \"Plain-language summary of the criterion, authored by the community and shown to prospective applicants. Informative: where it and `vetting` disagree, `vetting` governs.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"id\": {\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"presentationDefinition\": {\n \"description\": \"The presentation-definition an applicant must satisfy for this criterion (opaque here).\",\n \"type\": \"object\"\n },\n \"requirementsDigest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"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. REQUIRED when `vetting` is present.\"\n },\n \"vetting\": {\n \"$ref\": \"#/$defs/VettingRequirements\"\n }\n },\n \"required\": [\n \"id\",\n \"presentationDefinition\"\n ],\n \"title\": \"Criterion\",\n \"type\": \"object\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"Duration\": {\n \"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.\",\n \"maxLength\": 32,\n \"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)$\",\n \"title\": \"Duration\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"ExtCritical\": {\n \"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.\",\n \"examples\": [\n [\n \"org.openvtc.hidden-vetting\"\n ]\n ],\n \"items\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\",\n \"type\": \"string\"\n },\n \"minItems\": 1,\n \"title\": \"ExtCritical\",\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"properties\": {\n \"branding\": {\n \"$ref\": \"#/$defs/CommunityBranding\"\n },\n \"communityDid\": {\n \"pattern\": \"^did:\",\n \"type\": \"string\"\n },\n \"criteria\": {\n \"items\": {\n \"$ref\": \"#/$defs/Criterion\"\n },\n \"type\": \"array\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"requestedAttributes\": {\n \"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.\",\n \"items\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"purpose\": {\n \"description\": \"Why the community asks, in words shown to the applicant before they disclose.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"required\": {\n \"default\": true,\n \"description\": \"False for an attribute the applicant may decline. A submission missing a required one is refused with vtc/join-requests/submit:attributesMissing.\",\n \"type\": \"boolean\"\n },\n \"type\": {\n \"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.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^(x:)?[a-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*)*$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"type\"\n ],\n \"type\": \"object\"\n },\n \"maxItems\": 32,\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"communityDid\",\n \"criteria\"\n ],\n \"title\": \"VTC Join-Requests Manifest — response payload\",\n \"type\": \"object\"\n },\n \"VettingDocumentation\": {\n \"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.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][a-zA-Z0-9]*$\",\n \"title\": \"VettingDocumentation\",\n \"type\": \"string\"\n },\n \"VettingMethod\": {\n \"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.\",\n \"enum\": [\n \"inPerson\",\n \"video\",\n \"priorAcquaintance\"\n ],\n \"title\": \"VettingMethod\",\n \"type\": \"string\"\n },\n \"VettingRelationship\": {\n \"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.\",\n \"enum\": [\n \"none\",\n \"communityColleague\",\n \"sameEmployer\",\n \"family\",\n \"otherPersonal\"\n ],\n \"title\": \"VettingRelationship\",\n \"type\": \"string\"\n },\n \"VettingRequirements\": {\n \"additionalProperties\": true,\n \"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.\",\n \"properties\": {\n \"acceptedDocumentClasses\": {\n \"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.\",\n \"items\": {\n \"$ref\": \"#/$defs/VettingDocumentation\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"acceptedMethods\": {\n \"description\": \"Methods whose statements count at all.\",\n \"items\": {\n \"$ref\": \"#/$defs/VettingMethod\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"decisionSla\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"eligibleVetters\": {\n \"additionalProperties\": false,\n \"description\": \"How a vetter's eligibility is established.\",\n \"properties\": {\n \"role\": {\n \"description\": \"The role named in a community-issued `CommunityRole` endorsement credential (see `vtc/vetting/vetters/grant/0.1`). A statement counts only if its issuer holds that credential.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_-]*$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"role\"\n ],\n \"type\": \"object\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"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.\"\n },\n \"extCritical\": {\n \"$ref\": \"#/$defs/ExtCritical\",\n \"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.\"\n },\n \"governanceFrameworkUrl\": {\n \"description\": \"Where the community's vetting governance — including the attestation text vetters sign — is published.\",\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n },\n \"independence\": {\n \"additionalProperties\": false,\n \"description\": \"Caps on how much evidence may come from people close to the applicant. Absent: no caps.\",\n \"properties\": {\n \"maxByDeclaredRelationship\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"description\": \"The most counted statements that may come from vetters declaring each relationship — e.g. `{ \\\"family\\\": 0 }`.\",\n \"propertyNames\": {\n \"$ref\": \"#/$defs/VettingRelationship\"\n },\n \"type\": \"object\"\n },\n \"requireConsistentIdentityCommitment\": {\n \"description\": \"When true, every counted statement must carry the same identity commitment — all vetters verified the same claimed identity. Absent: false.\",\n \"type\": \"boolean\"\n }\n },\n \"type\": \"object\"\n },\n \"invitation\": {\n \"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.\",\n \"enum\": [\n \"required\",\n \"optional\",\n \"none\"\n ],\n \"type\": \"string\"\n },\n \"maxStatementAge\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"minByMethod\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"description\": \"Per-method floors within `minStatements` — e.g. `{ \\\"inPerson\\\": 1 }`. Every method named MUST also be in `acceptedMethods`. Absent: no method floor.\",\n \"propertyNames\": {\n \"$ref\": \"#/$defs/VettingMethod\"\n },\n \"type\": \"object\"\n },\n \"minStatements\": {\n \"description\": \"How many counted statements are needed, counting each vetter once however many DIDs they hold.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"optionalClaims\": {\n \"description\": \"Claim types an applicant MAY add to the card and a vetter MAY verify. They never affect whether a statement counts.\",\n \"items\": {\n \"$ref\": \"#/$defs/ClaimType\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"requiredClaims\": {\n \"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.\",\n \"items\": {\n \"$ref\": \"#/$defs/ClaimType\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"requirementsGrace\": {\n \"$ref\": \"#/$defs/Duration\"\n },\n \"statementType\": {\n \"description\": \"The endorsement type URI a counted vetting statement carries as `credentialSubject.endorsement.type`, as registered with the community via vtc/endorsement-types/register.\",\n \"format\": \"uri\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"version\": {\n \"description\": \"Version of this requirements object's shape. `0.1` for the members defined here.\",\n \"pattern\": \"^(0|[1-9][0-9]*)\\\\.(0|[1-9][0-9]*)$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"version\",\n \"statementType\",\n \"minStatements\",\n \"acceptedMethods\",\n \"eligibleVetters\"\n ],\n \"title\": \"VettingRequirements\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[];
#[cfg(test)]
mod conformance {
//! Round-trip tests harvested from the spec's `spec.md`,
//! plus a `rejects_invalid_examples` test for any fixtures
//! in `payload.invalid-examples.json` (validate feature).
#[test]
fn request_example_1() {
const JSON: &str = "{\n \"id\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c01\",\n \"type\": \"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2\",\n \"recipient\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"issuedAt\": \"2026-09-12T08:00:00Z\",\n \"payload\": {}\n}\n";
let doc: crate::TrustTask<super::Payload> =
serde_json::from_str(JSON).expect("deserialize request example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "request example failed round-trip");
}
#[test]
fn response_example_1() {
const JSON: &str = "{\n \"id\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c02\",\n \"type\": \"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2#response\",\n \"threadId\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c01\",\n \"issuer\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"issuedAt\": \"2026-09-12T08:00:01Z\",\n \"payload\": {\n \"communityDid\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"criteria\": [\n {\n \"id\": \"kernel-developer\",\n \"description\": \"Two kernel vetters must confirm who you are. At least one must meet you in person.\",\n \"presentationDefinition\": {\n \"credentials\": [\n {\n \"id\": \"vetting\",\n \"format\": \"ldp_vc\",\n \"multiple\": true,\n \"meta\": { \"type_values\": [[\"EndorsementCredential\"]] }\n }\n ]\n },\n \"vetting\": {\n \"version\": \"0.1\",\n \"statementType\": \"https://firstperson.network/endorsements/identity-vetting/0.1\",\n \"minStatements\": 2,\n \"minByMethod\": { \"inPerson\": 1 },\n \"acceptedMethods\": [\"inPerson\", \"video\", \"priorAcquaintance\"],\n \"requiredClaims\": [\"name.legal\"],\n \"optionalClaims\": [\"account.handle\", \"url.homepage\"],\n \"maxStatementAge\": \"P120D\",\n \"eligibleVetters\": { \"role\": \"vetter\" },\n \"independence\": {\n \"maxByDeclaredRelationship\": { \"family\": 0, \"sameEmployer\": 1 },\n \"requireConsistentIdentityCommitment\": true\n },\n \"invitation\": \"optional\",\n \"decisionSla\": \"P14D\",\n \"requirementsGrace\": \"P30D\",\n \"governanceFrameworkUrl\": \"https://kernel-vtc.example/governance#vetting\"\n },\n \"requirementsDigest\": \"zQmYZQN9M169SXXg1sZdpNCDAjoVajkrPLaFhJ6A4mecQfC\"\n },\n {\n \"id\": \"invited\",\n \"description\": \"Present an invitation issued by this community.\",\n \"presentationDefinition\": {\n \"credentials\": [\n {\n \"id\": \"invitation\",\n \"format\": \"ldp_vc\",\n \"meta\": { \"type_values\": [[\"InvitationCredential\"]] }\n }\n ]\n }\n }\n ]\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
#[test]
fn response_example_2() {
const JSON: &str = "{\n \"id\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c04\",\n \"type\": \"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2#response\",\n \"threadId\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c03\",\n \"issuer\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"issuedAt\": \"2026-09-12T08:00:01Z\",\n \"payload\": {\n \"communityDid\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"criteria\": [\n {\n \"id\": \"kernel-developer-private\",\n \"description\": \"Two kernel vetters must confirm who you are, and the community is told how many vouched for you without being told who.\",\n \"presentationDefinition\": {\n \"credentials\": [\n {\n \"id\": \"vetting\",\n \"format\": \"ldp_vc\",\n \"multiple\": true,\n \"meta\": {\n \"type_values\": [\n [\n \"EndorsementCredential\"\n ]\n ]\n }\n }\n ]\n },\n \"vetting\": {\n \"version\": \"0.1\",\n \"statementType\": \"https://firstperson.network/endorsements/identity-vetting/0.1\",\n \"minStatements\": 2,\n \"minByMethod\": {\n \"inPerson\": 1\n },\n \"acceptedMethods\": [\n \"inPerson\",\n \"video\"\n ],\n \"requiredClaims\": [\n \"name.legal\"\n ],\n \"maxStatementAge\": \"P120D\",\n \"eligibleVetters\": {\n \"role\": \"vetter\"\n },\n \"invitation\": \"none\",\n \"decisionSla\": \"P14D\",\n \"ext\": {\n \"org.openvtc.hidden-vetting\": {\n \"suite\": \"ps-ddh-bls12381\",\n \"helperKey\": \"zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqn97FrCvKsrXJ8uPGZvRZzKVQvKKQBYJnGm4VnGWTGqCgKPsNjCNhbXJgHLPJwCJHKxqLvDfPqFcMhJQFYrJKxCgKPs\",\n \"tokenKey\": \"zUC7YNfqQjHvbYWVYKxPvqJJ8ycDPZ4pGQHNqEwUbNLPjYhLcKGVKqWMxQfVzYjWQEyPQpLZnVvBqzKhQxNWbGYJRcPvMKnDLbYqWQJPKZGvYHcMXrLPqDvbNKcYzWMqJHLPvKx\",\n \"vetterLabels\": [\n \"vetter/2026-10\",\n \"vetter/2026-09\"\n ],\n \"tokenLabels\": [\n \"token/2026-10\",\n \"token/2026-09\"\n ]\n }\n },\n \"extCritical\": [\n \"org.openvtc.hidden-vetting\"\n ],\n \"governanceFrameworkUrl\": \"https://kernel-vtc.example/governance#vetting\"\n },\n \"requirementsDigest\": \"zQmQUdMbmzC6HZtWwSkrqt88djK9oC9b7sabrNuxnDZ63Vd\"\n }\n ]\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
#[test]
fn response_example_3() {
const JSON: &str = "{\n \"id\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c04\",\n \"type\": \"https://trusttasks.org/spec/vtc/join-requests/manifest/0.2#response\",\n \"threadId\": \"urn:uuid:5b0f3c1e-7d2a-4c8e-9f41-2a6b8d0e1c03\",\n \"issuer\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"issuedAt\": \"2026-09-12T08:05:01Z\",\n \"payload\": {\n \"communityDid\": \"did:webvh:QmVtcScid:kernel-vtc.example\",\n \"criteria\": [\n {\n \"id\": \"invited\",\n \"description\": \"Present an invitation issued by this community.\",\n \"presentationDefinition\": {\n \"credentials\": [\n {\n \"id\": \"invitation\",\n \"format\": \"ldp_vc\",\n \"meta\": { \"type_values\": [[\"InvitationCredential\"]] }\n }\n ]\n }\n }\n ],\n \"branding\": {\n \"displayName\": \"Kernel VTC\",\n \"accentColor\": \"#1f6feb\",\n \"logoUrl\": \"https://kernel-vtc.example/assets/logo.svg\"\n }\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
/// Each fixture in `payload.invalid-examples.json` MUST be
/// rejected by at least one of: serde deserialization, or
/// JSON-Schema validation under the `validate` feature. The
/// fixture file documents the producer-side bug class that
/// each payload exemplifies; this generated test pins it.
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
(
"The request takes no parameters; a stray field is rejected.",
"{\n \"communityDid\": \"did:webvh:x\"\n}",
),
(
"Nor does the request name a criterion to fetch. A per-criterion read would let the community see which requirement a caller cares about — the one thing discovery must not reveal.",
"{\n \"criterionId\": \"kernel-developer\"\n}",
),
(
"`ext` keys must be reverse-DNS namespaces (SPEC §4.5.1); a bare word is not one.",
"{\n \"ext\": {\n \"mine\": {\n \"a\": 1\n }\n }\n}",
),
];
for (i, (note, raw)) in fixtures.iter().enumerate() {
let value: serde_json::Value = match serde_json::from_str(raw) {
Ok(v) => v,
Err(_) => continue,
};
let serde_ok = serde_json::from_value::<super::Payload>(value.clone()).is_ok();
let schema_ok = super::Payload::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid-example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}