mod parse;
pub mod types;
pub use parse::parse;
pub use types::{
Eval, EvalExpectation, Frontmatter, IntrEntryMeta, ModelHints, ParseError, ParseResult,
ParseWarning, Picoschema,
};
pub fn extract_metadata(bytes: &[u8]) -> (Option<String>, Vec<String>) {
let src = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(_) => return (None, vec![]),
};
let src = src.trim_start();
if !src.starts_with("---") {
return (None, vec![]);
}
let after_open = src[3..].trim_start_matches([' ', '\t', '\r', '\n']);
let yaml_block = {
let mut close_pos = None;
for (i, _) in after_open.char_indices() {
let rest = &after_open[i..];
if (i == 0 || after_open.as_bytes().get(i - 1) == Some(&b'\n'))
&& rest.starts_with("---")
{
close_pos = Some(i);
break;
}
}
match close_pos {
Some(pos) => &after_open[..pos],
None => return (None, vec![]),
}
};
let value: serde_yaml::Value = match serde_yaml::from_str(yaml_block) {
Ok(v) => v,
Err(_) => return (None, vec![]),
};
let map = match value.as_mapping() {
Some(m) => m,
None => return (None, vec![]),
};
let description = map
.get("description")
.and_then(|v| v.as_str())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty());
let tags: Vec<String> = map
.get("tags")
.and_then(|v| v.as_sequence())
.map(|seq| {
seq.iter()
.filter_map(|v| v.as_str().map(str::to_string))
.collect()
})
.or_else(|| {
map.get("intentry")
.and_then(|v| v.as_mapping())
.and_then(|m| m.get("tags"))
.and_then(|v| v.as_sequence())
.map(|seq| {
seq.iter()
.filter_map(|v| v.as_str().map(str::to_string))
.collect()
})
})
.unwrap_or_default();
(description, tags)
}