use std::{io, path::Path};
use ic_host_fs::read::open_regular_file;
use ic_host_text::{
escape::terminal_text,
yaml::{self, DecodePolicy},
};
use serde::Deserialize;
const ICP_YAML_PATH: &str = "icp.yaml";
#[derive(Deserialize)]
struct Manifest {
environments: Option<Vec<Environment>>,
}
#[derive(Deserialize)]
struct Environment {
name: String,
canisters: Option<Vec<String>>,
}
pub(super) fn known_canisters(environment: &str) -> Result<Vec<String>, String> {
let contents = read_manifest()?;
parse_canisters(&contents, environment)
}
fn read_manifest() -> Result<String, String> {
let file = open_regular_file(Path::new(ICP_YAML_PATH))
.map_err(|err| format!("read {ICP_YAML_PATH}: {err}"))?;
io::read_to_string(file).map_err(|err| format!("read {ICP_YAML_PATH}: {err}"))
}
pub(super) fn parse_canisters(contents: &str, environment: &str) -> Result<Vec<String>, String> {
let manifest: Option<Manifest> = yaml::from_slice(
contents.as_bytes(),
contents.len(),
DecodePolicy {
ignore_unknown_tags: true,
preserve_merge_keys: true,
},
)
.map_err(|error| {
format!(
"parse {ICP_YAML_PATH}: {}",
terminal_text(&error.to_string())
)
})?;
let mut canisters = manifest
.and_then(|manifest| manifest.environments)
.and_then(|environments| {
environments
.into_iter()
.find(|item| item.name == environment)
})
.and_then(|item| item.canisters)
.unwrap_or_default();
canisters.retain(|name| !name.is_empty());
canisters.sort();
Ok(canisters)
}