~raph/glyphstool

c093d14ce98970b88f2458dfc75db2c0683bccb8 — Raph Levien 2 years ago 9103080
Fine-tune serialization

Escape strings properly, make sure unicode values (eg 0041) survive.
2 files changed, 44 insertions(+), 9 deletions(-)

M src/font.rs
M src/plist.rs
M src/font.rs => src/font.rs +1 -1
@@ 7,8 7,8 @@
use std::collections::HashMap;

use crate::from_plist::FromPlist;
use crate::to_plist::ToPlist;
use crate::plist::Plist;
use crate::to_plist::ToPlist;

#[derive(Debug, FromPlist, ToPlist)]
pub struct Font {

M src/plist.rs => src/plist.rs +43 -8
@@ 39,10 39,25 @@ fn is_alnum(b: u8) -> bool {
    is_numeric(b) || (b >= b'A' && b <= b'Z') || (b >= b'a' && b <= b'z') || b == b'_'
}

// Used for serialization; make sure UUID's get quoted
fn is_alnum_strict(b: u8) -> bool {
    is_alnum(b) && b != b'-'
}

fn is_ascii_whitespace(b: u8) -> bool {
    b == b' ' || b == b'\t' || b == b'\r' || b == b'\n'
}

fn numeric_ok(s: &str) -> bool {
    if s.is_empty() {
        return false;
    }
    if s.len() > 1 && s.as_bytes()[0] == b'0' {
        return !s.as_bytes().iter().all(|&b| b >= b'0' && b <= b'9')
    }
    true
}

fn skip_ws(s: &str, mut ix: usize) -> usize {
    while ix < s.len() && is_ascii_whitespace(s.as_bytes()[ix]) {
        ix += 1;


@@ 50,6 65,30 @@ fn skip_ws(s: &str, mut ix: usize) -> usize {
    ix
}

fn escape_string(buf: &mut String, s: &str) {
    if !s.is_empty() && s.as_bytes().iter().all(|&b| is_alnum_strict(b)) {
        buf.push_str(s);
    } else {
        buf.push('"');
        let mut start = 0;
        let mut ix = start;
        while ix < s.len() {
            let b = s.as_bytes()[ix];
            match b {
                b'"' | b'\\' => {
                    buf.push_str(&s[start..ix]);
                    buf.push('\\');
                    start = ix;
                }
                _ => (),
            }
            ix += 1;
        }
        buf.push_str(&s[start..]);
        buf.push('"');
    }
}

impl Plist {
    pub fn parse(s: &str) -> Result<Plist, Error> {
        let (plist, _ix) = Plist::parse_rec(s, 0)?;


@@ 174,7 213,7 @@ impl Plist {
    }

    fn parse_atom(s: &str) -> Plist {
        if !s.is_empty() && is_numeric(s.as_bytes()[0]) {
        if numeric_ok(s) {
            if let Ok(num) = s.parse() {
                return Plist::Integer(num);
            }


@@ 205,21 244,17 @@ impl Plist {
            }
            Plist::Dictionary(a) => {
                s.push_str("{\n");
                // TODO: probably want to sort keys
                for (k, el) in a {
                    // TODO: quote if needed?
                    s.push_str(k);
                    escape_string(s, k);
                    s.push_str(" = ");
                    el.push_to_string(s);
                    s.push_str(";\n");
                }
                s.push_str("}");
            }
            Plist::String(st) => {
                // TODO: more careful escaping
                s.push_str("\"");
                s.push_str(&st);
                s.push_str("\"");
            }
            Plist::String(st) => escape_string(s, st),
            Plist::Integer(i) => {
                s.push_str(&format!("{}", i));
            }