<commit_before>#[doc(
    brief = "The attribute parsing pass",
    desc =
    "Traverses the document tree, pulling relevant documention out of the \
     corresponding AST nodes. The information gathered here is the basis \
     of the natural-language documentation for a crate."
)];

import rustc::syntax::ast;
import rustc::middle::ast_map;

export mk_pass;

fn mk_pass() -> pass {
    run
}

fn run(
    srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_crate: fold_crate,
        fold_item: fold_item,
        fold_fn: fold_fn,
        fold_enum: fold_enum,
        fold_res: fold_res,
        fold_iface: fold_iface,
        fold_impl: fold_impl
        with *fold::default_any_fold(srv)
    });
    fold.fold_crate(fold, doc)
}

fn fold_crate(
    fold: fold::fold<astsrv::srv>,
    doc: doc::cratedoc
) -> doc::cratedoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_crate(fold, doc);

    let attrs = astsrv::exec(srv) {|ctxt|
        let attrs = ctxt.ast.node.attrs;
        attr_parser::parse_crate(attrs)
    };

    {
        topmod: {
            item: {
                name: option::from_maybe(doc.topmod.name(), attrs.name)
                with doc.topmod.item
            }
            with doc.topmod
        }
    }
}

#[test]
fn should_replace_top_module_name_with_crate_name() {
    let doc = test::mk_doc("#[link(name = \"bond\")];");
    assert doc.topmod.name() == "bond";
}

fn fold_item(
    fold: fold::fold<astsrv::srv>,
    doc: doc::itemdoc
) -> doc::itemdoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_item(fold, doc);

    let attrs = if doc.id == ast::crate_node_id {
        // This is the top-level mod, use the crate attributes
        astsrv::exec(srv) {|ctxt|
            attr_parser::parse_basic(ctxt.ast.node.attrs)
        }
    } else {
        parse_item_attrs(srv, doc.id, attr_parser::parse_basic)
    };

    {
        brief: attrs.brief,
        desc: attrs.desc
        with doc
    }
}

fn parse_item_attrs<T:send>(
    srv: astsrv::srv,
    id: doc::ast_id,
    parse_attrs: fn~([ast::attribute]) -> T) -> T {
    astsrv::exec(srv) {|ctxt|
        let attrs = alt ctxt.ast_map.get(id) {
          ast_map::node_item(item, _) { item.attrs }
          ast_map::node_native_item(item, _) { item.attrs }
          _ {
            fail "parse_item_attrs: not an item";
          }
        };
        parse_attrs(attrs)
    }
}

#[test]
fn should_should_extract_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] mod a { }");
    assert doc.topmod.mods()[0].desc() == some("test");
}

#[test]
fn should_extract_top_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"];");
    assert doc.topmod.desc() == some("test");
}

#[test]
fn should_extract_native_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] native mod a { }");
    assert doc.topmod.nmods()[0].desc() == some("test");
}

#[test]
fn should_extract_native_fn_attributes() {
    let doc = test::mk_doc("native mod a { #[doc = \"test\"] fn a(); }");
    assert doc.topmod.nmods()[0].fns[0].desc() == some("test");
}

fn fold_fn(
    fold: fold::fold<astsrv::srv>,
    doc: doc::fndoc
) -> doc::fndoc {

    let srv = fold.ctxt;

    let doc = fold::default_seq_fold_fn(fold, doc);
    let attrs = parse_item_attrs(srv, doc.id(), attr_parser::parse_fn);
    ret merge_fn_attrs(doc, attrs);

    fn merge_fn_attrs(
        doc: doc::fndoc,
        attrs: attr_parser::fn_attrs
    ) -> doc::fndoc {
        ret {
            args: merge_arg_attrs(doc.args, attrs.args),
            return: merge_ret_attrs(doc.return, attrs.return),
            failure: attrs.failure
            with doc
        };
    }
}

fn merge_arg_attrs(
    docs: [doc::argdoc],
    attrs: [attr_parser::arg_attrs]
) -> [doc::argdoc] {
    vec::map(docs) {|doc|
        alt vec::find(attrs) {|attr|
            attr.name == doc.name
        } {
            some(attr) {
                {
                    desc: some(attr.desc)
                    with doc
                }
            }
            none { doc }
        }
    }
    // FIXME: Warning when documenting a non-existent arg
}

fn merge_ret_attrs(
    doc: doc::retdoc,
    attrs: option<str>
) -> doc::retdoc {
    {
        desc: attrs
        with doc
    }
}

#[test]
fn should_extract_fn_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] fn a() -> int { }");
    assert doc.topmod.fns()[0].desc() == some("test");
}

#[test]
fn should_extract_fn_arg_attributes() {
    let doc = test::mk_doc("#[doc(args(a = \"b\"))] fn c(a: bool) { }");
    assert doc.topmod.fns()[0].args[0].desc == some("b");
}

#[test]
fn should_extract_fn_return_attributes() {
    let source = "#[doc(return = \"what\")] fn a() -> int { }";
    astsrv::from_str(source) {|srv|
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let fold = fold::default_seq_fold(srv);
        let doc = fold_fn(fold, doc.topmod.fns()[0]);
        assert doc.return.desc == some("what");
    }
}

#[test]
fn should_preserve_fn_sig() {
    let source = "fn a() -> int { }";
    astsrv::from_str(source) {|srv|
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let fold = fold::default_seq_fold(srv);
        let doc = fold_fn(fold, doc.topmod.fns()[0]);
        assert doc.sig == some("fn a() -> int");
    }
}

#[test]
fn should_extract_fn_failure_conditions() {
    let doc = test::mk_doc("#[doc(failure = \"what\")] fn a() { }");
    assert doc.topmod.fns()[0].failure == some("what");
}

#[test]
fn should_extract_const_docs() {
    let doc = test::mk_doc("#[doc(brief = \"foo\", desc = \"bar\")]\
                            const a: bool = true;");
    assert doc.topmod.consts()[0].brief() == some("foo");
    assert doc.topmod.consts()[0].desc() == some("bar");
}

fn fold_enum(
    fold: fold::fold<astsrv::srv>,
    doc: doc::enumdoc
) -> doc::enumdoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_enum(fold, doc);

    {
        variants: vec::map(doc.variants) {|variant|
            let attrs = astsrv::exec(srv) {|ctxt|
                alt check ctxt.ast_map.get(doc.id()) {
                  ast_map::node_item(@{
                    node: ast::item_enum(ast_variants, _), _
                  }, _) {
                    let ast_variant = option::get(
                        vec::find(ast_variants) {|v|
                            v.node.name == variant.name
                        });

                    attr_parser::parse_variant(ast_variant.node.attrs)
                  }
                }
            };

            {
                desc: attrs.desc
                with variant
            }
        }
        with doc
    }
}

#[test]
fn should_extract_enum_docs() {
    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
                            enum a { v }");
    assert doc.topmod.enums()[0].brief() == some("a");
    assert doc.topmod.enums()[0].desc() == some("b");
}

#[test]
fn should_extract_variant_docs() {
    let doc = test::mk_doc("enum a { #[doc = \"c\"] v }");
    assert doc.topmod.enums()[0].variants[0].desc == some("c");
}

fn fold_res(
    fold: fold::fold<astsrv::srv>,
    doc: doc::resdoc
) -> doc::resdoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_res(fold, doc);
    let attrs = parse_item_attrs(srv, doc.id(), attr_parser::parse_res);

    {
        args: vec::map(doc.args) {|doc|
            alt vec::find(attrs.args) {|attr|
                attr.name == doc.name
            } {
                some(attr) {
                    {
                        desc: some(attr.desc)
                        with doc
                    }
                }
                none { doc }
            }
        }
        with doc
    }
}

#[test]
fn should_extract_res_docs() {
    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
                            resource r(b: bool) { }");
    assert doc.topmod.resources()[0].brief() == some("a");
    assert doc.topmod.resources()[0].desc() == some("b");
}

#[test]
fn should_extract_res_arg_docs() {
    let doc = test::mk_doc("#[doc(args(a = \"b\"))]\
                            resource r(a: bool) { }");
    assert doc.topmod.resources()[0].args[0].name == "a";
    assert doc.topmod.resources()[0].args[0].desc == some("b");
}

fn fold_iface(
    fold: fold::fold<astsrv::srv>,
    doc: doc::ifacedoc
) -> doc::ifacedoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_iface(fold, doc);

    {
        methods: merge_method_attrs(srv, doc.id(), doc.methods)
        with doc
    }
}

fn merge_method_attrs(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    docs: [doc::methoddoc]
) -> [doc::methoddoc] {

    type method_attrs = (attr_parser::basic_attrs,
                         attr_parser::method_attrs);

    // Create an assoc list from method name to attributes
    let attrs: [(str, method_attrs)] = astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_iface(_, methods), _
          }, _) {
            vec::map(methods) {|method|
                (method.ident,
                 (attr_parser::parse_basic(method.attrs),
                  attr_parser::parse_method(method.attrs)
                 ))
            }
          }
          ast_map::node_item(@{
            node: ast::item_impl(_, _, _, methods), _
          }, _) {
            vec::map(methods) {|method|
                (method.ident,
                 (attr_parser::parse_basic(method.attrs),
                  attr_parser::parse_method(method.attrs)
                  ))
            }
          }
          _ { fail "unexpected item" }
        }
    };

    vec::map2(docs, attrs) {|doc, attrs|
        assert doc.name == tuple::first(attrs);
        let basic_attrs = tuple::first(tuple::second(attrs));
        let method_attrs = tuple::second(tuple::second(attrs));

        {
            brief: basic_attrs.brief,
            desc: basic_attrs.desc,
            args: merge_arg_attrs(doc.args, method_attrs.args),
            return: merge_ret_attrs(doc.return, method_attrs.return),
            failure: method_attrs.failure
            with doc
        }
    }
}

#[test]
fn should_extract_iface_docs() {
    let doc = test::mk_doc("#[doc = \"whatever\"] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].desc() == some("whatever");
}

#[test]
fn should_extract_iface_method_docs() {
    let doc = test::mk_doc(
        "iface i {\
         #[doc(\
         brief = \"brief\",\
         desc = \"desc\",\
         args(a = \"a\"),\
         return = \"return\",\
         failure = \"failure\")]\
         fn f(a: bool) -> bool;\
         }");
    assert doc.topmod.ifaces()[0].methods[0].brief == some("brief");
    assert doc.topmod.ifaces()[0].methods[0].desc == some("desc");
    assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
    assert doc.topmod.ifaces()[0].methods[0].return.desc == some("return");
    assert doc.topmod.ifaces()[0].methods[0].failure == some("failure");
}

fn fold_impl(
    fold: fold::fold<astsrv::srv>,
    doc: doc::impldoc
) -> doc::impldoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_impl(fold, doc);

    {
        methods: merge_method_attrs(srv, doc.id(), doc.methods)
        with doc
    }
}

#[test]
fn should_extract_impl_docs() {
    let doc = test::mk_doc(
        "#[doc = \"whatever\"] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].desc() == some("whatever");
}

#[test]
fn should_extract_impl_method_docs() {
    let doc = test::mk_doc(
        "impl i for int {\
         #[doc(\
         brief = \"brief\",\
         desc = \"desc\",\
         args(a = \"a\"),\
         return = \"return\",\
         failure = \"failure\")]\
         fn f(a: bool) -> bool { }\
         }");
    assert doc.topmod.impls()[0].methods[0].brief == some("brief");
    assert doc.topmod.impls()[0].methods[0].desc == some("desc");
    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
    assert doc.topmod.impls()[0].methods[0].return.desc == some("return");
    assert doc.topmod.impls()[0].methods[0].failure == some("failure");
}

#[test]
fn should_extract_type_docs() {
    let doc = test::mk_doc(
        "#[doc(brief = \"brief\", desc = \"desc\")]\
         type t = int;");
    assert doc.topmod.types()[0].brief() == some("brief");
    assert doc.topmod.types()[0].desc() == some("desc");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: str) -> doc::cratedoc {
        astsrv::from_str(source) {|srv|
            let doc = extract::from_srv(srv, "");
            run(srv, doc)
        }
    }
}<commit_msg>rustdoc: Use default_any_fold in some tests<commit_after>#[doc(
    brief = "The attribute parsing pass",
    desc =
    "Traverses the document tree, pulling relevant documention out of the \
     corresponding AST nodes. The information gathered here is the basis \
     of the natural-language documentation for a crate."
)];

import rustc::syntax::ast;
import rustc::middle::ast_map;

export mk_pass;

fn mk_pass() -> pass {
    run
}

fn run(
    srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_crate: fold_crate,
        fold_item: fold_item,
        fold_fn: fold_fn,
        fold_enum: fold_enum,
        fold_res: fold_res,
        fold_iface: fold_iface,
        fold_impl: fold_impl
        with *fold::default_any_fold(srv)
    });
    fold.fold_crate(fold, doc)
}

fn fold_crate(
    fold: fold::fold<astsrv::srv>,
    doc: doc::cratedoc
) -> doc::cratedoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_crate(fold, doc);

    let attrs = astsrv::exec(srv) {|ctxt|
        let attrs = ctxt.ast.node.attrs;
        attr_parser::parse_crate(attrs)
    };

    {
        topmod: {
            item: {
                name: option::from_maybe(doc.topmod.name(), attrs.name)
                with doc.topmod.item
            }
            with doc.topmod
        }
    }
}

#[test]
fn should_replace_top_module_name_with_crate_name() {
    let doc = test::mk_doc("#[link(name = \"bond\")];");
    assert doc.topmod.name() == "bond";
}

fn fold_item(
    fold: fold::fold<astsrv::srv>,
    doc: doc::itemdoc
) -> doc::itemdoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_item(fold, doc);

    let attrs = if doc.id == ast::crate_node_id {
        // This is the top-level mod, use the crate attributes
        astsrv::exec(srv) {|ctxt|
            attr_parser::parse_basic(ctxt.ast.node.attrs)
        }
    } else {
        parse_item_attrs(srv, doc.id, attr_parser::parse_basic)
    };

    {
        brief: attrs.brief,
        desc: attrs.desc
        with doc
    }
}

fn parse_item_attrs<T:send>(
    srv: astsrv::srv,
    id: doc::ast_id,
    parse_attrs: fn~([ast::attribute]) -> T) -> T {
    astsrv::exec(srv) {|ctxt|
        let attrs = alt ctxt.ast_map.get(id) {
          ast_map::node_item(item, _) { item.attrs }
          ast_map::node_native_item(item, _) { item.attrs }
          _ {
            fail "parse_item_attrs: not an item";
          }
        };
        parse_attrs(attrs)
    }
}

#[test]
fn should_should_extract_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] mod a { }");
    assert doc.topmod.mods()[0].desc() == some("test");
}

#[test]
fn should_extract_top_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"];");
    assert doc.topmod.desc() == some("test");
}

#[test]
fn should_extract_native_mod_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] native mod a { }");
    assert doc.topmod.nmods()[0].desc() == some("test");
}

#[test]
fn should_extract_native_fn_attributes() {
    let doc = test::mk_doc("native mod a { #[doc = \"test\"] fn a(); }");
    assert doc.topmod.nmods()[0].fns[0].desc() == some("test");
}

fn fold_fn(
    fold: fold::fold<astsrv::srv>,
    doc: doc::fndoc
) -> doc::fndoc {

    let srv = fold.ctxt;

    let doc = fold::default_seq_fold_fn(fold, doc);
    let attrs = parse_item_attrs(srv, doc.id(), attr_parser::parse_fn);
    ret merge_fn_attrs(doc, attrs);

    fn merge_fn_attrs(
        doc: doc::fndoc,
        attrs: attr_parser::fn_attrs
    ) -> doc::fndoc {
        ret {
            args: merge_arg_attrs(doc.args, attrs.args),
            return: merge_ret_attrs(doc.return, attrs.return),
            failure: attrs.failure
            with doc
        };
    }
}

fn merge_arg_attrs(
    docs: [doc::argdoc],
    attrs: [attr_parser::arg_attrs]
) -> [doc::argdoc] {
    vec::map(docs) {|doc|
        alt vec::find(attrs) {|attr|
            attr.name == doc.name
        } {
            some(attr) {
                {
                    desc: some(attr.desc)
                    with doc
                }
            }
            none { doc }
        }
    }
    // FIXME: Warning when documenting a non-existent arg
}

fn merge_ret_attrs(
    doc: doc::retdoc,
    attrs: option<str>
) -> doc::retdoc {
    {
        desc: attrs
        with doc
    }
}

#[test]
fn should_extract_fn_attributes() {
    let doc = test::mk_doc("#[doc = \"test\"] fn a() -> int { }");
    assert doc.topmod.fns()[0].desc() == some("test");
}

#[test]
fn should_extract_fn_arg_attributes() {
    let doc = test::mk_doc("#[doc(args(a = \"b\"))] fn c(a: bool) { }");
    assert doc.topmod.fns()[0].args[0].desc == some("b");
}

#[test]
fn should_extract_fn_return_attributes() {
    let source = "#[doc(return = \"what\")] fn a() -> int { }";
    astsrv::from_str(source) {|srv|
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let fold = fold::default_any_fold(srv);
        let doc = fold_fn(fold, doc.topmod.fns()[0]);
        assert doc.return.desc == some("what");
    }
}

#[test]
fn should_preserve_fn_sig() {
    let source = "fn a() -> int { }";
    astsrv::from_str(source) {|srv|
        let doc = extract::from_srv(srv, "");
        let doc = tystr_pass::mk_pass()(srv, doc);
        let fold = fold::default_any_fold(srv);
        let doc = fold_fn(fold, doc.topmod.fns()[0]);
        assert doc.sig == some("fn a() -> int");
    }
}

#[test]
fn should_extract_fn_failure_conditions() {
    let doc = test::mk_doc("#[doc(failure = \"what\")] fn a() { }");
    assert doc.topmod.fns()[0].failure == some("what");
}

#[test]
fn should_extract_const_docs() {
    let doc = test::mk_doc("#[doc(brief = \"foo\", desc = \"bar\")]\
                            const a: bool = true;");
    assert doc.topmod.consts()[0].brief() == some("foo");
    assert doc.topmod.consts()[0].desc() == some("bar");
}

fn fold_enum(
    fold: fold::fold<astsrv::srv>,
    doc: doc::enumdoc
) -> doc::enumdoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_enum(fold, doc);

    {
        variants: vec::map(doc.variants) {|variant|
            let attrs = astsrv::exec(srv) {|ctxt|
                alt check ctxt.ast_map.get(doc.id()) {
                  ast_map::node_item(@{
                    node: ast::item_enum(ast_variants, _), _
                  }, _) {
                    let ast_variant = option::get(
                        vec::find(ast_variants) {|v|
                            v.node.name == variant.name
                        });

                    attr_parser::parse_variant(ast_variant.node.attrs)
                  }
                }
            };

            {
                desc: attrs.desc
                with variant
            }
        }
        with doc
    }
}

#[test]
fn should_extract_enum_docs() {
    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
                            enum a { v }");
    assert doc.topmod.enums()[0].brief() == some("a");
    assert doc.topmod.enums()[0].desc() == some("b");
}

#[test]
fn should_extract_variant_docs() {
    let doc = test::mk_doc("enum a { #[doc = \"c\"] v }");
    assert doc.topmod.enums()[0].variants[0].desc == some("c");
}

fn fold_res(
    fold: fold::fold<astsrv::srv>,
    doc: doc::resdoc
) -> doc::resdoc {

    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_res(fold, doc);
    let attrs = parse_item_attrs(srv, doc.id(), attr_parser::parse_res);

    {
        args: vec::map(doc.args) {|doc|
            alt vec::find(attrs.args) {|attr|
                attr.name == doc.name
            } {
                some(attr) {
                    {
                        desc: some(attr.desc)
                        with doc
                    }
                }
                none { doc }
            }
        }
        with doc
    }
}

#[test]
fn should_extract_res_docs() {
    let doc = test::mk_doc("#[doc(brief = \"a\", desc = \"b\")]\
                            resource r(b: bool) { }");
    assert doc.topmod.resources()[0].brief() == some("a");
    assert doc.topmod.resources()[0].desc() == some("b");
}

#[test]
fn should_extract_res_arg_docs() {
    let doc = test::mk_doc("#[doc(args(a = \"b\"))]\
                            resource r(a: bool) { }");
    assert doc.topmod.resources()[0].args[0].name == "a";
    assert doc.topmod.resources()[0].args[0].desc == some("b");
}

fn fold_iface(
    fold: fold::fold<astsrv::srv>,
    doc: doc::ifacedoc
) -> doc::ifacedoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_iface(fold, doc);

    {
        methods: merge_method_attrs(srv, doc.id(), doc.methods)
        with doc
    }
}

fn merge_method_attrs(
    srv: astsrv::srv,
    item_id: doc::ast_id,
    docs: [doc::methoddoc]
) -> [doc::methoddoc] {

    type method_attrs = (attr_parser::basic_attrs,
                         attr_parser::method_attrs);

    // Create an assoc list from method name to attributes
    let attrs: [(str, method_attrs)] = astsrv::exec(srv) {|ctxt|
        alt ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_iface(_, methods), _
          }, _) {
            vec::map(methods) {|method|
                (method.ident,
                 (attr_parser::parse_basic(method.attrs),
                  attr_parser::parse_method(method.attrs)
                 ))
            }
          }
          ast_map::node_item(@{
            node: ast::item_impl(_, _, _, methods), _
          }, _) {
            vec::map(methods) {|method|
                (method.ident,
                 (attr_parser::parse_basic(method.attrs),
                  attr_parser::parse_method(method.attrs)
                  ))
            }
          }
          _ { fail "unexpected item" }
        }
    };

    vec::map2(docs, attrs) {|doc, attrs|
        assert doc.name == tuple::first(attrs);
        let basic_attrs = tuple::first(tuple::second(attrs));
        let method_attrs = tuple::second(tuple::second(attrs));

        {
            brief: basic_attrs.brief,
            desc: basic_attrs.desc,
            args: merge_arg_attrs(doc.args, method_attrs.args),
            return: merge_ret_attrs(doc.return, method_attrs.return),
            failure: method_attrs.failure
            with doc
        }
    }
}

#[test]
fn should_extract_iface_docs() {
    let doc = test::mk_doc("#[doc = \"whatever\"] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].desc() == some("whatever");
}

#[test]
fn should_extract_iface_method_docs() {
    let doc = test::mk_doc(
        "iface i {\
         #[doc(\
         brief = \"brief\",\
         desc = \"desc\",\
         args(a = \"a\"),\
         return = \"return\",\
         failure = \"failure\")]\
         fn f(a: bool) -> bool;\
         }");
    assert doc.topmod.ifaces()[0].methods[0].brief == some("brief");
    assert doc.topmod.ifaces()[0].methods[0].desc == some("desc");
    assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
    assert doc.topmod.ifaces()[0].methods[0].return.desc == some("return");
    assert doc.topmod.ifaces()[0].methods[0].failure == some("failure");
}

fn fold_impl(
    fold: fold::fold<astsrv::srv>,
    doc: doc::impldoc
) -> doc::impldoc {
    let srv = fold.ctxt;
    let doc = fold::default_seq_fold_impl(fold, doc);

    {
        methods: merge_method_attrs(srv, doc.id(), doc.methods)
        with doc
    }
}

#[test]
fn should_extract_impl_docs() {
    let doc = test::mk_doc(
        "#[doc = \"whatever\"] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].desc() == some("whatever");
}

#[test]
fn should_extract_impl_method_docs() {
    let doc = test::mk_doc(
        "impl i for int {\
         #[doc(\
         brief = \"brief\",\
         desc = \"desc\",\
         args(a = \"a\"),\
         return = \"return\",\
         failure = \"failure\")]\
         fn f(a: bool) -> bool { }\
         }");
    assert doc.topmod.impls()[0].methods[0].brief == some("brief");
    assert doc.topmod.impls()[0].methods[0].desc == some("desc");
    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
    assert doc.topmod.impls()[0].methods[0].return.desc == some("return");
    assert doc.topmod.impls()[0].methods[0].failure == some("failure");
}

#[test]
fn should_extract_type_docs() {
    let doc = test::mk_doc(
        "#[doc(brief = \"brief\", desc = \"desc\")]\
         type t = int;");
    assert doc.topmod.types()[0].brief() == some("brief");
    assert doc.topmod.types()[0].desc() == some("desc");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: str) -> doc::cratedoc {
        astsrv::from_str(source) {|srv|
            let doc = extract::from_srv(srv, "");
            run(srv, doc)
        }
    }
}<|endoftext|>


<commit_before>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RgbaColor = [f32; 4];

const RED: RgbaColor = [1.0, 0.0, 0.0, 1.0];
const GREEN: RgbaColor = [0.0, 1.0, 0.0, 1.0];
const BLACK: RgbaColor = [0.0, 0.0, 0.0, 1.0];

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: RgbaColor
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: RgbaColor,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: RgbaColor,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: GREEN,
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create window: {}");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .y_motion_inverted(true)
        .build_translator();

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(RED, INITIAL_CHARACTER_POS, 50.0)
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: BLACK
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<commit_msg>Remove `#![allow(unused_variables)]` annotation<commit_after>extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RgbaColor = [f32; 4];

const RED: RgbaColor = [1.0, 0.0, 0.0, 1.0];
const GREEN: RgbaColor = [0.0, 1.0, 0.0, 1.0];
const BLACK: RgbaColor = [0.0, 0.0, 0.0, 1.0];

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: RgbaColor
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: RgbaColor,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: RgbaColor,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: GREEN,
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create window: {}");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .y_motion_inverted(true)
        .build_translator();

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(RED, INITIAL_CHARACTER_POS, 50.0)
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: BLACK
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<|endoftext|>


<commit_before>use au = gfx::geometry;
use au::au;
use core::dvec::DVec;
use dl = gfx::display_list;
use dom::node::Node;
use geom::rect::Rect;
use geom::point::Point2D;
// TODO: pub-use these
use layout::block::BlockFlowData;
use layout::box::{LogicalBefore, LogicalAfter, RenderBox};
use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods;
use layout::inline::InlineFlowData;
use layout::root::RootFlowData;
use util::tree;

/** Servo's experimental layout system builds a tree of FlowContexts
and RenderBoxes, and figures out positions and display attributes of
tree nodes. Positions are computed in several tree traversals driven
by fundamental data dependencies of inline and block layout.

Flows are interior nodes in the layout tree, and correspond closely to
flow contexts in the CSS specification. Flows are responsible for
positioning their child flow contexts and render boxes. Flows have
purpose-specific fields, such as auxilliary line box structs,
out-of-flow child lists, and so on.

Currently, the important types of flows are:

 * BlockFlow: a flow that establishes a block context. It has several
   child flows, each of which are positioned according to block
   formatting context rules (as if child flows CSS block boxes). Block
   flows also contain a single GenericBox to represent their rendered
   borders, padding, etc. (In the future, this render box may be
   folded into BlockFlow to save space.)

 * InlineFlow: a flow that establishes an inline context. It has a
   flat list of child boxes/flows that are subject to inline layout
   and line breaking, and structs to represent line breaks and mapping
   to CSS boxes, for the purpose of handling `getClientRects()`.

*/

/* The type of the formatting context, and data specific to each
context, such as linebox structures or float lists */
enum FlowContext {
    AbsoluteFlow(FlowData),
    BlockFlow(FlowData, BlockFlowData),
    FloatFlow(FlowData),
    InlineBlockFlow(FlowData),
    InlineFlow(FlowData, InlineFlowData),
    RootFlow(FlowData, RootFlowData),
    TableFlow(FlowData)
}

enum FlowContextType {
    Flow_Absolute,
    Flow_Block,
    Flow_Float,
    Flow_InlineBlock,
    Flow_Inline,
    Flow_Root,
    Flow_Table
}

trait FlowContextMethods {
    pure fn d(&self) -> &self/FlowData;
    pure fn inline(&self) -> &self/InlineFlowData;
    pure fn block(&self) -> &self/BlockFlowData;
    pure fn root(&self) -> &self/RootFlowData;
    fn bubble_widths(@self, &LayoutContext);
    fn assign_widths(@self, &LayoutContext);
    fn assign_height(@self, &LayoutContext);
    fn build_display_list_recurse(@self, &dl::DisplayListBuilder, dirty: &Rect<au>,
                                  offset: &Point2D<au>, &dl::DisplayList);
    pure fn foldl_boxes_for_node<B: Copy>(Node, +seed: B, cb: pure fn&(+a: B,@RenderBox) -> B) -> B;
    pure fn iter_boxes_for_node<T>(Node, cb: pure fn&(@RenderBox) -> T);
}

/* A particular kind of layout context. It manages the positioning of
   render boxes within the context.  */
struct FlowData {
    mut node: Option<Node>,
    /* reference to parent, children flow contexts */
    tree: tree::Tree<@FlowContext>,
    /* TODO (Issue #87): debug only */
    mut id: int,

    /* layout computations */
    // TODO: min/pref and position are used during disjoint phases of
    // layout; maybe combine into a single enum to save space.
    mut min_width: au,
    mut pref_width: au,
    mut position: Rect<au>,
}

fn FlowData(id: int) -> FlowData {
    FlowData {
        node: None,
        tree: tree::empty(),
        id: id,

        min_width: au(0),
        pref_width: au(0),
        position: au::zero_rect()
    }
}

struct PendingEntry {
    start_box: @RenderBox,
    start_idx: uint
}

// helper object for building the initial box list and making the
// mapping between DOM nodes and boxes.
struct BoxConsumer {
    flow: @FlowContext,
    stack: DVec<PendingEntry>,
}

fn BoxConsumer(flow: @FlowContext) -> BoxConsumer {
    debug!("Creating box consumer for flow: f%s", flow.debug_str());
    BoxConsumer {
        flow: flow,
        stack: DVec()
    }
}

impl BoxConsumer {
    pub fn push_box(ctx: &LayoutContext, box: @RenderBox) {
        debug!("BoxConsumer: pushing box b%d to flow f%d", box.d().id, self.flow.d().id);
        let length = match self.flow {
            @InlineFlow(*) => self.flow.inline().boxes.len(),
            _ => 0
        };
        let entry = PendingEntry { start_box: box, start_idx: length };
        self.stack.push(entry);

        match self.flow {
            @InlineFlow(*) => {
                if box.requires_inline_spacers() {
                    do box.create_inline_spacer_for_side(ctx, LogicalBefore).iter |spacer: &@RenderBox| {
                        self.flow.inline().boxes.push(*spacer);
                    }
                }
            },
            @BlockFlow(*) | @RootFlow(*) => {
                assert self.stack.len() == 1;
            },
            _ => { warn!("push_box() not implemented for flow f%d", self.flow.d().id) }
        }
    }

    pub fn pop_box(ctx: &LayoutContext, box: @RenderBox) {
        assert self.stack.len() > 0;
        let entry = self.stack.pop();
        assert core::box::ptr_eq(box, entry.start_box);

        debug!("BoxConsumer: popping box b%d to flow f%d", box.d().id, self.flow.d().id);

        match self.flow {
            @InlineFlow(*) => {
                let span_length = self.flow.inline().boxes.len() - entry.start_idx + 1;
                match (span_length, box.requires_inline_spacers()) {
                    // leaf box
                    (1, _) => { self.flow.inline().boxes.push(box); return; },
                    // if this non-leaf box generates extra horizontal
                    // spacing, add a SpacerBox for it.
                    (_, true) => {
                        do box.create_inline_spacer_for_side(ctx, LogicalAfter).iter |spacer: &@RenderBox| {
                            self.flow.inline().boxes.push(*spacer);
                        }
                    },
                    // non-leaf with no spacer; do nothing
                    (_, false) => { }
                }

                // only create NodeRanges for non-leaf nodes.
                let final_span_length = self.flow.inline().boxes.len() - entry.start_idx + 1;
                assert final_span_length > 1;
                let mapping = { node: copy box.d().node,
                               span: {
                                   mut start: entry.start_idx as u16,
                                   mut len: final_span_length as u16
                               }
                              };
                debug!("BoxConsumer: adding element range=%?", mapping.span);
                self.flow.inline().elems.push(mapping);
            },
            @BlockFlow(*) => {
                assert self.flow.block().box.is_none();
                self.flow.block().box = Some(entry.start_box);
            },
            @RootFlow(*) => {
                assert self.flow.root().box.is_none();
                self.flow.root().box = Some(entry.start_box);
            },
            _ => { warn!("pop_box not implemented for flow %?", self.flow.d().id) }
        }
    }
}

impl FlowContext : FlowContextMethods {
    pure fn d(&self) -> &self/FlowData {
        match *self {
            AbsoluteFlow(ref d)    => d,
            BlockFlow(ref d, _)    => d,
            FloatFlow(ref d)       => d,
            InlineBlockFlow(ref d) => d,
            InlineFlow(ref d, _)   => d,
            RootFlow(ref d, _)     => d,
            TableFlow(ref d)       => d
        }
    }

    pure fn inline(&self) -> &self/InlineFlowData {
        match *self {
            InlineFlow(_, ref i) => i,
            _ => fail fmt!("Tried to access inline data of non-inline: f%d", self.d().id)
        }
    }

    pure fn block(&self) -> &self/BlockFlowData {
        match *self {
            BlockFlow(_, ref b) => b,
            _ => fail fmt!("Tried to access block data of non-block: f%d", self.d().id)
        }
    }

    pure fn root(&self) -> &self/RootFlowData {
        match *self {
            RootFlow(_, ref r) => r,
            _ => fail fmt!("Tried to access root data of non-root: f%d", self.d().id)
        }
    }

    fn bubble_widths(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.bubble_widths_block(ctx),
            @InlineFlow(*) => self.bubble_widths_inline(ctx),
            @RootFlow(*)   => self.bubble_widths_root(ctx),
            _ => fail fmt!("Tried to bubble_widths of flow: f%d", self.d().id)
        }
    }

    fn assign_widths(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.assign_widths_block(ctx),
            @InlineFlow(*) => self.assign_widths_inline(ctx),
            @RootFlow(*)   => self.assign_widths_root(ctx),
            _ => fail fmt!("Tried to assign_widths of flow: f%d", self.d().id)
        }
    }

    fn assign_height(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.assign_height_block(ctx),
            @InlineFlow(*) => self.assign_height_inline(ctx),
            @RootFlow(*)   => self.assign_height_root(ctx),
            _ => fail fmt!("Tried to assign_height of flow: f%d", self.d().id)
        }
    }

    fn build_display_list_recurse(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
                                  offset: &Point2D<au>, list: &dl::DisplayList) {
        match self {
            @RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list),
            @BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list),
            @InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list),
            _ => fail fmt!("Tried to build_display_list_recurse of flow: %?", self)
        }
    }

    // Actual methods that do not require much flow-specific logic
    pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B, blk: pure fn&(a: B,@RenderBox) -> B) -> B {
        match self {
            RootFlow(*) => match self.root().box {
                Some(box) if box.d().node == node => { blk(seed, box) },
                _ => seed
            },
            BlockFlow(*) => match self.block().box {
                Some(box)  if box.d().node == node => { blk(seed, box) },
                _ => seed
            },
            InlineFlow(*) => do self.inline().boxes.foldl(seed) |acc, box| {
                // FIXME: Bad copies. foldl's accumulator should be by-value
                if box.d().node == node { blk(*acc, *box) }
                else { *acc }
            },
            _ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self)
        }
    }

    pure fn iter_boxes_for_node<T>(node: Node, cb: pure fn&(@RenderBox) -> T) {
        match self {
            RootFlow(*) => match self.root().box {
                Some(box) if box.d().node == node => { cb(box); },
                _ => {}
            },
            BlockFlow(*) => match self.block().box {
                Some(box) if box.d().node == node => { cb(box); },
                _ => {}
            },
            InlineFlow(*) => {
                for self.inline().boxes.each |box| {
                    if box.d().node == node { cb(*box); }
                }
            },
            _ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self)
        }
    }
}

/* The tree holding FlowContexts */
enum FlowTree { FlowTree }

impl FlowTree : tree::ReadMethods<@FlowContext> {
    fn each_child(ctx: @FlowContext, f: fn(box: @FlowContext) -> bool) {
        tree::each_child(&self, &ctx, |box| f(*box) )
    }

    fn with_tree_fields<R>(box: &@FlowContext, f: fn(&tree::Tree<@FlowContext>) -> R) -> R {
        f(&box.d().tree)
    }
}

impl FlowTree : tree::WriteMethods<@FlowContext> {
    fn add_child(parent: @FlowContext, child: @FlowContext) {
        tree::add_child(&self, parent, child)
    }

    pure fn eq(a: &@FlowContext, b: &@FlowContext) -> bool { core::box::ptr_eq(*a, *b) }

    fn with_tree_fields<R>(box: &@FlowContext, f: fn(&tree::Tree<@FlowContext>) -> R) -> R {
        f(&box.d().tree)
    }
}

impl FlowContext : BoxedDebugMethods {
    fn dump(@self) {
        self.dump_indent(0u);
    }

    /** Dumps the flow tree, for debugging, with indentation. */
    fn dump_indent(@self, indent: uint) {
        let mut s = ~"|";
        for uint::range(0u, indent) |_i| {
            s += ~"---- ";
        }

        s += self.debug_str();
        debug!("%s", s);

        for FlowTree.each_child(self) |child| {
            child.dump_indent(indent + 1u)
        }
    }

    fn debug_str(@self) -> ~str {
        let repr = match *self {
            InlineFlow(*) => {
                let mut s = self.inline().boxes.foldl(~"InlineFlow(children=", |s, box| {
                    fmt!("%s b%d", *s, box.d().id)
                });
                s += ~")"; s
            },
            BlockFlow(*) => {
                match self.block().box {
                    Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id),
                    None => ~"BlockFlow",
                }
            },
            RootFlow(*) => {
                match self.root().box {
                    Some(box) => fmt!("RootFlo(box=b%d)", box.d().id),
                    None => ~"RootFlow",
                }
            },
            _ => ~"(Unknown flow)"
        };

        fmt!("f%? %?", self.d().id, repr)
    }
}
<commit_msg>Rewrite FlowContext's box iterator methods to compose better.<commit_after>use au = gfx::geometry;
use au::au;
use core::dvec::DVec;
use dl = gfx::display_list;
use dom::node::Node;
use geom::rect::Rect;
use geom::point::Point2D;
// TODO: pub-use these
use layout::block::BlockFlowData;
use layout::box::{LogicalBefore, LogicalAfter, RenderBox};
use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods;
use layout::inline::InlineFlowData;
use layout::root::RootFlowData;
use util::tree;

/** Servo's experimental layout system builds a tree of FlowContexts
and RenderBoxes, and figures out positions and display attributes of
tree nodes. Positions are computed in several tree traversals driven
by fundamental data dependencies of inline and block layout.

Flows are interior nodes in the layout tree, and correspond closely to
flow contexts in the CSS specification. Flows are responsible for
positioning their child flow contexts and render boxes. Flows have
purpose-specific fields, such as auxilliary line box structs,
out-of-flow child lists, and so on.

Currently, the important types of flows are:

 * BlockFlow: a flow that establishes a block context. It has several
   child flows, each of which are positioned according to block
   formatting context rules (as if child flows CSS block boxes). Block
   flows also contain a single GenericBox to represent their rendered
   borders, padding, etc. (In the future, this render box may be
   folded into BlockFlow to save space.)

 * InlineFlow: a flow that establishes an inline context. It has a
   flat list of child boxes/flows that are subject to inline layout
   and line breaking, and structs to represent line breaks and mapping
   to CSS boxes, for the purpose of handling `getClientRects()`.

*/

/* The type of the formatting context, and data specific to each
context, such as linebox structures or float lists */
enum FlowContext {
    AbsoluteFlow(FlowData),
    BlockFlow(FlowData, BlockFlowData),
    FloatFlow(FlowData),
    InlineBlockFlow(FlowData),
    InlineFlow(FlowData, InlineFlowData),
    RootFlow(FlowData, RootFlowData),
    TableFlow(FlowData)
}

enum FlowContextType {
    Flow_Absolute,
    Flow_Block,
    Flow_Float,
    Flow_InlineBlock,
    Flow_Inline,
    Flow_Root,
    Flow_Table
}

trait FlowContextMethods {
    pure fn d(&self) -> &self/FlowData;
    pure fn inline(&self) -> &self/InlineFlowData;
    pure fn block(&self) -> &self/BlockFlowData;
    pure fn root(&self) -> &self/RootFlowData;
    fn bubble_widths(@self, &LayoutContext);
    fn assign_widths(@self, &LayoutContext);
    fn assign_height(@self, &LayoutContext);
    fn build_display_list_recurse(@self, &dl::DisplayListBuilder, dirty: &Rect<au>,
                                  offset: &Point2D<au>, &dl::DisplayList);
    pure fn foldl_boxes_for_node<B: Copy>(Node, +seed: B, cb: pure fn&(+a: B,@RenderBox) -> B) -> B;
    pure fn iter_boxes_for_node<T>(Node, cb: pure fn&(@RenderBox) -> T);
}

/* A particular kind of layout context. It manages the positioning of
   render boxes within the context.  */
struct FlowData {
    mut node: Option<Node>,
    /* reference to parent, children flow contexts */
    tree: tree::Tree<@FlowContext>,
    /* TODO (Issue #87): debug only */
    mut id: int,

    /* layout computations */
    // TODO: min/pref and position are used during disjoint phases of
    // layout; maybe combine into a single enum to save space.
    mut min_width: au,
    mut pref_width: au,
    mut position: Rect<au>,
}

fn FlowData(id: int) -> FlowData {
    FlowData {
        node: None,
        tree: tree::empty(),
        id: id,

        min_width: au(0),
        pref_width: au(0),
        position: au::zero_rect()
    }
}

struct PendingEntry {
    start_box: @RenderBox,
    start_idx: uint
}

// helper object for building the initial box list and making the
// mapping between DOM nodes and boxes.
struct BoxConsumer {
    flow: @FlowContext,
    stack: DVec<PendingEntry>,
}

fn BoxConsumer(flow: @FlowContext) -> BoxConsumer {
    debug!("Creating box consumer for flow: f%s", flow.debug_str());
    BoxConsumer {
        flow: flow,
        stack: DVec()
    }
}

impl BoxConsumer {
    pub fn push_box(ctx: &LayoutContext, box: @RenderBox) {
        debug!("BoxConsumer: pushing box b%d to flow f%d", box.d().id, self.flow.d().id);
        let length = match self.flow {
            @InlineFlow(*) => self.flow.inline().boxes.len(),
            _ => 0
        };
        let entry = PendingEntry { start_box: box, start_idx: length };
        self.stack.push(entry);

        match self.flow {
            @InlineFlow(*) => {
                if box.requires_inline_spacers() {
                    do box.create_inline_spacer_for_side(ctx, LogicalBefore).iter |spacer: &@RenderBox| {
                        self.flow.inline().boxes.push(*spacer);
                    }
                }
            },
            @BlockFlow(*) | @RootFlow(*) => {
                assert self.stack.len() == 1;
            },
            _ => { warn!("push_box() not implemented for flow f%d", self.flow.d().id) }
        }
    }

    pub fn pop_box(ctx: &LayoutContext, box: @RenderBox) {
        assert self.stack.len() > 0;
        let entry = self.stack.pop();
        assert core::box::ptr_eq(box, entry.start_box);

        debug!("BoxConsumer: popping box b%d to flow f%d", box.d().id, self.flow.d().id);

        match self.flow {
            @InlineFlow(*) => {
                let span_length = self.flow.inline().boxes.len() - entry.start_idx + 1;
                match (span_length, box.requires_inline_spacers()) {
                    // leaf box
                    (1, _) => { self.flow.inline().boxes.push(box); return; },
                    // if this non-leaf box generates extra horizontal
                    // spacing, add a SpacerBox for it.
                    (_, true) => {
                        do box.create_inline_spacer_for_side(ctx, LogicalAfter).iter |spacer: &@RenderBox| {
                            self.flow.inline().boxes.push(*spacer);
                        }
                    },
                    // non-leaf with no spacer; do nothing
                    (_, false) => { }
                }

                // only create NodeRanges for non-leaf nodes.
                let final_span_length = self.flow.inline().boxes.len() - entry.start_idx + 1;
                assert final_span_length > 1;
                let mapping = { node: copy box.d().node,
                               span: {
                                   mut start: entry.start_idx as u16,
                                   mut len: final_span_length as u16
                               }
                              };
                debug!("BoxConsumer: adding element range=%?", mapping.span);
                self.flow.inline().elems.push(mapping);
            },
            @BlockFlow(*) => {
                assert self.flow.block().box.is_none();
                self.flow.block().box = Some(entry.start_box);
            },
            @RootFlow(*) => {
                assert self.flow.root().box.is_none();
                self.flow.root().box = Some(entry.start_box);
            },
            _ => { warn!("pop_box not implemented for flow %?", self.flow.d().id) }
        }
    }
}

impl FlowContext : FlowContextMethods {
    pure fn d(&self) -> &self/FlowData {
        match *self {
            AbsoluteFlow(ref d)    => d,
            BlockFlow(ref d, _)    => d,
            FloatFlow(ref d)       => d,
            InlineBlockFlow(ref d) => d,
            InlineFlow(ref d, _)   => d,
            RootFlow(ref d, _)     => d,
            TableFlow(ref d)       => d
        }
    }

    pure fn inline(&self) -> &self/InlineFlowData {
        match *self {
            InlineFlow(_, ref i) => i,
            _ => fail fmt!("Tried to access inline data of non-inline: f%d", self.d().id)
        }
    }

    pure fn block(&self) -> &self/BlockFlowData {
        match *self {
            BlockFlow(_, ref b) => b,
            _ => fail fmt!("Tried to access block data of non-block: f%d", self.d().id)
        }
    }

    pure fn root(&self) -> &self/RootFlowData {
        match *self {
            RootFlow(_, ref r) => r,
            _ => fail fmt!("Tried to access root data of non-root: f%d", self.d().id)
        }
    }

    fn bubble_widths(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.bubble_widths_block(ctx),
            @InlineFlow(*) => self.bubble_widths_inline(ctx),
            @RootFlow(*)   => self.bubble_widths_root(ctx),
            _ => fail fmt!("Tried to bubble_widths of flow: f%d", self.d().id)
        }
    }

    fn assign_widths(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.assign_widths_block(ctx),
            @InlineFlow(*) => self.assign_widths_inline(ctx),
            @RootFlow(*)   => self.assign_widths_root(ctx),
            _ => fail fmt!("Tried to assign_widths of flow: f%d", self.d().id)
        }
    }

    fn assign_height(@self, ctx: &LayoutContext) {
        match self {
            @BlockFlow(*)  => self.assign_height_block(ctx),
            @InlineFlow(*) => self.assign_height_inline(ctx),
            @RootFlow(*)   => self.assign_height_root(ctx),
            _ => fail fmt!("Tried to assign_height of flow: f%d", self.d().id)
        }
    }

    fn build_display_list_recurse(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
                                  offset: &Point2D<au>, list: &dl::DisplayList) {
        match self {
            @RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list),
            @BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list),
            @InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list),
            _ => fail fmt!("Tried to build_display_list_recurse of flow: %?", self)
        }
    }

    // Actual methods that do not require much flow-specific logic
    pure fn foldl_all_boxes<B: Copy>(seed: B,
                                     cb: pure fn&(a: B,@RenderBox) -> B) -> B {
        match self {
            RootFlow(*)   => option::map_default(&self.root().box, seed, |box| { cb(seed, *box) }),
            BlockFlow(*)  => option::map_default(&self.block().box, seed, |box| { cb(seed, *box) }),
            InlineFlow(*) => do self.inline().boxes.foldl(seed) |acc, box| { cb(*acc, *box) },
            _ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self)
        }
    }

    pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B,
                                          cb: pure fn&(a: B,@RenderBox) -> B) -> B {
        do self.foldl_all_boxes(seed) |acc, box| {
            if box.d().node == node { cb(acc, box) }
            else { acc }
        }
    }

    pure fn iter_all_boxes<T>(cb: pure fn&(@RenderBox) -> T) {
        match self {
            RootFlow(*)   => do self.root().box.iter |box| { cb(*box); },
            BlockFlow(*)  => do self.block().box.iter |box| { cb(*box); },
            InlineFlow(*) => for self.inline().boxes.each |box| { cb(*box); },
            _ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self)
        }
    }

    pure fn iter_boxes_for_node<T>(node: Node,
                                   cb: pure fn&(@RenderBox) -> T) {
        do self.iter_all_boxes |box| {
            if box.d().node == node { cb(box); }
        }
    }
}

/* The tree holding FlowContexts */
enum FlowTree { FlowTree }

impl FlowTree : tree::ReadMethods<@FlowContext> {
    fn each_child(ctx: @FlowContext, f: fn(box: @FlowContext) -> bool) {
        tree::each_child(&self, &ctx, |box| f(*box) )
    }

    fn with_tree_fields<R>(box: &@FlowContext, f: fn(&tree::Tree<@FlowContext>) -> R) -> R {
        f(&box.d().tree)
    }
}

impl FlowTree : tree::WriteMethods<@FlowContext> {
    fn add_child(parent: @FlowContext, child: @FlowContext) {
        tree::add_child(&self, parent, child)
    }

    pure fn eq(a: &@FlowContext, b: &@FlowContext) -> bool { core::box::ptr_eq(*a, *b) }

    fn with_tree_fields<R>(box: &@FlowContext, f: fn(&tree::Tree<@FlowContext>) -> R) -> R {
        f(&box.d().tree)
    }
}

impl FlowContext : BoxedDebugMethods {
    fn dump(@self) {
        self.dump_indent(0u);
    }

    /** Dumps the flow tree, for debugging, with indentation. */
    fn dump_indent(@self, indent: uint) {
        let mut s = ~"|";
        for uint::range(0u, indent) |_i| {
            s += ~"---- ";
        }

        s += self.debug_str();
        debug!("%s", s);

        for FlowTree.each_child(self) |child| {
            child.dump_indent(indent + 1u)
        }
    }

    fn debug_str(@self) -> ~str {
        let repr = match *self {
            InlineFlow(*) => {
                let mut s = self.inline().boxes.foldl(~"InlineFlow(children=", |s, box| {
                    fmt!("%s b%d", *s, box.d().id)
                });
                s += ~")"; s
            },
            BlockFlow(*) => {
                match self.block().box {
                    Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id),
                    None => ~"BlockFlow",
                }
            },
            RootFlow(*) => {
                match self.root().box {
                    Some(box) => fmt!("RootFlo(box=b%d)", box.d().id),
                    None => ~"RootFlow",
                }
            },
            _ => ~"(Unknown flow)"
        };

        fmt!("f%? %?", self.d().id, repr)
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;

use std::sync::mpsc::{Sender};

pub struct SubscriptionRequest {
  pub sub_id: String,
  pub subscriber: Sender<SerialResponse>
}

/// Represents the valid json requests that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialRequest {
  /// Open a port for reading
  Open { sub_id: String, port: String},
  /// Take control of a port for writing
  WriteLock { sub_id: String, port: String },
  /// Release control of a port for writing
  /// If no port is given, release all write locks
  /// for all ports
  ReleaseWriteLock {
    sub_id: String,
    port: Option<String>,
  },
  /// Write data, only works if we have control
  Write {
    sub_id: String,
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Close a port
  Close {  sub_id: String, port: Option<String> },
}

/// Represents the possible error types
/// that can be returned
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum ErrorType {
  /// Failed to parse message
  JsonParseFailure,
  /// Unknown request
  UnknownRequest,
  /// Someone else has already locked the port for writing
  AlreadyWriteLocked,
  /// WriteLock needed
  NeedWriteLock,
  /// Error reading serial port
  ReadError,
  /// Error writing serial port
  WriteError,
}

/// Represents the valid serial responses that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialResponse {
  /// Error response
  Error {
    kind: ErrorType,
    msg: String,
    detail: Option<String>,
    port: Option<String>,
    sub_id: Option<String>,
  },
  /// Data that was read from port
  Read {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Port was closed
  Closed { port: String },
  /// Ok response showing that command was accepted
  Accepted { request: SerialRequest },
}
<commit_msg>Refactoring errors to support new error handling tools<commit_after>extern crate serde_json;

use std::sync::mpsc::{Sender};

pub struct SubscriptionRequest {
  pub sub_id: String,
  pub subscriber: Sender<SerialResponse>
}

/// Represents the valid json requests that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialRequest {
  /// Open a port for reading
  Open { sub_id: String, port: String},
  /// Take control of a port for writing
  WriteLock { sub_id: String, port: String },
  /// Release control of a port for writing
  /// If no port is given, release all write locks
  /// for all ports
  ReleaseWriteLock {
    sub_id: String,
    port: Option<String>,
  },
  /// Write data, only works if we have control
  Write {
    sub_id: String,
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Close a port
  Close {  sub_id: String, port: Option<String> },
}

/// Represents the possible error types
/// that can be returned
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum ErrorType {
  /// Port not found
  PortNotFound,
  /// Subscription Id not found
  SubscriptionNotFound,
  /// Failed to parse message
  JsonParseFailure,
  /// Unknown request
  UnknownRequest,
  /// Someone else has already locked the port for writing
  AlreadyWriteLocked,
  /// WriteLock needed
  NeedWriteLock,
  /// Error reading serial port
  ReadError,
  /// Error writing serial port
  WriteError,
}

/// Represents the valid serial responses that can be made
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum SerialResponse {
  /// Error response
  Error {
    kind: ErrorType,
    msg: String,
    detail: Option<String>,
    port: Option<String>,
    sub_id: Option<String>,
  },
  /// Data that was read from port
  Read {
    port: String,
    data: String,
    base64: Option<bool>,
  },
  /// Port was closed
  Closed { port: String },
  /// Ok response showing that command was accepted
  Accepted { request: SerialRequest },
}

// #[derive(Serialize, Deserialize, Clone, Debug)]
// enum SerialResponseError{
//   UnknownRequest,
//   PortNotFound()
// }<|endoftext|>


<commit_before>
pub struct Gfx {
    pub ram: [u8; 8192],
}

impl Gfx {
    pub fn new() -> Gfx {
        Gfx { ram: [0u8; 8192] }
    }

    pub fn write_byte(&mut self, addr: u16, byte: u8) {
        self.ram[addr as usize] = byte;
    }
}

#[cfg(test)]
pub mod tests {
    #[test]
    pub fn can_write_to_gfx_memory() {
        let ldhlda = vec![0x32, 0xCB, 0x7C];
        let mut cpu = ::gameboy::Cpu::new(true, ldhlda);

        cpu.step();

        // Graphics RAM
    }
}
<commit_msg>Added todo<commit_after>
pub struct Gfx {
    pub ram: [u8; 8192],
}

impl Gfx {
    pub fn new() -> Gfx {
        Gfx { ram: [0u8; 8192] }
    }

    pub fn write_byte(&mut self, addr: u16, byte: u8) {
        self.ram[addr as usize] = byte;
    }
}

#[cfg(test)]
pub mod tests {
    #[test]
    pub fn can_write_to_gfx_memory() {
        let ldhlda = vec![0x32, 0xCB, 0x7C];
        let mut cpu = ::gameboy::Cpu::new(true, ldhlda);

        cpu.step();

        // TODO: Test that graphics memory is supported for the LD (HL-),A opcode (0x32)
    }
}
<|endoftext|>


<commit_before>use std::iter::{repeat};
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n > kb.powf(4_f64) => format!("{:.*} TG", 2, $n / kb.powf(4_f64)),
            $n if $n > kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n > kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    is_finish: bool,
    show_bar: bool,
    show_speed: bool,
    show_percent: bool,
    show_counter: bool,
    show_time_left: bool,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pb::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: false,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pb::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is "[=>-]"
    ///
    /// # Examples
    ///
    /// ```no_run
    /// pb.format("[=>_]")
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    /// Add to current value
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        return self.add(1);
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if !self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{}/s ", speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::{ProgressBar};

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2, "should add the given `n` to current");
        assert!(pb.add(2) == pb.current, "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let FORMAT = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(FORMAT);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain + &pb.bar_end == FORMAT);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total, "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }
}
<commit_msg>test(pb): kb_fmt<commit_after>use std::iter::{repeat};
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    is_finish: bool,
    show_bar: bool,
    show_speed: bool,
    show_percent: bool,
    show_counter: bool,
    show_time_left: bool,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pb::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: false,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pb::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is "[=>-]"
    ///
    /// # Examples
    ///
    /// ```no_run
    /// pb.format("[=>_]")
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    /// Add to current value
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        return self.add(1);
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if !self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{}/s ", speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::{ProgressBar};

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2, "should add the given `n` to current");
        assert!(pb.add(2) == pb.current, "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let FORMAT = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(FORMAT);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain + &pb.bar_end == FORMAT);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total, "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<|endoftext|>