<commit_before>#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "clippy", allow(used_underscore_binding))]
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]

// The `quote!` macro requires deep recursion.
#![recursion_limit = "192"]

extern crate serde_codegen_internals as internals;

#[cfg(feature = "with-syntex")]
extern crate syntex;

#[cfg(feature = "with-syntex")]
#[macro_use]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

extern crate syn;
#[macro_use]
extern crate quote;

#[cfg(feature = "with-syntex")]
use std::path::Path;

#[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType;

mod bound;
mod de;
mod ser;

#[cfg(feature = "with-syntex")]
fn syntex_registry() -> syntex::Registry {
    use syntax::{ast, fold};

    /// Strip the serde attributes from the crate.
    #[cfg(feature = "with-syntex")]
    fn strip_attributes(krate: ast::Crate) -> ast::Crate {
        /// Helper folder that strips the serde attributes after the extensions have been expanded.
        struct StripAttributeFolder;

        impl fold::Folder for StripAttributeFolder {
            fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
                match attr.node.value.node {
                    ast::MetaItemKind::List(ref n, _) if n == &"serde" => { return None; }
                    _ => {}
                }

                Some(attr)
            }

            fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
                fold::noop_fold_mac(mac, self)
            }
        }

        fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
    }

    let mut reg = syntex::Registry::new();

    reg.add_attr("feature(custom_derive)");
    reg.add_attr("feature(custom_attribute)");

    reg.add_decorator("derive_Serialize", expand_derive_serialize);
    reg.add_decorator("derive_Deserialize", expand_derive_deserialize);

    reg.add_post_expansion_pass(strip_attributes);

    reg
}

#[cfg(feature = "with-syntex")]
pub fn expand_str(src: &str) -> Result<String, syntex::Error> {
    let src = src.to_owned();

    let expand_thread = move || {
        syntex_registry().expand_str("", "", &src)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>,
          D: AsRef<Path>,
{
    let src = src.as_ref().to_owned();
    let dst = dst.as_ref().to_owned();

    let expand_thread = move || {
        syntex_registry().expand("", src, dst)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(not(feature = "with-syntex"))]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Serialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_serialize)));

    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Deserialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_deserialize)));

    reg.register_attribute("serde".to_owned(), AttributeType::Normal);
}

macro_rules! shim {
    ($name:ident $pkg:ident :: $func:ident) => {
        fn $func(
            cx: &mut ::syntax::ext::base::ExtCtxt,
            span: ::syntax::codemap::Span,
            meta_item: &::syntax::ast::MetaItem,
            annotatable: &::syntax::ext::base::Annotatable,
            push: &mut FnMut(::syntax::ext::base::Annotatable)
        ) {
            let item = match *annotatable {
                ::syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        concat!("`#[derive(",
                                stringify!($name),
                                ")]` may only be applied to structs and enums"));
                    return;
                }
            };

            use syntax::{attr, ast, visit};
            struct MarkSerdeAttributesUsed;
            impl visit::Visitor for MarkSerdeAttributesUsed {
                fn visit_attribute(&mut self, attr: &ast::Attribute) {
                    match attr.node.value.node {
                        ast::MetaItemKind::List(ref name, _) if name == "serde" => {
                            attr::mark_used(attr);
                        }
                        _ => {}
                    }
                }
            }
            visit::walk_item(&mut MarkSerdeAttributesUsed, item);

            use syntax::print::pprust;
            let s = pprust::item_to_string(item);

            let syn_item = syn::parse_macro_input(&s).unwrap();
            let expanded = match $pkg::$func(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($name).to_string();
            let cfg = Vec::new();
            let sess = parse::ParseSess::new();
            let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, &sess);
            push(::syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim!(Serialize ser::expand_derive_serialize);
shim!(Deserialize de::expand_derive_deserialize);

#[cfg(feature = "with-syn")]
pub fn expand_single_item(item: &str) -> Result<String, String> {
    let syn_item = syn::parse_macro_input(item).unwrap();
    let (ser, de, syn_item) = strip_serde_derives(syn_item);
    let expanded_ser = if ser {
        Some(try!(ser::expand_derive_serialize(&syn_item)))
    } else {
        None
    };
    let expanded_de = if de {
        Some(try!(de::expand_derive_deserialize(&syn_item)))
    } else {
        None::<quote::Tokens>
    };
    let syn_item = strip_serde_attrs(syn_item);
    return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string());

    fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) {
        let mut ser = false;
        let mut de = false;
        let item = syn::MacroInput {
            attrs: item.attrs.into_iter().flat_map(|attr| {
                if attr.is_sugared_doc || attr.style != syn::AttrStyle::Outer {
                    return Some(attr);
                }
                let (name, nested) = match attr.value {
                    syn::MetaItem::List(name, nested) => (name, nested),
                    _ => return Some(attr)
                };
                if name != "derive" {
                    return Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, nested),
                        is_sugared_doc: false,
                    });
                }
                let rest: Vec<_> = nested.into_iter().filter(|nested| {
                    match *nested {
                        syn::MetaItem::Word(ref word) if word == "Serialize" => {
                            ser = true;
                            false
                        }
                        syn::MetaItem::Word(ref word) if word == "Deserialize" => {
                            de = true;
                            false
                        }
                        _ => true,
                    }
                }).collect();
                if rest.is_empty() {
                    None
                } else {
                    Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, rest),
                        is_sugared_doc: false,
                    })
                }
            }).collect(),
            ..item
        };
        (ser, de, item)
    }

    fn strip_serde_attrs(item: syn::MacroInput) -> syn::MacroInput {
        syn::MacroInput {
            attrs: strip_serde_from_attrs(item.attrs),
            body: match item.body {
                syn::Body::Enum(variants) => syn::Body::Enum(
                    variants.into_iter().map(|variant| {
                        syn::Variant {
                            ident: variant.ident,
                            attrs: strip_serde_from_attrs(variant.attrs),
                            data: strip_serde_from_variant_data(variant.data),
                            discriminant: variant.discriminant,
                        }
                    }).collect()
                ),
                syn::Body::Struct(variant_data) => syn::Body::Struct(
                    strip_serde_from_variant_data(variant_data)
                ),
            },
            ..item
        }
    }

    fn strip_serde_from_variant_data(data: syn::VariantData) -> syn::VariantData {
        match data {
            syn::VariantData::Struct(fields) => syn::VariantData::Struct(
                fields.into_iter().map(strip_serde_from_field).collect()
            ),
            syn::VariantData::Tuple(fields) => syn::VariantData::Tuple(
                fields.into_iter().map(strip_serde_from_field).collect()
            ),
            syn::VariantData::Unit => syn::VariantData::Unit,
        }
    }

    fn strip_serde_from_field(field: syn::Field) -> syn::Field {
        syn::Field {
            attrs: strip_serde_from_attrs(field.attrs),
            ..field
        }
    }

    fn strip_serde_from_attrs(attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
        attrs.into_iter().filter(|attr| {
            match attr.value {
                syn::MetaItem::List(ref ident, _) => ident != "serde",
                _ => true,
            }
        }).collect()
    }
}
<commit_msg>Not safe to share Spans from one ParseSess to another<commit_after>#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "clippy", allow(used_underscore_binding))]
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]

// The `quote!` macro requires deep recursion.
#![recursion_limit = "192"]

extern crate serde_codegen_internals as internals;

#[cfg(feature = "with-syntex")]
extern crate syntex;

#[cfg(feature = "with-syntex")]
#[macro_use]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

extern crate syn;
#[macro_use]
extern crate quote;

#[cfg(feature = "with-syntex")]
use std::path::Path;

#[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType;

mod bound;
mod de;
mod ser;

#[cfg(feature = "with-syntex")]
fn syntex_registry() -> syntex::Registry {
    use syntax::{ast, fold};

    /// Strip the serde attributes from the crate.
    #[cfg(feature = "with-syntex")]
    fn strip_attributes(krate: ast::Crate) -> ast::Crate {
        /// Helper folder that strips the serde attributes after the extensions have been expanded.
        struct StripAttributeFolder;

        impl fold::Folder for StripAttributeFolder {
            fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
                match attr.node.value.node {
                    ast::MetaItemKind::List(ref n, _) if n == &"serde" => { return None; }
                    _ => {}
                }

                Some(attr)
            }

            fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
                fold::noop_fold_mac(mac, self)
            }
        }

        fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
    }

    let mut reg = syntex::Registry::new();

    reg.add_attr("feature(custom_derive)");
    reg.add_attr("feature(custom_attribute)");

    reg.add_decorator("derive_Serialize", expand_derive_serialize);
    reg.add_decorator("derive_Deserialize", expand_derive_deserialize);

    reg.add_post_expansion_pass(strip_attributes);

    reg
}

#[cfg(feature = "with-syntex")]
pub fn expand_str(src: &str) -> Result<String, syntex::Error> {
    let src = src.to_owned();

    let expand_thread = move || {
        syntex_registry().expand_str("", "", &src)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>,
          D: AsRef<Path>,
{
    let src = src.as_ref().to_owned();
    let dst = dst.as_ref().to_owned();

    let expand_thread = move || {
        syntex_registry().expand("", src, dst)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(not(feature = "with-syntex"))]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Serialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_serialize)));

    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Deserialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_deserialize)));

    reg.register_attribute("serde".to_owned(), AttributeType::Normal);
}

macro_rules! shim {
    ($name:ident $pkg:ident :: $func:ident) => {
        fn $func(
            cx: &mut ::syntax::ext::base::ExtCtxt,
            span: ::syntax::codemap::Span,
            meta_item: &::syntax::ast::MetaItem,
            annotatable: &::syntax::ext::base::Annotatable,
            push: &mut FnMut(::syntax::ext::base::Annotatable)
        ) {
            let item = match *annotatable {
                ::syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        concat!("`#[derive(",
                                stringify!($name),
                                ")]` may only be applied to structs and enums"));
                    return;
                }
            };

            use syntax::{attr, ast, visit};
            struct MarkSerdeAttributesUsed;
            impl visit::Visitor for MarkSerdeAttributesUsed {
                fn visit_attribute(&mut self, attr: &ast::Attribute) {
                    match attr.node.value.node {
                        ast::MetaItemKind::List(ref name, _) if name == "serde" => {
                            attr::mark_used(attr);
                        }
                        _ => {}
                    }
                }
            }
            visit::walk_item(&mut MarkSerdeAttributesUsed, item);

            use syntax::print::pprust;
            let s = pprust::item_to_string(item);

            let syn_item = syn::parse_macro_input(&s).unwrap();
            let expanded = match $pkg::$func(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($name).to_string();
            let cfg = Vec::new();
            let sess = cx.parse_sess;
            let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, sess);
            push(::syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim!(Serialize ser::expand_derive_serialize);
shim!(Deserialize de::expand_derive_deserialize);

#[cfg(feature = "with-syn")]
pub fn expand_single_item(item: &str) -> Result<String, String> {
    let syn_item = syn::parse_macro_input(item).unwrap();
    let (ser, de, syn_item) = strip_serde_derives(syn_item);
    let expanded_ser = if ser {
        Some(try!(ser::expand_derive_serialize(&syn_item)))
    } else {
        None
    };
    let expanded_de = if de {
        Some(try!(de::expand_derive_deserialize(&syn_item)))
    } else {
        None::<quote::Tokens>
    };
    let syn_item = strip_serde_attrs(syn_item);
    return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string());

    fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) {
        let mut ser = false;
        let mut de = false;
        let item = syn::MacroInput {
            attrs: item.attrs.into_iter().flat_map(|attr| {
                if attr.is_sugared_doc || attr.style != syn::AttrStyle::Outer {
                    return Some(attr);
                }
                let (name, nested) = match attr.value {
                    syn::MetaItem::List(name, nested) => (name, nested),
                    _ => return Some(attr)
                };
                if name != "derive" {
                    return Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, nested),
                        is_sugared_doc: false,
                    });
                }
                let rest: Vec<_> = nested.into_iter().filter(|nested| {
                    match *nested {
                        syn::MetaItem::Word(ref word) if word == "Serialize" => {
                            ser = true;
                            false
                        }
                        syn::MetaItem::Word(ref word) if word == "Deserialize" => {
                            de = true;
                            false
                        }
                        _ => true,
                    }
                }).collect();
                if rest.is_empty() {
                    None
                } else {
                    Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, rest),
                        is_sugared_doc: false,
                    })
                }
            }).collect(),
            ..item
        };
        (ser, de, item)
    }

    fn strip_serde_attrs(item: syn::MacroInput) -> syn::MacroInput {
        syn::MacroInput {
            attrs: strip_serde_from_attrs(item.attrs),
            body: match item.body {
                syn::Body::Enum(variants) => syn::Body::Enum(
                    variants.into_iter().map(|variant| {
                        syn::Variant {
                            ident: variant.ident,
                            attrs: strip_serde_from_attrs(variant.attrs),
                            data: strip_serde_from_variant_data(variant.data),
                            discriminant: variant.discriminant,
                        }
                    }).collect()
                ),
                syn::Body::Struct(variant_data) => syn::Body::Struct(
                    strip_serde_from_variant_data(variant_data)
                ),
            },
            ..item
        }
    }

    fn strip_serde_from_variant_data(data: syn::VariantData) -> syn::VariantData {
        match data {
            syn::VariantData::Struct(fields) => syn::VariantData::Struct(
                fields.into_iter().map(strip_serde_from_field).collect()
            ),
            syn::VariantData::Tuple(fields) => syn::VariantData::Tuple(
                fields.into_iter().map(strip_serde_from_field).collect()
            ),
            syn::VariantData::Unit => syn::VariantData::Unit,
        }
    }

    fn strip_serde_from_field(field: syn::Field) -> syn::Field {
        syn::Field {
            attrs: strip_serde_from_attrs(field.attrs),
            ..field
        }
    }

    fn strip_serde_from_attrs(attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
        attrs.into_iter().filter(|attr| {
            match attr.value {
                syn::MetaItem::List(ref ident, _) => ident != "serde",
                _ => true,
            }
        }).collect()
    }
}
<|endoftext|>


<commit_before>use nom::{self, alphanumeric, IResult};
use std::str;

#[derive(Debug)]
pub enum Expr<'a> {
    StrLit(&'a str),
    Var(&'a str),
    Filter(&'a str, Box<Expr<'a>>),
    BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
}

#[derive(Debug)]
pub enum Target<'a> {
    Name(&'a str),
}

#[derive(Clone, Copy, Debug)]
pub struct WS(pub bool, pub bool);

#[derive(Debug)]
pub enum Node<'a> {
    Lit(&'a str, &'a str, &'a str),
    Expr(WS, Expr<'a>),
    Cond(Vec<(WS, Option<Expr<'a>>, Vec<Node<'a>>)>, WS),
    Loop(WS, Target<'a>, Expr<'a>, Vec<Node<'a>>, WS),
    Extends(Expr<'a>),
    BlockDef(WS, &'a str, Vec<Node<'a>>, WS),
    Block(WS, &'a str, WS),
}

pub type Cond<'a> = (WS, Option<Expr<'a>>, Vec<Node<'a>>);

fn split_ws_parts(s: &[u8]) -> Node {
    if s.is_empty() {
        let rs = str::from_utf8(s).unwrap();
        return Node::Lit(rs, rs, rs);
    }
    let is_ws = |c: &u8| {
        *c != b' ' && *c != b'\t' && *c != b'\r' && *c != b'\n'
    };
    let start = s.iter().position(&is_ws);
    let res = if start.is_none() {
            (s, &s[0..0], &s[0..0])
        } else {
            let start = start.unwrap();
            let end = s.iter().rposition(&is_ws);
            if end.is_none() {
                (&s[..start], &s[start..], &s[0..0])
            } else {
                let end = end.unwrap();
                (&s[..start], &s[start..end + 1], &s[end + 1..])
            }
        };
    Node::Lit(str::from_utf8(res.0).unwrap(),
              str::from_utf8(res.1).unwrap(),
              str::from_utf8(res.2).unwrap())
}

fn take_content(i: &[u8]) -> IResult<&[u8], Node> {
    if i.len() < 1 || i[0] == b'{' {
        return IResult::Error(error_position!(nom::ErrorKind::TakeUntil, i));
    }
    for (j, c) in i.iter().enumerate() {
        if *c == b'{' {
            if i.len() < j + 2 {
                return IResult::Done(&i[..0], split_ws_parts(&i[..]));
            } else if i[j + 1] == b'{' || i[j + 1] == b'%' {
                return IResult::Done(&i[j..], split_ws_parts(&i[..j]));
            }
        }
    }
    IResult::Done(&i[..0], split_ws_parts(&i[..]))
}

named!(expr_str_lit<Expr>, map!(
    delimited!(char!('"'), is_not!("\""), char!('"')),
    |s| Expr::StrLit(str::from_utf8(s).unwrap())
));

named!(expr_var<Expr>, map!(alphanumeric,
    |s| Expr::Var(str::from_utf8(s).unwrap())
));

named!(target_single<Target>, map!(alphanumeric,
    |s| Target::Name(str::from_utf8(s).unwrap())
));

fn expr_filtered(i: &[u8]) -> IResult<&[u8], Expr> {
    let (mut left, mut expr) = match expr_var(i) {
        IResult::Error(err) => { return IResult::Error(err); },
        IResult::Incomplete(needed) => { return IResult::Incomplete(needed); },
        IResult::Done(left, res) => (left, res),
    };
    while left[0] == b'|' {
        match alphanumeric(&left[1..]) {
            IResult::Error(err) => {
                return IResult::Error(err);
            },
            IResult::Incomplete(needed) => {
                return IResult::Incomplete(needed);
            },
            IResult::Done(new_left, res) => {
                left = new_left;
                expr = Expr::Filter(str::from_utf8(res).unwrap(), Box::new(expr));
            },
        };
    }
    IResult::Done(left, expr)
}

named!(expr_single<Expr>, alt!(
    expr_filtered |
    expr_str_lit
));

macro_rules! expr_prec_layer {
    ( $name:ident, $inner:ident, $( $op:expr ),* ) => {
        named!($name<Expr>, alt!(
            do_parse!(
                left: $inner >>
                op: ws!(alt!($( tag_s!($op) )|*)) >>
                right: $inner >>
                (Expr::BinOp(str::from_utf8(op).unwrap(),
                             Box::new(left), Box::new(right)))
            ) | $inner
        ));
    }
}

expr_prec_layer!(expr_muldivmod, expr_single, "*", "/", "%");
expr_prec_layer!(expr_addsub, expr_muldivmod, "+", "-");
expr_prec_layer!(expr_shifts, expr_addsub, ">>", "<<");
expr_prec_layer!(expr_band, expr_shifts, "&");
expr_prec_layer!(expr_bxor, expr_band, "^");
expr_prec_layer!(expr_bor, expr_bxor, "|");
expr_prec_layer!(expr_compare, expr_bor,
    "==", "!=", ">=", ">", "<=", "<"
);
expr_prec_layer!(expr_and, expr_compare, "&&");
expr_prec_layer!(expr_any, expr_and, "||");

named!(expr_node<Node>, do_parse!(
    tag_s!("{{") >>
    pws: opt!(tag_s!("-")) >>
    expr: ws!(expr_any) >>
    nws: opt!(tag_s!("-")) >>
    tag_s!("}}") >>
    (Node::Expr(WS(pws.is_some(), nws.is_some()), expr))
));

named!(cond_if<Expr>, do_parse!(
    ws!(tag_s!("if")) >>
    cond: ws!(expr_any) >>
    (cond)
));

named!(cond_block<Cond>, do_parse!(
    tag_s!("{%") >>
    pws: opt!(tag_s!("-")) >>
    ws!(tag_s!("else")) >>
    cond: opt!(cond_if) >>
    nws: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    (WS(pws.is_some(), nws.is_some()), cond, block)
));

named!(block_if<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    cond: ws!(cond_if) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    elifs: many0!(cond_block) >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endif")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    ({
       let mut res = Vec::new();
       res.push((WS(pws1.is_some(), nws1.is_some()), Some(cond), block));
       res.extend(elifs);
       Node::Cond(res, WS(pws2.is_some(), nws2.is_some()))
    })
));

named!(block_for<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    ws!(tag_s!("for")) >>
    var: ws!(target_single) >>
    ws!(tag_s!("in")) >>
    iter: ws!(expr_any) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endfor")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    (Node::Loop(WS(pws1.is_some(), nws1.is_some()),
                var, iter, block,
                WS(pws2.is_some(), pws2.is_some())))
));

named!(block_extends<Node>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("extends")) >>
    name: ws!(expr_str_lit) >>
    tag_s!("%}") >>
    (Node::Extends(name))
));

named!(block_block<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    ws!(tag_s!("block")) >>
    name: ws!(alphanumeric) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    contents: parse_template >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endblock")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    (Node::BlockDef(WS(pws1.is_some(), nws1.is_some()),
                    str::from_utf8(name).unwrap(), contents,
                    WS(pws2.is_some(), pws2.is_some())))
));

named!(parse_template<Vec<Node<'a>>>, many0!(alt!(
    take_content |
    expr_node |
    block_if |
    block_for |
    block_extends |
    block_block
)));

pub fn parse(src: &str) -> Vec<Node> {
    match parse_template(src.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("problems parsing template source: {}", err),
        IResult::Incomplete(_) => panic!("parsing incomplete"),
    }
}

#[cfg(test)]
mod tests {
    fn check_ws_split(s: &str, res: &(&str, &str, &str)) {
        let node = super::split_ws_parts(s.as_bytes());
        match node {
            super::Node::Lit(lws, s, rws) => {
                assert_eq!(lws, res.0);
                assert_eq!(s, res.1);
                assert_eq!(rws, res.2);
            },
            _ => { panic!("fail"); },
        }
    }
    #[test]
    fn test_ws_splitter() {
        check_ws_split("", &("", "", ""));
        check_ws_split("a", &("", "a", ""));
        check_ws_split("\ta", &("\t", "a", ""));
        check_ws_split("b\n", &("", "b", "\n"));
        check_ws_split(" \t\r\n", &(" \t\r\n", "", ""));
    }
}
<commit_msg>Use better parsing strategy for filter expressions<commit_after>use nom::{self, alphanumeric, IResult};
use std::str;

#[derive(Debug)]
pub enum Expr<'a> {
    StrLit(&'a str),
    Var(&'a str),
    Filter(&'a str, Box<Expr<'a>>),
    BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
}

#[derive(Debug)]
pub enum Target<'a> {
    Name(&'a str),
}

#[derive(Clone, Copy, Debug)]
pub struct WS(pub bool, pub bool);

#[derive(Debug)]
pub enum Node<'a> {
    Lit(&'a str, &'a str, &'a str),
    Expr(WS, Expr<'a>),
    Cond(Vec<(WS, Option<Expr<'a>>, Vec<Node<'a>>)>, WS),
    Loop(WS, Target<'a>, Expr<'a>, Vec<Node<'a>>, WS),
    Extends(Expr<'a>),
    BlockDef(WS, &'a str, Vec<Node<'a>>, WS),
    Block(WS, &'a str, WS),
}

pub type Cond<'a> = (WS, Option<Expr<'a>>, Vec<Node<'a>>);

fn split_ws_parts(s: &[u8]) -> Node {
    if s.is_empty() {
        let rs = str::from_utf8(s).unwrap();
        return Node::Lit(rs, rs, rs);
    }
    let is_ws = |c: &u8| {
        *c != b' ' && *c != b'\t' && *c != b'\r' && *c != b'\n'
    };
    let start = s.iter().position(&is_ws);
    let res = if start.is_none() {
            (s, &s[0..0], &s[0..0])
        } else {
            let start = start.unwrap();
            let end = s.iter().rposition(&is_ws);
            if end.is_none() {
                (&s[..start], &s[start..], &s[0..0])
            } else {
                let end = end.unwrap();
                (&s[..start], &s[start..end + 1], &s[end + 1..])
            }
        };
    Node::Lit(str::from_utf8(res.0).unwrap(),
              str::from_utf8(res.1).unwrap(),
              str::from_utf8(res.2).unwrap())
}

fn take_content(i: &[u8]) -> IResult<&[u8], Node> {
    if i.len() < 1 || i[0] == b'{' {
        return IResult::Error(error_position!(nom::ErrorKind::TakeUntil, i));
    }
    for (j, c) in i.iter().enumerate() {
        if *c == b'{' {
            if i.len() < j + 2 {
                return IResult::Done(&i[..0], split_ws_parts(&i[..]));
            } else if i[j + 1] == b'{' || i[j + 1] == b'%' {
                return IResult::Done(&i[j..], split_ws_parts(&i[..j]));
            }
        }
    }
    IResult::Done(&i[..0], split_ws_parts(&i[..]))
}

named!(expr_str_lit<Expr>, map!(
    delimited!(char!('"'), is_not!("\""), char!('"')),
    |s| Expr::StrLit(str::from_utf8(s).unwrap())
));

named!(expr_var<Expr>, map!(alphanumeric,
    |s| Expr::Var(str::from_utf8(s).unwrap())
));

named!(target_single<Target>, map!(alphanumeric,
    |s| Target::Name(str::from_utf8(s).unwrap())
));

named!(expr_single<Expr>, alt!(
    expr_str_lit |
    expr_var
));

named!(filter, do_parse!(
    tag_s!("|") >>
    fname: alphanumeric >>
    (fname)
));

named!(expr_filtered<Expr>, do_parse!(
    obj: expr_single >>
    filters: many0!(filter) >>
    ({
       let mut res = obj;
       for f in filters {
           let fname = str::from_utf8(f).unwrap();
           res = Expr::Filter(fname, Box::new(res));
       }
       res
    })
));

macro_rules! expr_prec_layer {
    ( $name:ident, $inner:ident, $( $op:expr ),* ) => {
        named!($name<Expr>, alt!(
            do_parse!(
                left: $inner >>
                op: ws!(alt!($( tag_s!($op) )|*)) >>
                right: $inner >>
                (Expr::BinOp(str::from_utf8(op).unwrap(),
                             Box::new(left), Box::new(right)))
            ) | $inner
        ));
    }
}

expr_prec_layer!(expr_muldivmod, expr_filtered, "*", "/", "%");
expr_prec_layer!(expr_addsub, expr_muldivmod, "+", "-");
expr_prec_layer!(expr_shifts, expr_addsub, ">>", "<<");
expr_prec_layer!(expr_band, expr_shifts, "&");
expr_prec_layer!(expr_bxor, expr_band, "^");
expr_prec_layer!(expr_bor, expr_bxor, "|");
expr_prec_layer!(expr_compare, expr_bor,
    "==", "!=", ">=", ">", "<=", "<"
);
expr_prec_layer!(expr_and, expr_compare, "&&");
expr_prec_layer!(expr_any, expr_and, "||");

named!(expr_node<Node>, do_parse!(
    tag_s!("{{") >>
    pws: opt!(tag_s!("-")) >>
    expr: ws!(expr_any) >>
    nws: opt!(tag_s!("-")) >>
    tag_s!("}}") >>
    (Node::Expr(WS(pws.is_some(), nws.is_some()), expr))
));

named!(cond_if<Expr>, do_parse!(
    ws!(tag_s!("if")) >>
    cond: ws!(expr_any) >>
    (cond)
));

named!(cond_block<Cond>, do_parse!(
    tag_s!("{%") >>
    pws: opt!(tag_s!("-")) >>
    ws!(tag_s!("else")) >>
    cond: opt!(cond_if) >>
    nws: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    (WS(pws.is_some(), nws.is_some()), cond, block)
));

named!(block_if<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    cond: ws!(cond_if) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    elifs: many0!(cond_block) >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endif")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    ({
       let mut res = Vec::new();
       res.push((WS(pws1.is_some(), nws1.is_some()), Some(cond), block));
       res.extend(elifs);
       Node::Cond(res, WS(pws2.is_some(), nws2.is_some()))
    })
));

named!(block_for<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    ws!(tag_s!("for")) >>
    var: ws!(target_single) >>
    ws!(tag_s!("in")) >>
    iter: ws!(expr_any) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    block: parse_template >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endfor")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    (Node::Loop(WS(pws1.is_some(), nws1.is_some()),
                var, iter, block,
                WS(pws2.is_some(), pws2.is_some())))
));

named!(block_extends<Node>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("extends")) >>
    name: ws!(expr_str_lit) >>
    tag_s!("%}") >>
    (Node::Extends(name))
));

named!(block_block<Node>, do_parse!(
    tag_s!("{%") >>
    pws1: opt!(tag_s!("-")) >>
    ws!(tag_s!("block")) >>
    name: ws!(alphanumeric) >>
    nws1: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    contents: parse_template >>
    tag_s!("{%") >>
    pws2: opt!(tag_s!("-")) >>
    ws!(tag_s!("endblock")) >>
    nws2: opt!(tag_s!("-")) >>
    tag_s!("%}") >>
    (Node::BlockDef(WS(pws1.is_some(), nws1.is_some()),
                    str::from_utf8(name).unwrap(), contents,
                    WS(pws2.is_some(), pws2.is_some())))
));

named!(parse_template<Vec<Node<'a>>>, many0!(alt!(
    take_content |
    expr_node |
    block_if |
    block_for |
    block_extends |
    block_block
)));

pub fn parse(src: &str) -> Vec<Node> {
    match parse_template(src.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("problems parsing template source: {}", err),
        IResult::Incomplete(_) => panic!("parsing incomplete"),
    }
}

#[cfg(test)]
mod tests {
    fn check_ws_split(s: &str, res: &(&str, &str, &str)) {
        let node = super::split_ws_parts(s.as_bytes());
        match node {
            super::Node::Lit(lws, s, rws) => {
                assert_eq!(lws, res.0);
                assert_eq!(s, res.1);
                assert_eq!(rws, res.2);
            },
            _ => { panic!("fail"); },
        }
    }
    #[test]
    fn test_ws_splitter() {
        check_ws_split("", &("", "", ""));
        check_ws_split("a", &("", "a", ""));
        check_ws_split("\ta", &("\t", "a", ""));
        check_ws_split("b\n", &("", "b", "\n"));
        check_ws_split(" \t\r\n", &(" \t\r\n", "", ""));
    }
}
<|endoftext|>


<commit_before>pub mod simple {
    use ::front::ast;

    pub fn function(fun: &ast::Node<ast::Function>) -> String {
        // arguments
        let args = fun.node
            .args
            .iter()
            .map(|ref arg| format!("{:?} {}", arg.type_, arg.name))
            .collect::<Vec<String>>()
            .join(", ");

        format!("{}:{}\t{} ({}) -> {:?}",
                fun.node.filename,
                fun.pos.line,
                fun.node.name,
                args,
                fun.node.ret_type)
    }

    pub fn statement(stmt: &ast::Node<ast::Statement>) -> String {
        match stmt.node {
            ast::Statement::Expression { .. } => "Expression".to_owned(),
            ast::Statement::Declaration { .. } => "Declaration".to_owned(),
            ast::Statement::Assignment { .. } => "Assignment".to_owned(),
            ast::Statement::If { .. } => "If".to_owned(),
            ast::Statement::While { .. } => "While".to_owned(),
            ast::Statement::Return { .. } => "Return".to_owned(),
            ast::Statement::Compound { .. } => "Compound".to_owned(),
        }
    }

    pub fn expression(expr: &ast::Node<ast::Expression>) -> String {
        match expr.node {
            ast::Expression::Literal { ref lit } => literal(lit),
            ast::Expression::Variable { ref var } => variable(var.as_ref()),
            ast::Expression::Call { ref function, .. } => format!("{}(..)", function),
            ast::Expression::Unary { ref op, .. } => format!("{:?} ", op),
            ast::Expression::Binary { ref op, .. } => format!(" {:?} ", op),
            ast::Expression::Parenthesis { .. } => "(  )".to_owned(),
        }
    }

    pub fn literal(lit: &ast::Literal) -> String {
        match *lit {
            ast::Literal::Bool(ref v) => format!("{}", v).to_owned(),
            ast::Literal::Int(ref v) => format!("{}", v).to_owned(),
            ast::Literal::Float(ref v) => format!("{}f", v).to_owned(),
            ast::Literal::Str(ref v) => format!("\"{}\"", v).to_owned(),
        }
    }

    pub fn variable(var: &ast::Variable) -> String {
        format!("{}_{} ({:?})", var.id, var.name, var.type_)
    }
}

pub mod dot {
    use ::front::ast;
    use super::simple;

    pub fn function(fun: &ast::Node<ast::Function>) -> String {
        let options = vec!["\tnodesep=0.6".to_owned()];
        format!("digraph \"{}_{}\" {{\n{}\n{}{}{}}}\n",
                fun.node.filename,
                fun.node.name,
                options.join("\n"),
                node(fun, &simple::function(fun)),
                edge(fun, &fun.node.body, "body"),
                statement(&fun.node.body))
    }

    fn statement(stmt: &ast::Node<ast::Statement>) -> String {
        let mut ret = node(stmt, &simple::statement(stmt));
        match stmt.node {
            ast::Statement::Expression { ref expr } => {
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::Declaration { ref var } => {
                ret.push_str(&node_var(var));
                ret.push_str(&edge_to_var(stmt, var))
            }
            ast::Statement::Assignment { ref var, ref expr } => {
                ret.push_str(&node_var(var));
                ret.push_str(&edge_to_var(stmt, var));
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::If { ref cond, ref on_true, ref on_false } => {
                ret.push_str(&expression(cond));
                ret.push_str(&edge(stmt, cond, "cond"));
                ret.push_str(&statement(on_true));
                ret.push_str(&edge(stmt, on_true, "on_true"));
                if let Some(ref else_part) = *on_false {
                    ret.push_str(&statement(else_part));
                    ret.push_str(&edge(stmt, else_part, "on_false"));
                }
            }
            ast::Statement::While { ref cond, ref body } => {
                ret.push_str(&expression(cond));
                ret.push_str(&edge(stmt, cond, "cond"));
                ret.push_str(&statement(body));
                ret.push_str(&edge(stmt, body, "body"));
            }
            ast::Statement::Return { expr: Some(ref expr) } => {
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::Compound { ref stmts, .. } => {
                for (i, s) in stmts.iter().enumerate() {
                    ret.push_str(&statement(s));
                    ret.push_str(&edge(stmt, s, &*i.to_string()));
                }
            }
            _ => (),
        }
        ret
    }

    fn expression(expr: &ast::Node<ast::Expression>) -> String {
        let mut ret = node(expr, &simple::expression(expr));
        match expr.node {
            ast::Expression::Call { ref args, .. } => {
                for (i, arg) in args.iter().enumerate() {
                    ret.push_str(&expression(arg));
                    ret.push_str(&edge(expr, arg, &*i.to_string()));
                }
            }
            ast::Expression::Unary { expr: ref e, .. } |
            ast::Expression::Parenthesis { expr: ref e } => {
                ret.push_str(&expression(e));
                ret.push_str(&edge(expr, e, "expr"));
            }
            ast::Expression::Binary { ref left, ref right, .. } => {
                ret.push_str(&expression(left));
                ret.push_str(&edge(expr, left, "left"));
                ret.push_str(&expression(right));
                ret.push_str(&edge(expr, right, "right"));
            }
            _ => (),
        }
        ret
    }

    fn node<N>(node: &ast::Node<N>, label: &str) -> String {
        format!("\t\"{:p}\" [shape=box, label=\"{}\", fontname=\"Monospace\"];\n",
                node,
                label)
    }

    fn node_var(var: &ast::Variable) -> String {
        format!("\t\"{:p}\" [shape=box, label=\"{}\"];\n",
                var,
                simple::variable(var))
    }

    fn edge<N, M>(from: &ast::Node<N>, to: &ast::Node<M>, label: &str) -> String {
        format!("\t\"{:p}\" -> \"{:p}\" [label=\"{}\"];\n", from, to, label)
    }

    fn edge_to_var<N>(from: &ast::Node<N>, to: &ast::Variable) -> String {
        format!("\t\"{:p}\" -> \"{:p}\" [label=\"var\"];\n", from, to)
    }
}
<commit_msg>Diag: Simplify literal printing logic<commit_after>pub mod simple {
    use ::front::ast;

    pub fn function(fun: &ast::Node<ast::Function>) -> String {
        // arguments
        let args = fun.node
            .args
            .iter()
            .map(|ref arg| format!("{:?} {}", arg.type_, arg.name))
            .collect::<Vec<String>>()
            .join(", ");

        format!("{}:{}\t{} ({}) -> {:?}",
                fun.node.filename,
                fun.pos.line,
                fun.node.name,
                args,
                fun.node.ret_type)
    }

    pub fn statement(stmt: &ast::Node<ast::Statement>) -> String {
        match stmt.node {
            ast::Statement::Expression { .. } => "Expression".to_owned(),
            ast::Statement::Declaration { .. } => "Declaration".to_owned(),
            ast::Statement::Assignment { .. } => "Assignment".to_owned(),
            ast::Statement::If { .. } => "If".to_owned(),
            ast::Statement::While { .. } => "While".to_owned(),
            ast::Statement::Return { .. } => "Return".to_owned(),
            ast::Statement::Compound { .. } => "Compound".to_owned(),
        }
    }

    pub fn expression(expr: &ast::Node<ast::Expression>) -> String {
        match expr.node {
            ast::Expression::Literal { ref lit } => literal(lit),
            ast::Expression::Variable { ref var } => variable(var.as_ref()),
            ast::Expression::Call { ref function, .. } => format!("{}(..)", function),
            ast::Expression::Unary { ref op, .. } => format!("{:?} ", op),
            ast::Expression::Binary { ref op, .. } => format!(" {:?} ", op),
            ast::Expression::Parenthesis { .. } => "(  )".to_owned(),
        }
    }

    pub fn literal(lit: &ast::Literal) -> String {
        match *lit {
            ast::Literal::Bool(ref v) => v.to_string(),
            ast::Literal::Int(ref v) => v.to_string(),
            ast::Literal::Float(ref v) => format!("{}f", v),
            ast::Literal::Str(ref v) => format!("\"{}\"", v),
        }
    }

    pub fn variable(var: &ast::Variable) -> String {
        format!("{}_{} ({:?})", var.id, var.name, var.type_)
    }
}

pub mod dot {
    use ::front::ast;
    use super::simple;

    pub fn function(fun: &ast::Node<ast::Function>) -> String {
        let options = vec!["\tnodesep=0.6".to_owned()];
        format!("digraph \"{}_{}\" {{\n{}\n{}{}{}}}\n",
                fun.node.filename,
                fun.node.name,
                options.join("\n"),
                node(fun, &simple::function(fun)),
                edge(fun, &fun.node.body, "body"),
                statement(&fun.node.body))
    }

    fn statement(stmt: &ast::Node<ast::Statement>) -> String {
        let mut ret = node(stmt, &simple::statement(stmt));
        match stmt.node {
            ast::Statement::Expression { ref expr } => {
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::Declaration { ref var } => {
                ret.push_str(&node_var(var));
                ret.push_str(&edge_to_var(stmt, var))
            }
            ast::Statement::Assignment { ref var, ref expr } => {
                ret.push_str(&node_var(var));
                ret.push_str(&edge_to_var(stmt, var));
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::If { ref cond, ref on_true, ref on_false } => {
                ret.push_str(&expression(cond));
                ret.push_str(&edge(stmt, cond, "cond"));
                ret.push_str(&statement(on_true));
                ret.push_str(&edge(stmt, on_true, "on_true"));
                if let Some(ref else_part) = *on_false {
                    ret.push_str(&statement(else_part));
                    ret.push_str(&edge(stmt, else_part, "on_false"));
                }
            }
            ast::Statement::While { ref cond, ref body } => {
                ret.push_str(&expression(cond));
                ret.push_str(&edge(stmt, cond, "cond"));
                ret.push_str(&statement(body));
                ret.push_str(&edge(stmt, body, "body"));
            }
            ast::Statement::Return { expr: Some(ref expr) } => {
                ret.push_str(&expression(expr));
                ret.push_str(&edge(stmt, expr, "expr"));
            }
            ast::Statement::Compound { ref stmts, .. } => {
                for (i, s) in stmts.iter().enumerate() {
                    ret.push_str(&statement(s));
                    ret.push_str(&edge(stmt, s, &*i.to_string()));
                }
            }
            _ => (),
        }
        ret
    }

    fn expression(expr: &ast::Node<ast::Expression>) -> String {
        let mut ret = node(expr, &simple::expression(expr));
        match expr.node {
            ast::Expression::Call { ref args, .. } => {
                for (i, arg) in args.iter().enumerate() {
                    ret.push_str(&expression(arg));
                    ret.push_str(&edge(expr, arg, &*i.to_string()));
                }
            }
            ast::Expression::Unary { expr: ref e, .. } |
            ast::Expression::Parenthesis { expr: ref e } => {
                ret.push_str(&expression(e));
                ret.push_str(&edge(expr, e, "expr"));
            }
            ast::Expression::Binary { ref left, ref right, .. } => {
                ret.push_str(&expression(left));
                ret.push_str(&edge(expr, left, "left"));
                ret.push_str(&expression(right));
                ret.push_str(&edge(expr, right, "right"));
            }
            _ => (),
        }
        ret
    }

    fn node<N>(node: &ast::Node<N>, label: &str) -> String {
        format!("\t\"{:p}\" [shape=box, label=\"{}\", fontname=\"Monospace\"];\n",
                node,
                label)
    }

    fn node_var(var: &ast::Variable) -> String {
        format!("\t\"{:p}\" [shape=box, label=\"{}\"];\n",
                var,
                simple::variable(var))
    }

    fn edge<N, M>(from: &ast::Node<N>, to: &ast::Node<M>, label: &str) -> String {
        format!("\t\"{:p}\" -> \"{:p}\" [label=\"{}\"];\n", from, to, label)
    }

    fn edge_to_var<N>(from: &ast::Node<N>, to: &ast::Variable) -> String {
        format!("\t\"{:p}\" -> \"{:p}\" [label=\"var\"];\n", from, to)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::str::FromStr;
use syntax::{ast, ext};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::{attr, codemap};
use syntax::parse::token;
use syntax::ptr::P;

/// A component modifier.
#[deriving(PartialEq)]
enum Modifier {
    /// Corresponds to the `#[normalized]` attribute.
    ///
    /// Normalizes the component at runtime. Unsigned integers are normalized to
    /// `[0, 1]`. Signed integers are normalized to `[-1, 1]`.
    Normalized,
    /// Corresponds to the `#[as_float]` attribute.
    ///
    /// Casts the component to a float precision floating-point number at runtime.
    AsFloat,
    /// Corresponds to the `#[as_double]` attribute.
    ///
    /// Specifies a high-precision float.
    AsDouble,
}

impl fmt::Show for Modifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Modifier::Normalized => write!(f, "normalized"),
            Modifier::AsFloat => write!(f, "as_float"),
            Modifier::AsDouble => write!(f, "as_double"),
        }
    }
}

impl FromStr for Modifier {
    fn from_str(src: &str) -> Option<Modifier> {
        match src {
            "normalized" => Some(Modifier::Normalized),
            "as_float" => Some(Modifier::AsFloat),
            "as_double" => Some(Modifier::AsDouble),
            _ => None,
        }
    }
}

/// Scan through the field's attributes and extract a relevant modifier. If
/// multiple modifier attributes are found, use the first modifier and emit a
/// warning.
fn find_modifier(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                 attributes: &[ast::Attribute]) -> Option<Modifier> {
    attributes.iter().fold(None, |modifier, attribute| {
        match attribute.node.value.node {
            ast::MetaWord(ref word) => {
                from_str(word.get()).and_then(|new_modifier| {
                    attr::mark_used(attribute);
                    modifier.map_or(Some(new_modifier), |modifier| {
                        cx.span_warn(span, format!(
                            "Extra attribute modifier detected: `#[{}]` - \
                            ignoring in favour of `#[{}]`.", new_modifier, modifier
                        ).as_slice());
                        None
                    })
                }).or(modifier)
            },
            _ => modifier,
        }
    })
}

/// Find a `gfx::attrib::Type` that describes the given type identifier.
fn decode_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
               ty_ident: &ast::Ident, modifier: Option<Modifier>,
               path_root: ast::Ident) -> P<ast::Expr> {
    let ty_str = ty_ident.name.as_str();
    match ty_str {
        "f32" | "f64" => {
            let kind = cx.ident_of(match modifier {
                None | Some(Modifier::AsFloat) => "FloatSubType::Default",
                Some(Modifier::AsDouble) => "FloatSubType::Precision",
                Some(Modifier::Normalized) => {
                    cx.span_warn(span, format!(
                        "Incompatible float modifier attribute: `#[{}]`", modifier
                    ).as_slice());
                    ""
                }
            });
            let sub_type = cx.ident_of(format!("F{}", ty_str.slice_from(1)).as_slice());
            quote_expr!(cx, $path_root::gfx::attrib::Type::Float($path_root::gfx::attrib::$kind,
                                                                 $path_root::gfx::attrib::$sub_type))
        },
        "u8" | "u16" | "u32" | "u64" |
        "i8" | "i16" | "i32" | "i64" => {
            let sign = cx.ident_of({
                if ty_str.starts_with("i") { "SignFlag::Signed" } else { "SignFlag::Unsigned" }
            });
            let kind = cx.ident_of(match modifier {
                None => "IntSubType::Raw",
                Some(Modifier::Normalized) => "IntSubType::Normalized",
                Some(Modifier::AsFloat) => "IntSubType::AsFloat",
                Some(Modifier::AsDouble) => {
                    cx.span_warn(span, format!(
                        "Incompatible int modifier attribute: `#[{}]`", modifier
                    ).as_slice());
                    ""
                }
            });
            let sub_type = cx.ident_of(format!("U{}", ty_str.slice_from(1)).as_slice());
            quote_expr!(cx, $path_root::gfx::attrib::Int($path_root::gfx::attrib::$kind,
                                                         $path_root::gfx::attrib::$sub_type,
                                                         $path_root::gfx::attrib::$sign))
        },
        "uint" | "int" => {
            cx.span_err(span, format!("Pointer-sized integer components are \
                                      not supported, but found: `{}`. Use an \
                                      integer component with an explicit size \
                                      instead.", ty_str).as_slice());
            cx.expr_tuple(span, vec![])
        },
        ty_str => {
            cx.span_err(span, format!("Unrecognized component type: `{}`",
                                      ty_str).as_slice());
            cx.expr_tuple(span, vec![])
        },
    }
}

fn decode_count_and_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                         field: &ast::StructField,
                         path_root: ast::Ident) -> (P<ast::Expr>, P<ast::Expr>) {
    let modifier = find_modifier(cx, span, field.node.attrs.as_slice());
    match field.node.ty.node {
        ast::TyPath(ref p, _, _) => (
            cx.expr_lit(span, ast::LitInt(1, ast::UnsuffixedIntLit(ast::Plus))),
            decode_type(cx, span, &p.segments[0].identifier, modifier, path_root),
        ),
        ast::TyFixedLengthVec(ref pty, ref expr) => (expr.clone(), match pty.node {
            ast::TyPath(ref p, _, _) => {
                decode_type(cx, span, &p.segments[0].identifier, modifier, path_root)
            },
            _ => {
                cx.span_err(span, format!("Unsupported fixed vector sub-type: \
                                          `{}`",pty.node).as_slice());
                cx.expr_tuple(span, vec![])
            },
        }),
        _ => {
            cx.span_err(span, format!("Unsupported attribute type: `{}`",
                                      field.node.ty.node).as_slice());
            (cx.expr_tuple(span, vec![]), cx.expr_tuple(span, vec![]))
        },
    }
}

/// Generates the the method body for `gfx::VertexFormat::generate`.
fn method_body(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                   substr: &generic::Substructure,
                   path_root: ast::Ident) -> P<ast::Expr> {
    match *substr.fields {
        generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let attribute_pushes = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let struct_ident = substr.type_ident;
                    let buffer_expr = &substr.nonself_args[1];
                    let (count_expr, type_expr) = decode_count_and_type(cx, span, def, path_root);
                    let ident_str = match super::find_name(cx, span, def.node.attrs.as_slice()) {
                        Some(name) => name,
                        None => token::get_ident(ident),
                    };
                    let ident_str = ident_str.get();
                    let instance_expr = cx.expr_u8(span, 0); // not supposed to be set by the macro
                    quote_expr!(cx, {
                        attributes.push($path_root::gfx::Attribute {
                            name: $ident_str.to_string(),
                            buffer: $buffer_expr,
                            format: $path_root::gfx::attrib::Format {
                                elem_count: $count_expr,
                                elem_type: $type_expr,
                                offset: unsafe {
                                    let x: $struct_ident = ::std::mem::uninitialized();
                                    let offset = (&x.$ident as *const _ as uint) -
                                        (&x as *const _ as uint);
                                    ::std::mem::forget(x);
                                    offset as $path_root::gfx::attrib::Offset
                                },
                                stride: { use std::mem;
                                    mem::size_of::<$struct_ident>() as
                                        $path_root::gfx::attrib::Stride
                                },
                                instance_rate: $instance_expr,
                            }
                        });
                    })
                }).collect::<Vec<P<ast::Expr>>>();
            let capacity = fields.len();
            quote_expr!(cx, {
                let mut attributes = Vec::with_capacity($capacity);
                $attribute_pushes;
                attributes
            })
        },
        _ => {
            cx.span_err(span, "Unable to implement `gfx::VertexFormat::generate` \
                              on a non-structure");
            cx.expr_tuple(span, vec![])
        }
    }
}

/// Derive a `gfx::VertexFormat` implementation for the `struct`
pub fn expand(context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: |P<ast::Item>|) {
    // Insert the `gfx` reexport module
    let path_root = super::extern_crate_hack(context, span, |i| push(i));
    let fixup = |item| {
        push(super::fixup_extern_crate_paths(item, path_root))
    };

    // `impl gfx::VertexFormat for $item`
    generic::TraitDef {
        span: span,
        attributes: Vec::new(),
        path: generic::ty::Path {
            path: vec![super::EXTERN_CRATE_HACK, "gfx", "VertexFormat"],
            lifetime: None,
            params: Vec::new(),
            global: true,
        },
        additional_bounds: Vec::new(),
        generics: generic::ty::LifetimeBounds::empty(),
        methods: vec![
            // `fn generate(Option<Self>, gfx::RawBufferHandle) -> Vec<gfx::Attribute>`
            generic::MethodDef {
                name: "generate",
                generics: generic::ty::LifetimeBounds::empty(),
                explicit_self: None,
                args: vec![
                    generic::ty::Literal(generic::ty::Path {
                        path: vec!["Option"],
                        lifetime: None,
                        params: vec![box generic::ty::Self],
                        global: false,
                    }),
                    generic::ty::Literal(generic::ty::Path::new(
                        vec![super::EXTERN_CRATE_HACK, "gfx", "RawBufferHandle"]
                    )),
                ],
                ret_ty: generic::ty::Literal(
                    generic::ty::Path {
                        path: vec!["Vec"],
                        lifetime: None,
                        params: vec![
                            box generic::ty::Literal(generic::ty::Path::new(
                                vec![super::EXTERN_CRATE_HACK, "gfx", "Attribute"])),
                        ],
                        global: false,
                    },
                ),
                attributes: Vec::new(),
                // generate the method body
                combine_substructure: generic::combine_substructure(
                    |c, s, ss| method_body(c, s, ss, path_root)),
            },
        ],
    }.expand(context, meta_item, item, fixup);
}
<commit_msg>gfx_macros fix with namespaced enums<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::str::FromStr;
use syntax::{ast, ext};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::{attr, codemap};
use syntax::parse::token;
use syntax::ptr::P;

/// A component modifier.
#[deriving(PartialEq)]
enum Modifier {
    /// Corresponds to the `#[normalized]` attribute.
    ///
    /// Normalizes the component at runtime. Unsigned integers are normalized to
    /// `[0, 1]`. Signed integers are normalized to `[-1, 1]`.
    Normalized,
    /// Corresponds to the `#[as_float]` attribute.
    ///
    /// Casts the component to a float precision floating-point number at runtime.
    AsFloat,
    /// Corresponds to the `#[as_double]` attribute.
    ///
    /// Specifies a high-precision float.
    AsDouble,
}

impl fmt::Show for Modifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Modifier::Normalized => write!(f, "normalized"),
            Modifier::AsFloat => write!(f, "as_float"),
            Modifier::AsDouble => write!(f, "as_double"),
        }
    }
}

impl FromStr for Modifier {
    fn from_str(src: &str) -> Option<Modifier> {
        match src {
            "normalized" => Some(Modifier::Normalized),
            "as_float" => Some(Modifier::AsFloat),
            "as_double" => Some(Modifier::AsDouble),
            _ => None,
        }
    }
}

/// Scan through the field's attributes and extract a relevant modifier. If
/// multiple modifier attributes are found, use the first modifier and emit a
/// warning.
fn find_modifier(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                 attributes: &[ast::Attribute]) -> Option<Modifier> {
    attributes.iter().fold(None, |modifier, attribute| {
        match attribute.node.value.node {
            ast::MetaWord(ref word) => {
                from_str(word.get()).and_then(|new_modifier| {
                    attr::mark_used(attribute);
                    modifier.map_or(Some(new_modifier), |modifier| {
                        cx.span_warn(span, format!(
                            "Extra attribute modifier detected: `#[{}]` - \
                            ignoring in favour of `#[{}]`.", new_modifier, modifier
                        ).as_slice());
                        None
                    })
                }).or(modifier)
            },
            _ => modifier,
        }
    })
}

/// Find a `gfx::attrib::Type` that describes the given type identifier.
fn decode_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
               ty_ident: &ast::Ident, modifier: Option<Modifier>,
               path_root: ast::Ident) -> P<ast::Expr> {
    let ty_str = ty_ident.name.as_str();
    match ty_str {
        "f32" | "f64" => {
            let kind = cx.ident_of(match modifier {
                None | Some(Modifier::AsFloat) => "Default",
                Some(Modifier::AsDouble) => "Precision",
                Some(Modifier::Normalized) => {
                    cx.span_warn(span, format!(
                        "Incompatible float modifier attribute: `#[{}]`", modifier
                    ).as_slice());
                    ""
                }
            });
            let sub_type = cx.ident_of(format!("F{}", ty_str.slice_from(1)).as_slice());
            quote_expr!(cx, $path_root::gfx::attrib::Type::Float($path_root::gfx::attrib::FloatSubType::$kind,
                                                                 $path_root::gfx::attrib::$sub_type))
        },
        "u8" | "u16" | "u32" | "u64" |
        "i8" | "i16" | "i32" | "i64" => {
            let sign = cx.ident_of({
                if ty_str.starts_with("i") { "Signed" } else { "Unsigned" }
            });
            let kind = cx.ident_of(match modifier {
                None => "IntSubType::Raw",
                Some(Modifier::Normalized) => "Normalized",
                Some(Modifier::AsFloat) => "AsFloat",
                Some(Modifier::AsDouble) => {
                    cx.span_warn(span, format!(
                        "Incompatible int modifier attribute: `#[{}]`", modifier
                    ).as_slice());
                    ""
                }
            });
            let sub_type = cx.ident_of(format!("U{}", ty_str.slice_from(1)).as_slice());
            quote_expr!(cx, $path_root::gfx::attrib::Int($path_root::gfx::attrib::IntSubType::$kind,
                                                         $path_root::gfx::attrib::$sub_type,
                                                         $path_root::gfx::attrib::SignFlag::$sign))
        },
        "uint" | "int" => {
            cx.span_err(span, format!("Pointer-sized integer components are \
                                      not supported, but found: `{}`. Use an \
                                      integer component with an explicit size \
                                      instead.", ty_str).as_slice());
            cx.expr_tuple(span, vec![])
        },
        ty_str => {
            cx.span_err(span, format!("Unrecognized component type: `{}`",
                                      ty_str).as_slice());
            cx.expr_tuple(span, vec![])
        },
    }
}

fn decode_count_and_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                         field: &ast::StructField,
                         path_root: ast::Ident) -> (P<ast::Expr>, P<ast::Expr>) {
    let modifier = find_modifier(cx, span, field.node.attrs.as_slice());
    match field.node.ty.node {
        ast::TyPath(ref p, _, _) => (
            cx.expr_lit(span, ast::LitInt(1, ast::UnsuffixedIntLit(ast::Plus))),
            decode_type(cx, span, &p.segments[0].identifier, modifier, path_root),
        ),
        ast::TyFixedLengthVec(ref pty, ref expr) => (expr.clone(), match pty.node {
            ast::TyPath(ref p, _, _) => {
                decode_type(cx, span, &p.segments[0].identifier, modifier, path_root)
            },
            _ => {
                cx.span_err(span, format!("Unsupported fixed vector sub-type: \
                                          `{}`",pty.node).as_slice());
                cx.expr_tuple(span, vec![])
            },
        }),
        _ => {
            cx.span_err(span, format!("Unsupported attribute type: `{}`",
                                      field.node.ty.node).as_slice());
            (cx.expr_tuple(span, vec![]), cx.expr_tuple(span, vec![]))
        },
    }
}

/// Generates the the method body for `gfx::VertexFormat::generate`.
fn method_body(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
                   substr: &generic::Substructure,
                   path_root: ast::Ident) -> P<ast::Expr> {
    match *substr.fields {
        generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let attribute_pushes = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let struct_ident = substr.type_ident;
                    let buffer_expr = &substr.nonself_args[1];
                    let (count_expr, type_expr) = decode_count_and_type(cx, span, def, path_root);
                    let ident_str = match super::find_name(cx, span, def.node.attrs.as_slice()) {
                        Some(name) => name,
                        None => token::get_ident(ident),
                    };
                    let ident_str = ident_str.get();
                    let instance_expr = cx.expr_u8(span, 0); // not supposed to be set by the macro
                    quote_expr!(cx, {
                        attributes.push($path_root::gfx::Attribute {
                            name: $ident_str.to_string(),
                            buffer: $buffer_expr,
                            format: $path_root::gfx::attrib::Format {
                                elem_count: $count_expr,
                                elem_type: $type_expr,
                                offset: unsafe {
                                    let x: $struct_ident = ::std::mem::uninitialized();
                                    let offset = (&x.$ident as *const _ as uint) -
                                        (&x as *const _ as uint);
                                    ::std::mem::forget(x);
                                    offset as $path_root::gfx::attrib::Offset
                                },
                                stride: { use std::mem;
                                    mem::size_of::<$struct_ident>() as
                                        $path_root::gfx::attrib::Stride
                                },
                                instance_rate: $instance_expr,
                            }
                        });
                    })
                }).collect::<Vec<P<ast::Expr>>>();
            let capacity = fields.len();
            quote_expr!(cx, {
                let mut attributes = Vec::with_capacity($capacity);
                $attribute_pushes;
                attributes
            })
        },
        _ => {
            cx.span_err(span, "Unable to implement `gfx::VertexFormat::generate` \
                              on a non-structure");
            cx.expr_tuple(span, vec![])
        }
    }
}

/// Derive a `gfx::VertexFormat` implementation for the `struct`
pub fn expand(context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: |P<ast::Item>|) {
    // Insert the `gfx` reexport module
    let path_root = super::extern_crate_hack(context, span, |i| push(i));
    let fixup = |item| {
        push(super::fixup_extern_crate_paths(item, path_root))
    };

    // `impl gfx::VertexFormat for $item`
    generic::TraitDef {
        span: span,
        attributes: Vec::new(),
        path: generic::ty::Path {
            path: vec![super::EXTERN_CRATE_HACK, "gfx", "VertexFormat"],
            lifetime: None,
            params: Vec::new(),
            global: true,
        },
        additional_bounds: Vec::new(),
        generics: generic::ty::LifetimeBounds::empty(),
        methods: vec![
            // `fn generate(Option<Self>, gfx::RawBufferHandle) -> Vec<gfx::Attribute>`
            generic::MethodDef {
                name: "generate",
                generics: generic::ty::LifetimeBounds::empty(),
                explicit_self: None,
                args: vec![
                    generic::ty::Literal(generic::ty::Path {
                        path: vec!["Option"],
                        lifetime: None,
                        params: vec![box generic::ty::Self],
                        global: false,
                    }),
                    generic::ty::Literal(generic::ty::Path::new(
                        vec![super::EXTERN_CRATE_HACK, "gfx", "RawBufferHandle"]
                    )),
                ],
                ret_ty: generic::ty::Literal(
                    generic::ty::Path {
                        path: vec!["Vec"],
                        lifetime: None,
                        params: vec![
                            box generic::ty::Literal(generic::ty::Path::new(
                                vec![super::EXTERN_CRATE_HACK, "gfx", "Attribute"])),
                        ],
                        global: false,
                    },
                ),
                attributes: Vec::new(),
                // generate the method body
                combine_substructure: generic::combine_substructure(
                    |c, s, ss| method_body(c, s, ss, path_root)),
            },
        ],
    }.expand(context, meta_item, item, fixup);
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fs;
use std::io;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use std::str;
use std::sync::{Mutex, atomic};
use time;

use backend::StoreBackend;
use key;
use util::{FileIterator, SyncPool, PathHandler};

struct FileEntry {
    key_entry: key::Entry,
    metadata: fs::Metadata,
    full_path: PathBuf,
    link_path: Option<PathBuf>,
}

impl FileEntry {
    fn new(full_path: PathBuf, parent: Option<u64>) -> Result<FileEntry, Box<Error>> {
        debug!("FileEntry::new({:?})", full_path);

        let filename_opt =
            full_path.file_name().and_then(|n| n.to_str()).map(|n| n.bytes().collect());

        if filename_opt.is_some() {
            let md = try!(fs::symlink_metadata(&full_path));
            let link_path = fs::read_link(&full_path).ok();
            Ok(FileEntry {
                key_entry: key::Entry {
                    name: filename_opt.unwrap(),
                    created: Some(md.ctime_nsec()),
                    modified: Some(md.mtime_nsec()),
                    accessed: Some(md.atime_nsec()),
                    parent_id: parent,
                    data_length: Some(md.len()),
                    data_hash: None,
                    id: None,
                    permissions: None,
                    user_id: None,
                    group_id: None,
                },
                metadata: md,
                full_path: full_path,
                link_path: link_path,
            })
        } else {
            Err(From::from("Could not parse filename."[..].to_owned()))
        }
    }

    fn is_directory(&self) -> bool {
        self.metadata.is_dir()
    }
    fn is_symlink(&self) -> bool {
        self.link_path.is_some()
    }
}

pub struct InsertPathHandler<B: StoreBackend> {
    count: atomic::AtomicIsize,
    last_print: Mutex<time::Timespec>,
    key_store: SyncPool<key::StoreProcess<FileIterator, B>>,
}

impl<B: StoreBackend> InsertPathHandler<B> {
    pub fn new(key_stores: Vec<key::StoreProcess<FileIterator, B>>) -> InsertPathHandler<B> {
        InsertPathHandler {
            count: atomic::AtomicIsize::new(0),
            last_print: Mutex::new(time::now().to_timespec()),
            key_store: SyncPool::new(key_stores),
        }
    }
}

impl<B: StoreBackend> PathHandler<Option<u64>> for InsertPathHandler<B> {
    type DirItem = fs::DirEntry;
    type DirIter = fs::ReadDir;

    fn read_dir(&self, path: &PathBuf) -> io::Result<Self::DirIter> {
        fs::read_dir(path)
    }

    fn handle_path(&self, parent: &Option<u64>, path: &PathBuf) -> Option<Option<u64>> {
        let count = self.count.fetch_add(1, atomic::Ordering::SeqCst) + 1;

        if count % 16 == 0 {
            // don't hammer the mutex
            let mut guarded_last_print = self.last_print.lock().unwrap();
            let now = time::now().to_timespec();
            if guarded_last_print.sec <= now.sec - 1 {
                println!("#{}: {}", count, path.display());
                *guarded_last_print = now;
            }
        }

        match FileEntry::new(path.clone(), parent.clone()) {
            Err(e) => {
                println!("Skipping '{}': {}", path.display(), e);
            }
            Ok(file_entry) => {
                if file_entry.is_symlink() {
                    return None;
                }
                let is_directory = file_entry.is_directory();
                let local_root = path.clone();
                let full_path = file_entry.full_path.clone();

                let ks = self.key_store.lock().unwrap();
                match ks.send_reply(key::Msg::Insert(file_entry.key_entry,
                                                     if is_directory {
                                                         None
                                                     } else {
                                                         Some(Box::new(move |()| {
                        match FileIterator::new(&full_path) {
                            Err(e) => {
                                println!("Skipping '{}': {}", local_root.display(), e.to_string());
                                None
                            }
                            Ok(it) => Some(it),
                        }
                    }))
                                                     })) {
                    Ok(key::Reply::Id(id)) => {
                        if is_directory {
                            return Some(Some(id));
                        }
                    }
                    _ => panic!("Unexpected reply from key store."),
                }
            }
        }

        None
    }
}
<commit_msg>Cargo fmt.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fs;
use std::io;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use std::str;
use std::sync::{Mutex, atomic};
use time;

use backend::StoreBackend;
use key;
use util::{FileIterator, PathHandler, SyncPool};

struct FileEntry {
    key_entry: key::Entry,
    metadata: fs::Metadata,
    full_path: PathBuf,
    link_path: Option<PathBuf>,
}

impl FileEntry {
    fn new(full_path: PathBuf, parent: Option<u64>) -> Result<FileEntry, Box<Error>> {
        debug!("FileEntry::new({:?})", full_path);

        let filename_opt =
            full_path.file_name().and_then(|n| n.to_str()).map(|n| n.bytes().collect());

        if filename_opt.is_some() {
            let md = try!(fs::symlink_metadata(&full_path));
            let link_path = fs::read_link(&full_path).ok();
            Ok(FileEntry {
                key_entry: key::Entry {
                    name: filename_opt.unwrap(),
                    created: Some(md.ctime_nsec()),
                    modified: Some(md.mtime_nsec()),
                    accessed: Some(md.atime_nsec()),
                    parent_id: parent,
                    data_length: Some(md.len()),
                    data_hash: None,
                    id: None,
                    permissions: None,
                    user_id: None,
                    group_id: None,
                },
                metadata: md,
                full_path: full_path,
                link_path: link_path,
            })
        } else {
            Err(From::from("Could not parse filename."[..].to_owned()))
        }
    }

    fn is_directory(&self) -> bool {
        self.metadata.is_dir()
    }
    fn is_symlink(&self) -> bool {
        self.link_path.is_some()
    }
}

pub struct InsertPathHandler<B: StoreBackend> {
    count: atomic::AtomicIsize,
    last_print: Mutex<time::Timespec>,
    key_store: SyncPool<key::StoreProcess<FileIterator, B>>,
}

impl<B: StoreBackend> InsertPathHandler<B> {
    pub fn new(key_stores: Vec<key::StoreProcess<FileIterator, B>>) -> InsertPathHandler<B> {
        InsertPathHandler {
            count: atomic::AtomicIsize::new(0),
            last_print: Mutex::new(time::now().to_timespec()),
            key_store: SyncPool::new(key_stores),
        }
    }
}

impl<B: StoreBackend> PathHandler<Option<u64>> for InsertPathHandler<B> {
    type DirItem = fs::DirEntry;
    type DirIter = fs::ReadDir;

    fn read_dir(&self, path: &PathBuf) -> io::Result<Self::DirIter> {
        fs::read_dir(path)
    }

    fn handle_path(&self, parent: &Option<u64>, path: &PathBuf) -> Option<Option<u64>> {
        let count = self.count.fetch_add(1, atomic::Ordering::SeqCst) + 1;

        if count % 16 == 0 {
            // don't hammer the mutex
            let mut guarded_last_print = self.last_print.lock().unwrap();
            let now = time::now().to_timespec();
            if guarded_last_print.sec <= now.sec - 1 {
                println!("#{}: {}", count, path.display());
                *guarded_last_print = now;
            }
        }

        match FileEntry::new(path.clone(), parent.clone()) {
            Err(e) => {
                println!("Skipping '{}': {}", path.display(), e);
            }
            Ok(file_entry) => {
                if file_entry.is_symlink() {
                    return None;
                }
                let is_directory = file_entry.is_directory();
                let local_root = path.clone();
                let full_path = file_entry.full_path.clone();

                let ks = self.key_store.lock().unwrap();
                match ks.send_reply(key::Msg::Insert(file_entry.key_entry,
                                                     if is_directory {
                                                         None
                                                     } else {
                                                         Some(Box::new(move |()| {
                        match FileIterator::new(&full_path) {
                            Err(e) => {
                                println!("Skipping '{}': {}", local_root.display(), e.to_string());
                                None
                            }
                            Ok(it) => Some(it),
                        }
                    }))
                                                     })) {
                    Ok(key::Reply::Id(id)) => {
                        if is_directory {
                            return Some(Some(id));
                        }
                    }
                    _ => panic!("Unexpected reply from key store."),
                }
            }
        }

        None
    }
}
<|endoftext|>