bindist 0.1.0

Rust client library for the BinDist Customer API
Documentation
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Release channel. By default the API returns only versions on the
/// production channel; passing a [`Channel`] sends the `X-Channel` header
/// to expose versions published on a non-default channel (for example
/// disabled/pre-release versions via [`Channel::Test`]).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Channel {
    Test,
}

impl Channel {
    pub fn as_str(self) -> &'static str {
        match self {
            Channel::Test => "Test",
        }
    }
}

impl AsRef<str> for Channel {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

/// Type of a file in a version.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum FileType {
    #[serde(rename = "MAIN")]
    Main,
    #[serde(rename = "DEPENDENCY")]
    Dependency,
    #[serde(rename = "DOCUMENTATION")]
    Documentation,
    #[serde(rename = "CONFIGURATION")]
    Configuration,
    #[serde(untagged)]
    Other(String),
}

/// Pagination information returned alongside list endpoints.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Pagination {
    pub page: u32,
    pub limit: u32,
    pub total: u64,
    pub has_next: bool,
    pub has_previous: bool,
}

/// Response-level metadata (request id, pagination, ...).
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Meta {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub request_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub pagination: Option<Pagination>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Application {
    pub application_id: String,
    pub name: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(default)]
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    #[serde(default)]
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Version {
    pub version_id: String,
    pub application_id: String,
    pub version: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub release_notes: Option<String>,
    #[serde(default)]
    pub is_active: bool,
    #[serde(default)]
    pub is_enabled: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    #[serde(default)]
    pub file_size: u64,
    #[serde(default)]
    pub download_count: u64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VersionFile {
    pub file_id: String,
    pub file_name: String,
    pub file_type: FileType,
    pub file_size: u64,
    pub checksum: String,
    #[serde(default)]
    pub order: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DownloadInfo {
    pub download_id: String,
    pub url: String,
    pub expires_at: DateTime<Utc>,
    pub file_name: String,
    pub file_size: u64,
    pub checksum: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShareLink {
    pub share_url: String,
    pub expires_at: DateTime<Utc>,
}

/// Query options for [`Client::list_applications`].
#[derive(Debug, Clone, Default)]
pub struct ListApplicationsOptions {
    /// Page number (1-based). Defaults to server's default (1).
    pub page: Option<u32>,
    /// Items per page. Defaults to server's default (20, max 100).
    pub limit: Option<u32>,
    /// Search by name or description.
    pub search: Option<String>,
    /// Filter by tag.
    pub tag: Option<String>,
    /// Filter by active status.
    pub is_active: Option<bool>,
}

/// Options for [`Client::list_versions`].
#[derive(Debug, Clone, Default)]
pub struct ListVersionsOptions {
    /// Search term to filter versions by release notes (case-insensitive).
    pub changelog: Option<String>,
    /// When set, send `X-Channel` (e.g. [`Channel::Test`] to include
    /// disabled versions).
    pub channel: Option<Channel>,
}

/// Options for [`Client::get_download_info`].
#[derive(Debug, Clone, Default)]
pub struct GetDownloadInfoOptions {
    /// Specific file id. Defaults to the `MAIN` file when omitted.
    pub file_id: Option<String>,
    /// When set, send `X-Channel` to access non-default channels.
    pub channel: Option<Channel>,
}

/// Request body for [`Client::create_share_link`].
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateShareLinkRequest {
    pub application_id: String,
    pub version: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub file_id: Option<String>,
    /// Link expiry in minutes (5-1440, default 30 when omitted).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expires_in_minutes: Option<u32>,
}

// ---- internal envelope types -----------------------------------------------

#[derive(Debug, Deserialize)]
pub(crate) struct Envelope<T> {
    #[serde(default = "Option::default")]
    pub data: Option<T>,
    #[serde(default)]
    pub error: Option<crate::error::ApiError>,
    #[serde(default)]
    pub meta: Option<Meta>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct ApplicationsData {
    #[serde(default)]
    pub applications: Vec<Application>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct VersionsData {
    #[serde(default)]
    pub versions: Vec<Version>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct FilesData {
    #[serde(default)]
    pub files: Vec<VersionFile>,
}