#![forbid(unsafe_code)]
use std::process::exit;
use serde::de::IgnoredAny;
use serde_saphyr::{Error, from_str_with_options};
fn usage() -> &'static str {
"Usage: serde-saphyr [--plain] [--include <path>] <path>\n\
\n\
Reads the YAML file at <path> and prints a budget summary.\n\
It can also be used as a YAML validator.\n\
\n\
Options:\n\
--plain Disable miette formatting and print errors in plain text\n\
--include <path> Configure parser to allow file inclusion from <path> directory"
}
fn main() {
let mut plain = false;
let mut path: Option<String> = None;
let mut include_path: Option<String> = None;
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"--plain" => plain = true,
"--include" => {
include_path = match args.next() {
Some(p) => Some(p),
None => {
eprintln!("Missing path for --include\n\n{}", usage());
exit(1);
}
};
}
"--help" | "-h" => {
println!("{}", usage());
return;
}
_ if arg.starts_with('-') => {
eprintln!("Unknown option: {arg}\n\n{}", usage());
exit(1);
}
_ => {
if path.is_some() {
eprintln!("Unexpected extra argument: {arg}\n\n{}", usage());
exit(1);
}
path = Some(arg);
}
}
}
let path = match path {
Some(path) => path,
None => {
eprintln!("{}", usage());
exit(1);
}
};
let content = match std::fs::read_to_string(&path) {
Ok(content) => content,
Err(err) => {
eprintln!("Failed to read {path}: {err}");
exit(2);
}
};
#[allow(deprecated)]
let mut options = if plain {
serde_saphyr::options! {
with_snippet: true,
}
} else {
serde_saphyr::options! {
with_snippet: cfg!(feature = "miette") == false,
}
}
.with_budget_report(|report| match serde_saphyr::to_string(&report) {
Ok(serialized) => println!("Budget report:\n{serialized}"),
Err(err) => eprintln!("Failed to serialize budget report: {err}"),
});
if let Some(path) = include_path {
options = options
.with_filesystem_root(&path)
.expect("failed to create filesystem include resolver");
}
let r: Result<IgnoredAny, Error> = from_str_with_options(&content, options);
if let Err(err) = r {
if plain {
eprintln!("{path} invalid:\n{err}");
exit(3);
}
#[cfg(feature = "miette")]
{
let report = serde_saphyr::miette::to_miette_report(&err, &content, &path);
eprintln!("{report:?}");
exit(3);
}
#[cfg(not(feature = "miette"))]
{
eprintln!("{path} invalid:\n{err}");
exit(3);
}
}
}