use std::sync::OnceLock;
use crate::ast::Grammar;
pub(crate) const CORE_RULE_COUNT: usize = 16;
pub(crate) const CORE_RULES: &str = r#"; RFC 5234 Appendix B.1 - Core Rules
; Transcribed from https://www.rfc-editor.org/rfc/rfc5234.txt
; Errata applied: none (no verified erratum touches Appendix B)
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
BIT = "0" / "1"
CHAR = %x01-7F
; any 7-bit US-ASCII character,
; excluding NUL
CR = %x0D
; carriage return
CRLF = CR LF
; Internet standard newline
CTL = %x00-1F / %x7F
; controls
DIGIT = %x30-39
; 0-9
DQUOTE = %x22
; " (Double Quote)
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
HTAB = %x09
; horizontal tab
LF = %x0A
; linefeed
LWSP = *(WSP / CRLF WSP)
; Use of this linear-white-space rule
; permits lines containing only white
; space that are no longer legal in
; mail headers and have caused
; interoperability problems in other
; contexts.
; Do not use when defining mail
; headers and use with caution in
; other contexts.
OCTET = %x00-FF
; 8 bits of data
SP = %x20
VCHAR = %x21-7E
; visible (printing) characters
WSP = SP / HTAB
; white space
"#;
pub(crate) fn core_grammar() -> &'static Grammar {
static CORE: OnceLock<Grammar> = OnceLock::new();
CORE.get_or_init(|| {
let grammar =
Grammar::parse(CORE_RULES).expect("the core rules are a constant and must parse");
assert_eq!(
grammar.definitions().len(),
CORE_RULE_COUNT,
"RFC 5234 Appendix B.1 defines exactly {CORE_RULE_COUNT} rules"
);
grammar
})
}
pub(crate) fn is_core_rule(name: &str) -> bool {
core_grammar()
.definitions()
.iter()
.any(|definition| definition.name.key() == name.to_ascii_lowercase())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_core_rules_parse_to_appendix_b() {
let grammar = core_grammar();
let names: Vec<&str> = grammar
.definitions()
.iter()
.map(|definition| definition.name.as_str())
.collect();
assert_eq!(
names,
[
"ALPHA", "BIT", "CHAR", "CR", "CRLF", "CTL", "DIGIT", "DQUOTE", "HEXDIG", "HTAB",
"LF", "LWSP", "OCTET", "SP", "VCHAR", "WSP"
]
);
assert_eq!(names.len(), CORE_RULE_COUNT);
}
#[test]
fn the_constant_matches_the_fixture() {
let fixture = include_str!("../tests/grammars/rfc5234-core.abnf");
assert!(
CORE_RULES.lines().eq(fixture.lines()),
"src/core_rules.rs and tests/grammars/rfc5234-core.abnf have drifted"
);
}
#[test]
fn lwsp_is_the_one_nullable_core_rule() {
assert!(CORE_RULES.contains("LWSP = *(WSP / CRLF WSP)"));
}
}