~raph/glyphstool

9103080698bc1e974f4f723c43756ae02f1d3020 — Raph Levien 2 years ago 112f5d2
Add serialization
6 files changed, 228 insertions(+), 7 deletions(-)

M plist_derive/src/lib.rs
M src/font.rs
M src/from_plist.rs
M src/main.rs
M src/plist.rs
A src/to_plist.rs
M plist_derive/src/lib.rs => plist_derive/src/lib.rs +68 -0
@@ 25,6 25,26 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    proc_macro::TokenStream::from(expanded)
}

#[proc_macro_derive(ToPlist, attributes(rest))]
pub fn derive_to(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;

    let ser_rest = add_ser_rest(&input.data);
    let ser = add_ser(&input.data);

    let expanded = quote! {
        impl crate::to_plist::ToPlist for #name {
            fn to_plist(self) -> crate::plist::Plist {
                #ser_rest
                #ser
                hashmap.into()
            }
        }
    };
    proc_macro::TokenStream::from(expanded)
}

fn add_deser(data: &Data) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {


@@ 64,6 84,54 @@ fn add_deser(data: &Data) -> TokenStream {
    }
}

fn add_ser(data: &Data) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {
            Fields::Named(ref fields) => {
                let recurse = fields.named.iter().filter_map(|f| {
                    if !is_rest(&f.attrs) {
                        let name = &f.ident;
                        let name_str = name.as_ref().unwrap().to_string();
                        let snake_name = snake_to_camel_case(&name_str);
                        Some(quote_spanned! {f.span() =>
                            if let Some(plist) = crate::to_plist::ToPlistOpt::to_plist(self.#name) {
                                hashmap.insert(#snake_name.to_string(), plist);
                            }
                        })
                    } else {
                        None
                    }
                });
                quote! {
                    #( #recurse )*
                }
            }
            _ => unimplemented!(),
        },
        _ => unimplemented!(),
    }
}

fn add_ser_rest(data: &Data) -> TokenStream {
    match *data {
        Data::Struct(ref data) => match data.fields {
            Fields::Named(ref fields) => {
                for f in fields.named.iter() {
                    if is_rest(&f.attrs) {
                        let name = &f.ident;
                        return quote_spanned! { f.span() =>
                            let mut hashmap = self.#name;
                        }
                    }
                }
                quote! { let mut hashmap = HashMap::new(); }
            }
            _ => unimplemented!(),
        },
        _ => unimplemented!(),
    }
}

fn is_rest(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        attr.path

M src/font.rs => src/font.rs +23 -5
@@ 7,20 7,21 @@
use std::collections::HashMap;

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

#[derive(Debug, FromPlist)]
#[derive(Debug, FromPlist, ToPlist)]
pub struct Font {
    pub glyphs: Vec<Glyph>,
}

#[derive(Debug, FromPlist)]
#[derive(Debug, FromPlist, ToPlist)]
pub struct Glyph {
    pub layers: Vec<Layer>,
    pub glyphname: String,
}

#[derive(Debug, FromPlist)]
#[derive(Debug, FromPlist, ToPlist)]
pub struct Layer {
    pub layer_id: String,
    pub width: f64,


@@ 30,7 31,7 @@ pub struct Layer {
    pub other_stuff: HashMap<String, Plist>,
}

#[derive(Debug, FromPlist)]
#[derive(Debug, FromPlist, ToPlist)]
pub struct Path {
    pub closed: bool,
    pub nodes: Vec<Node>,


@@ 51,7 52,7 @@ pub enum NodeType {
    CurveSmooth,
}

#[derive(Debug, FromPlist)]
#[derive(Debug, FromPlist, ToPlist)]
pub struct Component {
    pub name: String,
    pub transform: Option<String>,


@@ 79,3 80,20 @@ impl std::str::FromStr for NodeType {
        }
    }
}

impl NodeType {
    fn glyphs_str(&self) -> &'static str {
        match self {
            NodeType::Line => "LINE",
            NodeType::OffCurve => "OFFCURVE",
            NodeType::Curve => "CURVE",
            NodeType::CurveSmooth => "CURVE SMOOTH",
        }
    }
}

impl ToPlist for Node {
    fn to_plist(self) -> Plist {
        format!("{} {} {}", self.x, self.y, self.node_type.glyphs_str()).into()
    }
}

M src/from_plist.rs => src/from_plist.rs +1 -1
@@ 1,4 1,4 @@
pub use plist_derive::*;
pub use plist_derive::FromPlist;

use crate::plist::Plist;


M src/main.rs => src/main.rs +5 -1
@@ 4,10 4,12 @@ use std::fs;
mod font;
mod from_plist;
mod plist;
mod to_plist;

use font::Font;
use from_plist::FromPlist;
use plist::Plist;
use to_plist::ToPlist;

fn usage() {
    eprintln!("usage: glyphstool font.glyphs");


@@ 38,5 40,7 @@ fn main() {
    }
    */
    let font: Font = FromPlist::from_plist(plist);
    println!("{:?}", font);
    //println!("{:?}", font);
    let plist = font.to_plist();
    println!("{}", plist.to_string());
}

M src/plist.rs => src/plist.rs +74 -0
@@ 184,6 184,50 @@ impl Plist {
        }
        Plist::String(s.into())
    }

    pub fn to_string(&self) -> String {
        let mut s = String::new();
        self.push_to_string(&mut s);
        s
    }

    fn push_to_string(&self, s: &mut String) {
        match self {
            Plist::Array(a) => {
                s.push_str("(");
                let mut delim = "\n";
                for el in a {
                    s.push_str(delim);
                    el.push_to_string(s);
                    delim = ",\n";
                }
                s.push_str("\n)");
            }
            Plist::Dictionary(a) => {
                s.push_str("{\n");
                for (k, el) in a {
                    // TODO: quote if needed?
                    s.push_str(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::Integer(i) => {
                s.push_str(&format!("{}", i));
            }
            Plist::Float(f) => {
                s.push_str(&format!("{}", f));
            }
        }
    }
}

impl<'a> Token<'a> {


@@ 275,3 319,33 @@ impl<'a> Token<'a> {
        None
    }
}

impl From<String> for Plist {
    fn from(x: String) -> Plist {
        Plist::String(x)
    }
}

impl From<i64> for Plist {
    fn from(x: i64) -> Plist {
        Plist::Integer(x)
    }
}

impl From<f64> for Plist {
    fn from(x: f64) -> Plist {
        Plist::Float(x)
    }
}

impl From<Vec<Plist>> for Plist {
    fn from(x: Vec<Plist>) -> Plist {
        Plist::Array(x)
    }
}

impl From<HashMap<String, Plist>> for Plist {
    fn from(x: HashMap<String, Plist>) -> Plist {
        Plist::Dictionary(x)
    }
}

A src/to_plist.rs => src/to_plist.rs +57 -0
@@ 0,0 1,57 @@
pub use plist_derive::ToPlist;

use crate::plist::Plist;

pub trait ToPlist {
    fn to_plist(self) -> Plist;
}

pub trait ToPlistOpt {
    fn to_plist(self) -> Option<Plist>;
}

impl ToPlist for String {
    fn to_plist(self) -> Plist {
        self.into()
    }
}

impl ToPlist for bool {
    fn to_plist(self) -> Plist {
        (self as i64).into()
    }
}

impl ToPlist for i64 {
    fn to_plist(self) -> Plist {
        self.into()
    }
}

impl ToPlist for f64 {
    fn to_plist(self) -> Plist {
        self.into()
    }
}

impl<T: ToPlist> ToPlist for Vec<T> {
    fn to_plist(self) -> Plist {
        let mut result = Vec::new();
        for element in self {
            result.push(ToPlist::to_plist(element));
        }
        result.into()
    }
}

impl<T: ToPlist> ToPlistOpt for T {
    fn to_plist(self) -> Option<Plist> {
        Some(ToPlist::to_plist(self))
    }
}

impl<T: ToPlist> ToPlistOpt for Option<T> {
    fn to_plist(self) -> Option<Plist> {
        self.map(ToPlist::to_plist)
    }
}