canic-host 0.68.23

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use super::{DeclaredFleetRoleSource, support::toml_string_literal};
use crate::release_set::config::model::DeclaredFleetRole;
use canic_core::{bootstrap::parse_config_model, ids::CanisterRole};

pub(in crate::release_set) fn declare_fleet_role_source(
    config_source: &str,
    expected_fleet: &str,
    role: &str,
    package: &str,
) -> Result<DeclaredFleetRoleSource, Box<dyn std::error::Error>> {
    let role = role.trim();
    let package = package.trim();
    if role.is_empty() {
        return Err("role must not be empty".into());
    }
    if package.is_empty() {
        return Err("package must not be empty".into());
    }
    if role == "root" {
        return Err("root role must be attached to topology; declare ordinary roles only".into());
    }
    if !role
        .bytes()
        .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'-')
    {
        return Err("role must contain only ASCII letters, numbers, '_' or '-'".into());
    }

    let config = parse_config_model(config_source).map_err(|err| err.to_string())?;
    let actual_fleet = config
        .fleet_name()
        .ok_or_else(|| "missing required [fleet].name in canic.toml".to_string())?;
    if actual_fleet != expected_fleet {
        return Err(format!(
            "selected config declares fleet {actual_fleet:?}, not {expected_fleet:?}"
        )
        .into());
    }

    let role_id = CanisterRole::owned(role.to_string());
    if config.declares_role(&role_id) {
        return Err(format!("role {expected_fleet}.{role} is already declared").into());
    }

    let mut source = config_source.trim_end().to_string();
    source.push_str("\n\n[roles.");
    source.push_str(&toml_string_literal(role));
    source.push_str("]\nkind = \"canister\"\npackage = ");
    source.push_str(&toml_string_literal(package));
    source.push('\n');

    parse_config_model(&source).map_err(|err| err.to_string())?;

    Ok(DeclaredFleetRoleSource {
        source,
        role: DeclaredFleetRole {
            fleet: expected_fleet.to_string(),
            role: role.to_string(),
            display: format!("{expected_fleet}.{role}"),
            package: package.to_string(),
        },
    })
}