配置: - 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 不一致,已对齐
93 lines
2.9 KiB
Rust
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");
|
|
}
|
|
}
|