use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageManifest {
pub manufacturer: String,
pub name: String,
pub version: String,
pub binary: BinaryEntry,
#[serde(default = "default_sig_algorithm")]
pub signature_algorithm: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub signing_key_id: Option<String>,
#[serde(default)]
pub resources: Vec<ResourceEntry>,
#[serde(default)]
pub proto_files: Vec<ProtoFileEntry>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lock_file: Option<LockFileEntry>,
#[serde(default)]
pub metadata: ManifestMetadata,
}
fn default_sig_algorithm() -> String {
"ed25519".to_string()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BinaryKind {
CoreModule,
Component,
NativeCdylib,
}
impl BinaryKind {
pub(crate) fn legacy_default_for(target: &str) -> Self {
if target.starts_with("wasm32-") {
BinaryKind::CoreModule
} else {
BinaryKind::NativeCdylib
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BinaryEntry {
pub path: String,
pub target: String,
pub hash: String,
pub size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<BinaryKind>,
}
impl BinaryEntry {
pub fn hash_bytes(&self) -> Result<[u8; 32], crate::error::PackError> {
let hex = &self.hash;
if hex.len() != 64 {
return Err(crate::error::PackError::ManifestParseError(
"binary.hash must be a 64-character hex string (32 bytes)".to_string(),
));
}
let mut out = [0u8; 32];
for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
let s = std::str::from_utf8(chunk).map_err(|_| {
crate::error::PackError::ManifestParseError(
"binary.hash contains non-UTF-8 characters".to_string(),
)
})?;
out[i] = u8::from_str_radix(s, 16).map_err(|_| {
crate::error::PackError::ManifestParseError(
"binary.hash contains invalid hex characters".to_string(),
)
})?;
}
Ok(out)
}
pub fn is_wasm_target(&self) -> bool {
self.target.starts_with("wasm32-")
}
pub fn resolved_kind(&self) -> BinaryKind {
self.kind
.unwrap_or_else(|| BinaryKind::legacy_default_for(&self.target))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceEntry {
pub path: String,
pub hash: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProtoFileEntry {
pub name: String,
pub path: String,
pub hash: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LockFileEntry {
pub path: String,
pub hash: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ManifestMetadata {
pub description: Option<String>,
pub license: Option<String>,
}
impl PackageManifest {
pub fn actr_type_str(&self) -> String {
format!("{}:{}:{}", self.manufacturer, self.name, self.version)
}
pub fn from_toml(s: &str) -> Result<Self, crate::error::PackError> {
toml::from_str(s).map_err(|e| crate::error::PackError::ManifestParseError(e.to_string()))
}
pub fn to_toml(&self) -> Result<String, crate::error::PackError> {
toml::to_string_pretty(self)
.map_err(|e| crate::error::PackError::ManifestParseError(e.to_string()))
}
}