use crate::span::Span;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Trivia {
pub kind: TriviaKind,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, gen_platform::IsVariant)]
pub enum TriviaKind {
LineComment(String),
BlankLine,
Shebang(String),
}
impl Trivia {
#[must_use]
pub const fn comment_text(&self) -> Option<&str> {
match &self.kind {
TriviaKind::LineComment(s) => Some(s.as_str()),
TriviaKind::BlankLine | TriviaKind::Shebang(_) => None,
}
}
}
#[cfg(test)]
mod is_variant_tests {
use super::*;
fn all_variants() -> Vec<(TriviaKind, &'static str)> {
vec![
(TriviaKind::LineComment("hello".into()), "LineComment"),
(TriviaKind::BlankLine, "BlankLine"),
(
TriviaKind::Shebang("#!/usr/bin/env tatara-script".into()),
"Shebang",
),
]
}
fn predicate_row(k: &TriviaKind) -> [bool; 3] {
[k.is_line_comment(), k.is_blank_line(), k.is_shebang()]
}
#[test]
fn trivia_kind_is_variant_predicates_partition_the_arm_set() {
let variants = all_variants();
for (idx, (variant, name)) in variants.iter().enumerate() {
let observed = predicate_row(variant);
let mut expected = [false; 3];
expected[idx] = true;
assert_eq!(
observed, expected,
"TriviaKind::{name} at declaration-order slot {idx} must \
satisfy exactly one is_* predicate (its own); observed \
row must equal the one-hot expected row"
);
}
}
#[test]
fn trivia_comment_text_projection_is_const_fn() {
const fn comment_text_via_const_fn(t: &Trivia) -> Option<&str> {
t.comment_text()
}
for (variant, expected) in [
(TriviaKind::LineComment("hello".into()), Some("hello")),
(TriviaKind::BlankLine, None),
(
TriviaKind::Shebang("#!/usr/bin/env tatara-script".into()),
None,
),
] {
let trivia = Trivia {
kind: variant,
span: Span::default(),
};
assert_eq!(
comment_text_via_const_fn(&trivia),
expected,
"Trivia::comment_text must project through the pub const \
fn body byte-equal to the pre-lift open-coded match on \
the same fixture",
);
assert_eq!(comment_text_via_const_fn(&trivia), trivia.comment_text());
}
}
#[test]
fn trivia_kind_is_blank_line_and_is_line_comment_byte_equal_pre_lift_matches_shape() {
for (variant, name) in all_variants() {
let via_matches_blank = matches!(variant, TriviaKind::BlankLine);
let via_predicate_blank = variant.is_blank_line();
assert_eq!(
via_predicate_blank, via_matches_blank,
"TriviaKind::{name}.is_blank_line() must byte-equal \
matches!(_, TriviaKind::BlankLine) — otherwise the \
converged trim_leading_blanks call site in caixa-fmt \
would silently disagree with its pre-lift shape"
);
let via_matches_line = matches!(variant, TriviaKind::LineComment(_));
let via_predicate_line = variant.is_line_comment();
assert_eq!(
via_predicate_line, via_matches_line,
"TriviaKind::{name}.is_line_comment() must byte-equal \
matches!(_, TriviaKind::LineComment(_)) — otherwise the \
converged contains_comment_trivia call site in caixa-fmt \
would silently disagree with its pre-lift shape"
);
}
}
}