heddle-cli-contract 0.28.1

Heddle's CLI verb catalog, schemas, help, and recovery contract.
// SPDX-License-Identifier: Apache-2.0
//! Wire payloads for `clone`, `import`, `remote add/remove/set-default`,
//! `pull`, and `push`.

use std::path::PathBuf;

use schemars::JsonSchema;
use serde::Serialize;
use verbs::{
    ActionTemplate, PullOutcome, PushOutcome, RepositoryVerificationState,
    source_heads::SourceHeadsReport,
};

use super::bridge::SkippedRefOutput;

/// JSON payload for `heddle clone`. One struct for both transports: the
/// transport-specific facts are optional fields, omitted when absent.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "CloneSchema")]
pub struct CloneOutput {
    pub output_kind: &'static str,
    pub action: &'static str,
    pub status: &'static str,
    pub success: bool,
    pub cloned: bool,
    pub transport: &'static str,
    pub remote: String,
    pub local: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub branch: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub repository_capability: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub commits_imported: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub states_created: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub objects: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<String>,
    #[serde(rename = "verification")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trust: Option<RepositoryVerificationState>,
    /// Threads cloned with several concurrent source heads. One head was
    /// checked out by the documented default; the rest stay selectable.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub source_heads: Vec<SourceHeadsReport>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_action: Option<String>,
}

/// JSON payload for `heddle import local`.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "ImportLocalSchema")]
pub struct AdoptOutput {
    pub output_kind: &'static str,
    pub status: &'static str,
    pub action: &'static str,
    pub adopted: bool,
    pub initialized: bool,
    #[schemars(with = "String")]
    pub path: PathBuf,
    pub refs: Vec<String>,
    pub commits_imported: usize,
    pub states_created: usize,
    pub branches_synced: usize,
    pub tags_synced: usize,
    pub skipped_non_commit_refs: usize,
    pub skipped_refs: Vec<SkippedRefOutput>,
    pub already_in_sync: bool,
    pub recommended_action: Option<String>,
    pub recommended_action_template: Option<ActionTemplate>,
    #[serde(rename = "verification")]
    pub trust: RepositoryVerificationState,
}

/// JSON payload for `remote add` / `remote remove` / `remote set-default`.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "RemoteMutationSchema")]
pub struct RemoteMutationOutput {
    pub output_kind: &'static str,
    pub status: &'static str,
    pub action: &'static str,
    pub name: String,
    pub url: Option<String>,
    pub default: Option<String>,
    pub message: String,
    #[serde(rename = "verification")]
    pub trust: RepositoryVerificationState,
}

/// Executor's fidelity assessment of the retained Git import.
#[derive(Serialize, JsonSchema)]
pub struct ImportReportOutput {
    pub fidelity: &'static str,
    pub commits: u64,
    pub branches: u64,
    pub tags: u64,
    pub skipped_refs: Vec<SkippedImportRefOutput>,
}

#[derive(Serialize, JsonSchema)]
pub struct SkippedImportRefOutput {
    pub name: String,
    pub reason: String,
}

/// One committed hosted import operation update.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "ImportOperationOutput")]
pub struct ImportOperationOutput {
    pub output_kind: &'static str,
    pub event: &'static str,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source: Option<String>,
    pub destination: String,
    pub operation_id: String,
    pub client_operation_id: String,
    pub state: String,
    pub completed_units: u64,
    pub total_units: Option<u64>,
    pub unit: String,
    pub terminal: bool,
    pub success: bool,
    pub summary: String,
    pub import_report: Option<ImportReportOutput>,
    pub results: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub failure: Option<String>,
}

/// Finite result returned after an explicit import retry is admitted.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "ImportRetryOutput")]
pub struct ImportRetryOutput {
    pub output_kind: &'static str,
    pub action: &'static str,
    pub status: &'static str,
    pub success: bool,
    pub destination: String,
    pub original_operation_id: String,
    pub operation_id: String,
    pub client_operation_id: String,
}

/// One replication surface of a push (source, discussions, context,
/// reviews). Surfaces succeed or fail independently of each other.
#[derive(Serialize, JsonSchema, Clone, Debug, PartialEq, Eq)]
pub struct PushReplicationOutcome {
    pub status: &'static str,
    /// Records the remote accepted during this push.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub count: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Local work that did not reach the remote and is retained for a later
    /// push once its recovery is done.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub unsent: Vec<PushReplicationItem>,
    /// Local work that stays local: no hosted command can carry it, or its
    /// author replaced it with an explicit revision.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub local_only: Vec<PushReplicationItem>,
}

impl PushReplicationOutcome {
    pub fn with_status(status: &'static str) -> Self {
        Self {
            status,
            count: None,
            error: None,
            unsent: Vec::new(),
            local_only: Vec::new(),
        }
    }
}

/// One collaboration record the remote does not hold after a push, with a
/// bounded recovery for it.
#[derive(Serialize, JsonSchema, Clone, Debug, PartialEq, Eq)]
pub struct PushReplicationItem {
    /// Local discussion or annotation ID; null when the whole surface failed
    /// before any record was attempted.
    pub record_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub revision_id: Option<String>,
    /// `stale_version`, `invalid_command`, `transient`, `permission_denied`,
    /// `not_replicable`, or `superseded`.
    pub kind: &'static str,
    /// Whether resending the same signed command can succeed.
    pub retry_unchanged: bool,
    /// Command ID of the signed command involved. Kept across retries.
    pub client_operation_id: Option<String>,
    /// Content ID of the signed operation involved. Kept across retries.
    pub signed_operation_id: Option<String>,
    pub message: String,
    pub guidance: String,
    /// Ordered steps; run each after the previous one.
    pub recovery_commands: Vec<String>,
    pub recovery_action_templates: Vec<ActionTemplate>,
}

/// JSON payload for `heddle pull`: the verbs [`PullOutcome`] body beside
/// repository verification.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "PullOutput")]
pub struct PullOutput {
    #[serde(flatten)]
    pub outcome: PullOutcome,
    #[serde(rename = "verification")]
    pub trust: RepositoryVerificationState,
    /// The pulled Thread has several concurrent source heads. One is checked
    /// out by the documented default; the rest stay selectable.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_heads: Option<SourceHeadsReport>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_action: Option<String>,
}

/// JSON payload for `heddle push`: the verbs [`PushOutcome`] body beside
/// verification-derived recovery actions.
#[derive(Serialize, JsonSchema)]
#[schemars(rename = "PushOutput")]
pub struct PushOutput {
    #[serde(flatten)]
    pub outcome: PushOutcome,
    pub next_action: Option<String>,
    pub next_action_template: Option<ActionTemplate>,
    pub recommended_action: Option<String>,
    pub recommended_action_template: Option<ActionTemplate>,
    pub source: PushReplicationOutcome,
    pub discussions: PushReplicationOutcome,
    pub context: PushReplicationOutcome,
    pub reviews: PushReplicationOutcome,
    #[serde(rename = "verification")]
    pub trust: RepositoryVerificationState,
}