kbvm 0.1.5

An implementation of the XKB specification
Documentation
use {
    crate::xkb::{
        code::Code,
        code_map::CodeMap,
        diagnostic::{Diagnostic, DiagnosticKind, DiagnosticSink},
        interner::Interner,
        span::SpanExt,
        string_cooker::StringCooker,
    },
    bstr::ByteSlice,
    std::sync::Arc,
};

fn cook_(s: &str) -> (String, Vec<Diagnostic>) {
    let mut interner = Interner::default();
    let mut map = CodeMap::default();
    let code = Code::new(&Arc::new(s.as_bytes().to_vec()));
    let span = map.add(None, None, &code);
    let interned = interner.intern(&code.to_slice());
    let mut diagnostics = vec![];
    let mut sink = DiagnosticSink::new(&mut diagnostics);
    let mut cooker = StringCooker::default();
    let res = cooker.cook(&mut map, &mut sink, &mut interner, interned.spanned2(span));
    let res = interner.get(res).to_str().unwrap().to_owned();
    (res, diagnostics)
}

fn cook(s: &str) -> String {
    let (res, diagnostics) = cook_(s);
    assert!(diagnostics.is_empty());
    res
}

#[test]
fn success() {
    assert_eq!(cook(r#""#), "");
    assert_eq!(cook(r#"abc"#), "abc");
    assert_eq!(cook(r#"\\\n\t\r\b\f\v\e\""#), "\\\n\t\r\x08\x0c\x0b\x1b\"");
    assert_eq!(cook(r#"\1"#), (0o1 as char).to_string());
    assert_eq!(cook(r#"\12"#), (0o12 as char).to_string());
    assert_eq!(cook(r#"\123"#), (0o123 as char).to_string());
    assert_eq!(cook(r#"\1234"#), format!("{}4", 0o123 as char));
    assert_eq!(cook(r#"\u{65e5}"#), format!(""));
    assert_eq!(cook(r#"a\u{65e5}b"#), format!("a日b"));
}

#[test]
fn warning() {
    let (c, d) = cook_(r#"\n\777\n"#);
    assert_eq!(c, "\n\n");
    assert_eq!(d.len(), 1);
    assert_eq!(d[0].kind(), DiagnosticKind::OctalStringEscapeOverflow);

    let (c, d) = cook_(r#"\n\q\n"#);
    assert_eq!(c, "\n\n");
    assert_eq!(d.len(), 1);
    assert_eq!(d[0].kind(), DiagnosticKind::UnknownEscapeSequence);

    let (c, d) = cook_(r#"\n\u{65e5"#);
    assert_eq!(c, "\n");
    assert_eq!(d.len(), 1);
    assert_eq!(d[0].kind(), DiagnosticKind::UnterminatedUnicodeEscape);

    let (c, d) = cook_(r#"\n\u_65e5}"#);
    assert_eq!(c, "\n");
    assert_eq!(d.len(), 1);
    assert_eq!(d[0].kind(), DiagnosticKind::UnopenedUnicodeEscape);

    let (c, d) = cook_(r#"\n\u{x}\n"#);
    assert_eq!(c, "\n\n");
    assert_eq!(d.len(), 1);
    assert_eq!(
        d[0].kind(),
        DiagnosticKind::InvalidUnicodeEscapeRepresentation
    );

    let (c, d) = cook_(r#"\n\u{aaaaaaaa}\n"#);
    assert_eq!(c, "\n\n");
    assert_eq!(d.len(), 1);
    assert_eq!(d[0].kind(), DiagnosticKind::InvalidUnicodeCodepoint);
}