#[derive(Debug)]
pub struct Concept {
pub name: &'static str,
pub source: &'static str,
}
pub const REGISTRY: &[Concept] = &[
Concept {
name: "companion",
source: include_str!("../model/companion.user.md"),
},
Concept {
name: "config",
source: include_str!("config.user.md"),
},
Concept {
name: "decision-record",
source: include_str!("../model/decision_record.user.md"),
},
Concept {
name: "decisions",
source: include_str!("../model/decision_record/dr_status.user.md"),
},
Concept {
name: "entry-identity",
source: include_str!("../model/entry_identity.user.md"),
},
Concept {
name: "event-log",
source: include_str!("../model/event/event_log.user.md"),
},
Concept {
name: "frontmatter",
source: include_str!("frontmatter.user.md"),
},
Concept {
name: "issue",
source: include_str!("../model/issue.user.md"),
},
Concept {
name: "issue-link",
source: include_str!("../model/issue/issue_link.user.md"),
},
Concept {
name: "record-link",
source: include_str!("../model/decision_record/record_link.user.md"),
},
Concept {
name: "status",
source: include_str!("../model/status.user.md"),
},
Concept {
name: "tag",
source: include_str!("../model/tag.user.md"),
},
Concept {
name: "tag-descriptor",
source: include_str!("../model/tag_descriptor.user.md"),
},
Concept {
name: "ulid",
source: include_str!("../model/ulid.user.md"),
},
Concept {
name: "workspace",
source: include_str!("workspace.user.md"),
},
];
pub(crate) fn split(source: &str) -> (Option<&str>, &str) {
let Some(rest) = source.strip_prefix("---\n") else {
return (None, source);
};
let Some(end) = rest.find("\n---") else {
return (None, source);
};
let frontmatter = &rest[..end];
let body = rest[end + 4..].trim_start_matches('\n');
let summary = frontmatter
.lines()
.find_map(|l| l.strip_prefix("summary:").map(str::trim));
(summary, body)
}
#[cfg(test)]
mod tests {
use super::*;
const SUMMARY_MAX_CHARS: usize = 100;
#[test]
fn every_concept_has_a_unique_name() {
let mut names: Vec<&str> = REGISTRY.iter().map(|c| c.name).collect();
names.sort();
let original_len = names.len();
names.dedup();
assert_eq!(
names.len(),
original_len,
"duplicate concept name in REGISTRY"
);
}
#[test]
fn every_concept_carries_a_summary_under_the_max_length() {
for c in REGISTRY {
let summary = split(c.source).0.unwrap_or_else(|| {
panic!("concept '{}' is missing a `summary:` frontmatter", c.name)
});
assert!(
!summary.is_empty(),
"concept '{}' has an empty summary",
c.name
);
assert!(
summary.chars().count() <= SUMMARY_MAX_CHARS,
"concept '{}' summary is {} chars (max {SUMMARY_MAX_CHARS})",
c.name,
summary.chars().count(),
);
}
}
#[test]
fn split_handles_source_without_frontmatter() {
let (summary, body) = split("# Heading\n\nbody");
assert!(summary.is_none());
assert_eq!(body, "# Heading\n\nbody");
}
#[test]
fn split_extracts_summary_and_strips_frontmatter() {
let src = "---\nsummary: A short summary.\n---\n# Heading\n";
let (summary, body) = split(src);
assert_eq!(summary, Some("A short summary."));
assert_eq!(body, "# Heading\n");
}
}