PicoBot/src/frontmatter.rs
oudecheng cda14360af chore: 建立工程化基线(rustfmt + clippy + CI + eslint + prettier)
配置:
- rustfmt.toml: 固化 max_width=100 / 4 空格缩进,cargo fmt 全量格式化
- Cargo.toml: 配置 [lints.rust] 与 [lints.clippy] 渐进式规则
- .github/workflows/ci.yml: Rust(fmt+clippy+test) + 前端(eslint+tsc+test) 双平台 CI
- Makefile: 新增 check/fmt/fix 目标,clippy 对齐 --all-targets --all-features
- web: eslint flat config + prettier 配置 + package.json 脚本与依赖
- src/main.rs: loop→while 修复 clippy::never_loop

对抗性审查发现并修复:
- eslint 缺 caughtErrorsIgnorePattern 导致 catch(_) 误报为 error
- 前端 lint 未接入 CI,现已补上 Lint 步骤
- Makefile 与 CI 的 clippy flags 不一致,已对齐
2026-08-03 23:24:02 +08:00

93 lines
2.9 KiB
Rust

use gray_matter::Matter;
use gray_matter::engine::YAML;
use serde::de::DeserializeOwned;
/// Parse a markdown document with YAML frontmatter into `(frontmatter, body)`.
///
/// Tolerates CRLF, CR, and LF line endings. A `---` appearing inside the body
/// is not treated as a delimiter (only the leading frontmatter block is split).
///
/// - Returns `Err("missing YAML frontmatter block")` when no leading `---`
/// delimiter is present.
/// - Returns `Err("invalid YAML frontmatter: {e}")` when the block is present
/// but the YAML fails to parse or deserialize into `T`.
pub fn parse<T: DeserializeOwned>(content: &str) -> Result<(T, String), String> {
let normalized = content.replace("\r\n", "\n").replace('\r', "\n");
if !normalized.starts_with("---\n") {
return Err("missing YAML frontmatter block".to_string());
}
let matter = Matter::<YAML>::new();
let result = matter.parse(&normalized);
let pod = result
.data
.ok_or_else(|| "invalid YAML frontmatter".to_string())?;
let data: T = pod
.deserialize()
.map_err(|e| format!("invalid YAML frontmatter: {}", e))?;
Ok((data, result.content))
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct FrontMatter {
description: String,
#[serde(default)]
name: Option<String>,
}
#[test]
fn parses_lf_endings() {
let input = "---\ndescription: demo\n---\nbody text";
let (fm, body) = parse::<FrontMatter>(input).unwrap();
assert_eq!(
fm,
FrontMatter {
description: "demo".to_string(),
name: None
}
);
assert_eq!(body, "body text");
}
#[test]
fn parses_crlf_endings() {
let input = "---\r\ndescription: demo\r\n---\r\nbody text";
let (fm, body) = parse::<FrontMatter>(input).unwrap();
assert_eq!(fm.description, "demo");
assert_eq!(body, "body text");
}
#[test]
fn parses_cr_endings() {
let input = "---\rdescription: demo\r---\rbody text";
let (fm, body) = parse::<FrontMatter>(input).unwrap();
assert_eq!(fm.description, "demo");
assert_eq!(body, "body text");
}
#[test]
fn missing_block_is_rejected() {
let err = parse::<FrontMatter>("no front matter here").unwrap_err();
assert_eq!(err, "missing YAML frontmatter block");
}
#[test]
fn invalid_yaml_is_rejected() {
let err = parse::<FrontMatter>("---\n: : bad\n---\nbody").unwrap_err();
assert!(err.starts_with("invalid YAML frontmatter"));
}
#[test]
fn body_with_inner_delimiter_is_preserved() {
let input = "---\ndescription: demo\n---\nexcerpt\n---\nmore content";
let (_fm, body) = parse::<FrontMatter>(input).unwrap();
assert_eq!(body, "excerpt\n---\nmore content");
}
}