1pub mod cache;
2pub mod columns;
3pub mod discovery;
4pub mod jinja;
5pub mod manifest;
6pub mod project;
7pub mod sql;
8#[allow(dead_code)]
9pub mod yaml_schema;
10
11use anyhow::Result;
12use serde::de::DeserializeOwned;
13
14pub fn yaml_from_str<T: DeserializeOwned>(content: &str, location: &str) -> Result<T> {
21 let value: serde_json::Value = match serde_saphyr::from_str(content) {
22 Ok(v) => v,
23 Err(e) => {
24 if is_duplicate_key_error(&e) {
25 let key = extract_duplicate_key(&e).unwrap_or("unknown");
26 crate::warn!(
27 "duplicate YAML key '{}' in {} (using last value)",
28 key,
29 location,
30 );
31 let options = serde_saphyr::options! {
32 duplicate_keys: serde_saphyr::options::DuplicateKeyPolicy::LastWins
33 };
34 serde_saphyr::from_str_with_options(content, options)?
35 } else {
36 return Err(e.into());
37 }
38 }
39 };
40 if value.is_null() {
41 return Ok(serde_json::from_value(serde_json::Value::Object(
43 Default::default(),
44 ))?);
45 }
46 Ok(serde_json::from_value(value)?)
47}
48
49fn is_duplicate_key_error(e: &serde_saphyr::Error) -> bool {
51 match e {
52 serde_saphyr::Error::DuplicateMappingKey { .. } => true,
53 serde_saphyr::Error::WithSnippet { error, .. } => is_duplicate_key_error(error),
54 _ => false,
55 }
56}
57
58fn extract_duplicate_key(e: &serde_saphyr::Error) -> Option<&str> {
60 match e {
61 serde_saphyr::Error::DuplicateMappingKey { key, .. } => key.as_deref(),
62 serde_saphyr::Error::WithSnippet { error, .. } => extract_duplicate_key(error),
63 _ => None,
64 }
65}