Skip to main content

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    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
47/// Load a Spin locked app from a standalone Wasm file.
48pub 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/// The strategy to use for mounting WASI files into a guest.
56#[derive(Debug)]
57pub enum FilesMountStrategy {
58    /// Copy files into the given mount root directory.
59    Copy(PathBuf),
60    /// Mount files directly from their source director(ies). This only
61    /// supports mounting full directories; mounting single files, glob
62    /// patterns, and `exclude_files` are not supported.
63    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}