liboliphaunt-wasix-portable 0.1.0

Portable WASIX runtime assets for oliphaunt-wasix
Documentation
#![deny(unsafe_code)]

use serde::{Deserialize, Serialize};

include!(concat!(env!("OUT_DIR"), "/generated_assets.rs"));

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct AssetManifest {
    pub format_version: u32,
    #[serde(default)]
    pub source_lane: Option<String>,
    #[serde(default)]
    pub source_fingerprint: Option<String>,
    pub runtime: RuntimeAsset,
    #[serde(default)]
    pub runtime_support: Vec<BinaryAsset>,
    #[serde(default)]
    pub initdb: Option<BinaryAsset>,
    #[serde(default)]
    pub pgdata_template: Option<PgDataTemplateAsset>,
    #[serde(default)]
    pub extensions: Vec<ExtensionAsset>,
    #[serde(default)]
    pub sources: Vec<SourcePin>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct RuntimeAsset {
    pub archive: String,
    pub sha256: String,
    #[serde(default)]
    pub module_sha256: String,
    pub postgres_version: String,
    pub runtime_kind: String,
    #[serde(default)]
    pub link: Option<WasmLinkMetadata>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct BinaryAsset {
    pub name: String,
    pub path: String,
    pub sha256: String,
    #[serde(default)]
    pub module_sha256: String,
    #[serde(default)]
    pub native_module: Option<String>,
    pub size: u64,
    #[serde(default)]
    pub link: Option<WasmLinkMetadata>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct PgDataTemplateAsset {
    pub archive: String,
    pub manifest: String,
    pub sha256: String,
    pub size: u64,
    pub runtime_module_sha256: String,
    pub initdb_module_sha256: String,
    pub source_pins_sha256: String,
    #[serde(default)]
    pub source_lane: Option<String>,
    #[serde(default)]
    pub source_fingerprint: Option<String>,
    pub postgres_version: String,
    pub catalog_version: String,
    pub init_profile: String,
    pub wasmer_version: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct ExtensionAsset {
    pub name: String,
    pub sql_name: String,
    #[serde(default)]
    pub source_kind: String,
    pub archive: String,
    pub sha256: String,
    #[serde(default)]
    pub module_sha256: String,
    #[serde(default)]
    pub native_modules: Vec<BinaryAsset>,
    pub size: u64,
    #[serde(default)]
    pub stable: bool,
    #[serde(default)]
    pub control_files: Vec<String>,
    #[serde(default)]
    pub dependencies: Vec<String>,
    #[serde(default)]
    pub native_dependencies: Vec<String>,
    #[serde(default)]
    pub load_order: Vec<String>,
    #[serde(default)]
    pub lifecycle: Option<ExtensionLifecycle>,
    #[serde(default)]
    pub extension_imports: Vec<WasmImport>,
    #[serde(default)]
    pub core_exports_required: Vec<String>,
    #[serde(default)]
    pub unresolved_imports: Vec<WasmImport>,
    #[serde(default)]
    pub installed_files: Vec<String>,
    #[serde(default)]
    pub smoke_status: Option<ExtensionSmokeStatus>,
    #[serde(default)]
    pub link: Option<WasmLinkMetadata>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct ExtensionLifecycle {
    pub create_extension: bool,
    #[serde(default)]
    pub create_schema: Option<String>,
    #[serde(default)]
    pub load_sql: Vec<String>,
    #[serde(default)]
    pub post_create_sql: Vec<String>,
    #[serde(default)]
    pub startup_config: Vec<String>,
    #[serde(default)]
    pub preload_required: bool,
    #[serde(default)]
    pub restart_required: bool,
    #[serde(default)]
    pub shared_memory_required: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct ExtensionSmokeStatus {
    pub promoted: bool,
    pub direct: String,
    pub server: String,
    pub restart: String,
    pub dump_restore: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmLinkMetadata {
    pub has_dylink0: bool,
    #[serde(default)]
    pub dylink_needed: Vec<String>,
    #[serde(default)]
    pub dylink_runtime_paths: Vec<String>,
    #[serde(default)]
    pub dylink_memory: Option<WasmDylinkMemory>,
    #[serde(default)]
    pub dylink_imports: Vec<WasmDylinkSymbol>,
    #[serde(default)]
    pub dylink_exports: Vec<WasmDylinkSymbol>,
    #[serde(default)]
    pub imports: Vec<WasmImport>,
    #[serde(default)]
    pub exports: Vec<WasmExport>,
    #[serde(default)]
    pub memories: Vec<WasmMemory>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmDylinkMemory {
    pub memory_size: u32,
    pub memory_alignment: u32,
    pub table_size: u32,
    pub table_alignment: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmDylinkSymbol {
    pub module: Option<String>,
    pub name: String,
    pub flags: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmImport {
    pub module: String,
    pub name: String,
    pub kind: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmExport {
    pub name: String,
    pub kind: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct WasmMemory {
    pub initial_pages: u64,
    pub maximum_pages: Option<u64>,
    pub memory64: bool,
    pub shared: bool,
    pub page_size_log2: Option<u32>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct SourcePin {
    pub name: String,
    pub url: String,
    pub branch: String,
    pub commit: String,
}

pub fn manifest() -> Result<AssetManifest, serde_json::Error> {
    serde_json::from_str(MANIFEST_JSON)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn manifest_parses_and_keeps_core_payload_extension_free() {
        let manifest = manifest().expect("asset manifest should parse");
        if !HAS_EMBEDDED_ASSETS {
            assert_eq!(manifest.runtime.runtime_kind, "source-only-template");
            if SELECTED_EXTENSION_SQL_NAMES.is_empty() {
                assert!(manifest.extensions.is_empty());
            }
            return;
        }
        assert_eq!(manifest.runtime.postgres_version, "18.4");
        assert_eq!(manifest.runtime.runtime_kind, "wasix-dynamic-main");
        if SELECTED_EXTENSION_SQL_NAMES.is_empty() {
            assert!(manifest.extensions.is_empty());
        }
    }

    #[test]
    fn pg18_manifest_metadata_round_trips() {
        let manifest: AssetManifest = serde_json::from_str(
            r#"{
              "format-version": 1,
              "source-lane": "stable",
              "source-fingerprint": "postgresql-18.4:patch-stack",
              "runtime": {
                "archive": "oliphaunt.wasix.tar.zst",
                "sha256": "runtime-archive",
                "module-sha256": "runtime-module",
                "postgres-version": "18.4",
                "runtime-kind": "wasix-dynamic-main"
              },
              "runtime-support": [],
              "pgdata-template": {
                "archive": "prepopulated/pgdata-template.tar.zst",
                "manifest": "prepopulated/pgdata-template.json",
                "sha256": "template-archive",
                "size": 1,
                "runtime-module-sha256": "runtime-module",
                "initdb-module-sha256": "initdb-module",
                "source-pins-sha256": "source-pins",
                "source-lane": "stable",
                "source-fingerprint": "postgresql-18.4:patch-stack",
                "postgres-version": "18",
                "catalog-version": "202505281",
                "init-profile": "default",
                "wasmer-version": "6.0.0"
              },
              "extensions": [],
              "sources": []
            }"#,
        )
        .expect("PG18 asset manifest metadata should parse");

        assert_eq!(manifest.source_lane.as_deref(), Some("stable"));
        assert_eq!(
            manifest.source_fingerprint.as_deref(),
            Some("postgresql-18.4:patch-stack")
        );
        let template = manifest.pgdata_template.expect("PGDATA template asset");
        assert_eq!(template.source_lane.as_deref(), Some("stable"));
        assert_eq!(
            template.source_fingerprint.as_deref(),
            Some("postgresql-18.4:patch-stack")
        );
    }
}