use std::fs;
use crate::process::{ProcessSpec, run_capture};
use crate::project::ProjectConfig;
use super::artifact::Artifact;
use super::compare;
pub(crate) fn check(project: &ProjectConfig) -> Result<Artifact, String> {
let backend = backend_artifact(project)?;
let frontend = frontend_artifact(project)?;
compare::compare(&backend, &frontend).and_then(|()| frontend_components(project))?;
typescript_contract_current(&backend, project)?;
Ok(backend)
}
pub(crate) fn load(project: &ProjectConfig) -> Result<Artifact, String> {
backend_artifact(project)
}
fn frontend_components(project: &ProjectConfig) -> Result<(), String> {
run_capture(
&ProcessSpec::new("node", project.frontend_root()).arg("scripts/verify-contract.mjs"),
)
.map(|_| ())
.map_err(|error| format!("frontend contract component check failed: {error}"))
}
fn backend_artifact(project: &ProjectConfig) -> Result<Artifact, String> {
let output = run_capture(&ProcessSpec::new("cargo", project.root()).args([
"run",
"--quiet",
"--package",
&project.backend_package,
"--bin",
"arcature-contract",
]))
.map_err(|error| format!("cannot inspect the registered Rust page contracts: {error}"))?;
serde_json::from_slice(&output)
.map_err(|error| format!("Rust page-contract binary did not emit valid JSON: {error}"))
}
fn frontend_artifact(project: &ProjectConfig) -> Result<Artifact, String> {
let path = project.frontend_root().join("arcature.contract.json");
let source =
fs::read(&path).map_err(|error| format!("cannot read {}: {error}", path.display()))?;
serde_json::from_slice(&source).map_err(|error| {
format!(
"invalid frontend page-contract artifact {}: {error}",
path.display()
)
})
}
fn typescript_contract_current(
backend: &super::artifact::Artifact,
project: &ProjectConfig,
) -> Result<(), String> {
let generated = backend.to_typescript();
let path = project
.frontend_root()
.join("src")
.join("arcature-contracts.ts");
let committed =
fs::read(&path).map_err(|error| format!("cannot read {}: {error}", path.display()))?;
if generated.as_slice() != committed.as_slice() {
return Err(format!(
"stale TypeScript contract at {}: rerun `arc check` to regenerate, or run \
`cargo run --bin arcature-contract -- --typescript > {}`",
path.display(),
path.display(),
));
}
Ok(())
}