1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct PackageManifest {
6 pub manufacturer: String,
7 pub name: String,
8 pub version: String,
9 pub binary: BinaryEntry,
10 #[serde(default = "default_sig_algorithm")]
11 pub signature_algorithm: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
14 pub signing_key_id: Option<String>,
15 #[serde(default)]
16 pub resources: Vec<ResourceEntry>,
17 #[serde(default)]
19 pub proto_files: Vec<ProtoFileEntry>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub lock_file: Option<LockFileEntry>,
23 #[serde(default)]
24 pub metadata: ManifestMetadata,
25}
26
27fn default_sig_algorithm() -> String {
28 "ed25519".to_string()
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40#[serde(rename_all = "kebab-case")]
41pub enum BinaryKind {
42 CoreModule,
46 Component,
48 NativeCdylib,
50}
51
52impl BinaryKind {
53 pub(crate) fn legacy_default_for(target: &str) -> Self {
58 if target.starts_with("wasm32-") {
59 BinaryKind::CoreModule
60 } else {
61 BinaryKind::NativeCdylib
62 }
63 }
64}
65
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
67pub struct BinaryEntry {
68 pub path: String,
69 pub target: String,
70 pub hash: String,
72 pub size: Option<u64>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub kind: Option<BinaryKind>,
82}
83
84impl BinaryEntry {
85 pub fn hash_bytes(&self) -> Result<[u8; 32], crate::error::PackError> {
90 let hex = &self.hash;
91 if hex.len() != 64 {
92 return Err(crate::error::PackError::ManifestParseError(
93 "binary.hash must be a 64-character hex string (32 bytes)".to_string(),
94 ));
95 }
96 let mut out = [0u8; 32];
97 for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
98 let s = std::str::from_utf8(chunk).map_err(|_| {
99 crate::error::PackError::ManifestParseError(
100 "binary.hash contains non-UTF-8 characters".to_string(),
101 )
102 })?;
103 out[i] = u8::from_str_radix(s, 16).map_err(|_| {
104 crate::error::PackError::ManifestParseError(
105 "binary.hash contains invalid hex characters".to_string(),
106 )
107 })?;
108 }
109 Ok(out)
110 }
111
112 pub fn is_wasm_target(&self) -> bool {
115 self.target.starts_with("wasm32-")
116 }
117
118 pub fn resolved_kind(&self) -> BinaryKind {
121 self.kind
122 .unwrap_or_else(|| BinaryKind::legacy_default_for(&self.target))
123 }
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ResourceEntry {
128 pub path: String,
129 pub hash: String,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct ProtoFileEntry {
136 pub name: String,
138 pub path: String,
140 pub hash: String,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct LockFileEntry {
146 pub path: String,
148 pub hash: String,
150}
151
152#[derive(Debug, Clone, Default, Serialize, Deserialize)]
153pub struct ManifestMetadata {
154 pub description: Option<String>,
155 pub license: Option<String>,
156}
157
158impl PackageManifest {
159 pub fn actr_type_str(&self) -> String {
161 format!("{}:{}:{}", self.manufacturer, self.name, self.version)
162 }
163
164 pub fn from_toml(s: &str) -> Result<Self, crate::error::PackError> {
166 toml::from_str(s).map_err(|e| crate::error::PackError::ManifestParseError(e.to_string()))
167 }
168
169 pub fn to_toml(&self) -> Result<String, crate::error::PackError> {
171 toml::to_string_pretty(self)
172 .map_err(|e| crate::error::PackError::ManifestParseError(e.to_string()))
173 }
174}