#[derive(Debug, Clone, Default)]
pub struct ModuleMetadata {
pub module_name: String,
pub description: String,
pub keywords: Vec<String>,
pub env_vars: Vec<String>,
pub quickrefs: Vec<QuickRef>,
pub auto_functions: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct QuickRef {
pub signature: String,
pub return_hint: String,
pub description: String,
}
pub fn parse_metadata(source: &str) -> ModuleMetadata {
let mut meta = ModuleMetadata::default();
parse_header_tags(source, &mut meta);
extract_auto_functions(source, &mut meta);
meta
}
fn parse_header_tags(source: &str, meta: &mut ModuleMetadata) {
for line in source.lines() {
let trimmed = line.trim();
if !trimmed.starts_with("---") {
break;
}
let after_dashes = trimmed.trim_start_matches('-').trim();
if let Some(rest) = after_dashes.strip_prefix('@')
&& let Some((tag, value)) = rest.split_once(char::is_whitespace)
{
let value = value.trim();
match tag {
"module" => meta.module_name = value.to_string(),
"description" => meta.description = value.to_string(),
"keywords" => {
meta.keywords = split_comma_list(value);
}
"env" => {
meta.env_vars = split_comma_list(value);
}
"quickref" => {
if let Some(qr) = parse_quickref(value) {
meta.quickrefs.push(qr);
}
}
_ => {} }
}
}
}
fn split_comma_list(value: &str) -> Vec<String> {
value
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
}
fn parse_quickref(value: &str) -> Option<QuickRef> {
let (signature, rest) = value.split_once(" -> ")?;
let (return_hint, description) = rest.split_once(" | ")?;
Some(QuickRef {
signature: signature.trim().to_string(),
return_hint: return_hint.trim().to_string(),
description: description.trim().to_string(),
})
}
fn extract_auto_functions(source: &str, meta: &mut ModuleMetadata) {
for line in source.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("function ") {
if let Some(name) = extract_function_name(rest)
&& !name.is_empty()
{
meta.auto_functions.push(name);
}
}
}
}
fn extract_function_name(rest: &str) -> Option<String> {
let sep_pos = rest.find([':', '.'])?;
let after_sep = &rest[sep_pos + 1..];
let name = after_sep.split('(').next()?;
let name = name.trim();
if name.is_empty() {
return None;
}
Some(name.to_string())
}