zenith_cli/commands/plugin/
assets.rs1include!(concat!(env!("OUT_DIR"), "/skill_assets.rs"));
7
8pub const SKILL_ENTRY: &str = "SKILL.md";
10
11pub fn skill_file(rel: &str) -> Option<&'static str> {
13 SKILL_FILES
14 .iter()
15 .find(|(name, _)| *name == rel)
16 .map(|(_, body)| *body)
17}
18
19pub fn skill_md_raw() -> &'static str {
21 skill_file(SKILL_ENTRY).unwrap_or("")
22}
23
24pub fn skill_md_body() -> &'static str {
26 strip_frontmatter(skill_md_raw())
27}
28
29pub fn skill_description() -> Option<String> {
31 frontmatter(skill_md_raw()).and_then(|fm| {
32 fm.lines()
33 .find_map(|l| l.trim().strip_prefix("description:"))
34 .map(|v| unquote(v.trim()))
35 })
36}
37
38fn split_frontmatter(s: &str) -> (Option<&str>, &str) {
41 let s = s.strip_prefix('\u{feff}').unwrap_or(s);
42 let rest = if let Some(rest) = s.strip_prefix("---\r\n") {
43 rest
44 } else if let Some(rest) = s.strip_prefix("---\n") {
45 rest
46 } else {
47 return (None, s);
48 };
49
50 for (line_start, line) in rest.split_inclusive('\n').scan(0usize, |offset, line| {
51 let line_start = *offset;
52 *offset += line.len();
53 Some((line_start, line))
54 }) {
55 if line.trim_end_matches(['\r', '\n']) == "---" {
56 return (Some(&rest[..line_start]), &rest[line_start + line.len()..]);
57 }
58 }
59
60 (None, s)
61}
62
63fn frontmatter(s: &str) -> Option<&str> {
64 split_frontmatter(s).0
65}
66
67fn strip_frontmatter(s: &str) -> &str {
68 split_frontmatter(s).1
69}
70
71fn unquote(s: &str) -> String {
73 let bytes = s.as_bytes();
74 if bytes.len() >= 2 {
75 let first = bytes[0];
76 let last = bytes[bytes.len() - 1];
77 if (first == b'"' && last == b'"') || (first == b'\'' && last == b'\'') {
78 return s[1..s.len() - 1].to_owned();
79 }
80 }
81 s.to_owned()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn split_frontmatter_accepts_lf() {
90 let (fm, body) = split_frontmatter("---\ndescription: test\n---\n# Body\n");
91
92 assert_eq!(fm, Some("description: test\n"));
93 assert_eq!(body, "# Body\n");
94 }
95
96 #[test]
97 fn split_frontmatter_accepts_crlf() {
98 let (fm, body) = split_frontmatter("---\r\ndescription: test\r\n---\r\n# Body\r\n");
99
100 assert_eq!(fm, Some("description: test\r\n"));
101 assert_eq!(body, "# Body\r\n");
102 }
103
104 #[test]
105 fn split_frontmatter_accepts_utf8_bom() {
106 let (fm, body) = split_frontmatter("\u{feff}---\r\ndescription: test\r\n---\r\n# Body\r\n");
107
108 assert_eq!(fm, Some("description: test\r\n"));
109 assert_eq!(body, "# Body\r\n");
110 }
111
112 #[test]
113 fn split_frontmatter_leaves_plain_markdown_unchanged() {
114 let input = "# Body\n---\nnot frontmatter\n";
115
116 assert_eq!(split_frontmatter(input), (None, input));
117 }
118}