use canic_core::bootstrap::parse_config_model;
use std::{collections::BTreeSet, fs, path::Path};
pub fn configured_release_roles(
config_path: &Path,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let config_source = fs::read_to_string(config_path)?;
configured_release_roles_from_source(&config_source)
.map_err(|err| format!("invalid {}: {err}", config_path.display()).into())
}
pub fn configured_install_targets(
config_path: &Path,
root_canister: &str,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let mut targets = vec![root_canister.to_string()];
targets.extend(configured_release_roles(config_path)?);
Ok(targets)
}
pub(super) fn configured_release_roles_from_source(
config_source: &str,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let config = parse_config_model(config_source).map_err(|err| err.to_string())?;
let mut roles = BTreeSet::new();
let mut root_subnet_roles = None;
for (subnet_role, subnet) in &config.subnets {
if !subnet
.canisters
.keys()
.any(canic::ids::CanisterRole::is_root)
{
continue;
}
if root_subnet_roles.is_some() {
return Err(format!(
"multiple subnets define a root canister; release-set staging requires exactly one root subnet (found at least '{subnet_role}')"
)
.into());
}
root_subnet_roles = Some(
subnet
.canisters
.keys()
.filter(|role| !role.is_root() && !role.is_wasm_store())
.map(|role| role.as_str().to_string())
.collect::<Vec<_>>(),
);
}
let root_subnet_roles = root_subnet_roles.ok_or_else(|| {
"no subnet defines a root canister; release-set staging requires exactly one root subnet"
.to_string()
})?;
for role in root_subnet_roles {
roles.insert(role);
}
Ok(roles.into_iter().collect())
}