use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_saphyr::{DuplicateKeyPolicy, MergeKeyPolicy, Options};
const MAX_REPLAYED_EVENTS: usize = 100_000;
fn options() -> Options {
let mut options = Options::default();
options.strict_booleans = true;
options.duplicate_keys = DuplicateKeyPolicy::Error;
options.merge_keys = MergeKeyPolicy::Error;
options.reject_unsupported_tags = true;
options.alias_limits.max_total_replayed_events = MAX_REPLAYED_EVENTS;
options
}
pub(crate) fn from_str<T: DeserializeOwned>(yaml: &str) -> Result<T, String> {
let mut path = None;
serde_saphyr::with_deserializer_from_str_with_options(yaml, options(), |de| {
serde_path_to_error::deserialize(de).map_err(|e| {
path = Some(e.path().to_string());
e.into_inner()
})
})
.map_err(|e| located(path.as_deref(), &e))
}
pub(crate) fn from_value<T: DeserializeOwned>(value: serde_json::Value) -> Result<T, String> {
serde_path_to_error::deserialize(value)
.map_err(|e| located(Some(&e.path().to_string()), e.inner()))
}
pub(crate) fn documents(yaml: &str) -> Result<Vec<serde_json::Value>, String> {
serde_saphyr::from_multiple_with_options(yaml, options()).map_err(|e| {
match e.location().map(|at| at.line()) {
Some(line) => format!("document {}: {e}", document_at(yaml, line)),
None => e.to_string(),
}
})
}
fn document_at(yaml: &str, line: u64) -> usize {
let mut ordinal = 0;
for event in serde_saphyr::granit_parser::Parser::new_from_str(yaml) {
let Ok((event, span)) = event else { break };
if u64::try_from(span.start.line()).unwrap_or(u64::MAX) > line {
break;
}
if matches!(event, serde_saphyr::granit_parser::Event::DocumentStart(..)) {
ordinal += 1;
}
}
ordinal.max(1)
}
fn located(path: Option<&str>, error: &dyn std::fmt::Display) -> String {
match path {
Some(path) if path != "." => format!("{path}: {error}"),
_ => error.to_string(),
}
}
pub(crate) fn to_string<T: Serialize>(value: &T) -> Result<String, String> {
serde_saphyr::to_string(value).map_err(|e| e.to_string())
}