use crate::block::{ElementKind, NodeId, Syntax, Tree};
use crate::fm::{FmSeverity, FrontmatterBlock};
use crate::span::Span;
use crate::validation::{Diagnostic, Severity};
use crate::yaml;
use std::path::Path;
const CARRIER_LANG: &str = "yaml";
const CARRIER_DISCRIMINATOR: &str = "lattice";
#[derive(Debug, Clone, Copy)]
pub struct Carrier {
pub node_span: Span,
pub body_span: Span,
}
fn is_carrier_info(info: &str) -> bool {
let mut tokens = info.split_whitespace();
tokens.next() == Some(CARRIER_LANG)
&& tokens.next() == Some(CARRIER_DISCRIMINATOR)
&& tokens.next().is_none()
}
fn is_carrier_near_miss(info: &str) -> bool {
let mut tokens = info.split_whitespace();
tokens.next() == Some(CARRIER_LANG)
&& tokens.next() == Some(CARRIER_DISCRIMINATOR)
&& tokens.next().is_some()
}
fn is_carrier_site(tree: &Tree, node_id: NodeId) -> bool {
let Some(parent_id) = tree.node(node_id).parent else {
return false;
};
let parent = tree.node(parent_id);
match parent.kind {
ElementKind::Document => true,
ElementKind::Details => parent.parent.is_some_and(|grandparent_id| {
matches!(tree.node(grandparent_id).kind, ElementKind::Document)
}),
_ => false,
}
}
fn fence_info(raw: &str) -> Option<&str> {
let first_line = raw.lines().next()?;
let trimmed = first_line.trim_start_matches(' ');
let fence_char = trimmed.as_bytes().first().copied()?;
if fence_char != b'`' && fence_char != b'~' {
return None;
}
let fence_len = trimmed.bytes().take_while(|&b| b == fence_char).count();
if fence_len < 3 {
return None;
}
Some(trimmed[fence_len..].trim())
}
fn fence_body_span(node_span: Span, source: &str) -> Span {
let raw = &source[node_span.start..node_span.end];
let Some(open_nl) = raw.find('\n') else {
return Span::new(node_span.end, node_span.end);
};
let body_start = node_span.start + open_nl + 1;
let first_line = &raw[..open_nl];
let open_trimmed = first_line.trim_start_matches(' ');
let Some(fence_char) = open_trimmed.as_bytes().first().copied() else {
return Span::new(body_start, node_span.end);
};
let open_len = open_trimmed
.bytes()
.take_while(|&b| b == fence_char)
.count();
let mut offset = body_start;
let remainder = &source[body_start..node_span.end];
for line in remainder.split_inclusive('\n') {
let line_no_eol = line.trim_end_matches(['\n', '\r']);
let trimmed = line_no_eol.trim_start_matches(' ');
let indent = line_no_eol.len() - trimmed.len();
let close_len = trimmed.bytes().take_while(|&b| b == fence_char).count();
let is_close = indent <= 3
&& close_len >= open_len
&& close_len > 0
&& trimmed[close_len..].trim().is_empty();
if is_close {
return Span::new(body_start, offset);
}
offset += line.len();
}
Span::new(body_start, node_span.end)
}
fn has_leading_frontmatter(tree: &Tree) -> bool {
tree.nodes()
.iter()
.any(|node| matches!(node.kind, ElementKind::Frontmatter))
}
fn scan_carriers(tree: &Tree) -> Vec<Carrier> {
let source = tree.source();
let mut carriers = Vec::new();
for (node_id, node) in tree.nodes().iter().enumerate() {
if !matches!(node.kind, ElementKind::CodeBlock) || node.syntax == Syntax::Html {
continue;
}
if !is_carrier_site(tree, node_id) {
continue;
}
let raw = &source[node.span.start..node.span.end];
let Some(info) = fence_info(raw) else {
continue;
};
if !is_carrier_info(info) {
continue;
}
carriers.push(Carrier {
node_span: node.span,
body_span: fence_body_span(node.span, source),
});
}
carriers
}
#[must_use]
pub fn parse_carrier_block(tree: &Tree) -> Option<FrontmatterBlock> {
let carrier = scan_carriers(tree).into_iter().next()?;
let body = &tree.source()[carrier.body_span.start..carrier.body_span.end];
Some(yaml::parse_yaml_body(body, carrier.body_span.start))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DetailsLine {
Blank,
SummaryEnd,
FenceOpen,
FenceClose,
DetailsClose,
Other,
}
pub fn carrier_diagnostics(tree: &Tree, rel_path: &Path, out: &mut Vec<Diagnostic>) {
let source = tree.source();
let carriers = scan_carriers(tree);
if has_leading_frontmatter(tree) {
for carrier in &carriers {
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, carrier.node_span.start),
severity: Severity::Warning,
message: "a `yaml lattice` metadata carrier coexists with leading frontmatter — \
document metadata has one home; keep the frontmatter or the carrier, not both"
.to_string(),
span: Some(carrier.node_span),
});
}
}
for carrier in carriers.iter().skip(1) {
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, carrier.node_span.start),
severity: Severity::Warning,
message:
"more than one `yaml lattice` metadata carrier — document metadata is singular; \
merge them into one carrier"
.to_string(),
span: Some(carrier.node_span),
});
}
if let Some(block) = parse_carrier_block(tree) {
for diag in &block.diagnostics {
let severity = match diag.severity {
FmSeverity::Error => Severity::Error,
FmSeverity::Warning => Severity::Warning,
};
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, diag.span.start),
severity,
message: format!("metadata carrier: {}", diag.message),
span: Some(diag.span),
});
}
}
for (node_id, node) in tree.nodes().iter().enumerate() {
if !matches!(node.kind, ElementKind::CodeBlock) || node.syntax == Syntax::Html {
continue;
}
if !is_carrier_site(tree, node_id) {
continue;
}
let raw = &source[node.span.start..node.span.end];
let Some(info) = fence_info(raw) else {
continue;
};
if !is_carrier_near_miss(info) {
continue;
}
let span = fence_open_line_span(source, node.span.start, node.span.end);
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, node.span.start),
severity: Severity::Warning,
message:
"this looks like a `yaml lattice` metadata carrier but the info string is not \
exactly `yaml lattice`, so its metadata is not loaded — drop the extra tokens"
.to_string(),
span: Some(span),
});
}
for node in tree.nodes() {
if !matches!(node.kind, ElementKind::Details) {
continue;
}
emit_details_gotchas(node.span, source, rel_path, out);
}
}
fn emit_details_gotchas(
details_span: Span,
source: &str,
rel_path: &Path,
out: &mut Vec<Diagnostic>,
) {
let raw = &source[details_span.start..details_span.end];
let mut classes: Vec<(DetailsLine, usize)> = Vec::new();
let mut offset = details_span.start;
let mut in_carrier_fence = false;
for line in raw.split_inclusive('\n') {
let line_no_eol = line.trim_end_matches(['\n', '\r']);
let trimmed = line_no_eol.trim();
let info = fence_info(line_no_eol);
let class = if in_carrier_fence {
if info == Some("") {
in_carrier_fence = false;
DetailsLine::FenceClose
} else {
DetailsLine::Other
}
} else if info.is_some_and(is_carrier_info) {
in_carrier_fence = true;
DetailsLine::FenceOpen
} else if trimmed.is_empty() {
DetailsLine::Blank
} else if trimmed.ends_with("</summary>") {
DetailsLine::SummaryEnd
} else if trimmed == "</details>" {
DetailsLine::DetailsClose
} else {
DetailsLine::Other
};
classes.push((class, offset));
offset += line.len();
}
for (idx, &(class, line_start)) in classes.iter().enumerate() {
let span = fence_open_line_span(source, line_start, details_span.end);
match class {
DetailsLine::FenceOpen if idx > 0 && classes[idx - 1].0 == DetailsLine::SummaryEnd => {
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, line_start),
severity: Severity::Warning,
message:
"metadata carrier: add a blank line after `</summary>` — without it GitHub \
renders the fence as literal text"
.to_string(),
span: Some(span),
});
}
DetailsLine::FenceClose
if idx + 1 < classes.len() && classes[idx + 1].0 == DetailsLine::DetailsClose =>
{
out.push(Diagnostic {
file: rel_path.to_path_buf(),
line: crate::block::byte_offset_to_line(source, line_start),
severity: Severity::Warning,
message: "metadata carrier: add a blank line before `</details>` — without it \
GitHub renders the fence as literal text"
.to_string(),
span: Some(span),
});
}
_ => {}
}
}
}
fn fence_open_line_span(source: &str, line_start: usize, limit: usize) -> Span {
let slice = &source[line_start..limit];
let end = slice.find('\n').map_or(limit, |nl| line_start + nl);
let end = if end > line_start && source.as_bytes().get(end - 1) == Some(&b'\r') {
end - 1
} else {
end
};
Span::new(line_start, end)
}
#[cfg(test)]
#[allow(
clippy::expect_used,
clippy::panic,
reason = "tests use expect and panic for clarity"
)]
mod tests {
use super::carrier_diagnostics;
use crate::block;
use crate::config::Config;
use crate::invariants::assert_structural_diagnostics_valid;
use crate::validation::{Diagnostic, Severity};
use crate::workspace::parse_content;
use std::path::Path;
fn tree_of(content: &str) -> block::Tree {
let fm = crate::yaml::parse_frontmatter_block(content);
let fm_span = fm.as_ref().map(|b| b.span);
block::parse_tree(content, fm_span)
}
fn carrier_diags(content: &str) -> Vec<Diagnostic> {
let tree = tree_of(content);
let mut out = Vec::new();
carrier_diagnostics(&tree, Path::new("test.md"), &mut out);
assert_structural_diagnostics_valid(content, &out);
out
}
fn backlinks_of(content: &str) -> std::collections::HashMap<String, Vec<String>> {
let file = parse_content(content, Path::new("test.md"), &Config::default());
file.frontmatter.map(|fm| fm.backlinks).unwrap_or_default()
}
#[test]
fn naked_carrier_populates_backlinks() {
let content =
"# Title\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - README.md\n```\n";
let backlinks = backlinks_of(content);
assert_eq!(
backlinks.get("referenced_by").map(Vec::as_slice),
Some(["README.md".to_string()].as_slice()),
"a naked carrier populates frontmatter backlinks: {backlinks:?}"
);
}
#[test]
fn details_wrapped_carrier_populates_backlinks() {
let content = "# Title\n\n<details><summary>lattice</summary>\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - README.md\n```\n\n</details>\n";
let backlinks = backlinks_of(content);
assert_eq!(
backlinks.get("referenced_by").map(Vec::as_slice),
Some(["README.md".to_string()].as_slice()),
"a `<details>`-wrapped carrier populates backlinks identically: {backlinks:?}"
);
}
#[test]
fn carrier_matches_leading_frontmatter() {
let carrier =
"# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n - b.md\n```\n";
let leading = "---\nbacklinks:\n referenced_by:\n - a.md\n - b.md\n---\n# T\n";
assert_eq!(
backlinks_of(carrier),
backlinks_of(leading),
"a carrier populates backlinks identically to a leading `---` block"
);
}
#[test]
fn carrier_populates_exceptions() {
let content = "# T\n\n```yaml lattice\nexceptions:\n stale_references:\n \"old.md\": \"migrated\"\n```\n";
let file = parse_content(content, Path::new("test.md"), &Config::default());
let fm = file.frontmatter.expect("carrier populates frontmatter");
assert_eq!(
fm.exceptions.stale_references.len(),
1,
"a carrier populates the exceptions block: {:?}",
fm.exceptions
);
assert_eq!(
fm.exceptions.stale_references[0].reference, "old.md",
"the exception reference is carried through: {:?}",
fm.exceptions
);
}
#[test]
fn carrier_position_is_free_no_placement_diagnostic() {
for content in [
"```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n\n# Title\n",
"# Title\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n",
"# Title\n\nbody\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n",
] {
let backlinks = backlinks_of(content);
assert!(
backlinks.contains_key("referenced_by"),
"carrier populates regardless of position: {content:?}"
);
assert!(
carrier_diags(content).is_empty(),
"no placement diagnostic for any carrier position: {content:?}"
);
}
}
#[test]
fn carrier_inside_outer_fence_is_inert() {
let content = "# Docs\n\n````markdown\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n````\n";
assert!(
backlinks_of(content).is_empty(),
"a nested carrier extracts no metadata"
);
assert!(
carrier_diags(content).is_empty(),
"a nested carrier emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn incidental_yaml_block_is_not_a_carrier() {
let content = "# T\n\n```yaml\nbacklinks:\n referenced_by:\n - a.md\n```\n";
assert!(
backlinks_of(content).is_empty(),
"an incidental `yaml` block is not recognized"
);
assert!(
carrier_diags(content).is_empty(),
"an incidental `yaml` block emits no carrier diagnostic"
);
}
#[test]
fn carrier_inside_blockquote_is_inert() {
let content =
"# T\n\n> ```yaml lattice\n> backlinks:\n> referenced_by:\n> - a.md\n> ```\n";
assert!(
backlinks_of(content).is_empty(),
"a carrier inside a blockquote extracts no metadata: {:?}",
backlinks_of(content)
);
assert!(
carrier_diags(content).is_empty(),
"a carrier inside a blockquote emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn carrier_inside_list_item_is_inert() {
let content =
"# T\n\n- ```yaml lattice\n backlinks:\n referenced_by:\n - a.md\n ```\n";
assert!(
backlinks_of(content).is_empty(),
"a carrier inside a list item extracts no metadata: {:?}",
backlinks_of(content)
);
assert!(
carrier_diags(content).is_empty(),
"a carrier inside a list item emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn carrier_inside_generic_container_is_inert() {
let content = "# T\n\n<div>\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n\n</div>\n";
assert!(
backlinks_of(content).is_empty(),
"a carrier inside a generic container extracts no metadata: {:?}",
backlinks_of(content)
);
assert!(
carrier_diags(content).is_empty(),
"a carrier inside a generic container emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn carrier_text_in_table_cell_is_inert() {
let content = "# T\n\n| a | b |\n| --- | --- |\n| ```yaml lattice``` | x |\n";
assert!(
backlinks_of(content).is_empty(),
"a `yaml lattice` mention in a table cell extracts no metadata: {:?}",
backlinks_of(content)
);
assert!(
carrier_diags(content).is_empty(),
"a `yaml lattice` mention in a table cell emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn carrier_inside_nested_details_is_inert() {
let content = "# T\n\n> <details><summary>lattice</summary>\n>\n> ```yaml lattice\n> backlinks:\n> referenced_by:\n> - a.md\n> ```\n>\n> </details>\n";
assert!(
backlinks_of(content).is_empty(),
"a carrier inside a nested `<details>` extracts no metadata: {:?}",
backlinks_of(content)
);
assert!(
carrier_diags(content).is_empty(),
"a carrier inside a nested `<details>` emits no diagnostic: {:?}",
carrier_diags(content)
);
}
#[test]
fn near_miss_extra_attribute_warns_and_loads_nothing() {
let content =
"# T\n\n```yaml lattice title=\"x\"\nbacklinks:\n referenced_by:\n - a.md\n```\n";
let diags = carrier_diags(content);
assert!(
diags
.iter()
.any(|d| d.severity == Severity::Warning && d.message.contains("not loaded")),
"a near-miss carrier earns a warning: {diags:?}"
);
assert!(
backlinks_of(content).is_empty(),
"a near-miss carrier loads no metadata: {:?}",
backlinks_of(content)
);
}
#[test]
fn near_miss_extra_bare_token_warns() {
let content =
"# T\n\n```yaml lattice lines\nbacklinks:\n referenced_by:\n - a.md\n```\n";
let diags = carrier_diags(content);
let warnings = diags
.iter()
.filter(|d| d.severity == Severity::Warning && d.message.contains("not loaded"))
.count();
assert_eq!(
warnings, 1,
"a near-miss carrier earns exactly one warning: {diags:?}"
);
}
#[test]
fn near_miss_warning_anchors_at_info_line() {
let content =
"# T\n\n```yaml lattice lines\nbacklinks:\n referenced_by:\n - a.md\n```\n";
let diags = carrier_diags(content);
let warning = diags
.iter()
.find(|d| d.message.contains("not loaded"))
.expect("a near-miss carrier emits a warning");
let span = warning.span.expect("near-miss warning carries a span");
let info_line_start = content
.find("```yaml lattice lines")
.expect("fixture contains the near-miss fence");
assert_eq!(
span.start, info_line_start,
"the near-miss span anchors at the fence-open/info line: {diags:?}"
);
}
#[test]
fn near_miss_inside_blockquote_is_silent() {
let content = "# T\n\n> ```yaml lattice title=\"x\"\n> backlinks:\n> referenced_by:\n> - a.md\n> ```\n";
assert!(
carrier_diags(content).is_empty(),
"a near-miss inside a blockquote is silent (nested wins): {:?}",
carrier_diags(content)
);
assert!(
backlinks_of(content).is_empty(),
"a near-miss inside a blockquote loads nothing: {:?}",
backlinks_of(content)
);
}
#[test]
fn near_miss_under_top_level_details_warns() {
let content = "# T\n\n<details><summary>lattice</summary>\n\n```yaml lattice title=\"x\"\nbacklinks:\n referenced_by:\n - a.md\n```\n\n</details>\n";
let diags = carrier_diags(content);
assert!(
diags
.iter()
.any(|d| d.severity == Severity::Warning && d.message.contains("not loaded")),
"a near-miss under a top-level `<details>` warns: {diags:?}"
);
}
#[test]
fn exact_carrier_is_not_a_near_miss() {
let content = "# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n";
assert!(
!carrier_diags(content)
.iter()
.any(|d| d.message.contains("not loaded")),
"an exact carrier is not a near-miss: {:?}",
carrier_diags(content)
);
}
#[test]
fn near_miss_span_round_trips_crlf() {
let content = "# T\r\n\r\n```yaml lattice title=\"x\"\r\nbacklinks:\r\n referenced_by:\r\n - a.md\r\n```\r\n";
let diags = carrier_diags(content);
assert!(
diags.iter().any(|d| d.message.contains("not loaded")),
"the near-miss warning fires under CRLF: {diags:?}"
);
}
#[test]
fn missing_blank_after_summary_warns() {
let content = "<details><summary>lattice</summary>\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n\n</details>\n";
let diags = carrier_diags(content);
assert!(
diags.iter().any(|d| d.severity == Severity::Warning
&& d.message.contains("blank line after `</summary>`")),
"missing blank after `</summary>` is a render-gotcha warning: {diags:?}"
);
}
#[test]
fn missing_blank_before_details_close_warns() {
let content = "<details><summary>lattice</summary>\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n</details>\n";
let diags = carrier_diags(content);
assert!(
diags.iter().any(|d| d.severity == Severity::Warning
&& d.message.contains("blank line before `</details>`")),
"missing blank before `</details>` is a render-gotcha warning: {diags:?}"
);
}
#[test]
fn well_formed_details_has_no_gotcha() {
let content = "<details><summary>lattice</summary>\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n\n</details>\n";
let diags = carrier_diags(content);
assert!(
diags.is_empty(),
"a well-formed `<details>` carrier emits no gotcha: {diags:?}"
);
}
#[test]
fn malformed_carrier_yaml_is_an_error() {
let content = "# T\n\n```yaml lattice\nbacklinks:\n bad_indent\n```\n";
let diags = carrier_diags(content);
assert!(
diags.iter().any(
|d| d.severity == Severity::Error && d.message.starts_with("metadata carrier:")
),
"malformed inner YAML is a carrier error: {diags:?}"
);
}
#[test]
fn unterminated_details_is_an_error() {
let content = "<details><summary>lattice</summary>\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n";
let tree = tree_of(content);
assert!(
tree.diagnostics()
.iter()
.any(|d| d.level == block::DiagnosticLevel::Error
&& d.message.contains("unclosed `<details>`")),
"an unterminated `<details>` is a parser error: {:?}",
tree.diagnostics()
);
assert!(
backlinks_of(content).contains_key("referenced_by"),
"the carrier still populates backlinks despite the unclosed tag"
);
}
#[test]
fn second_carrier_warns() {
let content = "# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - b.md\n```\n";
let diags = carrier_diags(content);
let warnings = diags
.iter()
.filter(|d| d.severity == Severity::Warning && d.message.contains("more than one"))
.count();
assert_eq!(
warnings, 1,
"a second carrier yields exactly one warning: {diags:?}"
);
}
#[test]
fn single_carrier_no_duplicate_warning() {
let content = "# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n";
assert!(
!carrier_diags(content)
.iter()
.any(|d| d.message.contains("more than one")),
"a single carrier yields no duplicate warning"
);
}
#[test]
fn frontmatter_and_carrier_coexist_warns() {
let content = "---\nbacklinks:\n referenced_by:\n - a.md\n---\n# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - b.md\n```\n";
let diags = carrier_diags(content);
let coexist: Vec<_> = diags
.iter()
.filter(|d| d.severity == Severity::Warning && d.message.contains("coexists"))
.collect();
assert_eq!(
coexist.len(),
1,
"frontmatter + carrier yields exactly one coexistence warning: {diags:?}"
);
let carrier_start = content
.find("```yaml lattice")
.expect("fixture contains a fenced carrier");
assert_eq!(
coexist[0]
.span
.expect("coexistence warning carries a span")
.start,
carrier_start,
"the coexistence warning anchors at the fenced carrier, not the frontmatter: {diags:?}"
);
}
#[test]
fn carrier_only_no_coexistence_warning() {
let content = "# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - a.md\n```\n";
assert!(
!carrier_diags(content)
.iter()
.any(|d| d.message.contains("coexists")),
"a lone carrier yields no coexistence warning"
);
}
#[test]
fn frontmatter_only_no_coexistence_warning() {
let content = "---\nbacklinks:\n referenced_by:\n - a.md\n---\n# T\n";
assert!(
carrier_diags(content).is_empty(),
"leading frontmatter with no carrier emits no carrier diagnostic"
);
}
#[test]
fn frontmatter_and_duplicate_carriers_warn_per_carrier() {
let content = "---\nbacklinks:\n referenced_by:\n - a.md\n---\n# T\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - b.md\n```\n\n```yaml lattice\nbacklinks:\n referenced_by:\n - c.md\n```\n";
let diags = carrier_diags(content);
let coexist = diags
.iter()
.filter(|d| d.message.contains("coexists"))
.count();
let duplicate = diags
.iter()
.filter(|d| d.message.contains("more than one"))
.count();
assert_eq!(
coexist, 2,
"each carrier coexisting with frontmatter is flagged: {diags:?}"
);
assert_eq!(
duplicate, 1,
"the second carrier still draws the duplicate warning: {diags:?}"
);
}
#[test]
fn coexistence_warning_spans_round_trip_crlf() {
let content = "---\r\nbacklinks:\r\n referenced_by:\r\n - a.md\r\n---\r\n# T\r\n\r\n```yaml lattice\r\nbacklinks:\r\n referenced_by:\r\n - b.md\r\n```\r\n";
let diags = carrier_diags(content);
assert!(
diags.iter().any(|d| d.message.contains("coexists")),
"the coexistence warning fires under CRLF: {diags:?}"
);
}
#[test]
fn carrier_diagnostic_spans_round_trip_crlf() {
let content = "# T\r\n\r\n```yaml lattice\r\nbacklinks:\r\n referenced_by:\r\n - a.md\r\n```\r\n\r\n```yaml lattice\r\nbacklinks:\r\n referenced_by:\r\n - b.md\r\n```\r\n";
let diags = carrier_diags(content);
assert!(
!diags.is_empty(),
"the duplicate carrier emits a diagnostic under CRLF"
);
}
}