spin_loader/
lib.rs

1//! Loaders for Spin applications.
2//! This crate implements the possible application sources for Spin applications,
3//! and includes functionality to convert the specific configuration (for example
4//! local configuration files or from OCI) into Spin configuration that
5//! can be consumed by the Spin execution context.
6//!
7//! This crate can be extended (or replaced entirely) to support additional loaders,
8//! and any implementation that produces a `Application` is compatible
9//! with the Spin execution context.
10
11#![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
29/// Maximum number of files to copy (or download) concurrently
30pub(crate) const MAX_FILE_LOADING_CONCURRENCY: usize = 16;
31
32/// Load a Spin locked app from a spin.toml manifest file. If `files_mount_root`
33/// is given, `files` mounts will be copied to that directory. If not, `files`
34/// mounts will validated as "direct mounts".
35pub async fn from_file(
36    manifest_path: impl AsRef<Path>,
37    files_mount_strategy: FilesMountStrategy,
38    cache_root: Option<PathBuf>,
39) -> Result<LockedApp> {
40    let path = manifest_path.as_ref();
41    let app_root = parent_dir(path).context("manifest path has no parent directory")?;
42    let loader = LocalLoader::new(&app_root, files_mount_strategy, cache_root).await?;
43    loader.load_file(path).await
44}
45
46/// Load a Spin locked app from a standalone Wasm file.
47pub async fn from_wasm_file(wasm_path: impl AsRef<Path>) -> Result<LockedApp> {
48    let app_root = std::env::current_dir()?;
49    let manifest = single_file_manifest(wasm_path)?;
50    let loader = LocalLoader::new(&app_root, FilesMountStrategy::Direct, None).await?;
51    loader.load_manifest(manifest).await
52}
53
54/// The strategy to use for mounting WASI files into a guest.
55#[derive(Debug)]
56pub enum FilesMountStrategy {
57    /// Copy files into the given mount root directory.
58    Copy(PathBuf),
59    /// Mount files directly from their source director(ies). This only
60    /// supports mounting full directories; mounting single files, glob
61    /// patterns, and `exclude_files` are not supported.
62    Direct,
63}
64
65fn single_file_manifest(
66    wasm_path: impl AsRef<Path>,
67) -> anyhow::Result<spin_manifest::schema::v2::AppManifest> {
68    use serde::Deserialize;
69
70    let wasm_path_str = wasm_path
71        .as_ref()
72        .to_str()
73        .context("Failed to stringise Wasm file path")?
74        .to_owned();
75    let app_name = wasm_path
76        .as_ref()
77        .file_stem()
78        .and_then(|s| s.to_str())
79        .unwrap_or("wasm-file")
80        .to_owned();
81
82    let manifest = toml::toml!(
83        spin_manifest_version = 2
84
85        [application]
86        name = app_name
87
88        [[trigger.http]]
89        route = "/..."
90        component = { source = wasm_path_str }
91    );
92
93    let manifest = spin_manifest::schema::v2::AppManifest::deserialize(manifest)?;
94
95    Ok(manifest)
96}