use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackMeta {
pub name: String,
pub version: String,
#[serde(default)]
pub description: Option<String>,
pub digest: String,
#[serde(default)]
pub size: Option<u64>,
#[serde(default)]
pub published_at: Option<DateTime<Utc>>,
#[serde(default)]
pub signed: bool,
#[serde(default)]
pub key_id: Option<String>,
#[serde(default)]
pub deprecated: bool,
#[serde(default)]
pub deprecation_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionsResponse {
pub name: String,
pub versions: Vec<VersionInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionInfo {
pub version: String,
pub digest: String,
#[serde(default)]
pub published_at: Option<DateTime<Utc>>,
#[serde(default)]
pub deprecated: bool,
}
#[derive(Debug, Clone)]
pub struct PackHeaders {
pub digest: Option<String>,
pub signature: Option<String>,
pub key_id: Option<String>,
pub etag: Option<String>,
pub cache_control: Option<String>,
pub content_length: Option<u64>,
}
impl PackHeaders {
pub fn from_headers(headers: &reqwest::header::HeaderMap) -> Self {
Self {
digest: headers
.get("x-pack-digest")
.and_then(|v| v.to_str().ok())
.map(String::from),
signature: headers
.get("x-pack-signature")
.and_then(|v| v.to_str().ok())
.map(String::from),
key_id: headers
.get("x-pack-key-id")
.and_then(|v| v.to_str().ok())
.map(String::from),
etag: headers
.get(reqwest::header::ETAG)
.and_then(|v| v.to_str().ok())
.map(String::from),
cache_control: headers
.get(reqwest::header::CACHE_CONTROL)
.and_then(|v| v.to_str().ok())
.map(String::from),
content_length: headers
.get(reqwest::header::CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse().ok()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeysManifest {
pub version: u8,
pub keys: Vec<TrustedKey>,
#[serde(default)]
pub expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrustedKey {
pub key_id: String,
pub algorithm: String,
pub public_key: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub added_at: Option<DateTime<Utc>>,
#[serde(default)]
pub expires_at: Option<DateTime<Utc>>,
#[serde(default)]
pub revoked: bool,
}
#[derive(Debug, Clone)]
pub struct FetchResult {
pub content: String,
pub headers: PackHeaders,
pub computed_digest: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DsseEnvelope {
#[serde(rename = "payloadType")]
pub payload_type: String,
pub payload: String,
pub signatures: Vec<DsseSignature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DsseSignature {
#[serde(rename = "keyid")]
pub key_id: String,
#[serde(rename = "sig")]
pub signature: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegistryConfig {
#[serde(default = "default_registry_url")]
pub url: String,
#[serde(default)]
pub token: Option<String>,
#[serde(default)]
pub allow_unsigned: bool,
#[serde(default = "default_timeout")]
pub timeout_secs: u64,
#[serde(default = "default_max_retries")]
pub max_retries: u32,
}
fn default_registry_url() -> String {
"https://registry.getassay.dev/v1".to_string()
}
fn default_timeout() -> u64 {
30
}
fn default_max_retries() -> u32 {
3
}
impl Default for RegistryConfig {
fn default() -> Self {
Self {
url: default_registry_url(),
token: None,
allow_unsigned: false,
timeout_secs: default_timeout(),
max_retries: default_max_retries(),
}
}
}
impl RegistryConfig {
pub fn from_env() -> Self {
Self {
url: std::env::var("ASSAY_REGISTRY_URL").unwrap_or_else(|_| default_registry_url()),
token: std::env::var("ASSAY_REGISTRY_TOKEN").ok(),
allow_unsigned: std::env::var("ASSAY_ALLOW_UNSIGNED_PACKS")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false),
timeout_secs: std::env::var("ASSAY_REGISTRY_TIMEOUT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or_else(default_timeout),
max_retries: std::env::var("ASSAY_REGISTRY_MAX_RETRIES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or_else(default_max_retries),
}
}
pub fn with_token(mut self, token: impl Into<String>) -> Self {
self.token = Some(token.into());
self
}
pub fn with_url(mut self, url: impl Into<String>) -> Self {
self.url = url.into();
self
}
pub fn with_allow_unsigned(mut self, allow: bool) -> Self {
self.allow_unsigned = allow;
self
}
}