use std::{io::Write, process::ExitCode};
use recourse::catalog::{AcceptanceError, AcceptanceMode, CatalogLock};
use crate::{
arguments::{CatalogPaths, OutputFormat},
error::CommandError,
files, report,
};
pub(super) fn check(paths: &CatalogPaths) -> Result<ExitCode, CommandError> {
let current = files::read_artifact(&paths.current)?;
let lock = files::read_lock(&paths.lock)?;
let report = lock.check(¤t);
report::write_report(&report, paths.format)?;
Ok(compatibility_exit(&report))
}
pub(super) fn accept(
paths: &CatalogPaths,
acknowledge_breaking: bool,
) -> Result<ExitCode, CommandError> {
let current = files::read_artifact(&paths.current)?;
let Some(mut lock) = files::read_optional_lock(&paths.lock)? else {
let lock = CatalogLock::from_artifact(¤t);
files::write_lock(&paths.lock, &lock)?;
report::write_report(&lock.check(¤t), paths.format)?;
return Ok(ExitCode::SUCCESS);
};
let mode = if acknowledge_breaking {
AcceptanceMode::AcknowledgeBreaking
} else {
AcceptanceMode::CompatibleOnly
};
match lock.accept(¤t, mode) {
Ok(report) => {
files::write_lock(&paths.lock, &lock)?;
report::write_report(&report, paths.format)?;
Ok(ExitCode::SUCCESS)
}
Err(
AcceptanceError::Forbidden(report)
| AcceptanceError::BreakingRequiresAcknowledgement(report),
) => {
report::write_report(&report, paths.format)?;
Ok(ExitCode::from(1))
}
}
}
pub(super) fn reserve(
path: &std::path::Path,
reservation: recourse::catalog::Reservation,
format: OutputFormat,
) -> Result<ExitCode, CommandError> {
let mut lock = files::read_lock(path)?;
let entry = lock
.reserve(reservation)
.map_err(|source| CommandError::Write {
path: path.to_owned(),
source: std::io::Error::other(source),
})?;
let code = entry.code().clone();
let type_uri = entry.type_uri().to_owned();
files::write_lock(path, &lock)?;
write_reservation(format, &code, &type_uri)?;
Ok(ExitCode::SUCCESS)
}
fn write_reservation(
format: OutputFormat,
code: &recourse::catalog::Code,
type_uri: &str,
) -> Result<(), CommandError> {
let mut output = std::io::stdout().lock();
match format {
OutputFormat::Human => writeln!(output, "{code}").map_err(CommandError::stdout),
OutputFormat::Json => {
let value = serde_json::json!({
"code": code,
"number": code.number(),
"state": "reserved",
"type": type_uri,
});
serde_json::to_writer(&mut output, &value).map_err(CommandError::Json)?;
writeln!(output).map_err(CommandError::stdout)
}
}
}
fn compatibility_exit(report: &recourse::catalog::CompatibilityReport) -> ExitCode {
if report.is_compatible() {
ExitCode::SUCCESS
} else {
ExitCode::from(1)
}
}