Skip to main content

liboliphaunt_wasix_portable/
lib.rs

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}