kbvm 0.1.5

An implementation of the XKB specification
Documentation
use {
    crate::xkb::{
        interner::{Interned, Interner},
        rmlvo::parser::{Line, MappingKey, MappingKeyIndex, MappingValue, RuleKey},
    },
    std::io::{self, Write},
};

pub(crate) struct Formatter<'a, W> {
    interner: &'a Interner,
    out: W,
}

pub(crate) trait Format {
    fn format<W>(&self, f: &mut Formatter<'_, W>) -> io::Result<()>
    where
        W: Write;
}

impl<'a, W> Formatter<'a, W>
where
    W: Write,
{
    pub(crate) fn new(interner: &'a Interner, out: W) -> Self {
        Self { interner, out }
    }

    fn write_all(&mut self, s: &str) -> io::Result<()> {
        self.out.write_all(s.as_bytes())
    }

    fn write_interned(&mut self, interned: Interned) -> io::Result<()> {
        let s = self.interner.get(interned);
        self.out.write_all(s.as_bytes())?;
        Ok(())
    }
}

impl Format for Line<'_> {
    fn format<W>(&self, f: &mut Formatter<'_, W>) -> io::Result<()>
    where
        W: Write,
    {
        match self {
            Line::Macro(k, v) => {
                f.write_all("! $")?;
                f.write_interned(*k)?;
                f.write_all(" = ")?;
                for v in *v {
                    f.write_all(" ")?;
                    f.write_interned(*v)?;
                }
            }
            Line::Include(i) => {
                f.write_all("! include ")?;
                f.write_interned(*i)?;
            }
            Line::Mapping(k, v) => {
                f.write_all("!")?;
                for k in *k {
                    f.write_all(" ")?;
                    let write_index = |f: &mut Formatter<'_, W>, idx: &MappingKeyIndex| {
                        f.write_all("[")?;
                        match idx {
                            MappingKeyIndex::Single => f.write_all("single")?,
                            MappingKeyIndex::First => f.write_all("first")?,
                            MappingKeyIndex::Later => f.write_all("later")?,
                            MappingKeyIndex::Any => f.write_all("any")?,
                            MappingKeyIndex::Value(v) => write!(f.out, "{}", v.raw())?,
                        }
                        f.write_all("]")
                    };
                    match k {
                        MappingKey::Model => f.write_all("model")?,
                        MappingKey::Option => f.write_all("option")?,
                        MappingKey::Layout(l) => {
                            f.write_all("layout")?;
                            if let Some(idx) = l {
                                write_index(f, idx)?;
                            }
                        }
                        MappingKey::Variant(l) => {
                            f.write_all("variant")?;
                            if let Some(idx) = l {
                                write_index(f, idx)?;
                            }
                        }
                    }
                }
                f.write_all(" =")?;
                for v in *v {
                    f.write_all(" ")?;
                    let n = match v {
                        MappingValue::Keycodes => "keycodes",
                        MappingValue::Symbols => "symbols",
                        MappingValue::Types => "types",
                        MappingValue::Compat => "compat",
                        MappingValue::Geometry => "geometry",
                    };
                    f.write_all(n)?;
                }
            }
            Line::Rule(k, v) => {
                f.write_all(" ")?;
                for k in *k {
                    f.write_all(" ")?;
                    match k {
                        RuleKey::Star => f.write_all("*")?,
                        RuleKey::Macro(l) => {
                            f.write_all("$")?;
                            f.write_interned(*l)?;
                        }
                        RuleKey::Ident(l) => f.write_interned(*l)?,
                        RuleKey::Any => f.write_all("<any>")?,
                        RuleKey::Some => f.write_all("<some>")?,
                        RuleKey::None => f.write_all("<none>")?,
                    }
                }
                f.write_all(" =")?;
                for v in *v {
                    f.write_all(" ")?;
                    f.write_interned(v.ident.val)?;
                }
            }
        }
        f.write_all("\n")
    }
}