use std::path::{Path, PathBuf};
use crate::model::*;
pub(crate) fn manifest_source_paths(manifest: &Manifest) -> Vec<(String, String, Vec<String>)> {
let mut out = Vec::new();
out.extend(
manifest
.sources
.docs
.iter()
.cloned()
.map(|p| ("docs".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.skills
.iter()
.cloned()
.map(|p| ("skills".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.agents
.iter()
.cloned()
.map(|p| ("agents".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.capabilities
.iter()
.cloned()
.map(|p| ("capabilities".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.brief_profiles
.iter()
.cloned()
.map(|p| ("brief_profiles".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.workflows
.iter()
.cloned()
.map(|p| ("workflows".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.evals
.iter()
.cloned()
.map(|p| ("evals".into(), p, Vec::new())),
);
out.extend(
manifest
.sources
.code
.iter()
.map(|c| ("code".into(), c.path.clone(), c.languages.clone())),
);
out
}
pub(crate) fn parse_inline_list(value: &str) -> Vec<String> {
let value = value.trim();
if !(value.starts_with('[') && value.ends_with(']')) {
return if value.is_empty() {
Vec::new()
} else {
vec![unquote(value).to_string()]
};
}
value[1..value.len() - 1]
.split(',')
.map(str::trim)
.filter(|item| !item.is_empty())
.map(unquote)
.map(str::to_string)
.collect()
}
pub(crate) fn unquote(s: &str) -> &str {
let s = s.trim();
s.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.or_else(|| s.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')))
.unwrap_or(s)
}
pub(crate) fn set_source_list(sources: &mut SourceManifest, key: &str, values: Vec<String>) {
match key {
"docs" => sources.docs = values,
"skills" => sources.skills = values,
"agents" => sources.agents = values,
"capabilities" => sources.capabilities = values,
"brief_profiles" => sources.brief_profiles = values,
"workflows" => sources.workflows = values,
"evals" => sources.evals = values,
_ => {}
}
}
pub(crate) fn unknown_field(line_no: usize, field: &str) -> EkfDiagnostic {
EkfDiagnostic {
severity: DiagnosticSeverity::Warning,
kind: "unknown_field".into(),
message: format!("unknown EKF field '{field}'"),
source: Some(format!("line {line_no}")),
}
}
pub(crate) fn malformed(line_no: usize, detail: &str) -> EkfDiagnostic {
EkfDiagnostic {
severity: DiagnosticSeverity::Warning,
kind: "malformed_line".into(),
message: format!("could not parse EKF manifest: {detail}"),
source: Some(format!("line {line_no}")),
}
}
pub(crate) fn resolve_path(root: &Path, path: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
root.join(path)
}
}