greentic_types/
distributor.rs

1//! Distributor API data transfer objects used by runner/deployer clients.
2//!
3//! These mirror the `greentic:distributor-api@1.0.0` WIT shapes.
4
5use alloc::string::String;
6use alloc::vec::Vec;
7
8#[cfg(feature = "schemars")]
9use schemars::JsonSchema;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14use crate::{SecretRequirement, TenantCtx};
15
16/// Identifier for a distributor environment.
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "schemars", derive(JsonSchema))]
20pub struct DistributorEnvironmentId(pub String);
21
22impl DistributorEnvironmentId {
23    /// Returns the underlying identifier as a string slice.
24    pub fn as_str(&self) -> &str {
25        &self.0
26    }
27}
28
29impl From<String> for DistributorEnvironmentId {
30    fn from(value: String) -> Self {
31        Self(value)
32    }
33}
34
35impl From<&str> for DistributorEnvironmentId {
36    fn from(value: &str) -> Self {
37        Self(value.to_owned())
38    }
39}
40
41/// Digest for a component artifact.
42#[derive(Clone, Debug, PartialEq, Eq, Hash)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(feature = "schemars", derive(JsonSchema))]
45pub struct ComponentDigest(pub String);
46
47impl ComponentDigest {
48    /// Returns the digest as a string slice.
49    pub fn as_str(&self) -> &str {
50        &self.0
51    }
52
53    /// Heuristic check for sha256-like digests: `sha256:` + 64 lowercase hex chars.
54    pub fn is_sha256_like(&self) -> bool {
55        const PREFIX: &str = "sha256:";
56        let s = self.0.as_str();
57        if !s.starts_with(PREFIX) {
58            return false;
59        }
60        let rest = &s[PREFIX.len()..];
61        if rest.len() != 64 {
62            return false;
63        }
64        rest.chars()
65            .all(|c| c.is_ascii_hexdigit() && c.is_ascii_lowercase() || c.is_ascii_digit())
66    }
67}
68
69impl From<String> for ComponentDigest {
70    fn from(value: String) -> Self {
71        Self(value)
72    }
73}
74
75impl From<&str> for ComponentDigest {
76    fn from(value: &str) -> Self {
77        Self(value.to_owned())
78    }
79}
80
81/// Resolution status for a component.
82#[derive(Clone, Debug, PartialEq, Eq)]
83#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
84#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
85#[cfg_attr(feature = "schemars", derive(JsonSchema))]
86pub enum ComponentStatus {
87    /// Resolution in progress or awaiting cache.
88    Pending,
89    /// Component is ready for use.
90    Ready,
91    /// Resolution failed with a reason.
92    Failed {
93        /// Human-readable failure explanation.
94        reason: String,
95    },
96}
97
98/// Location of the resolved artifact.
99#[derive(Clone, Debug, PartialEq, Eq)]
100#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101#[cfg_attr(feature = "schemars", derive(JsonSchema))]
102#[cfg_attr(feature = "serde", serde(tag = "kind", rename_all = "snake_case"))]
103pub enum ArtifactLocation {
104    /// Local file path on disk.
105    FilePath {
106        /// Absolute or relative path to the artifact.
107        path: String,
108    },
109    /// OCI reference to the artifact.
110    OciReference {
111        /// Reference string to the OCI artifact.
112        reference: String,
113    },
114    /// Internal distributor handle.
115    DistributorInternal {
116        /// Opaque handle understood by the distributor.
117        handle: String,
118    },
119}
120
121/// Summary of artifact signature verification.
122#[derive(Clone, Debug, PartialEq, Eq)]
123#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
124#[cfg_attr(feature = "schemars", derive(JsonSchema))]
125pub struct SignatureSummary {
126    /// Whether the signature verified.
127    pub verified: bool,
128    /// Signer identifier or key hint.
129    pub signer: String,
130    /// Opaque extra details.
131    pub extra: Value,
132}
133
134/// Cache metadata for resolved artifacts.
135#[derive(Clone, Debug, PartialEq, Eq)]
136#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
137#[cfg_attr(feature = "schemars", derive(JsonSchema))]
138pub struct CacheInfo {
139    /// Size of the cached artifact in bytes.
140    pub size_bytes: u64,
141    /// Last use timestamp in ISO 8601 (UTC).
142    pub last_used_utc: String,
143    /// Last refresh timestamp in ISO 8601 (UTC).
144    pub last_refreshed_utc: String,
145}
146
147/// Request to resolve a component for a tenant/environment.
148#[derive(Clone, Debug, PartialEq, Eq)]
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
150#[cfg_attr(feature = "schemars", derive(JsonSchema))]
151pub struct ResolveComponentRequest {
152    /// Tenant context for the request.
153    pub tenant: TenantCtx,
154    /// Distributor environment identifier.
155    pub environment_id: DistributorEnvironmentId,
156    /// Pack identifier.
157    pub pack_id: String,
158    /// Component identifier.
159    pub component_id: String,
160    /// Requested version or label.
161    pub version: String,
162    /// Opaque extension field.
163    pub extra: Value,
164}
165
166/// Response returned by the distributor.
167#[derive(Clone, Debug, PartialEq, Eq)]
168#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169#[cfg_attr(feature = "schemars", derive(JsonSchema))]
170pub struct ResolveComponentResponse {
171    /// Resolution status.
172    pub status: ComponentStatus,
173    /// Content digest of the component.
174    pub digest: ComponentDigest,
175    /// Location of the resolved artifact.
176    pub artifact: ArtifactLocation,
177    /// Signature summary.
178    pub signature: SignatureSummary,
179    /// Cache metadata.
180    pub cache: CacheInfo,
181    /// Declared secret requirements for the pack/component.
182    #[cfg_attr(
183        feature = "serde",
184        serde(default, skip_serializing_if = "Option::is_none")
185    )]
186    pub secret_requirements: Option<Vec<SecretRequirement>>,
187}
188
189/// Structured pack status response (v2) including optional secret requirements.
190#[derive(Clone, Debug, PartialEq)]
191#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
192#[cfg_attr(feature = "schemars", derive(JsonSchema))]
193pub struct PackStatusResponseV2 {
194    /// Opaque status payload returned by the distributor.
195    pub status: Value,
196    /// Declared secret requirements for the pack.
197    #[cfg_attr(
198        feature = "serde",
199        serde(default, skip_serializing_if = "Option::is_none")
200    )]
201    pub secret_requirements: Option<Vec<SecretRequirement>>,
202}