use lex_extension::schema::{BodyKind, BodyPresence, BodyShape, Capabilities, HookSet, Schema};
use std::collections::BTreeMap;
pub const METADATA_LABELS: &[&str] = &[
"lex.metadata.title",
"lex.metadata.author",
"lex.metadata.date",
"lex.metadata.tags",
"lex.metadata.category",
"lex.metadata.template",
"lex.metadata.publishing-date",
"lex.metadata.front-matter",
];
fn metadata_schema(label: &'static str, description: &'static str) -> Schema {
Schema {
schema_version: 1,
label: label.into(),
description: Some(description.into()),
params: BTreeMap::new(),
attaches_to: vec!["document".into()],
body: BodyShape {
kind: BodyKind::Text,
presence: BodyPresence::Optional,
description: Some(
"Annotation body (single-line text) carries the metadata value when no \
explicit parameter is supplied."
.into(),
),
},
verbatim_label: false,
capabilities: Capabilities::default(),
hooks: HookSet::default(),
handler: None,
diagnostics: Vec::new(),
}
}
pub fn lex_metadata_title_schema() -> Schema {
metadata_schema(
"lex.metadata.title",
"Document title. Phase 4 of #570 wires the render hook so HTML output emits \
`<title>` and `<meta name=\"title\">`; until then the legacy frontmatter \
promotion in lex-babel handles it.",
)
}
pub fn lex_metadata_author_schema() -> Schema {
metadata_schema(
"lex.metadata.author",
"Document author. Render hook (Phase 4 of #570) will emit `<meta name=\"author\">` \
in HTML output.",
)
}
pub fn lex_metadata_date_schema() -> Schema {
metadata_schema(
"lex.metadata.date",
"Document date. Render hook (Phase 4 of #570) will emit `<meta name=\"date\">` \
in HTML output.",
)
}
pub fn lex_metadata_tags_schema() -> Schema {
metadata_schema(
"lex.metadata.tags",
"Document tags (comma-separated). Render hook (Phase 4 of #570) will emit \
`<meta name=\"keywords\">` in HTML output.",
)
}
pub fn lex_metadata_category_schema() -> Schema {
metadata_schema(
"lex.metadata.category",
"Document category. Render hook (Phase 4 of #570) will emit \
`<meta name=\"category\">` in HTML output.",
)
}
pub fn lex_metadata_template_schema() -> Schema {
metadata_schema(
"lex.metadata.template",
"Template hint for renderers that select a layout per document.",
)
}
pub fn lex_metadata_publishing_date_schema() -> Schema {
metadata_schema(
"lex.metadata.publishing-date",
"Publishing date (distinct from authoring `date`). Render hook (Phase 4 of #570) \
will emit `<meta name=\"publishing-date\">` in HTML output.",
)
}
pub fn lex_metadata_front_matter_schema() -> Schema {
metadata_schema(
"lex.metadata.front-matter",
"Catch-all front-matter container for renderer-specific extensions.",
)
}
pub fn all_schemas() -> Vec<Schema> {
vec![
lex_metadata_title_schema(),
lex_metadata_author_schema(),
lex_metadata_date_schema(),
lex_metadata_tags_schema(),
lex_metadata_category_schema(),
lex_metadata_template_schema(),
lex_metadata_publishing_date_schema(),
lex_metadata_front_matter_schema(),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn metadata_labels_match_schema_outputs() {
let labels: Vec<String> = all_schemas().into_iter().map(|s| s.label).collect();
let expected: Vec<String> = METADATA_LABELS.iter().map(|s| (*s).to_string()).collect();
assert_eq!(
labels, expected,
"all_schemas() ordering must mirror METADATA_LABELS so the Phase 2 \
auto-rewrite has a single source of truth for the label set"
);
}
#[test]
fn every_metadata_schema_attaches_to_document() {
for schema in all_schemas() {
assert_eq!(
schema.attaches_to,
vec!["document".to_string()],
"{} must declare document-scope attachment",
schema.label
);
assert!(
!schema.verbatim_label,
"{} is an annotation label, not a verbatim label",
schema.label
);
}
}
#[test]
fn metadata_schemas_declare_no_hooks_in_phase_1() {
for schema in all_schemas() {
assert!(
!schema.hooks.resolve,
"{} resolve hook must stay off in Phase 1",
schema.label
);
assert!(
!schema.hooks.validate,
"{} validate hook must stay off in Phase 1",
schema.label
);
assert!(
schema.hooks.render.is_empty(),
"{} render hook must stay off in Phase 1",
schema.label
);
}
}
#[test]
fn metadata_schemas_round_trip_through_json() {
for schema in all_schemas() {
let json = serde_json::to_string(&schema).expect("serialize");
let back: Schema = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back, schema, "round trip for {}", schema.label);
}
}
}