1#![deny(missing_docs)]
12
13use std::path::{Path, PathBuf};
14
15use anyhow::{Context, Result};
16use local::LocalLoader;
17use spin_common::paths::parent_dir;
18use spin_locked_app::locked::LockedApp;
19
20pub mod cache;
21mod fs;
22#[cfg(feature = "async-io")]
23mod http;
24mod local;
25
26pub use local::requires_service_chaining;
27pub use local::WasmLoader;
28
29pub(crate) const MAX_FILE_LOADING_CONCURRENCY: usize = 16;
31
32pub async fn from_file(
36 manifest_path: impl AsRef<Path>,
37 files_mount_strategy: FilesMountStrategy,
38 profile: Option<&str>,
39 cache_root: Option<PathBuf>,
40) -> Result<LockedApp> {
41 let path = manifest_path.as_ref();
42 let app_root = parent_dir(path).context("manifest path has no parent directory")?;
43 let loader = LocalLoader::new(&app_root, files_mount_strategy, profile, cache_root).await?;
44 loader.load_file(path).await
45}
46
47pub async fn from_wasm_file(wasm_path: impl AsRef<Path>) -> Result<LockedApp> {
49 let app_root = std::env::current_dir()?;
50 let manifest = single_file_manifest(wasm_path)?;
51 let loader = LocalLoader::new(&app_root, FilesMountStrategy::Direct, None, None).await?;
52 loader.load_manifest(manifest, None).await
53}
54
55#[derive(Debug)]
57pub enum FilesMountStrategy {
58 Copy(PathBuf),
60 Direct,
64}
65
66fn single_file_manifest(
67 wasm_path: impl AsRef<Path>,
68) -> anyhow::Result<spin_manifest::schema::v2::AppManifest> {
69 use serde::Deserialize;
70
71 let wasm_path_str = wasm_path
72 .as_ref()
73 .to_str()
74 .context("Failed to stringise Wasm file path")?
75 .to_owned();
76 let app_name = wasm_path
77 .as_ref()
78 .file_stem()
79 .and_then(|s| s.to_str())
80 .unwrap_or("wasm-file")
81 .to_owned();
82
83 let manifest = toml::toml!(
84 spin_manifest_version = 2
85
86 [application]
87 name = app_name
88
89 [[trigger.http]]
90 route = "/..."
91 component = { source = wasm_path_str }
92 );
93
94 let manifest = spin_manifest::schema::v2::AppManifest::deserialize(manifest)?;
95
96 Ok(manifest)
97}