use std::path::{Path, PathBuf};
use std::process::ExitCode;
use loftspec::codec;
use loftspec::render::render;
use loftspec_store::fs;
use serde_json::json;
use crate::json;
use crate::stop::{self, Stop};
const SCHEMA: &str = "loft-render/1";
const DEFAULT_MARKDOWN: &str = "docs/spec.md";
const DEFAULT_JSON: &str = "docs/spec.json";
pub fn run(
root: &Path,
markdown: Option<PathBuf>,
json_path: Option<PathBuf>,
as_json: bool,
) -> ExitCode {
match write(root, markdown, json_path) {
Ok((written, unchanged)) => {
let names = |paths: &[PathBuf]| -> Vec<String> {
paths
.iter()
.map(|path| path.display().to_string())
.collect()
};
if as_json {
let fields = json!({ "written": names(&written), "unchanged": names(&unchanged) });
json::print(&json::result(SCHEMA, "rendered", fields, Vec::new()));
} else {
for path in &written {
println!("{}", path.display());
}
}
ExitCode::SUCCESS
}
Err(stop) => stop.exit(SCHEMA, as_json),
}
}
type Outcome = (Vec<PathBuf>, Vec<PathBuf>);
fn write(
root: &Path,
markdown: Option<PathBuf>,
json_path: Option<PathBuf>,
) -> Result<Outcome, Stop> {
let (markdown, json_path) = match (markdown, json_path) {
(None, None) => (Some(DEFAULT_MARKDOWN.into()), Some(DEFAULT_JSON.into())),
given => given,
};
let targets: Vec<&PathBuf> = markdown.iter().chain(json_path.iter()).collect();
if let Some(inside) = targets.iter().find(|path| fs::is_under_spec(root, path)) {
let message = format!(
"{}: a projection is never written under `spec/`",
inside.display()
);
return Err(Stop::one("refused", message));
}
let files = stop::read_files(root)?;
let document = codec::read(&files)?;
let projections = render(&document).map_err(|errors| {
let integrity = errors.integrity.iter().map(ToString::to_string);
let cycles = errors.cycles.iter().map(ToString::to_string);
Stop::all("integrity", integrity.chain(cycles).collect())
})?;
let outputs: Vec<(PathBuf, Vec<u8>)> = markdown
.map(|path| (path, projections.markdown.into_bytes()))
.into_iter()
.chain(json_path.map(|path| (path, projections.json)))
.collect();
let written =
fs::write_outputs(root, &outputs).map_err(|error| Stop::one("store", error.to_string()))?;
let unchanged = outputs
.into_iter()
.map(|(path, _)| path)
.filter(|path| !written.contains(path))
.collect();
Ok((written, unchanged))
}