use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use tracing::info;
use wasm_pkg_core::lock::LockFile;
use wit_parser::{Resolve, WorldId};
use crate::{
cli::CommonPackageArgs,
deps::WkgFetcher,
parser::{CommonConfig, ProjectConfig, RegistryConfig, TypeConfig},
};
mod component;
pub use component::*;
mod provider;
use provider::build_provider;
const WASMCLOUD_WASM_TAG_EXPERIMENTAL: &str = "wasmcloud.com/experimental";
const WIT_DEPS_TOML: &str = "deps.toml";
pub const PACKAGE_LOCK_FILE_NAME: &str = "wasmcloud.lock";
pub async fn load_lock_file(dir: impl AsRef<Path>) -> Result<LockFile> {
let maybe_wkg_path = dir.as_ref().join(wasm_pkg_core::lock::LOCK_FILE_NAME);
if tokio::fs::try_exists(&maybe_wkg_path).await? {
return LockFile::load_from_path(&maybe_wkg_path, false)
.await
.context("failed to load lock file");
}
let lock_file_path = dir.as_ref().join(PACKAGE_LOCK_FILE_NAME);
if tokio::fs::try_exists(&lock_file_path)
.await
.context("failed to check if lock file exists")?
{
LockFile::load_from_path(lock_file_path, false)
.await
.context("failed to load lock file")
} else {
let mut lock_file = LockFile::new_with_path([], lock_file_path)
.await
.context("failed to create lock file")?;
lock_file
.write()
.await
.context("failed to write newly created lock file")?;
Ok(lock_file)
}
}
#[derive(Debug, Clone, Default)]
pub struct SignConfig {
pub keys_directory: Option<PathBuf>,
pub issuer: Option<String>,
pub subject: Option<String>,
pub disable_keygen: bool,
}
pub async fn build_project(
config: &ProjectConfig,
signing: Option<&SignConfig>,
package_args: &CommonPackageArgs,
skip_fetch: bool,
) -> Result<PathBuf> {
let wit_deps_exists = tokio::fs::try_exists(config.common.wit_dir.join(WIT_DEPS_TOML)).await?;
if wit_deps_exists {
info!("Skipping fetching dependencies because deps.toml exists in the wit directory. Use 'wit-deps' to fetch dependencies.");
}
let wit_dir_exists = tokio::fs::metadata(&config.common.wit_dir).await.is_ok();
if !wit_dir_exists {
info!("Skipping fetching dependencies because the wit directory does not exist.");
info!("Assuming that dependencies are included in the project.");
}
if !skip_fetch && !wit_deps_exists && wit_dir_exists {
let mut wkg = WkgFetcher::from_common(package_args, config.package_config.clone()).await?;
if let ProjectConfig {
common:
CommonConfig {
registry:
RegistryConfig {
pull: Some(pull_cfg),
..
},
..
},
wasmcloud_toml_dir,
..
} = config
{
wkg.resolve_extended_pull_configs(pull_cfg, &wasmcloud_toml_dir)
.await?;
}
let mut lock = load_lock_file(&config.wasmcloud_toml_dir).await?;
wkg.monkey_patch_fetch_logging(&config.common.wit_dir, &mut lock)
.await
.context("Failed to update dependencies")?;
lock.write()
.await
.context("Unable to write lock file for dependencies")?;
}
match &config.project_type {
TypeConfig::Component(component_config) => {
build_component(component_config, &config.language, &config.common, signing).await
}
TypeConfig::Provider(provider_config) => {
build_provider(provider_config, &config.language, &config.common, signing).await
}
}
}
fn convert_wit_dir_to_world(
dir: impl AsRef<Path>,
world: impl AsRef<str>,
) -> Result<(Resolve, WorldId)> {
let mut resolve = wit_parser::Resolve::default();
let (package_id, _paths) = resolve
.push_dir(dir.as_ref())
.with_context(|| format!("failed to add WIT directory @ [{}]", dir.as_ref().display()))?;
info!("successfully loaded WIT @ [{}]", dir.as_ref().display());
let world_id = resolve
.select_world(package_id, world.as_ref().into())
.context("failed to select world from built resolver")?;
Ok((resolve, world_id))
}