use crate::workspace_discovery::{
discover_canister_manifest_from_metadata, discover_dfx_root_from, discover_workspace_root_from,
normalize_workspace_path,
};
use std::{
fs,
path::{Path, PathBuf},
};
use toml::Value as TomlValue;
use super::{
CANISTERS_ROOT_RELATIVE, ROOT_CONFIG_FILE, ROOT_RELEASE_SET_MANIFEST_FILE,
WORKSPACE_MANIFEST_RELATIVE,
};
pub fn workspace_root() -> Result<PathBuf, Box<dyn std::error::Error>> {
if let Ok(path) = std::env::var("CANIC_WORKSPACE_ROOT") {
return Ok(PathBuf::from(path).canonicalize()?);
}
if let Some(root) = std::env::var_os("CANIC_WORKSPACE_MANIFEST_PATH")
.map(PathBuf::from)
.and_then(|path| discover_workspace_root_from(&path))
{
return Ok(root);
}
if let Some(root) = std::env::var_os("CANIC_CONFIG_PATH")
.map(PathBuf::from)
.and_then(|path| discover_workspace_root_from(&path))
{
return Ok(root);
}
if let Some(root) = discover_workspace_root_from(&std::env::current_dir()?) {
return Ok(root);
}
Ok(std::env::current_dir()?.canonicalize()?)
}
pub fn dfx_root() -> Result<PathBuf, Box<dyn std::error::Error>> {
if let Ok(path) = std::env::var("CANIC_DFX_ROOT") {
return Ok(PathBuf::from(path).canonicalize()?);
}
let current_dir = std::env::current_dir()?.canonicalize()?;
if let Some(root) = discover_dfx_root_from(¤t_dir) {
return Ok(root);
}
if let Ok(path) = std::env::var("CANIC_WORKSPACE_ROOT") {
let workspace_root = PathBuf::from(path).canonicalize()?;
if let Some(root) = discover_dfx_root_from(&workspace_root) {
return Ok(root);
}
return Ok(workspace_root);
}
Ok(current_dir)
}
#[must_use]
pub fn config_path(workspace_root: &Path) -> PathBuf {
std::env::var_os("CANIC_CONFIG_PATH").map_or_else(
|| canisters_root(workspace_root).join(ROOT_CONFIG_FILE),
|path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
)
}
#[must_use]
pub fn canisters_root(workspace_root: &Path) -> PathBuf {
if let Some(path) = std::env::var_os("CANIC_CANISTERS_ROOT") {
return normalize_workspace_path(workspace_root, PathBuf::from(path));
}
if let Some(path) = std::env::var_os("CANIC_CONFIG_PATH") {
let config_path = normalize_workspace_path(workspace_root, PathBuf::from(path));
if let Some(parent) = config_path.parent() {
return parent.to_path_buf();
}
}
if let Some(manifest_path) = discover_canister_manifest_from_metadata(workspace_root, "root")
&& let Some(parent) = manifest_path.parent().and_then(Path::parent)
{
return parent.to_path_buf();
}
workspace_root.join(CANISTERS_ROOT_RELATIVE)
}
#[must_use]
pub fn root_manifest_path(workspace_root: &Path) -> PathBuf {
std::env::var_os("CANIC_ROOT_MANIFEST_PATH").map_or_else(
|| {
discover_canister_manifest_from_metadata(workspace_root, "root").unwrap_or_else(|| {
canisters_root(workspace_root)
.join("root")
.join("Cargo.toml")
})
},
|path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
)
}
#[must_use]
pub fn canister_manifest_path(workspace_root: &Path, canister_name: &str) -> PathBuf {
discover_canister_manifest_from_metadata(workspace_root, canister_name).unwrap_or_else(|| {
canisters_root(workspace_root)
.join(canister_name)
.join("Cargo.toml")
})
}
#[must_use]
pub fn workspace_manifest_path(workspace_root: &Path) -> PathBuf {
std::env::var_os("CANIC_WORKSPACE_MANIFEST_PATH").map_or_else(
|| workspace_root.join(WORKSPACE_MANIFEST_RELATIVE),
|path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
)
}
pub fn resolve_artifact_root(
dfx_root: &Path,
network: &str,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
let preferred = dfx_root.join(".dfx").join(network).join("canisters");
if preferred.is_dir() {
return Ok(preferred);
}
let fallback = dfx_root.join(".dfx/local/canisters");
if fallback.is_dir() {
return Ok(fallback);
}
Err(format!(
"missing built DFX artifacts under {} or {}",
preferred.display(),
fallback.display()
)
.into())
}
pub fn root_release_set_manifest_path(
artifact_root: &Path,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
let manifest_path = artifact_root
.join("root")
.join(ROOT_RELEASE_SET_MANIFEST_FILE);
if let Some(parent) = manifest_path.parent() {
fs::create_dir_all(parent)?;
}
Ok(manifest_path)
}
pub fn load_root_package_version(
root_manifest_path: &Path,
workspace_manifest_path: &Path,
) -> Result<String, Box<dyn std::error::Error>> {
let manifest_source = fs::read_to_string(root_manifest_path)?;
let manifest = toml::from_str::<TomlValue>(&manifest_source)?;
let version_value = manifest
.get("package")
.and_then(TomlValue::as_table)
.and_then(|package| package.get("version"))
.ok_or_else(|| {
format!(
"missing package.version in {}",
root_manifest_path.display()
)
})?;
if let Some(version) = version_value.as_str() {
return Ok(version.to_string());
}
if version_value
.as_table()
.and_then(|value| value.get("workspace"))
.and_then(TomlValue::as_bool)
== Some(true)
{
return load_workspace_package_version(workspace_manifest_path);
}
Err(format!(
"unsupported package.version format in {}",
root_manifest_path.display()
)
.into())
}
pub fn load_workspace_package_version(
workspace_manifest_path: &Path,
) -> Result<String, Box<dyn std::error::Error>> {
let manifest_source = fs::read_to_string(workspace_manifest_path)?;
let manifest = toml::from_str::<TomlValue>(&manifest_source)?;
let version = manifest
.get("workspace")
.and_then(TomlValue::as_table)
.and_then(|workspace| workspace.get("package"))
.and_then(TomlValue::as_table)
.and_then(|package| package.get("version"))
.and_then(TomlValue::as_str)
.ok_or_else(|| {
format!(
"missing workspace.package.version in {}",
workspace_manifest_path.display()
)
})?;
Ok(version.to_string())
}