use crate::cli::OutputFormat;
use crate::error::CommandError;
use crate::release::{self, ReleaseError};
const PLATFORM_DIR: &str = "platform";
pub(crate) fn run_platform_validate(
format: OutputFormat,
platform_name: &str,
) -> Result<(), CommandError> {
let manifest_path = format!("{PLATFORM_DIR}/{platform_name}.toml");
let manifest_text = std::fs::read_to_string(&manifest_path).map_err(|e| {
CommandError::Release(ReleaseError::Validation(vec![release::Diagnostic {
crate_name: "platform".to_string(),
message: format!("cannot read {manifest_path}: {e}"),
}]))
})?;
let manifest = parse_manifest(&manifest_text, platform_name)?;
let document = release::load_metadata()?;
let crates = release::discover(&document);
release::validate(&crates)
.map_err(|diagnostics| CommandError::Release(ReleaseError::Validation(diagnostics)))?;
let report = release::validate_platform(&manifest, &document, &crates);
match format {
OutputFormat::Human => {
if report.ok {
println!("Platform manifest {platform_name}: OK");
println!(" crates: {}", manifest.crates.len());
println!(" platform: {}", manifest.platform);
Ok(())
} else {
eprintln!(
"Platform manifest {platform_name}: {} finding(s)",
report.findings.len()
);
for finding in &report.findings {
eprintln!(" {finding}");
}
Err(CommandError::Release(ReleaseError::Validation(
report.findings,
)))
}
}
OutputFormat::Json => {
let payload = serde_json::json!({
"platform": manifest.platform.to_string(),
"crate_count": manifest.crates.len(),
"ok": report.ok,
"findings": report.findings.iter().map(|d| serde_json::json!({
"crate": d.crate_name,
"message": d.message,
})).collect::<Vec<_>>(),
});
println!("{}", serde_json::to_string_pretty(&payload)?);
if !report.ok {
return Err(CommandError::Release(ReleaseError::Validation(
report.findings,
)));
}
Ok(())
}
}
}
fn parse_manifest(
text: &str,
platform_name: &str,
) -> Result<release::PlatformManifest, CommandError> {
let platform_version = release::PlatformVersion::parse(platform_name).map_err(|e| {
CommandError::Release(ReleaseError::Validation(vec![release::Diagnostic {
crate_name: "platform".to_string(),
message: format!("{e}"),
}]))
})?;
let mut crates: std::collections::BTreeMap<String, release::Ybf> =
std::collections::BTreeMap::new();
let mut in_crates = false;
let mut found_platform = false;
for line in text.lines() {
let trimmed = line.trim();
if trimmed.starts_with('#') || trimmed.is_empty() {
continue;
}
if trimmed.starts_with('[') {
in_crates = trimmed == "[crates]";
continue;
}
if trimmed.starts_with("platform") && trimmed.contains('=') {
found_platform = true;
continue;
}
if in_crates && trimmed.contains('=') {
let mut parts = trimmed.splitn(2, '=');
let name = parts.next().unwrap().trim().to_string();
let value = parts
.next()
.unwrap()
.trim()
.trim_matches('"')
.trim_matches('\'');
let version = release::Ybf::parse(value).map_err(|e| {
CommandError::Release(ReleaseError::Validation(vec![release::Diagnostic {
crate_name: name.clone(),
message: format!("{e}"),
}]))
})?;
crates.insert(name, version);
}
}
if !found_platform {
return Err(CommandError::Release(ReleaseError::Validation(vec![
release::Diagnostic {
crate_name: "platform".to_string(),
message: "missing `platform` field".to_string(),
},
])));
}
Ok(release::PlatformManifest {
platform: platform_version,
crates,
})
}