1use std::path::Path;
2
3use crate::error::DkpResult;
4
5#[derive(Debug, Clone)]
7pub struct OkfConcept {
8 pub path: std::path::PathBuf,
9 pub frontmatter: serde_yaml::Value,
10 pub body: String,
11}
12
13pub fn parse_concept(path: &Path) -> DkpResult<OkfConcept> {
15 let content = std::fs::read_to_string(path)?;
16
17 if let Some(stripped) = content.strip_prefix("---\n") {
18 if let Some(end) = stripped.find("\n---\n") {
19 let yaml_str = &stripped[..end];
20 let body = stripped[end + 5..].to_string();
21 let frontmatter: serde_yaml::Value = serde_yaml::from_str(yaml_str).map_err(|e| {
22 crate::error::DkpError::OkfFrontmatter {
23 file: path.display().to_string(),
24 reason: e.to_string(),
25 }
26 })?;
27 return Ok(OkfConcept {
28 path: path.to_path_buf(),
29 frontmatter,
30 body,
31 });
32 }
33 }
34
35 Err(crate::error::DkpError::OkfFrontmatter {
36 file: path.display().to_string(),
37 reason: "missing YAML frontmatter delimiters".to_string(),
38 })
39}
40
41pub fn parse_okf_dir(okf_dir: &Path) -> DkpResult<Vec<OkfConcept>> {
43 let mut concepts = Vec::new();
44 fn walk(dir: &Path, out: &mut Vec<OkfConcept>) -> crate::error::DkpResult<()> {
45 for entry in std::fs::read_dir(dir)? {
46 let path = entry?.path();
47 if path.is_dir() {
48 walk(&path, out)?;
49 } else if path.extension().is_some_and(|e| e == "md") {
50 out.push(parse_concept(&path)?);
51 }
52 }
53 Ok(())
54 }
55 walk(okf_dir, &mut concepts)?;
56 Ok(concepts)
57}