1#![deny(unsafe_code)]
2
3use serde::{Deserialize, Serialize};
4
5include!(concat!(env!("OUT_DIR"), "/generated_assets.rs"));
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
8#[serde(rename_all = "kebab-case")]
9pub struct AssetManifest {
10 pub format_version: u32,
11 #[serde(default)]
12 pub source_lane: Option<String>,
13 #[serde(default)]
14 pub source_fingerprint: Option<String>,
15 pub runtime: RuntimeAsset,
16 #[serde(default)]
17 pub runtime_support: Vec<BinaryAsset>,
18 #[serde(default)]
19 pub initdb: Option<BinaryAsset>,
20 #[serde(default)]
21 pub pgdata_template: Option<PgDataTemplateAsset>,
22 #[serde(default)]
23 pub extensions: Vec<ExtensionAsset>,
24 #[serde(default)]
25 pub sources: Vec<SourcePin>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "kebab-case")]
30pub struct RuntimeAsset {
31 pub archive: String,
32 pub sha256: String,
33 #[serde(default)]
34 pub module_sha256: String,
35 pub postgres_version: String,
36 pub runtime_kind: String,
37 #[serde(default)]
38 pub link: Option<WasmLinkMetadata>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42#[serde(rename_all = "kebab-case")]
43pub struct BinaryAsset {
44 pub name: String,
45 pub path: String,
46 pub sha256: String,
47 #[serde(default)]
48 pub module_sha256: String,
49 #[serde(default)]
50 pub native_module: Option<String>,
51 pub size: u64,
52 #[serde(default)]
53 pub link: Option<WasmLinkMetadata>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
57#[serde(rename_all = "kebab-case")]
58pub struct PgDataTemplateAsset {
59 pub archive: String,
60 pub manifest: String,
61 pub sha256: String,
62 pub size: u64,
63 pub runtime_module_sha256: String,
64 pub initdb_module_sha256: String,
65 pub source_pins_sha256: String,
66 #[serde(default)]
67 pub source_lane: Option<String>,
68 #[serde(default)]
69 pub source_fingerprint: Option<String>,
70 pub postgres_version: String,
71 pub catalog_version: String,
72 pub init_profile: String,
73 pub wasmer_version: String,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "kebab-case")]
78pub struct ExtensionAsset {
79 pub name: String,
80 pub sql_name: String,
81 #[serde(default)]
82 pub source_kind: String,
83 pub archive: String,
84 pub sha256: String,
85 #[serde(default)]
86 pub module_sha256: String,
87 #[serde(default)]
88 pub native_modules: Vec<BinaryAsset>,
89 pub size: u64,
90 #[serde(default)]
91 pub stable: bool,
92 #[serde(default)]
93 pub control_files: Vec<String>,
94 #[serde(default)]
95 pub dependencies: Vec<String>,
96 #[serde(default)]
97 pub native_dependencies: Vec<String>,
98 #[serde(default)]
99 pub load_order: Vec<String>,
100 #[serde(default)]
101 pub lifecycle: Option<ExtensionLifecycle>,
102 #[serde(default)]
103 pub extension_imports: Vec<WasmImport>,
104 #[serde(default)]
105 pub core_exports_required: Vec<String>,
106 #[serde(default)]
107 pub unresolved_imports: Vec<WasmImport>,
108 #[serde(default)]
109 pub installed_files: Vec<String>,
110 #[serde(default)]
111 pub smoke_status: Option<ExtensionSmokeStatus>,
112 #[serde(default)]
113 pub link: Option<WasmLinkMetadata>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117#[serde(rename_all = "kebab-case")]
118pub struct ExtensionLifecycle {
119 pub create_extension: bool,
120 #[serde(default)]
121 pub create_schema: Option<String>,
122 #[serde(default)]
123 pub load_sql: Vec<String>,
124 #[serde(default)]
125 pub post_create_sql: Vec<String>,
126 #[serde(default)]
127 pub startup_config: Vec<String>,
128 #[serde(default)]
129 pub preload_required: bool,
130 #[serde(default)]
131 pub restart_required: bool,
132 #[serde(default)]
133 pub shared_memory_required: bool,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
137#[serde(rename_all = "kebab-case")]
138pub struct ExtensionSmokeStatus {
139 pub promoted: bool,
140 pub direct: String,
141 pub server: String,
142 pub restart: String,
143 pub dump_restore: String,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
147#[serde(rename_all = "kebab-case")]
148pub struct WasmLinkMetadata {
149 pub has_dylink0: bool,
150 #[serde(default)]
151 pub dylink_needed: Vec<String>,
152 #[serde(default)]
153 pub dylink_runtime_paths: Vec<String>,
154 #[serde(default)]
155 pub dylink_memory: Option<WasmDylinkMemory>,
156 #[serde(default)]
157 pub dylink_imports: Vec<WasmDylinkSymbol>,
158 #[serde(default)]
159 pub dylink_exports: Vec<WasmDylinkSymbol>,
160 #[serde(default)]
161 pub imports: Vec<WasmImport>,
162 #[serde(default)]
163 pub exports: Vec<WasmExport>,
164 #[serde(default)]
165 pub memories: Vec<WasmMemory>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
169#[serde(rename_all = "kebab-case")]
170pub struct WasmDylinkMemory {
171 pub memory_size: u32,
172 pub memory_alignment: u32,
173 pub table_size: u32,
174 pub table_alignment: u32,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
178#[serde(rename_all = "kebab-case")]
179pub struct WasmDylinkSymbol {
180 pub module: Option<String>,
181 pub name: String,
182 pub flags: u32,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
186#[serde(rename_all = "kebab-case")]
187pub struct WasmImport {
188 pub module: String,
189 pub name: String,
190 pub kind: String,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
194#[serde(rename_all = "kebab-case")]
195pub struct WasmExport {
196 pub name: String,
197 pub kind: String,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
201#[serde(rename_all = "kebab-case")]
202pub struct WasmMemory {
203 pub initial_pages: u64,
204 pub maximum_pages: Option<u64>,
205 pub memory64: bool,
206 pub shared: bool,
207 pub page_size_log2: Option<u32>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
211#[serde(rename_all = "kebab-case")]
212pub struct SourcePin {
213 pub name: String,
214 pub url: String,
215 pub branch: String,
216 pub commit: String,
217}
218
219pub fn manifest() -> Result<AssetManifest, serde_json::Error> {
220 serde_json::from_str(MANIFEST_JSON)
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn manifest_parses_and_keeps_core_payload_extension_free() {
229 let manifest = manifest().expect("asset manifest should parse");
230 if !HAS_EMBEDDED_ASSETS {
231 assert_eq!(manifest.runtime.runtime_kind, "source-only-template");
232 if SELECTED_EXTENSION_SQL_NAMES.is_empty() {
233 assert!(manifest.extensions.is_empty());
234 }
235 return;
236 }
237 assert_eq!(manifest.runtime.postgres_version, "18.4");
238 assert_eq!(manifest.runtime.runtime_kind, "wasix-dynamic-main");
239 if SELECTED_EXTENSION_SQL_NAMES.is_empty() {
240 assert!(manifest.extensions.is_empty());
241 }
242 }
243
244 #[test]
245 fn pg18_manifest_metadata_round_trips() {
246 let manifest: AssetManifest = serde_json::from_str(
247 r#"{
248 "format-version": 1,
249 "source-lane": "stable",
250 "source-fingerprint": "postgresql-18.4:patch-stack",
251 "runtime": {
252 "archive": "oliphaunt.wasix.tar.zst",
253 "sha256": "runtime-archive",
254 "module-sha256": "runtime-module",
255 "postgres-version": "18.4",
256 "runtime-kind": "wasix-dynamic-main"
257 },
258 "runtime-support": [],
259 "pgdata-template": {
260 "archive": "prepopulated/pgdata-template.tar.zst",
261 "manifest": "prepopulated/pgdata-template.json",
262 "sha256": "template-archive",
263 "size": 1,
264 "runtime-module-sha256": "runtime-module",
265 "initdb-module-sha256": "initdb-module",
266 "source-pins-sha256": "source-pins",
267 "source-lane": "stable",
268 "source-fingerprint": "postgresql-18.4:patch-stack",
269 "postgres-version": "18",
270 "catalog-version": "202505281",
271 "init-profile": "default",
272 "wasmer-version": "6.0.0"
273 },
274 "extensions": [],
275 "sources": []
276 }"#,
277 )
278 .expect("PG18 asset manifest metadata should parse");
279
280 assert_eq!(manifest.source_lane.as_deref(), Some("stable"));
281 assert_eq!(
282 manifest.source_fingerprint.as_deref(),
283 Some("postgresql-18.4:patch-stack")
284 );
285 let template = manifest.pgdata_template.expect("PGDATA template asset");
286 assert_eq!(template.source_lane.as_deref(), Some("stable"));
287 assert_eq!(
288 template.source_fingerprint.as_deref(),
289 Some("postgresql-18.4:patch-stack")
290 );
291 }
292}