//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `git-ns/projection/show`. Version: `0.1`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///The record's context as published: what the TRQP record has no member of its own for. Not itself governed by this specification — it is the projector's internal shape, carried through unchanged, and a consumer MUST treat unrecognised members as opaque rather than failing on them.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Context",
/// "description": "The record's context as published: what the TRQP record has no member of its own for. Not itself governed by this specification — it is the projector's internal shape, carried through unchanged, and a consumer MUST treat unrecognised members as opaque rather than failing on them.",
/// "type": "object",
/// "required": [
/// "framework"
/// ],
/// "properties": {
/// "activeFrom": {
/// "description": "When the underlying right took effect. Absent for a role-derived tuple, which has no grant time of its own.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "activeTo": {
/// "description": "When the underlying right expires. Absent: no expiry, or the longest of two merged sources' expiries once neither is absent.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "framework": {
/// "description": "The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
/// },
/// "impliedBy": {
/// "description": "Present when this tuple publishes a right implied by another recorded right (a repository's `git.commit.sign` implied by its `git.repo.own`, or a namespace's implied rights). Names the right that implies it.",
/// "$ref": "#/definitions/Right"
/// },
/// "origin": {
/// "description": "Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.",
/// "type": "string",
/// "enum": [
/// "roleDerived"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Context {
///When the underlying right took effect. Absent for a role-derived tuple, which has no grant time of its own.
#[serde(
rename = "activeFrom",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub active_from: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///When the underlying right expires. Absent: no expiry, or the longest of two merged sources' expiries once neither is absent.
#[serde(
rename = "activeTo",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub active_to: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.
pub framework: ContextFramework,
///Present when this tuple publishes a right implied by another recorded right (a repository's `git.commit.sign` implied by its `git.repo.own`, or a namespace's implied rights). Names the right that implies it.
#[serde(
rename = "impliedBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub implied_by: ::std::option::Option<Right>,
///Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub origin: ::std::option::Option<ContextOrigin>,
}
impl Context {
pub fn builder() -> builder::Context {
Default::default()
}
}
///The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ContextFramework(::std::string::String);
impl ::std::ops::Deref for ContextFramework {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ContextFramework> for ::std::string::String {
fn from(value: ContextFramework) -> Self {
value.0
}
}
impl ::std::str::FromStr for ContextFramework {
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 ContextFramework {
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 ContextFramework {
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 ContextFramework {
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 ContextFramework {
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())
})
}
}
///Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.",
/// "type": "string",
/// "enum": [
/// "roleDerived"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum ContextOrigin {
#[serde(rename = "roleDerived")]
RoleDerived,
}
impl ::std::fmt::Display for ContextOrigin {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::RoleDerived => f.write_str("roleDerived"),
}
}
}
impl ::std::str::FromStr for ContextOrigin {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"roleDerived" => Ok(Self::RoleDerived),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ContextOrigin {
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 ContextOrigin {
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 ContextOrigin {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Did",
/// "description": "A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Did(::std::string::String);
impl ::std::ops::Deref for Did {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Did> for ::std::string::String {
fn from(value: Did) -> Self {
value.0
}
}
impl ::std::str::FromStr for Did {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Did {
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 Did {
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 Did {
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 Did {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A community administrator reads what the VTC has published (and what it still owes) to the Trust Registry for its git namespaces. 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/git-ns/projection/show/0.1",
/// "title": "Payload",
/// "description": "A community administrator reads what the VTC has published (and what it still owes) to the Trust Registry for its git namespaces. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.",
/// "type": "object",
/// "properties": {
/// "cursor": {
/// "description": "Opaque continuation token from a previous response's `nextCursor`. A consumer supplying a `cursor` MUST NOT also change `resource` from the request that produced it — start a fresh query instead.",
/// "type": "string"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "limit": {
/// "description": "Maximum published records to return in this page. A VTC clamps to 1..=500 (default 100).",
/// "type": "integer",
/// "maximum": 500.0,
/// "minimum": 1.0
/// },
/// "resource": {
/// "description": "Only published records on this resource or inside it. Absent: every resource.",
/// "$ref": "#/definitions/Resource"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///Opaque continuation token from a previous response's `nextCursor`. A consumer supplying a `cursor` MUST NOT also change `resource` from the request that produced it — start a fresh query instead.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub cursor: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Maximum published records to return in this page. A VTC clamps to 1..=500 (default 100).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub limit: ::std::option::Option<::std::num::NonZeroU64>,
///Only published records on this resource or inside it. Absent: every resource.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub resource: ::std::option::Option<Resource>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
cursor: Default::default(),
ext: Default::default(),
limit: Default::default(),
resource: Default::default(),
}
}
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///One record the VTC has published to the Trust Registry.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PublishedRow",
/// "description": "One record the VTC has published to the Trust Registry.",
/// "type": "object",
/// "required": [
/// "action",
/// "context",
/// "entity",
/// "publishedAt",
/// "resource"
/// ],
/// "properties": {
/// "action": {
/// "description": "The TRQP action the record is published under.",
/// "$ref": "#/definitions/Right"
/// },
/// "context": {
/// "$ref": "#/definitions/Context"
/// },
/// "entity": {
/// "description": "The DID the record is about — the right's subject.",
/// "$ref": "#/definitions/Did"
/// },
/// "publishedAt": {
/// "description": "When the VTC last wrote this record to the registry.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "resource": {
/// "$ref": "#/definitions/Resource"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct PublishedRow {
///The TRQP action the record is published under.
pub action: Right,
pub context: Context,
///The DID the record is about — the right's subject.
pub entity: Did,
///When the VTC last wrote this record to the registry.
#[serde(rename = "publishedAt")]
pub published_at: ::chrono::DateTime<::chrono::offset::Utc>,
pub resource: Resource,
}
impl PublishedRow {
pub fn builder() -> builder::PublishedRow {
Default::default()
}
}
///A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Resource",
/// "description": "A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.",
/// "type": "string",
/// "maxLength": 455,
/// "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Resource(::std::string::String);
impl ::std::ops::Deref for Resource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Resource> for ::std::string::String {
fn from(value: Resource) -> Self {
value.0
}
}
impl ::std::str::FromStr for Resource {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 455usize {
return Err("longer than 455 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Resource {
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 Resource {
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 Resource {
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 Resource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///What is published, and how far it is from what the VTC's records currently call for.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "What is published, and how far it is from what the VTC's records currently call for.",
/// "type": "object",
/// "required": [
/// "pendingChanges",
/// "published",
/// "registryConfigured"
/// ],
/// "properties": {
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "nextCursor": {
/// "description": "Continuation token for the next page of `published`, or `null` when this is the last.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "pendingChanges": {
/// "description": "How many records the next reconciliation pass will add, remove or replace to bring the registry into line with the VTC's current rights — across the whole VTC, regardless of `resource` or paging. Zero means the registry already matches.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "published": {
/// "description": "Records matching `resource`, as the VTC's own mirror of what it last published, ordered by `resource` then `action` then `entity`.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/PublishedRow"
/// }
/// },
/// "registryConfigured": {
/// "description": "Whether this VTC can publish at all — a Trust Registry and its own publishing DID are configured. `false`: `published` is only what was last published before publishing was unconfigured, and `pendingChanges` still counts against the VTC's current records.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Continuation token for the next page of `published`, or `null` when this is the last.
#[serde(
rename = "nextCursor",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub next_cursor: ::std::option::Option<::std::string::String>,
///How many records the next reconciliation pass will add, remove or replace to bring the registry into line with the VTC's current rights — across the whole VTC, regardless of `resource` or paging. Zero means the registry already matches.
#[serde(rename = "pendingChanges")]
pub pending_changes: u64,
///Records matching `resource`, as the VTC's own mirror of what it last published, ordered by `resource` then `action` then `entity`.
pub published: ::std::vec::Vec<PublishedRow>,
///Whether this VTC can publish at all — a Trust Registry and its own publishing DID are configured. `false`: `published` is only what was last published before publishing was unconfigured, and `pendingChanges` still counts against the VTC's current records.
#[serde(rename = "registryConfigured")]
pub registry_configured: bool,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Right",
/// "description": "One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.",
/// "type": "string",
/// "enum": [
/// "git.ns.admin",
/// "git.repo.create",
/// "git.repo.own",
/// "git.repo.maintain",
/// "git.commit.sign"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum Right {
#[serde(rename = "git.ns.admin")]
GitNsAdmin,
#[serde(rename = "git.repo.create")]
GitRepoCreate,
#[serde(rename = "git.repo.own")]
GitRepoOwn,
#[serde(rename = "git.repo.maintain")]
GitRepoMaintain,
#[serde(rename = "git.commit.sign")]
GitCommitSign,
}
impl ::std::fmt::Display for Right {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::GitNsAdmin => f.write_str("git.ns.admin"),
Self::GitRepoCreate => f.write_str("git.repo.create"),
Self::GitRepoOwn => f.write_str("git.repo.own"),
Self::GitRepoMaintain => f.write_str("git.repo.maintain"),
Self::GitCommitSign => f.write_str("git.commit.sign"),
}
}
}
impl ::std::str::FromStr for Right {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"git.ns.admin" => Ok(Self::GitNsAdmin),
"git.repo.create" => Ok(Self::GitRepoCreate),
"git.repo.own" => Ok(Self::GitRepoOwn),
"git.repo.maintain" => Ok(Self::GitRepoMaintain),
"git.commit.sign" => Ok(Self::GitCommitSign),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Right {
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 Right {
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 Right {
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 Context {
active_from: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
active_to: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
framework: ::std::result::Result<super::ContextFramework, ::std::string::String>,
implied_by:
::std::result::Result<::std::option::Option<super::Right>, ::std::string::String>,
origin: ::std::result::Result<
::std::option::Option<super::ContextOrigin>,
::std::string::String,
>,
}
impl ::std::default::Default for Context {
fn default() -> Self {
Self {
active_from: Ok(Default::default()),
active_to: Ok(Default::default()),
framework: Err("no value supplied for framework".to_string()),
implied_by: Ok(Default::default()),
origin: Ok(Default::default()),
}
}
}
impl Context {
pub fn active_from<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.active_from = value
.try_into()
.map_err(|e| format!("error converting supplied value for active_from: {e}"));
self
}
pub fn active_to<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.active_to = value
.try_into()
.map_err(|e| format!("error converting supplied value for active_to: {e}"));
self
}
pub fn framework<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ContextFramework>,
T::Error: ::std::fmt::Display,
{
self.framework = value
.try_into()
.map_err(|e| format!("error converting supplied value for framework: {e}"));
self
}
pub fn implied_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Right>>,
T::Error: ::std::fmt::Display,
{
self.implied_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for implied_by: {e}"));
self
}
pub fn origin<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ContextOrigin>>,
T::Error: ::std::fmt::Display,
{
self.origin = value
.try_into()
.map_err(|e| format!("error converting supplied value for origin: {e}"));
self
}
}
impl ::std::convert::TryFrom<Context> for super::Context {
type Error = super::error::ConversionError;
fn try_from(value: Context) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
active_from: value.active_from?,
active_to: value.active_to?,
framework: value.framework?,
implied_by: value.implied_by?,
origin: value.origin?,
})
}
}
impl ::std::convert::From<super::Context> for Context {
fn from(value: super::Context) -> Self {
Self {
active_from: Ok(value.active_from),
active_to: Ok(value.active_to),
framework: Ok(value.framework),
implied_by: Ok(value.implied_by),
origin: Ok(value.origin),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
cursor: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
limit: ::std::result::Result<
::std::option::Option<::std::num::NonZeroU64>,
::std::string::String,
>,
resource:
::std::result::Result<::std::option::Option<super::Resource>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
cursor: Ok(Default::default()),
ext: Ok(Default::default()),
limit: Ok(Default::default()),
resource: Ok(Default::default()),
}
}
}
impl Payload {
pub fn cursor<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.cursor = value
.try_into()
.map_err(|e| format!("error converting supplied value for cursor: {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 limit<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::num::NonZeroU64>>,
T::Error: ::std::fmt::Display,
{
self.limit = value
.try_into()
.map_err(|e| format!("error converting supplied value for limit: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Resource>>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {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 {
cursor: value.cursor?,
ext: value.ext?,
limit: value.limit?,
resource: value.resource?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
cursor: Ok(value.cursor),
ext: Ok(value.ext),
limit: Ok(value.limit),
resource: Ok(value.resource),
}
}
}
#[derive(Clone, Debug)]
pub struct PublishedRow {
action: ::std::result::Result<super::Right, ::std::string::String>,
context: ::std::result::Result<super::Context, ::std::string::String>,
entity: ::std::result::Result<super::Did, ::std::string::String>,
published_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
resource: ::std::result::Result<super::Resource, ::std::string::String>,
}
impl ::std::default::Default for PublishedRow {
fn default() -> Self {
Self {
action: Err("no value supplied for action".to_string()),
context: Err("no value supplied for context".to_string()),
entity: Err("no value supplied for entity".to_string()),
published_at: Err("no value supplied for published_at".to_string()),
resource: Err("no value supplied for resource".to_string()),
}
}
}
impl PublishedRow {
pub fn action<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Right>,
T::Error: ::std::fmt::Display,
{
self.action = value
.try_into()
.map_err(|e| format!("error converting supplied value for action: {e}"));
self
}
pub fn context<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Context>,
T::Error: ::std::fmt::Display,
{
self.context = value
.try_into()
.map_err(|e| format!("error converting supplied value for context: {e}"));
self
}
pub fn entity<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.entity = value
.try_into()
.map_err(|e| format!("error converting supplied value for entity: {e}"));
self
}
pub fn published_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.published_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for published_at: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Resource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
}
impl ::std::convert::TryFrom<PublishedRow> for super::PublishedRow {
type Error = super::error::ConversionError;
fn try_from(
value: PublishedRow,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
action: value.action?,
context: value.context?,
entity: value.entity?,
published_at: value.published_at?,
resource: value.resource?,
})
}
}
impl ::std::convert::From<super::PublishedRow> for PublishedRow {
fn from(value: super::PublishedRow) -> Self {
Self {
action: Ok(value.action),
context: Ok(value.context),
entity: Ok(value.entity),
published_at: Ok(value.published_at),
resource: Ok(value.resource),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
next_cursor: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
pending_changes: ::std::result::Result<u64, ::std::string::String>,
published:
::std::result::Result<::std::vec::Vec<super::PublishedRow>, ::std::string::String>,
registry_configured: ::std::result::Result<bool, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
next_cursor: Ok(Default::default()),
pending_changes: Err("no value supplied for pending_changes".to_string()),
published: Err("no value supplied for published".to_string()),
registry_configured: Err("no value supplied for registry_configured".to_string()),
}
}
}
impl Response {
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn next_cursor<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.next_cursor = value
.try_into()
.map_err(|e| format!("error converting supplied value for next_cursor: {e}"));
self
}
pub fn pending_changes<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<u64>,
T::Error: ::std::fmt::Display,
{
self.pending_changes = value
.try_into()
.map_err(|e| format!("error converting supplied value for pending_changes: {e}"));
self
}
pub fn published<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::PublishedRow>>,
T::Error: ::std::fmt::Display,
{
self.published = value
.try_into()
.map_err(|e| format!("error converting supplied value for published: {e}"));
self
}
pub fn registry_configured<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.registry_configured = value.try_into().map_err(|e| {
format!("error converting supplied value for registry_configured: {e}")
});
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ext: value.ext?,
next_cursor: value.next_cursor?,
pending_changes: value.pending_changes?,
published: value.published?,
registry_configured: value.registry_configured?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
ext: Ok(value.ext),
next_cursor: Ok(value.next_cursor),
pending_changes: Ok(value.pending_changes),
published: Ok(value.published),
registry_configured: Ok(value.registry_configured),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/projection/show/0.1";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"Context\": {\n \"additionalProperties\": false,\n \"description\": \"The record's context as published: what the TRQP record has no member of its own for. Not itself governed by this specification — it is the projector's internal shape, carried through unchanged, and a consumer MUST treat unrecognised members as opaque rather than failing on them.\",\n \"properties\": {\n \"activeFrom\": {\n \"description\": \"When the underlying right took effect. Absent for a role-derived tuple, which has no grant time of its own.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"activeTo\": {\n \"description\": \"When the underlying right expires. Absent: no expiry, or the longest of two merged sources' expiries once neither is absent.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"framework\": {\n \"description\": \"The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"impliedBy\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"Present when this tuple publishes a right implied by another recorded right (a repository's `git.commit.sign` implied by its `git.repo.own`, or a namespace's implied rights). Names the right that implies it.\"\n },\n \"origin\": {\n \"description\": \"Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.\",\n \"enum\": [\n \"roleDerived\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"framework\"\n ],\n \"title\": \"Context\",\n \"type\": \"object\"\n },\n \"Did\": {\n \"description\": \"A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\",\n \"title\": \"Did\",\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 \"PublishedRow\": {\n \"additionalProperties\": false,\n \"description\": \"One record the VTC has published to the Trust Registry.\",\n \"properties\": {\n \"action\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"The TRQP action the record is published under.\"\n },\n \"context\": {\n \"$ref\": \"#/$defs/Context\"\n },\n \"entity\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The DID the record is about — the right's subject.\"\n },\n \"publishedAt\": {\n \"description\": \"When the VTC last wrote this record to the registry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n }\n },\n \"required\": [\n \"entity\",\n \"action\",\n \"resource\",\n \"context\",\n \"publishedAt\"\n ],\n \"title\": \"PublishedRow\",\n \"type\": \"object\"\n },\n \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"What is published, and how far it is from what the VTC's records currently call for.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"nextCursor\": {\n \"description\": \"Continuation token for the next page of `published`, or `null` when this is the last.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"pendingChanges\": {\n \"description\": \"How many records the next reconciliation pass will add, remove or replace to bring the registry into line with the VTC's current rights — across the whole VTC, regardless of `resource` or paging. Zero means the registry already matches.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"published\": {\n \"description\": \"Records matching `resource`, as the VTC's own mirror of what it last published, ordered by `resource` then `action` then `entity`.\",\n \"items\": {\n \"$ref\": \"#/$defs/PublishedRow\"\n },\n \"type\": \"array\"\n },\n \"registryConfigured\": {\n \"description\": \"Whether this VTC can publish at all — a Trust Registry and its own publishing DID are configured. `false`: `published` is only what was last published before publishing was unconfigured, and `pendingChanges` still counts against the VTC's current records.\",\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"registryConfigured\",\n \"published\",\n \"pendingChanges\"\n ],\n \"title\": \"Git Namespaces — Show Trust Registry Projection — response payload\",\n \"type\": \"object\"\n },\n \"Right\": {\n \"description\": \"One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\",\n \"git.repo.maintain\",\n \"git.commit.sign\"\n ],\n \"title\": \"Right\",\n \"type\": \"string\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/git-ns/projection/show/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"A community administrator reads what the VTC has published (and what it still owes) to the Trust Registry for its git namespaces. The outer document members (id, type, issuer, recipient, issuedAt, expiresAt, proof) are owned by the framework — SPEC §6.3.\",\n \"properties\": {\n \"cursor\": {\n \"description\": \"Opaque continuation token from a previous response's `nextCursor`. A consumer supplying a `cursor` MUST NOT also change `resource` from the request that produced it — start a fresh query instead.\",\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"limit\": {\n \"description\": \"Maximum published records to return in this page. A VTC clamps to 1..=500 (default 100).\",\n \"maximum\": 500,\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\",\n \"description\": \"Only published records on this resource or inside it. Absent: every resource.\"\n }\n },\n \"title\": \"Git Namespaces — Show Trust Registry Projection — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/git-ns/projection/show/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"Context\": {\n \"additionalProperties\": false,\n \"description\": \"The record's context as published: what the TRQP record has no member of its own for. Not itself governed by this specification — it is the projector's internal shape, carried through unchanged, and a consumer MUST treat unrecognised members as opaque rather than failing on them.\",\n \"properties\": {\n \"activeFrom\": {\n \"description\": \"When the underlying right took effect. Absent for a role-derived tuple, which has no grant time of its own.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"activeTo\": {\n \"description\": \"When the underlying right expires. Absent: no expiry, or the longest of two merged sources' expiries once neither is absent.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"framework\": {\n \"description\": \"The Trust Task type URI the record was published under — this specification's framework constant for a recorded or implied right, or the fixed `git-trust/grant/0.1` URI for a role-derived one.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"impliedBy\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"Present when this tuple publishes a right implied by another recorded right (a repository's `git.commit.sign` implied by its `git.repo.own`, or a namespace's implied rights). Names the right that implies it.\"\n },\n \"origin\": {\n \"description\": \"Present, and always `roleDerived`, exactly for a v0.1 `[hooks.git-trust] grant_on_role` tuple. Absent for a tuple sourced from a recorded or implied right.\",\n \"enum\": [\n \"roleDerived\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"framework\"\n ],\n \"title\": \"Context\",\n \"type\": \"object\"\n },\n \"Did\": {\n \"description\": \"A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\",\n \"title\": \"Did\",\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 \"PublishedRow\": {\n \"additionalProperties\": false,\n \"description\": \"One record the VTC has published to the Trust Registry.\",\n \"properties\": {\n \"action\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"The TRQP action the record is published under.\"\n },\n \"context\": {\n \"$ref\": \"#/$defs/Context\"\n },\n \"entity\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The DID the record is about — the right's subject.\"\n },\n \"publishedAt\": {\n \"description\": \"When the VTC last wrote this record to the registry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n }\n },\n \"required\": [\n \"entity\",\n \"action\",\n \"resource\",\n \"context\",\n \"publishedAt\"\n ],\n \"title\": \"PublishedRow\",\n \"type\": \"object\"\n },\n \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"What is published, and how far it is from what the VTC's records currently call for.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"nextCursor\": {\n \"description\": \"Continuation token for the next page of `published`, or `null` when this is the last.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"pendingChanges\": {\n \"description\": \"How many records the next reconciliation pass will add, remove or replace to bring the registry into line with the VTC's current rights — across the whole VTC, regardless of `resource` or paging. Zero means the registry already matches.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"published\": {\n \"description\": \"Records matching `resource`, as the VTC's own mirror of what it last published, ordered by `resource` then `action` then `entity`.\",\n \"items\": {\n \"$ref\": \"#/$defs/PublishedRow\"\n },\n \"type\": \"array\"\n },\n \"registryConfigured\": {\n \"description\": \"Whether this VTC can publish at all — a Trust Registry and its own publishing DID are configured. `false`: `published` is only what was last published before publishing was unconfigured, and `pendingChanges` still counts against the VTC's current records.\",\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"registryConfigured\",\n \"published\",\n \"pendingChanges\"\n ],\n \"title\": \"Git Namespaces — Show Trust Registry Projection — response payload\",\n \"type\": \"object\"\n },\n \"Right\": {\n \"description\": \"One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\",\n \"git.repo.maintain\",\n \"git.commit.sign\"\n ],\n \"title\": \"Right\",\n \"type\": \"string\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[error_codes::NOT_COMMUNITY_ADMINISTRATOR];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `git-ns/projection/show:notCommunityAdministrator`
///
/// The caller does not hold the community-administrator capability. Publishing configuration and the whole projection mirror are community-wide facts, not namespace-scoped ones, so holding git.ns.admin on a namespace is not enough.
///
/// Declared `retryable: false`.
pub const NOT_COMMUNITY_ADMINISTRATOR: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/projection/show:notCommunityAdministrator",
retryable: false,
};
}
#[cfg(test)]
mod conformance {
//! Round-trip tests harvested from the spec's `spec.md`,
//! plus a `rejects_invalid_examples` test for any fixtures
//! in `payload.invalid-examples.json` (validate feature).
#[test]
fn request_example_1() {
const JSON: &str = "{\n \"id\": \"urn:uuid:7a2e4c10-3b5d-4f6e-9a1b-2c3d4e5f6e01\",\n \"type\": \"https://trusttasks.org/spec/git-ns/projection/show/0.1\",\n \"threadId\": \"urn:uuid:7a2e4c10-3b5d-4f6e-9a1b-2c3d4e5f6e01\",\n \"issuer\": \"did:webvh:QmDanaScid8:acme-vtc.example:dana\",\n \"recipient\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"issuedAt\": \"2026-09-26T09:25: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:7a2e4c10-3b5d-4f6e-9a1b-2c3d4e5f6e02\",\n \"type\": \"https://trusttasks.org/spec/git-ns/projection/show/0.1#response\",\n \"threadId\": \"urn:uuid:7a2e4c10-3b5d-4f6e-9a1b-2c3d4e5f6e01\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmDanaScid8:acme-vtc.example:dana\",\n \"issuedAt\": \"2026-09-26T09:25:01Z\",\n \"payload\": {\n \"registryConfigured\": true,\n \"published\": [\n {\n \"entity\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"action\": \"git.repo.own\",\n \"resource\": \"github.com/acme/widgets\",\n \"context\": {\n \"framework\": \"https://trusttasks.org/spec/git-ns/right/grant/0.3\",\n \"activeFrom\": \"2026-09-23T09:50:00Z\"\n },\n \"publishedAt\": \"2026-09-23T09:50:05Z\"\n },\n {\n \"entity\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"action\": \"git.commit.sign\",\n \"resource\": \"github.com/acme/widgets\",\n \"context\": {\n \"framework\": \"https://trusttasks.org/spec/git-ns/right/grant/0.3\",\n \"activeFrom\": \"2026-09-23T09:50:00Z\",\n \"impliedBy\": \"git.repo.own\"\n },\n \"publishedAt\": \"2026-09-23T09:50:05Z\"\n },\n {\n \"entity\": \"did:webvh:QmFrankScid12:acme-vtc.example:frank\",\n \"action\": \"git.commit.sign\",\n \"resource\": \"github.com/acme/widgets\",\n \"context\": {\n \"framework\": \"https://trusttasks.org/spec/git-trust/grant/0.1\",\n \"origin\": \"roleDerived\"\n },\n \"publishedAt\": \"2026-09-25T14:05:00Z\"\n }\n ],\n \"pendingChanges\": 2\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)] = &[
(
"`resource` is not forge-qualified — no forge host segment.",
"{\n \"resource\": \"acme\"\n}",
),
("`limit` below the declared minimum of 1.", "{\n \"limit\": 0\n}"),
(
"Bare/unnamespaced ext key — SPEC §4.5.1 requires every immediate child of ext to be reverse-DNS namespaced.",
"{\n \"ext\": {\n \"bare-key\": {\n \"anything\": \"here\"\n }\n }\n}",
),
(
"Unknown top-level payload member — additionalProperties: false catches `namespace`, which this task filters by `resource`, not by namespace id.",
"{\n \"namespace\": \"ns_01J8Z6Q4M2\"\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
);
}
}
/// Each `"variant": "response"` fixture in
/// `payload.invalid-examples.json` MUST be rejected as a
/// response payload by at least one of: serde deserialization
/// into `Response`, or JSON-Schema validation against the
/// `$anchor: "response"` sub-schema (validate feature).
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_response_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
(
"Response variant: `context` carries an unrecognised member — the shape is closed, unlike the projector's own internal representation.",
"{\n \"pendingChanges\": 0,\n \"published\": [\n {\n \"action\": \"git.repo.own\",\n \"context\": {\n \"framework\": \"https://trusttasks.org/spec/git-ns/right/grant/0.3\",\n \"repoId\": \"812736451\"\n },\n \"entity\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"publishedAt\": \"2026-09-23T09:50:05Z\",\n \"resource\": \"github.com/acme/widgets\"\n }\n ],\n \"registryConfigured\": true\n}",
),
(
"Response variant: missing required `pendingChanges`.",
"{\n \"published\": [],\n \"registryConfigured\": true\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::Response>(value.clone()).is_ok();
let schema_ok = super::Response::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid response example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}