//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `git-ns/drift/resolve`. Version: `0.2`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///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())
})
}
}
///One difference between the forge's observed state and the VTC's projection of a repository.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DriftItem",
/// "description": "One difference between the forge's observed state and the VTC's projection of a repository.",
/// "type": "object",
/// "required": [
/// "resource",
/// "type"
/// ],
/// "properties": {
/// "account": {
/// "description": "Whose role differs, for the three role types.",
/// "$ref": "#/definitions/ForgeAccount"
/// },
/// "expected": {
/// "description": "What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.",
/// "type": "string",
/// "maxLength": 256
/// },
/// "observed": {
/// "description": "What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.",
/// "type": "string",
/// "maxLength": 256
/// },
/// "resource": {
/// "$ref": "#/definitions/RepoResource"
/// },
/// "type": {
/// "description": "`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.",
/// "type": "string",
/// "enum": [
/// "roleAdded",
/// "roleRemoved",
/// "roleChanged",
/// "requiredCheckMissing",
/// "protectionWeakened",
/// "bootstrapMissing"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct DriftItem {
///Whose role differs, for the three role types.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub account: ::std::option::Option<ForgeAccount>,
///What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub expected: ::std::option::Option<DriftItemExpected>,
///What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub observed: ::std::option::Option<DriftItemObserved>,
pub resource: RepoResource,
///`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.
#[serde(rename = "type")]
pub type_: DriftItemType,
}
impl DriftItem {
pub fn builder() -> builder::DriftItem {
Default::default()
}
}
///What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DriftItemExpected(::std::string::String);
impl ::std::ops::Deref for DriftItemExpected {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DriftItemExpected> for ::std::string::String {
fn from(value: DriftItemExpected) -> Self {
value.0
}
}
impl ::std::str::FromStr for DriftItemExpected {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 256usize {
return Err("longer than 256 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DriftItemExpected {
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 DriftItemExpected {
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 DriftItemExpected {
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 DriftItemExpected {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DriftItemObserved(::std::string::String);
impl ::std::ops::Deref for DriftItemObserved {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DriftItemObserved> for ::std::string::String {
fn from(value: DriftItemObserved) -> Self {
value.0
}
}
impl ::std::str::FromStr for DriftItemObserved {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 256usize {
return Err("longer than 256 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DriftItemObserved {
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 DriftItemObserved {
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 DriftItemObserved {
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 DriftItemObserved {
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())
})
}
}
///`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.",
/// "type": "string",
/// "enum": [
/// "roleAdded",
/// "roleRemoved",
/// "roleChanged",
/// "requiredCheckMissing",
/// "protectionWeakened",
/// "bootstrapMissing"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum DriftItemType {
#[serde(rename = "roleAdded")]
RoleAdded,
#[serde(rename = "roleRemoved")]
RoleRemoved,
#[serde(rename = "roleChanged")]
RoleChanged,
#[serde(rename = "requiredCheckMissing")]
RequiredCheckMissing,
#[serde(rename = "protectionWeakened")]
ProtectionWeakened,
#[serde(rename = "bootstrapMissing")]
BootstrapMissing,
}
impl ::std::fmt::Display for DriftItemType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::RoleAdded => f.write_str("roleAdded"),
Self::RoleRemoved => f.write_str("roleRemoved"),
Self::RoleChanged => f.write_str("roleChanged"),
Self::RequiredCheckMissing => f.write_str("requiredCheckMissing"),
Self::ProtectionWeakened => f.write_str("protectionWeakened"),
Self::BootstrapMissing => f.write_str("bootstrapMissing"),
}
}
}
impl ::std::str::FromStr for DriftItemType {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"roleAdded" => Ok(Self::RoleAdded),
"roleRemoved" => Ok(Self::RoleRemoved),
"roleChanged" => Ok(Self::RoleChanged),
"requiredCheckMissing" => Ok(Self::RequiredCheckMissing),
"protectionWeakened" => Ok(Self::ProtectionWeakened),
"bootstrapMissing" => Ok(Self::BootstrapMissing),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for DriftItemType {
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 DriftItemType {
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 DriftItemType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Which of the repository's outstanding drift items to resolve. Drift items carry no identifier of their own: they are recomputed by the bridge and replaced wholesale by every git-ns/bridge/event. An item is selected by its `type`, and for the three role types also by the account whose role differs, which is unique among a repository's outstanding items.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DriftSelector",
/// "description": "Which of the repository's outstanding drift items to resolve. Drift items carry no identifier of their own: they are recomputed by the bridge and replaced wholesale by every git-ns/bridge/event. An item is selected by its `type`, and for the three role types also by the account whose role differs, which is unique among a repository's outstanding items.",
/// "type": "object",
/// "required": [
/// "type"
/// ],
/// "properties": {
/// "account": {
/// "description": "The account whose role differs, as the drift item reports it. Required for `roleAdded`, `roleRemoved` and `roleChanged`, and absent for every other type. Matched by `forge` and `id`; `login` is display only.",
/// "$ref": "#/definitions/ForgeAccount"
/// },
/// "observed": {
/// "description": "The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.",
/// "type": "string",
/// "maxLength": 256
/// },
/// "type": {
/// "$ref": "#/definitions/DriftType"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct DriftSelector {
///The account whose role differs, as the drift item reports it. Required for `roleAdded`, `roleRemoved` and `roleChanged`, and absent for every other type. Matched by `forge` and `id`; `login` is display only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub account: ::std::option::Option<ForgeAccount>,
///The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub observed: ::std::option::Option<DriftSelectorObserved>,
#[serde(rename = "type")]
pub type_: DriftType,
}
impl DriftSelector {
pub fn builder() -> builder::DriftSelector {
Default::default()
}
}
///The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.",
/// "type": "string",
/// "maxLength": 256
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DriftSelectorObserved(::std::string::String);
impl ::std::ops::Deref for DriftSelectorObserved {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DriftSelectorObserved> for ::std::string::String {
fn from(value: DriftSelectorObserved) -> Self {
value.0
}
}
impl ::std::str::FromStr for DriftSelectorObserved {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 256usize {
return Err("longer than 256 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DriftSelectorObserved {
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 DriftSelectorObserved {
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 DriftSelectorObserved {
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 DriftSelectorObserved {
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 kinds of drift, named so that a task can select a drift item by kind. The same values, with the same meanings, as `DriftItem`'s `type`, which keeps its own inline list so that `DriftItem` stays textually identical to 0.1; the two lists change together. `roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DriftType",
/// "description": "The kinds of drift, named so that a task can select a drift item by kind. The same values, with the same meanings, as `DriftItem`'s `type`, which keeps its own inline list so that `DriftItem` stays textually identical to 0.1; the two lists change together. `roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.",
/// "type": "string",
/// "enum": [
/// "roleAdded",
/// "roleRemoved",
/// "roleChanged",
/// "requiredCheckMissing",
/// "protectionWeakened",
/// "bootstrapMissing"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum DriftType {
#[serde(rename = "roleAdded")]
RoleAdded,
#[serde(rename = "roleRemoved")]
RoleRemoved,
#[serde(rename = "roleChanged")]
RoleChanged,
#[serde(rename = "requiredCheckMissing")]
RequiredCheckMissing,
#[serde(rename = "protectionWeakened")]
ProtectionWeakened,
#[serde(rename = "bootstrapMissing")]
BootstrapMissing,
}
impl ::std::fmt::Display for DriftType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::RoleAdded => f.write_str("roleAdded"),
Self::RoleRemoved => f.write_str("roleRemoved"),
Self::RoleChanged => f.write_str("roleChanged"),
Self::RequiredCheckMissing => f.write_str("requiredCheckMissing"),
Self::ProtectionWeakened => f.write_str("protectionWeakened"),
Self::BootstrapMissing => f.write_str("bootstrapMissing"),
}
}
}
impl ::std::str::FromStr for DriftType {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"roleAdded" => Ok(Self::RoleAdded),
"roleRemoved" => Ok(Self::RoleRemoved),
"roleChanged" => Ok(Self::RoleChanged),
"requiredCheckMissing" => Ok(Self::RequiredCheckMissing),
"protectionWeakened" => Ok(Self::ProtectionWeakened),
"bootstrapMissing" => Ok(Self::BootstrapMissing),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for DriftType {
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 DriftType {
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 DriftType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///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())
})
}
}
///An owner of a repository, or a namespace admin over it, resolves one reported drift item: `adopt` records the forge-side role as a VTC right, under the same rules as git-ns/right/grant; `revert` has the bridge undo the forge-side change.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/git-ns/drift/resolve/0.2",
/// "title": "Payload",
/// "description": "An owner of a repository, or a namespace admin over it, resolves one reported drift item: `adopt` records the forge-side role as a VTC right, under the same rules as git-ns/right/grant; `revert` has the bridge undo the forge-side change.",
/// "type": "object",
/// "required": [
/// "action",
/// "drift",
/// "resource"
/// ],
/// "properties": {
/// "action": {
/// "description": "`adopt` — record the forge-side role as a VTC right, so the projection comes to match the forge. `revert` — have the bridge make the forge match the projection again.",
/// "type": "string",
/// "enum": [
/// "adopt",
/// "revert"
/// ]
/// },
/// "drift": {
/// "$ref": "#/definitions/DriftSelector"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "reason": {
/// "description": "Why, in the resolver's words. Kept in the VTC's audit record and, for `adopt`, as the recorded right's `reason`, disclosed only to the repository's owners and namespace admins.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "resource": {
/// "description": "The repository whose drift is resolved.",
/// "$ref": "#/definitions/RepoResource"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///`adopt` — record the forge-side role as a VTC right, so the projection comes to match the forge. `revert` — have the bridge make the forge match the projection again.
pub action: PayloadAction,
pub drift: DriftSelector,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Why, in the resolver's words. Kept in the VTC's audit record and, for `adopt`, as the recorded right's `reason`, disclosed only to the repository's owners and namespace admins.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<PayloadReason>,
///The repository whose drift is resolved.
pub resource: RepoResource,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///`adopt` — record the forge-side role as a VTC right, so the projection comes to match the forge. `revert` — have the bridge make the forge match the projection again.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "`adopt` — record the forge-side role as a VTC right, so the projection comes to match the forge. `revert` — have the bridge make the forge match the projection again.",
/// "type": "string",
/// "enum": [
/// "adopt",
/// "revert"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum PayloadAction {
#[serde(rename = "adopt")]
Adopt,
#[serde(rename = "revert")]
Revert,
}
impl ::std::fmt::Display for PayloadAction {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Adopt => f.write_str("adopt"),
Self::Revert => f.write_str("revert"),
}
}
}
impl ::std::str::FromStr for PayloadAction {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"adopt" => Ok(Self::Adopt),
"revert" => Ok(Self::Revert),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PayloadAction {
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 PayloadAction {
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 PayloadAction {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Why, in the resolver's words. Kept in the VTC's audit record and, for `adopt`, as the recorded right's `reason`, disclosed only to the repository's owners and namespace admins.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Why, in the resolver's words. Kept in the VTC's audit record and, for `adopt`, as the recorded right's `reason`, disclosed only to the repository's owners and namespace admins.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadReason(::std::string::String);
impl ::std::ops::Deref for PayloadReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadReason> for ::std::string::String {
fn from(value: PayloadReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadReason {
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 PayloadReason {
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 PayloadReason {
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 PayloadReason {
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 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())
})
}
}
///A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Resource",
/// "description": "A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.",
/// "type": "string",
/// "maxLength": 455,
/// "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Resource(::std::string::String);
impl ::std::ops::Deref for Resource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Resource> for ::std::string::String {
fn from(value: Resource) -> Self {
value.0
}
}
impl ::std::str::FromStr for Resource {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 455usize {
return Err("longer than 455 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Resource {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Resource {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Resource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for Resource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///What the resolution did, and where the repository's sync now stands.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "What the resolution did, and where the repository's sync now stands.",
/// "type": "object",
/// "required": [
/// "action",
/// "sync"
/// ],
/// "properties": {
/// "action": {
/// "description": "The action taken, as requested.",
/// "type": "string",
/// "enum": [
/// "adopt",
/// "revert"
/// ]
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "right": {
/// "description": "For `adopt`: the recorded right — newly recorded, or the live record that already existed. Absent for `revert`.",
/// "$ref": "#/definitions/RightRecord"
/// },
/// "sync": {
/// "description": "The repository's sync after the resolution. The resolved item is no longer outstanding; the state is `drift` while other items remain, and otherwise `pending` until the bridge confirms the forge matches.",
/// "$ref": "#/definitions/Sync"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///The action taken, as requested.
pub action: ResponseAction,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///For `adopt`: the recorded right — newly recorded, or the live record that already existed. Absent for `revert`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub right: ::std::option::Option<RightRecord>,
///The repository's sync after the resolution. The resolved item is no longer outstanding; the state is `drift` while other items remain, and otherwise `pending` until the bridge confirms the forge matches.
pub sync: Sync,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///The action taken, as requested.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The action taken, as requested.",
/// "type": "string",
/// "enum": [
/// "adopt",
/// "revert"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum ResponseAction {
#[serde(rename = "adopt")]
Adopt,
#[serde(rename = "revert")]
Revert,
}
impl ::std::fmt::Display for ResponseAction {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Adopt => f.write_str("adopt"),
Self::Revert => f.write_str("revert"),
}
}
}
impl ::std::str::FromStr for ResponseAction {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"adopt" => Ok(Self::Adopt),
"revert" => Ok(Self::Revert),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ResponseAction {
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 ResponseAction {
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 ResponseAction {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///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 recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RightRecord",
/// "description": "One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords.",
/// "type": "object",
/// "required": [
/// "grantedAt",
/// "grantedBy",
/// "resource",
/// "right",
/// "subject"
/// ],
/// "properties": {
/// "expiresAt": {
/// "description": "When the right lapses. Absent: no expiry.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "grantedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "grantedBy": {
/// "description": "The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.",
/// "$ref": "#/definitions/Did"
/// },
/// "reason": {
/// "description": "The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "resource": {
/// "$ref": "#/definitions/Resource"
/// },
/// "right": {
/// "$ref": "#/definitions/Right"
/// },
/// "subject": {
/// "description": "Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.",
/// "$ref": "#/definitions/Did"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RightRecord {
///When the right lapses. Absent: no expiry.
#[serde(
rename = "expiresAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub expires_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
#[serde(rename = "grantedAt")]
pub granted_at: ::chrono::DateTime<::chrono::offset::Utc>,
///The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.
#[serde(rename = "grantedBy")]
pub granted_by: Did,
///The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<RightRecordReason>,
pub resource: Resource,
pub right: Right,
///Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.
pub subject: Did,
}
impl RightRecord {
pub fn builder() -> builder::RightRecord {
Default::default()
}
}
///The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RightRecordReason(::std::string::String);
impl ::std::ops::Deref for RightRecordReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RightRecordReason> for ::std::string::String {
fn from(value: RightRecordReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for RightRecordReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RightRecordReason {
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 RightRecordReason {
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 RightRecordReason {
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 RightRecordReason {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///How the forge compares with the VTC's projection for one repository.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Sync",
/// "description": "How the forge compares with the VTC's projection for one repository.",
/// "type": "object",
/// "required": [
/// "drift",
/// "state"
/// ],
/// "properties": {
/// "checkedAt": {
/// "description": "When the forge was last compared. Absent if never.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "drift": {
/// "description": "Outstanding drift. Empty unless `state` is `drift`.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/DriftItem"
/// }
/// },
/// "state": {
/// "description": "`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).",
/// "type": "string",
/// "enum": [
/// "inSync",
/// "drift",
/// "pending",
/// "unchecked"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Sync {
///When the forge was last compared. Absent if never.
#[serde(
rename = "checkedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub checked_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Outstanding drift. Empty unless `state` is `drift`.
pub drift: ::std::vec::Vec<DriftItem>,
///`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).
pub state: SyncState,
}
impl Sync {
pub fn builder() -> builder::Sync {
Default::default()
}
}
///`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).",
/// "type": "string",
/// "enum": [
/// "inSync",
/// "drift",
/// "pending",
/// "unchecked"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum SyncState {
#[serde(rename = "inSync")]
InSync,
#[serde(rename = "drift")]
Drift,
#[serde(rename = "pending")]
Pending,
#[serde(rename = "unchecked")]
Unchecked,
}
impl ::std::fmt::Display for SyncState {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::InSync => f.write_str("inSync"),
Self::Drift => f.write_str("drift"),
Self::Pending => f.write_str("pending"),
Self::Unchecked => f.write_str("unchecked"),
}
}
}
impl ::std::str::FromStr for SyncState {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"inSync" => Ok(Self::InSync),
"drift" => Ok(Self::Drift),
"pending" => Ok(Self::Pending),
"unchecked" => Ok(Self::Unchecked),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SyncState {
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 SyncState {
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 SyncState {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct DriftItem {
account: ::std::result::Result<
::std::option::Option<super::ForgeAccount>,
::std::string::String,
>,
expected: ::std::result::Result<
::std::option::Option<super::DriftItemExpected>,
::std::string::String,
>,
observed: ::std::result::Result<
::std::option::Option<super::DriftItemObserved>,
::std::string::String,
>,
resource: ::std::result::Result<super::RepoResource, ::std::string::String>,
type_: ::std::result::Result<super::DriftItemType, ::std::string::String>,
}
impl ::std::default::Default for DriftItem {
fn default() -> Self {
Self {
account: Ok(Default::default()),
expected: Ok(Default::default()),
observed: Ok(Default::default()),
resource: Err("no value supplied for resource".to_string()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl DriftItem {
pub fn account<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<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 expected<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::DriftItemExpected>>,
T::Error: ::std::fmt::Display,
{
self.expected = value
.try_into()
.map_err(|e| format!("error converting supplied value for expected: {e}"));
self
}
pub fn observed<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::DriftItemObserved>>,
T::Error: ::std::fmt::Display,
{
self.observed = value
.try_into()
.map_err(|e| format!("error converting supplied value for observed: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RepoResource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DriftItemType>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<DriftItem> for super::DriftItem {
type Error = super::error::ConversionError;
fn try_from(
value: DriftItem,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
account: value.account?,
expected: value.expected?,
observed: value.observed?,
resource: value.resource?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::DriftItem> for DriftItem {
fn from(value: super::DriftItem) -> Self {
Self {
account: Ok(value.account),
expected: Ok(value.expected),
observed: Ok(value.observed),
resource: Ok(value.resource),
type_: Ok(value.type_),
}
}
}
#[derive(Clone, Debug)]
pub struct DriftSelector {
account: ::std::result::Result<
::std::option::Option<super::ForgeAccount>,
::std::string::String,
>,
observed: ::std::result::Result<
::std::option::Option<super::DriftSelectorObserved>,
::std::string::String,
>,
type_: ::std::result::Result<super::DriftType, ::std::string::String>,
}
impl ::std::default::Default for DriftSelector {
fn default() -> Self {
Self {
account: Ok(Default::default()),
observed: Ok(Default::default()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl DriftSelector {
pub fn account<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<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 observed<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::DriftSelectorObserved>>,
T::Error: ::std::fmt::Display,
{
self.observed = value
.try_into()
.map_err(|e| format!("error converting supplied value for observed: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DriftType>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<DriftSelector> for super::DriftSelector {
type Error = super::error::ConversionError;
fn try_from(
value: DriftSelector,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
account: value.account?,
observed: value.observed?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::DriftSelector> for DriftSelector {
fn from(value: super::DriftSelector) -> Self {
Self {
account: Ok(value.account),
observed: Ok(value.observed),
type_: Ok(value.type_),
}
}
}
#[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 {
action: ::std::result::Result<super::PayloadAction, ::std::string::String>,
drift: ::std::result::Result<super::DriftSelector, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
reason: ::std::result::Result<
::std::option::Option<super::PayloadReason>,
::std::string::String,
>,
resource: ::std::result::Result<super::RepoResource, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
action: Err("no value supplied for action".to_string()),
drift: Err("no value supplied for drift".to_string()),
ext: Ok(Default::default()),
reason: Ok(Default::default()),
resource: Err("no value supplied for resource".to_string()),
}
}
}
impl Payload {
pub fn action<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadAction>,
T::Error: ::std::fmt::Display,
{
self.action = value
.try_into()
.map_err(|e| format!("error converting supplied value for action: {e}"));
self
}
pub fn drift<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DriftSelector>,
T::Error: ::std::fmt::Display,
{
self.drift = value
.try_into()
.map_err(|e| format!("error converting supplied value for drift: {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 reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadReason>>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RepoResource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
action: value.action?,
drift: value.drift?,
ext: value.ext?,
reason: value.reason?,
resource: value.resource?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
action: Ok(value.action),
drift: Ok(value.drift),
ext: Ok(value.ext),
reason: Ok(value.reason),
resource: Ok(value.resource),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
action: ::std::result::Result<super::ResponseAction, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
right:
::std::result::Result<::std::option::Option<super::RightRecord>, ::std::string::String>,
sync: ::std::result::Result<super::Sync, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
action: Err("no value supplied for action".to_string()),
ext: Ok(Default::default()),
right: Ok(Default::default()),
sync: Err("no value supplied for sync".to_string()),
}
}
}
impl Response {
pub fn action<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseAction>,
T::Error: ::std::fmt::Display,
{
self.action = value
.try_into()
.map_err(|e| format!("error converting supplied value for action: {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 right<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RightRecord>>,
T::Error: ::std::fmt::Display,
{
self.right = value
.try_into()
.map_err(|e| format!("error converting supplied value for right: {e}"));
self
}
pub fn sync<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Sync>,
T::Error: ::std::fmt::Display,
{
self.sync = value
.try_into()
.map_err(|e| format!("error converting supplied value for sync: {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 {
action: value.action?,
ext: value.ext?,
right: value.right?,
sync: value.sync?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
action: Ok(value.action),
ext: Ok(value.ext),
right: Ok(value.right),
sync: Ok(value.sync),
}
}
}
#[derive(Clone, Debug)]
pub struct RightRecord {
expires_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
granted_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
granted_by: ::std::result::Result<super::Did, ::std::string::String>,
reason: ::std::result::Result<
::std::option::Option<super::RightRecordReason>,
::std::string::String,
>,
resource: ::std::result::Result<super::Resource, ::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 RightRecord {
fn default() -> Self {
Self {
expires_at: Ok(Default::default()),
granted_at: Err("no value supplied for granted_at".to_string()),
granted_by: Err("no value supplied for granted_by".to_string()),
reason: Ok(Default::default()),
resource: Err("no value supplied for resource".to_string()),
right: Err("no value supplied for right".to_string()),
subject: Err("no value supplied for subject".to_string()),
}
}
}
impl RightRecord {
pub fn expires_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.expires_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for expires_at: {e}"));
self
}
pub fn granted_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.granted_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for granted_at: {e}"));
self
}
pub fn granted_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.granted_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for granted_by: {e}"));
self
}
pub fn reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RightRecordReason>>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Resource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
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<RightRecord> for super::RightRecord {
type Error = super::error::ConversionError;
fn try_from(
value: RightRecord,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
expires_at: value.expires_at?,
granted_at: value.granted_at?,
granted_by: value.granted_by?,
reason: value.reason?,
resource: value.resource?,
right: value.right?,
subject: value.subject?,
})
}
}
impl ::std::convert::From<super::RightRecord> for RightRecord {
fn from(value: super::RightRecord) -> Self {
Self {
expires_at: Ok(value.expires_at),
granted_at: Ok(value.granted_at),
granted_by: Ok(value.granted_by),
reason: Ok(value.reason),
resource: Ok(value.resource),
right: Ok(value.right),
subject: Ok(value.subject),
}
}
}
#[derive(Clone, Debug)]
pub struct Sync {
checked_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
drift: ::std::result::Result<::std::vec::Vec<super::DriftItem>, ::std::string::String>,
state: ::std::result::Result<super::SyncState, ::std::string::String>,
}
impl ::std::default::Default for Sync {
fn default() -> Self {
Self {
checked_at: Ok(Default::default()),
drift: Err("no value supplied for drift".to_string()),
state: Err("no value supplied for state".to_string()),
}
}
}
impl Sync {
pub fn checked_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.checked_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for checked_at: {e}"));
self
}
pub fn drift<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::DriftItem>>,
T::Error: ::std::fmt::Display,
{
self.drift = value
.try_into()
.map_err(|e| format!("error converting supplied value for drift: {e}"));
self
}
pub fn state<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::SyncState>,
T::Error: ::std::fmt::Display,
{
self.state = value
.try_into()
.map_err(|e| format!("error converting supplied value for state: {e}"));
self
}
}
impl ::std::convert::TryFrom<Sync> for super::Sync {
type Error = super::error::ConversionError;
fn try_from(value: Sync) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
checked_at: value.checked_at?,
drift: value.drift?,
state: value.state?,
})
}
}
impl ::std::convert::From<super::Sync> for Sync {
fn from(value: super::Sync) -> Self {
Self {
checked_at: Ok(value.checked_at),
drift: Ok(value.drift),
state: Ok(value.state),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/drift/resolve/0.2";
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 \"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 \"DriftItem\": {\n \"additionalProperties\": false,\n \"description\": \"One difference between the forge's observed state and the VTC's projection of a repository.\",\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"Whose role differs, for the three role types.\"\n },\n \"expected\": {\n \"description\": \"What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"observed\": {\n \"description\": \"What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/RepoResource\"\n },\n \"type\": {\n \"description\": \"`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.\",\n \"enum\": [\n \"roleAdded\",\n \"roleRemoved\",\n \"roleChanged\",\n \"requiredCheckMissing\",\n \"protectionWeakened\",\n \"bootstrapMissing\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"type\",\n \"resource\"\n ],\n \"title\": \"DriftItem\",\n \"type\": \"object\"\n },\n \"DriftSelector\": {\n \"additionalProperties\": false,\n \"description\": \"Which of the repository's outstanding drift items to resolve. Drift items carry no identifier of their own: they are recomputed by the bridge and replaced wholesale by every git-ns/bridge/event. An item is selected by its `type`, and for the three role types also by the account whose role differs, which is unique among a repository's outstanding items.\",\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"The account whose role differs, as the drift item reports it. Required for `roleAdded`, `roleRemoved` and `roleChanged`, and absent for every other type. Matched by `forge` and `id`; `login` is display only.\"\n },\n \"observed\": {\n \"description\": \"The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"type\": {\n \"$ref\": \"#/$defs/DriftType\"\n }\n },\n \"required\": [\n \"type\"\n ],\n \"title\": \"DriftSelector\",\n \"type\": \"object\"\n },\n \"DriftType\": {\n \"description\": \"The kinds of drift, named so that a task can select a drift item by kind. The same values, with the same meanings, as `DriftItem`'s `type`, which keeps its own inline list so that `DriftItem` stays textually identical to 0.1; the two lists change together. `roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.\",\n \"enum\": [\n \"roleAdded\",\n \"roleRemoved\",\n \"roleChanged\",\n \"requiredCheckMissing\",\n \"protectionWeakened\",\n \"bootstrapMissing\"\n ],\n \"title\": \"DriftType\",\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 \"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 \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"What the resolution did, and where the repository's sync now stands.\",\n \"properties\": {\n \"action\": {\n \"description\": \"The action taken, as requested.\",\n \"enum\": [\n \"adopt\",\n \"revert\"\n ],\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/RightRecord\",\n \"description\": \"For `adopt`: the recorded right — newly recorded, or the live record that already existed. Absent for `revert`.\"\n },\n \"sync\": {\n \"$ref\": \"#/$defs/Sync\",\n \"description\": \"The repository's sync after the resolution. The resolved item is no longer outstanding; the state is `drift` while other items remain, and otherwise `pending` until the bridge confirms the forge matches.\"\n }\n },\n \"required\": [\n \"action\",\n \"sync\"\n ],\n \"title\": \"Git Namespaces — Resolve Drift — 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 \"RightRecord\": {\n \"additionalProperties\": false,\n \"description\": \"One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords.\",\n \"properties\": {\n \"expiresAt\": {\n \"description\": \"When the right lapses. Absent: no expiry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.\"\n },\n \"reason\": {\n \"description\": \"The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.\"\n }\n },\n \"required\": [\n \"subject\",\n \"right\",\n \"resource\",\n \"grantedBy\",\n \"grantedAt\"\n ],\n \"title\": \"RightRecord\",\n \"type\": \"object\"\n },\n \"Sync\": {\n \"additionalProperties\": false,\n \"description\": \"How the forge compares with the VTC's projection for one repository.\",\n \"properties\": {\n \"checkedAt\": {\n \"description\": \"When the forge was last compared. Absent if never.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"drift\": {\n \"description\": \"Outstanding drift. Empty unless `state` is `drift`.\",\n \"items\": {\n \"$ref\": \"#/$defs/DriftItem\"\n },\n \"type\": \"array\"\n },\n \"state\": {\n \"description\": \"`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).\",\n \"enum\": [\n \"inSync\",\n \"drift\",\n \"pending\",\n \"unchecked\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"state\",\n \"drift\"\n ],\n \"title\": \"Sync\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/git-ns/drift/resolve/0.2\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"An owner of a repository, or a namespace admin over it, resolves one reported drift item: `adopt` records the forge-side role as a VTC right, under the same rules as git-ns/right/grant; `revert` has the bridge undo the forge-side change.\",\n \"properties\": {\n \"action\": {\n \"description\": \"`adopt` — record the forge-side role as a VTC right, so the projection comes to match the forge. `revert` — have the bridge make the forge match the projection again.\",\n \"enum\": [\n \"adopt\",\n \"revert\"\n ],\n \"type\": \"string\"\n },\n \"drift\": {\n \"$ref\": \"#/$defs/DriftSelector\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"reason\": {\n \"description\": \"Why, in the resolver's words. Kept in the VTC's audit record and, for `adopt`, as the recorded right's `reason`, disclosed only to the repository's owners and namespace admins.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/RepoResource\",\n \"description\": \"The repository whose drift is resolved.\"\n }\n },\n \"required\": [\n \"resource\",\n \"drift\",\n \"action\"\n ],\n \"title\": \"Git Namespaces — Resolve Drift — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/drift/resolve/0.2#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 \"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 \"DriftItem\": {\n \"additionalProperties\": false,\n \"description\": \"One difference between the forge's observed state and the VTC's projection of a repository.\",\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"Whose role differs, for the three role types.\"\n },\n \"expected\": {\n \"description\": \"What the projection calls for, in the forge's own vocabulary. Absent when the projection calls for nothing.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"observed\": {\n \"description\": \"What the forge shows, in the forge's own vocabulary (a role name such as `maintain`, a setting name). Absent when nothing is there.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/RepoResource\"\n },\n \"type\": {\n \"description\": \"`roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.\",\n \"enum\": [\n \"roleAdded\",\n \"roleRemoved\",\n \"roleChanged\",\n \"requiredCheckMissing\",\n \"protectionWeakened\",\n \"bootstrapMissing\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"type\",\n \"resource\"\n ],\n \"title\": \"DriftItem\",\n \"type\": \"object\"\n },\n \"DriftSelector\": {\n \"additionalProperties\": false,\n \"description\": \"Which of the repository's outstanding drift items to resolve. Drift items carry no identifier of their own: they are recomputed by the bridge and replaced wholesale by every git-ns/bridge/event. An item is selected by its `type`, and for the three role types also by the account whose role differs, which is unique among a repository's outstanding items.\",\n \"properties\": {\n \"account\": {\n \"$ref\": \"#/$defs/ForgeAccount\",\n \"description\": \"The account whose role differs, as the drift item reports it. Required for `roleAdded`, `roleRemoved` and `roleChanged`, and absent for every other type. Matched by `forge` and `id`; `login` is display only.\"\n },\n \"observed\": {\n \"description\": \"The item's `observed` value as the caller last read it. When present, the item is selected only if the forge still shows exactly this, so a decision made about one state is never applied to another. Required for `adopt`.\",\n \"maxLength\": 256,\n \"type\": \"string\"\n },\n \"type\": {\n \"$ref\": \"#/$defs/DriftType\"\n }\n },\n \"required\": [\n \"type\"\n ],\n \"title\": \"DriftSelector\",\n \"type\": \"object\"\n },\n \"DriftType\": {\n \"description\": \"The kinds of drift, named so that a task can select a drift item by kind. The same values, with the same meanings, as `DriftItem`'s `type`, which keeps its own inline list so that `DriftItem` stays textually identical to 0.1; the two lists change together. `roleAdded` — someone holds a forge role the projection does not give them. `roleRemoved` — a projected role is missing. `roleChanged` — a projected role is present at another level. `requiredCheckMissing` — the verify-trust check is no longer required. `protectionWeakened` — branch protection or a ruleset is weaker than the projection in another way (force-push allowed, bypass actors added). `bootstrapMissing` — a bootstrap file or variable is gone.\",\n \"enum\": [\n \"roleAdded\",\n \"roleRemoved\",\n \"roleChanged\",\n \"requiredCheckMissing\",\n \"protectionWeakened\",\n \"bootstrapMissing\"\n ],\n \"title\": \"DriftType\",\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 \"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 \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"What the resolution did, and where the repository's sync now stands.\",\n \"properties\": {\n \"action\": {\n \"description\": \"The action taken, as requested.\",\n \"enum\": [\n \"adopt\",\n \"revert\"\n ],\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/RightRecord\",\n \"description\": \"For `adopt`: the recorded right — newly recorded, or the live record that already existed. Absent for `revert`.\"\n },\n \"sync\": {\n \"$ref\": \"#/$defs/Sync\",\n \"description\": \"The repository's sync after the resolution. The resolved item is no longer outstanding; the state is `drift` while other items remain, and otherwise `pending` until the bridge confirms the forge matches.\"\n }\n },\n \"required\": [\n \"action\",\n \"sync\"\n ],\n \"title\": \"Git Namespaces — Resolve Drift — 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 \"RightRecord\": {\n \"additionalProperties\": false,\n \"description\": \"One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords.\",\n \"properties\": {\n \"expiresAt\": {\n \"description\": \"When the right lapses. Absent: no expiry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.\"\n },\n \"reason\": {\n \"description\": \"The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.\"\n }\n },\n \"required\": [\n \"subject\",\n \"right\",\n \"resource\",\n \"grantedBy\",\n \"grantedAt\"\n ],\n \"title\": \"RightRecord\",\n \"type\": \"object\"\n },\n \"Sync\": {\n \"additionalProperties\": false,\n \"description\": \"How the forge compares with the VTC's projection for one repository.\",\n \"properties\": {\n \"checkedAt\": {\n \"description\": \"When the forge was last compared. Absent if never.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"drift\": {\n \"description\": \"Outstanding drift. Empty unless `state` is `drift`.\",\n \"items\": {\n \"$ref\": \"#/$defs/DriftItem\"\n },\n \"type\": \"array\"\n },\n \"state\": {\n \"description\": \"`inSync` — the last comparison found no drift. `drift` — it found some, listed in `drift`. `pending` — a change has been sent to the forge and not yet confirmed. `unchecked` — nothing compares this repository (a manual-mode namespace).\",\n \"enum\": [\n \"inSync\",\n \"drift\",\n \"pending\",\n \"unchecked\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"state\",\n \"drift\"\n ],\n \"title\": \"Sync\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[
error_codes::UNKNOWN_NAMESPACE,
error_codes::NAMESPACE_NOT_BOUND,
error_codes::UNKNOWN_REPO,
error_codes::REPO_NOT_ACTIVE,
error_codes::POLICY_DENIED,
error_codes::DRIFT_NOT_FOUND,
error_codes::NOT_ADOPTABLE,
error_codes::ACCOUNT_NOT_LINKED,
error_codes::NO_MATCHING_RIGHT,
error_codes::ROLE_MAP_UNKNOWN,
error_codes::NOT_REVERTIBLE,
];
/// 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:namespaceNotBound`
///
/// The namespace is still `pending`. Nothing is created, adopted or granted in it until binding completes.
///
/// Declared `retryable: false`.
pub const NAMESPACE_NOT_BOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:namespaceNotBound",
retryable: false,
};
/// `git-ns:unknownRepo`
///
/// The resource names no repository this VTC records.
///
/// Declared `retryable: false`.
pub const UNKNOWN_REPO: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:unknownRepo",
retryable: false,
};
/// `git-ns:repoNotActive`
///
/// The repository's state does not allow this operation; each task says which states it accepts.
///
/// Declared `retryable: false`.
pub const REPO_NOT_ACTIVE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:repoNotActive",
retryable: false,
};
/// `git-ns:policyDenied`
///
/// The community's git-namespace policy refused the request after the fixed rules passed.
///
/// Declared `retryable: false`.
pub const POLICY_DENIED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:policyDenied",
retryable: false,
};
/// `git-ns/drift/resolve:driftNotFound`
///
/// No outstanding drift item on the repository matches the selector: it was resolved already, the forge has changed since it was read (`observed` no longer matches), or it was never reported.
///
/// Declared `retryable: false`.
pub const DRIFT_NOT_FOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/drift/resolve:driftNotFound",
retryable: false,
};
/// `git-ns/drift/resolve:notAdoptable`
///
/// There is no right to record for this item: `adopt` was asked of a `roleRemoved`, `requiredCheckMissing`, `protectionWeakened` or `bootstrapMissing` item, or of a `roleChanged` item whose observed role is no higher than what the account's member already holds. Revert it, or revoke with git-ns/right/revoke.
///
/// Declared `retryable: false`.
pub const NOT_ADOPTABLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/drift/resolve:notAdoptable",
retryable: false,
};
/// `git-ns/drift/resolve:accountNotLinked`
///
/// `adopt` was asked of a role held by a forge account that is linked to no current member. Rights are held by DIDs, so there is nobody to record the right for; the role can only be reverted.
///
/// Declared `retryable: false`.
pub const ACCOUNT_NOT_LINKED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/drift/resolve:accountNotLinked",
retryable: false,
};
/// `git-ns/drift/resolve:noMatchingRight`
///
/// `adopt` was asked of a forge role that no git right projects to on this repository — `triage` or `read`, or `write` where committers get no forge role. Revert it, or grant a right with git-ns/right/grant and then revert the role.
///
/// Declared `retryable: false`.
pub const NO_MATCHING_RIGHT: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/drift/resolve:noMatchingRight",
retryable: false,
};
/// `git-ns:roleMapUnknown`
///
/// `adopt` was asked while the VTC holds no role map from the bridge that serves the namespace: that bridge has not reported one since the namespace was bound or came to be served by it, or it implements a git-ns/bridge/event version before 0.3 and never reports one. The VTC cannot tell which right the observed role is the projection of. Adopt once the bridge has reported its map, or revert.
///
/// Declared `retryable: true`.
pub const ROLE_MAP_UNKNOWN: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:roleMapUnknown",
retryable: true,
};
/// `git-ns/drift/resolve:notRevertible`
///
/// The bridge serving the namespace cannot perform the job this revert needs: it refused the job, or the revert needs `removeAccounts` and the bridge implements only git-ns/bridge/job 0.1.
///
/// Declared `retryable: false`.
pub const NOT_REVERTIBLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/drift/resolve:notRevertible",
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).
/// 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)] = &[
(
"`action` is `adopt` or `revert`; there is no action that ignores drift and leaves it outstanding silently.",
"{\n \"action\": \"ignore\",\n \"drift\": {\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"5550123\",\n \"login\": \"eve-dev\"\n },\n \"type\": \"roleAdded\"\n },\n \"resource\": \"github.com/acme/widgets\"\n}",
),
(
"Drift is reported per repository; a namespace resource names no drift item.",
"{\n \"action\": \"revert\",\n \"drift\": {\n \"type\": \"requiredCheckMissing\"\n },\n \"resource\": \"github.com/acme\"\n}",
),
(
"`drift.type` is one of the drift types git-ns/view and git-ns/bridge/event report.",
"{\n \"action\": \"revert\",\n \"drift\": {\n \"type\": \"collaboratorAdded\"\n },\n \"resource\": \"github.com/acme/widgets\"\n}",
),
(
"The selector is required: a resolution names the item it decides about.",
"{\n \"action\": \"revert\",\n \"resource\": \"github.com/acme/widgets\"\n}",
),
(
"Drift items have no identifier to select by; a made-up one is refused rather than ignored.",
"{\n \"action\": \"revert\",\n \"drift\": {\n \"id\": \"drift_01\",\n \"type\": \"roleAdded\"\n },\n \"resource\": \"github.com/acme/widgets\"\n}",
),
(
"`account` is a forge account, not a DID: the account whose role differs often belongs to someone with no DID.",
"{\n \"action\": \"adopt\",\n \"drift\": {\n \"account\": \"did:webvh:QmBobScid2:acme-vtc.example:bob\",\n \"type\": \"roleAdded\"\n },\n \"resource\": \"github.com/acme/widgets\"\n}",
),
(
"An adoption records the right the observed role projects to; the caller cannot name a different one. Granting something else is git-ns/right/grant.",
"{\n \"action\": \"adopt\",\n \"drift\": {\n \"account\": {\n \"forge\": \"github.com\",\n \"id\": \"9120045\",\n \"login\": \"bob-builds\"\n },\n \"observed\": \"maintain\",\n \"type\": \"roleAdded\"\n },\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\"\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
);
}
}
}