cnb 0.2.2

CNB (cnb.cool) API client for Rust — typed, async, production-ready
Documentation
//! Hand-written shared types for DTOs. Generated modules in this directory are
//! overwritten whenever the codegen runs — keep anything stable here.

use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// Repository visibility level.
///
/// Defined by the CNB API spec as string enum: `"Public"`, `"Private"`, `"Secret"`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Visibility {
    /// Public repository — accessible without authentication.
    Public,
    /// Private repository — requires explicit per-user access.
    Private,
    /// Secret (encrypted) repository — requires specific role credentials.
    Secret,
}

impl Visibility {
    /// Returns the canonical string form as declared by the API spec.
    pub fn as_str(&self) -> &'static str {
        match self {
            Visibility::Public => "Public",
            Visibility::Private => "Private",
            Visibility::Secret => "Secret",
        }
    }
}

impl std::fmt::Display for Visibility {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}

impl Serialize for Visibility {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for Visibility {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct VisibilityVisitor;

        impl<'de> serde::de::Visitor<'de> for VisibilityVisitor {
            type Value = Visibility;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str(
                    r#"a visibility string ("Public","Private","Secret") or integer (0, 10, 20)"#,
                )
            }

            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Visibility, E> {
                match v {
                    "Public" | "public" => Ok(Visibility::Public),
                    "Private" | "private" => Ok(Visibility::Private),
                    "Secret" | "secret" | "Internal" | "internal" => Ok(Visibility::Secret),
                    _ => Err(E::unknown_variant(v, &["Public", "Private", "Secret"])),
                }
            }

            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Visibility, E> {
                match v {
                    0 => Ok(Visibility::Public),
                    10 => Ok(Visibility::Secret),
                    20 => Ok(Visibility::Private),
                    _ => Err(E::invalid_value(
                        serde::de::Unexpected::Unsigned(v),
                        &"0, 10, or 20",
                    )),
                }
            }

            fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Visibility, E> {
                self.visit_u64(v as u64)
            }
        }

        deserializer.deserialize_any(VisibilityVisitor)
    }
}

/// Typed representation of a PR head/base reference.
///
/// Matches `api.PullRef` from the CNB API spec.
/// Fields: `ref` (branch reference name), `sha` (commit hash), `repo` (repository info).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PullRef {
    /// Branch reference name (e.g. `"refs/heads/main"`).
    #[serde(default, rename = "ref", skip_serializing_if = "Option::is_none")]
    pub ref_: Option<String>,
    /// Commit hash the ref points to.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sha: Option<String>,
    /// Associated repository information.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub repo: Option<serde_json::Value>,
}

/// A container image tag (returned by the registries package-tags endpoint).
///
/// Distinct from the git `Tag` struct which represents a git tag object.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RegistryPackageTag {
    /// Tag name (e.g. `"latest"`, `"v1.2.3"`).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// Last update timestamp.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<String>,
    /// Image digest (e.g. `"sha256:…"`).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub digest: Option<String>,
    /// Image size in bytes.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub size: Option<i64>,
}