//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vta/backup/finalize-import`. Version: `1.0`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Supplies the password for an uploaded bundle and asks the recipient to decrypt it and adopt what is inside, or to rehearse that without committing. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vta/backup/finalize-import/1.0",
/// "title": "Payload",
/// "description": "Supplies the password for an uploaded bundle and asks the recipient to decrypt it and adopt what is inside, or to rehearse that without committing. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.",
/// "type": "object",
/// "required": [
/// "bundleId",
/// "password"
/// ],
/// "properties": {
/// "bundleId": {
/// "description": "Handle from the initiate-import descriptor. Opaque: a producer quotes what it was given and must not derive, guess or enumerate one.",
/// "type": "string",
/// "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
/// },
/// "confirm": {
/// "description": "True commits and replaces the agent's state; false rehearses and mutates nothing. Absent means true — stated in prose rather than as a schema `default` on purpose, because a materialised default makes an omitted member indistinguishable from a deliberate one, and this is the member separating a rehearsal from an irreversible replacement. See Defaulting.",
/// "type": "boolean"
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "password": {
/// "description": "Key-derivation input that unlocks the bundle. Supplied here rather than at initiate-import so the key never travels alongside the slot holding the ciphertext. `writeOnly` is the machine-readable form of the rule in Data carried: this member goes in and never comes back, so a generated client must not surface it in a response type, and a recipient must never log, echo or persist it.",
/// "writeOnly": true,
/// "type": "string",
/// "maxLength": 1024,
/// "minLength": 15,
/// "$comment": "No example value anywhere in this directory: a specimen password is the one thing implementers copy. minLength mirrors the export floor so a bundle that could be created can be read back."
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///Handle from the initiate-import descriptor. Opaque: a producer quotes what it was given and must not derive, guess or enumerate one.
#[serde(rename = "bundleId")]
pub bundle_id: PayloadBundleId,
///True commits and replaces the agent's state; false rehearses and mutates nothing. Absent means true — stated in prose rather than as a schema `default` on purpose, because a materialised default makes an omitted member indistinguishable from a deliberate one, and this is the member separating a rehearsal from an irreversible replacement. See Defaulting.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub confirm: ::std::option::Option<bool>,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Key-derivation input that unlocks the bundle. Supplied here rather than at initiate-import so the key never travels alongside the slot holding the ciphertext. `writeOnly` is the machine-readable form of the rule in Data carried: this member goes in and never comes back, so a generated client must not surface it in a response type, and a recipient must never log, echo or persist it.
pub password: PayloadPassword,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///Handle from the initiate-import descriptor. Opaque: a producer quotes what it was given and must not derive, guess or enumerate one.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Handle from the initiate-import descriptor. Opaque: a producer quotes what it was given and must not derive, guess or enumerate one.",
/// "type": "string",
/// "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadBundleId(::std::string::String);
impl ::std::ops::Deref for PayloadBundleId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadBundleId> for ::std::string::String {
fn from(value: PayloadBundleId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadBundleId {
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]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
)
.unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadBundleId {
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 PayloadBundleId {
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 PayloadBundleId {
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 PayloadBundleId {
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())
})
}
}
///Key-derivation input that unlocks the bundle. Supplied here rather than at initiate-import so the key never travels alongside the slot holding the ciphertext. `writeOnly` is the machine-readable form of the rule in Data carried: this member goes in and never comes back, so a generated client must not surface it in a response type, and a recipient must never log, echo or persist it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Key-derivation input that unlocks the bundle. Supplied here rather than at initiate-import so the key never travels alongside the slot holding the ciphertext. `writeOnly` is the machine-readable form of the rule in Data carried: this member goes in and never comes back, so a generated client must not surface it in a response type, and a recipient must never log, echo or persist it.",
/// "writeOnly": true,
/// "type": "string",
/// "maxLength": 1024,
/// "minLength": 15,
/// "$comment": "No example value anywhere in this directory: a specimen password is the one thing implementers copy. minLength mirrors the export floor so a bundle that could be created can be read back."
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadPassword(::std::string::String);
impl ::std::ops::Deref for PayloadPassword {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadPassword> for ::std::string::String {
fn from(value: PayloadPassword) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadPassword {
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());
}
if value.chars().count() < 15usize {
return Err("shorter than 15 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadPassword {
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 PayloadPassword {
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 PayloadPassword {
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 PayloadPassword {
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())
})
}
}
///Counts and a status, never contents. This document is the operator-held record of a replacement the agent's own trail no longer describes.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Counts and a status, never contents. This document is the operator-held record of a replacement the agent's own trail no longer describes.",
/// "type": "object",
/// "required": [
/// "aclCount",
/// "auditCount",
/// "bundleId",
/// "contextCount",
/// "keyCount",
/// "status"
/// ],
/// "properties": {
/// "aclCount": {
/// "description": "How many access-control entries the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "auditCount": {
/// "description": "How many audit entries the bundle holds. Zero for a bundle exported without its trail, which is a legitimate outcome and not a sign of a truncated bundle.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "bundleId": {
/// "description": "Echoed so the response stands alone as a record.",
/// "type": "string",
/// "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
/// },
/// "contextCount": {
/// "description": "How many trust contexts the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "importedSecretCount": {
/// "description": "How many secrets the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "keyCount": {
/// "description": "How many keys the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "message": {
/// "description": "Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "sourceDid": {
/// "description": "Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
/// },
/// "status": {
/// "description": "Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.",
/// "type": "string",
/// "enum": [
/// "preview",
/// "committed"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///How many access-control entries the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.
#[serde(rename = "aclCount")]
pub acl_count: u64,
///How many audit entries the bundle holds. Zero for a bundle exported without its trail, which is a legitimate outcome and not a sign of a truncated bundle.
#[serde(rename = "auditCount")]
pub audit_count: u64,
///Echoed so the response stands alone as a record.
#[serde(rename = "bundleId")]
pub bundle_id: ResponseBundleId,
///How many trust contexts the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.
#[serde(rename = "contextCount")]
pub context_count: u64,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///How many secrets the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.
#[serde(
rename = "importedSecretCount",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub imported_secret_count: ::std::option::Option<u64>,
///How many keys the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.
#[serde(rename = "keyCount")]
pub key_count: u64,
///Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub message: ::std::option::Option<ResponseMessage>,
///Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.
#[serde(
rename = "sourceDid",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub source_did: ::std::option::Option<ResponseSourceDid>,
///Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.
pub status: ResponseStatus,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///Echoed so the response stands alone as a record.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Echoed so the response stands alone as a record.",
/// "type": "string",
/// "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseBundleId(::std::string::String);
impl ::std::ops::Deref for ResponseBundleId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseBundleId> for ::std::string::String {
fn from(value: ResponseBundleId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseBundleId {
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]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
)
.unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseBundleId {
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 ResponseBundleId {
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 ResponseBundleId {
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 ResponseBundleId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseMessage(::std::string::String);
impl ::std::ops::Deref for ResponseMessage {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseMessage> for ::std::string::String {
fn from(value: ResponseMessage) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseMessage {
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 ResponseMessage {
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 ResponseMessage {
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 ResponseMessage {
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 ResponseMessage {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseSourceDid(::std::string::String);
impl ::std::ops::Deref for ResponseSourceDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseSourceDid> for ::std::string::String {
fn from(value: ResponseSourceDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseSourceDid {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseSourceDid {
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 ResponseSourceDid {
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 ResponseSourceDid {
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 ResponseSourceDid {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.",
/// "type": "string",
/// "enum": [
/// "preview",
/// "committed"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum ResponseStatus {
#[serde(rename = "preview")]
Preview,
#[serde(rename = "committed")]
Committed,
}
impl ::std::fmt::Display for ResponseStatus {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Preview => f.write_str("preview"),
Self::Committed => f.write_str("committed"),
}
}
}
impl ::std::str::FromStr for ResponseStatus {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"preview" => Ok(Self::Preview),
"committed" => Ok(Self::Committed),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ResponseStatus {
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 ResponseStatus {
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 ResponseStatus {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct Payload {
bundle_id: ::std::result::Result<super::PayloadBundleId, ::std::string::String>,
confirm: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
password: ::std::result::Result<super::PayloadPassword, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
bundle_id: Err("no value supplied for bundle_id".to_string()),
confirm: Ok(Default::default()),
ext: Ok(Default::default()),
password: Err("no value supplied for password".to_string()),
}
}
}
impl Payload {
pub fn bundle_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadBundleId>,
T::Error: ::std::fmt::Display,
{
self.bundle_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for bundle_id: {e}"));
self
}
pub fn confirm<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.confirm = value
.try_into()
.map_err(|e| format!("error converting supplied value for confirm: {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 password<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadPassword>,
T::Error: ::std::fmt::Display,
{
self.password = value
.try_into()
.map_err(|e| format!("error converting supplied value for password: {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 {
bundle_id: value.bundle_id?,
confirm: value.confirm?,
ext: value.ext?,
password: value.password?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
bundle_id: Ok(value.bundle_id),
confirm: Ok(value.confirm),
ext: Ok(value.ext),
password: Ok(value.password),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
acl_count: ::std::result::Result<u64, ::std::string::String>,
audit_count: ::std::result::Result<u64, ::std::string::String>,
bundle_id: ::std::result::Result<super::ResponseBundleId, ::std::string::String>,
context_count: ::std::result::Result<u64, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
imported_secret_count:
::std::result::Result<::std::option::Option<u64>, ::std::string::String>,
key_count: ::std::result::Result<u64, ::std::string::String>,
message: ::std::result::Result<
::std::option::Option<super::ResponseMessage>,
::std::string::String,
>,
source_did: ::std::result::Result<
::std::option::Option<super::ResponseSourceDid>,
::std::string::String,
>,
status: ::std::result::Result<super::ResponseStatus, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
acl_count: Err("no value supplied for acl_count".to_string()),
audit_count: Err("no value supplied for audit_count".to_string()),
bundle_id: Err("no value supplied for bundle_id".to_string()),
context_count: Err("no value supplied for context_count".to_string()),
ext: Ok(Default::default()),
imported_secret_count: Ok(Default::default()),
key_count: Err("no value supplied for key_count".to_string()),
message: Ok(Default::default()),
source_did: Ok(Default::default()),
status: Err("no value supplied for status".to_string()),
}
}
}
impl Response {
pub fn acl_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.acl_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for acl_count: {e}"));
self
}
pub fn audit_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.audit_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for audit_count: {e}"));
self
}
pub fn bundle_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseBundleId>,
T::Error: ::std::fmt::Display,
{
self.bundle_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for bundle_id: {e}"));
self
}
pub fn context_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.context_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for context_count: {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 imported_secret_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<u64>>,
T::Error: ::std::fmt::Display,
{
self.imported_secret_count = value.try_into().map_err(|e| {
format!("error converting supplied value for imported_secret_count: {e}")
});
self
}
pub fn key_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.key_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for key_count: {e}"));
self
}
pub fn message<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ResponseMessage>>,
T::Error: ::std::fmt::Display,
{
self.message = value
.try_into()
.map_err(|e| format!("error converting supplied value for message: {e}"));
self
}
pub fn source_did<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ResponseSourceDid>>,
T::Error: ::std::fmt::Display,
{
self.source_did = value
.try_into()
.map_err(|e| format!("error converting supplied value for source_did: {e}"));
self
}
pub fn status<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseStatus>,
T::Error: ::std::fmt::Display,
{
self.status = value
.try_into()
.map_err(|e| format!("error converting supplied value for status: {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 {
acl_count: value.acl_count?,
audit_count: value.audit_count?,
bundle_id: value.bundle_id?,
context_count: value.context_count?,
ext: value.ext?,
imported_secret_count: value.imported_secret_count?,
key_count: value.key_count?,
message: value.message?,
source_did: value.source_did?,
status: value.status?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
acl_count: Ok(value.acl_count),
audit_count: Ok(value.audit_count),
bundle_id: Ok(value.bundle_id),
context_count: Ok(value.context_count),
ext: Ok(value.ext),
imported_secret_count: Ok(value.imported_secret_count),
key_count: Ok(value.key_count),
message: Ok(value.message),
source_did: Ok(value.source_did),
status: Ok(value.status),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vta/backup/finalize-import/1.0";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Counts and a status, never contents. This document is the operator-held record of a replacement the agent's own trail no longer describes.\",\n \"properties\": {\n \"aclCount\": {\n \"description\": \"How many access-control entries the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"auditCount\": {\n \"description\": \"How many audit entries the bundle holds. Zero for a bundle exported without its trail, which is a legitimate outcome and not a sign of a truncated bundle.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"bundleId\": {\n \"description\": \"Echoed so the response stands alone as a record.\",\n \"pattern\": \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n \"type\": \"string\"\n },\n \"contextCount\": {\n \"description\": \"How many trust contexts the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"importedSecretCount\": {\n \"description\": \"How many secrets the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"keyCount\": {\n \"description\": \"How many keys the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"message\": {\n \"description\": \"Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"sourceDid\": {\n \"description\": \"Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"status\": {\n \"description\": \"Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.\",\n \"enum\": [\n \"preview\",\n \"committed\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"bundleId\",\n \"status\",\n \"keyCount\",\n \"aclCount\",\n \"contextCount\",\n \"auditCount\"\n ],\n \"title\": \"VTA Backup Finalize Import — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vta/backup/finalize-import/1.0\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Supplies the password for an uploaded bundle and asks the recipient to decrypt it and adopt what is inside, or to rehearse that without committing. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.\",\n \"properties\": {\n \"bundleId\": {\n \"description\": \"Handle from the initiate-import descriptor. Opaque: a producer quotes what it was given and must not derive, guess or enumerate one.\",\n \"pattern\": \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n \"type\": \"string\"\n },\n \"confirm\": {\n \"description\": \"True commits and replaces the agent's state; false rehearses and mutates nothing. Absent means true — stated in prose rather than as a schema `default` on purpose, because a materialised default makes an omitted member indistinguishable from a deliberate one, and this is the member separating a rehearsal from an irreversible replacement. See Defaulting.\",\n \"type\": \"boolean\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"password\": {\n \"$comment\": \"No example value anywhere in this directory: a specimen password is the one thing implementers copy. minLength mirrors the export floor so a bundle that could be created can be read back.\",\n \"description\": \"Key-derivation input that unlocks the bundle. Supplied here rather than at initiate-import so the key never travels alongside the slot holding the ciphertext. `writeOnly` is the machine-readable form of the rule in Data carried: this member goes in and never comes back, so a generated client must not surface it in a response type, and a recipient must never log, echo or persist it.\",\n \"maxLength\": 1024,\n \"minLength\": 15,\n \"type\": \"string\",\n \"writeOnly\": true\n }\n },\n \"required\": [\n \"bundleId\",\n \"password\"\n ],\n \"title\": \"VTA Backup — Finalize Import — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/vta/backup/finalize-import/1.0#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Counts and a status, never contents. This document is the operator-held record of a replacement the agent's own trail no longer describes.\",\n \"properties\": {\n \"aclCount\": {\n \"description\": \"How many access-control entries the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"auditCount\": {\n \"description\": \"How many audit entries the bundle holds. Zero for a bundle exported without its trail, which is a legitimate outcome and not a sign of a truncated bundle.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"bundleId\": {\n \"description\": \"Echoed so the response stands alone as a record.\",\n \"pattern\": \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n \"type\": \"string\"\n },\n \"contextCount\": {\n \"description\": \"How many trust contexts the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"importedSecretCount\": {\n \"description\": \"How many secrets the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"keyCount\": {\n \"description\": \"How many keys the bundle holds. A count, not an inventory — enough to recognise a bundle as plausibly the right one, and not enough to learn what is in it.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"message\": {\n \"description\": \"Operator-facing text elaborating on the outcome. Advisory: a producer must not parse it or derive behaviour from it, and a recipient must not put bundle contents or a secret in it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"sourceDid\": {\n \"description\": \"Identifier of the agent the bundle was exported from. The one identifier a preview returns, because the common import error is a valid bundle from the wrong agent and this is what shows it. Absent where the bundle does not record one.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"status\": {\n \"description\": \"Which the recipient did. Reported rather than left to be inferred from the request, so that a response is complete evidence on its own: `committed` says the agent was replaced, `preview` says it was not.\",\n \"enum\": [\n \"preview\",\n \"committed\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"bundleId\",\n \"status\",\n \"keyCount\",\n \"aclCount\",\n \"contextCount\",\n \"auditCount\"\n ],\n \"title\": \"VTA Backup Finalize Import — response payload\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
#[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:00000000-0000-4000-8000-000000000009\",\n \"type\": \"https://trusttasks.org/spec/vta/backup/finalize-import/1.0#request\",\n \"issuer\": \"did:example:operator\",\n \"recipient\": \"did:example:agent\",\n \"issuedAt\": \"2026-01-01T01:02:00Z\",\n \"threadId\": \"urn:uuid:00000000-0000-4000-8000-0000000000fe\",\n \"payload\": {\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\",\n \"password\": \"correct horse battery staple\",\n \"confirm\": false\n }\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:00000000-0000-4000-8000-00000000000a\",\n \"type\": \"https://trusttasks.org/spec/vta/backup/finalize-import/1.0#response\",\n \"issuer\": \"did:example:agent\",\n \"recipient\": \"did:example:operator\",\n \"issuedAt\": \"2026-01-01T01:02:01Z\",\n \"threadId\": \"urn:uuid:00000000-0000-4000-8000-0000000000fe\",\n \"payload\": {\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\",\n \"status\": \"preview\",\n \"sourceDid\": \"did:example:agent\",\n \"keyCount\": 12,\n \"aclCount\": 34,\n \"contextCount\": 3,\n \"auditCount\": 0,\n \"importedSecretCount\": 12,\n \"message\": \"Bundle is readable. Nothing has been changed.\"\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)] = &[
(
"Missing both `bundleId` and `password`. There is no current bundle and no recipient-held password — an absent handle must never resolve to one, on the task that replaces the agent.",
"{}",
),
(
"Missing `password`. A bundle is encrypted; there is no unpassworded path, and an absent member must not be read as one.",
"{\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\"\n}",
),
(
"`bundleId` that is not a UUID. The pattern is what keeps a crafted handle from reaching the recipient's store as a lookup key.",
"{\n \"bundleId\": \"most-recent\",\n \"password\": \"a sufficiently long secret\"\n}",
),
(
"`confirm` as the string \"false\". A producer intending a rehearsal would otherwise be read as truthy by a lenient consumer and get an irreversible commit — the exact substitution this member exists to prevent.",
"{\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\",\n \"confirm\": \"false\",\n \"password\": \"a sufficiently long secret\"\n}",
),
(
"Bare/unnamespaced ext key — SPEC §4.5.1 requires every immediate child of ext to be reverse-DNS namespaced.",
"{\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\",\n \"ext\": {\n \"bare-key\": {\n \"anything\": \"here\"\n }\n },\n \"password\": \"a sufficiently long secret\"\n}",
),
(
"Unknown top-level member — additionalProperties: false catches `sourceDid`. What the bundle came from is what the recipient reads out of it and reports; a producer asserting it would be asserting the one fact the preview exists to establish.",
"{\n \"bundleId\": \"9c858901-8a57-4791-81fe-4c455b099bc9\",\n \"password\": \"a sufficiently long secret\",\n \"sourceDid\": \"did:example:agent\"\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
);
}
}
}