rowan-peg 0.2.2

Convert ABNF like grammar to rust-peg build `rowan` lossless syntax tree
Documentation
use std::ops;
use to_true::InTrue;

pub fn rule_name_of(name: &str) -> String {
    let name = name.to_ascii_lowercase();
    if RUST_KEYWORDS.contains(&&*name) {
        format!("{name}_")
    } else {
        name.replace('-', "_")
    }
}

pub fn kind_name_of(name: &str) -> String {
    name.trim_end_matches(['_', '-']).to_ascii_uppercase()
}

pub fn node_name_of(name: &str) -> String {
    to_upper_camel(name.trim_end_matches('_'), true)
}

pub fn punct_name_of(punct: &str) -> Option<String> {
    PUNCT_NAMES.iter()
        .find(|(it, _)| punct == *it)?
        .1.to_owned()
        .into()
}

pub fn to_upper_camel(s: &str, mut upper: bool) -> String {
    let mut buf = String::with_capacity(s.len() + s.len() / 4);
    let mut start = true;
    buf.extend(s.chars().filter_map(|ch| {
        let ch = if matches!(ch, '-' | '_') && !start {
            return upper.in_true(|| ch);
        } else if upper {
            upper = false;
            ch.to_ascii_uppercase()
        } else {
            ch.to_ascii_lowercase()
        };
        start = false;
        Some(ch)
    }));
    buf
}
#[test]
fn test_to_upper_camel() {
    assert_eq!("AbcDef", to_upper_camel("abc_def", true));
    assert_eq!("abcDef", to_upper_camel("abc_def", false));
    assert_eq!("_abcDef", to_upper_camel("_abc_def", false));
    assert_eq!("Abc_Def", to_upper_camel("abc__def", true));
}

pub fn one_elem<I: IntoIterator>(iter: I) -> Option<I::Item> {
    let mut iter = iter.into_iter();
    let first = iter.next()?;
    iter.next().is_none().then_some(first)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct UsedBound(pub u8, pub u8);

impl ops::MulAssign for UsedBound {
    fn mul_assign(&mut self, rhs: Self) {
        *self = *self * rhs;
    }
}

impl ops::AddAssign for UsedBound {
    fn add_assign(&mut self, rhs: Self) {
        *self = *self + rhs;
    }
}

impl ops::Add for UsedBound {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self(self.0.saturating_add(rhs.0), self.1.saturating_add(rhs.1))
    }
}

impl ops::Mul for UsedBound {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        Self(self.0.saturating_mul(rhs.0), self.1.saturating_mul(rhs.1))
    }
}

impl ops::AddAssign<u8> for UsedBound {
    fn add_assign(&mut self, rhs: u8) {
        *self = *self + rhs;
    }
}

impl ops::Add<u8> for UsedBound {
    type Output = Self;

    fn add(self, rhs: u8) -> Self::Output {
        Self(self.0.saturating_add(rhs), self.1.saturating_add(rhs))
    }
}

impl ops::MulAssign<u8> for UsedBound {
    fn mul_assign(&mut self, rhs: u8) {
        *self = *self * rhs;
    }
}

impl ops::Mul<u8> for UsedBound {
    type Output = Self;

    fn mul(self, rhs: u8) -> Self::Output {
        Self(self.0.saturating_mul(rhs), self.1.saturating_mul(rhs))
    }
}

impl UsedBound {
    pub fn cover(self, other: Self) -> Self {
        let min = self.0.min(other.0);
        let max = self.1.max(other.1);
        Self(min, max)
    }
}

const RUST_KEYWORDS: &[&str] = &[
    "Self",
    "abstract",
    "as",
    "become",
    "box",
    "break",
    "const",
    "continue",
    "crate",
    "do",
    "else",
    "enum",
    "extern",
    "false",
    "final",
    "fn",
    "for",
    "if",
    "impl",
    "in",
    "let",
    "loop",
    "macro",
    "match",
    "mod",
    "move",
    "mut",
    "override",
    "priv",
    "pub",
    "ref",
    "return",
    "self",
    "static",
    "struct",
    "super",
    "trait",
    "true",
    "type",
    "typeof",
    "unsafe",
    "unsized",
    "use",
    "virtual",
    "where",
    "while",
    "yield",
    "async",
    "await",
    "dyn",
    "gen",
    "try",
];
const PUNCT_NAMES: &[(&str, &str)] = &[
    ("$", "dollar"),
    (";", "semicolon"),
    (",", "comma"),
    ("(", "l_paren"),
    (")", "r_paren"),
    ("{", "l_curly"),
    ("}", "r_curly"),
    ("[", "l_brack"),
    ("]", "r_brack"),
    ("<", "l_angle"),
    (">", "r_angle"),
    ("@", "at"),
    ("#", "pound"),
    ("~", "tilde"),
    ("?", "question"),
    ("&", "amp"),
    ("|", "pipe"),
    ("+", "plus"),
    ("*", "star"),
    ("/", "slash"),
    ("\\", "backslash"),
    ("^", "caret"),
    ("%", "percent"),
    ("_", "underscore"),
    (".", "dot"),
    ("..", "dot2"),
    ("...", "dot3"),
    ("..=", "dot2eq"),
    (":", "colon"),
    ("::", "colon2"),
    ("=", "eq"),
    ("==", "eq2"),
    ("===", "eq3"),
    ("=>", "fat_arrow"),
    ("!", "bang"),
    ("!=", "neq"),
    ("!==", "neq2"),
    ("-", "minus"),
    ("->", "thin_arrow"),
    ("<=", "lteq"),
    (">=", "gteq"),
    ("+=", "pluseq"),
    ("-=", "minuseq"),
    ("|=", "pipeeq"),
    ("&=", "ampeq"),
    ("^=", "careteq"),
    ("/=", "slasheq"),
    ("*=", "stareq"),
    ("%=", "percenteq"),
    ("&&", "amp2"),
    ("||", "pipe2"),
    ("<<", "shl"),
    (">>", "shr"),
    ("<<=", "shleq"),
    (">>=", "shreq"),
    ("'", "single_quote"),
    ("\"", "double_quote"),
];