<commit_before>use super::classfile::*;

pub struct ClassfilePrinter;

impl ClassfilePrinter {
    pub fn render_lines(classfile: &Classfile) -> Vec<String> {
        let mut lines = vec![];

        lines.push(format!("class {}", ClassfilePrinter::resolve_class(&classfile.this_class, &classfile.constant_pool)));
        lines.push(format!("Minor version: {}", classfile.version.minor_version));
        lines.push(format!("Major version: {}", classfile.version.major_version));
        lines.push(format!("Flags: {}", ClassfilePrinter::render_flags(&classfile.access_flags)));
        lines.push(format!("Constant pool:"));

        let mut i: i32 = -1;

        let _: Vec<()> = ClassfilePrinter::render_constant_pool(&classfile.constant_pool).iter()
            .map(|constant| {
                i = i + 1;
                format!("{:>5} = {}", format!("#{}", i), constant)
            })
            .map(|line| lines.push(line))
            .collect();

        lines
    }

    pub fn render_flags(flags: &AccessFlags) -> String {
        let mut flag_vec = vec![];

        if flags.has_flag(ClassAccessFlags::Public as u16) {
            flag_vec.push("ACC_PUBLIC ");
        }

        if flags.has_flag(ClassAccessFlags::Super as u16) {
            flag_vec.push("ACC_SUPER ");
        }

        // TODO implement other access flags

        flag_vec.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc })
    }

    pub fn render_constant_pool(constant_pool: &ConstantPool) -> Vec<String> {
        constant_pool.constants.iter().map(|constant| {
            ClassfilePrinter::render_constant(&constant, constant_pool)
        }).collect()
    }

    pub fn render_constant(constant: &Constant, pool: &ConstantPool) -> String {
        match constant {
            &Constant::Utf8(ref content) => format!("Utf8               {}", String::from_utf8_lossy(content.as_slice())),
            &Constant::Integer(value) => format!("Integer            {}", value),
            &Constant::Float(value) => format!("Float               {}", value),
            &Constant::Long(value) => format!("Long               {}", value),
            &Constant::Double(value) => format!("Double              {}", value),
            &Constant::Class(ref index) => format!("Class              #{:<24}// {}", index.idx, ClassfilePrinter::resolve_utf8(index, pool)),
            &Constant::FieldRef { class_index: ref ci, name_and_type_index: ref ni } => format!("FieldRef           {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, &pool)),
            &Constant::MethodRef { class_index: ref ci, name_and_type_index: ref ni } => format!("MethodRef          {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::InterfaceMethodRef { class_index: ref ci, name_and_type_index: ref ni } => format!("InterfaceMethodRef {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::String(ref cpi) => format!("String             #{:<24}// {}", cpi.idx, ClassfilePrinter::resolve_utf8(cpi, pool)),
            &Constant::NameAndType { name_index: ref ni, descriptor_index: ref dp } => format!("NameAndType        {:<24} // {}:{}", format!("#{}.#{}", ni.idx, dp.idx), ClassfilePrinter::resolve_utf8(ni, pool), ClassfilePrinter::resolve_utf8(dp, pool)),
            &Constant::MethodHandle { reference_kind: ref kind, reference_index: ref ri } => format!("MethodHandle       {} #{}", ClassfilePrinter::resolve_reference_kind(kind), ri.idx),
            &Constant::MethodType(ref cpi) => format!("MethodType         #{}", cpi.idx),
            &Constant::InvokeDynamic { bootstrap_method_attr_index: ref bi, name_and_type_index: ref ni } => format!("InvokeDynamic      #{}.{}", bi.idx, ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::Unknown(value) => format!("Unknown constant        {}", value),
            &Constant::Placeholder => format!("Placeholder")
        }
    }

    pub fn render_utf8(index: &ConstantPoolIndex, cp: &ConstantPool) -> Option<String> {
        cp.get_utf8_string(index.idx as u16)
    }

    pub fn resolve_utf8(index: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        ClassfilePrinter::render_utf8(index, cp).unwrap_or(String::from("<Not UTF8>"))
    }

    pub fn resolve_class(index: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        cp.resolve_index(index).map(|constant| match constant {
            &Constant::Class(ref idx) => ClassfilePrinter::resolve_utf8(idx, cp),
            _ => String::from("<Not a class>")
        }).unwrap_or(String::from("<Not found>"))
    }

    pub fn resolve_name_and_type(nandt: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        cp.resolve_index(nandt).map(|constant| match constant {
            &Constant::NameAndType { name_index: ref ni, descriptor_index: ref di } => format!("{}:{}", ClassfilePrinter::resolve_utf8(ni, cp), ClassfilePrinter::resolve_utf8(di, cp)),
            _ => String::from("<Not a name and type index>")
        }).unwrap_or(String::from("<Not found>"))
    }

    pub fn resolve_reference_kind(kind: &ReferenceKind) -> String {
        String::from(match kind {
            &ReferenceKind::GetField => "GetField",
            &ReferenceKind::GetStatic => "GetStatic",
            &ReferenceKind::InvokeInterface => "InvokeInterface",
            &ReferenceKind::InvokeSpecial => "InvokeSpecial",
            &ReferenceKind::InvokeStatic => "InvokeStatic",
            &ReferenceKind::InvokeVirtual => "InvokeVirtual",
            &ReferenceKind::NewInvokeSpecial => "NewInvokeSpecial",
            &ReferenceKind::PutField => "PutField",
            &ReferenceKind::PutStatic => "PutStatic",
            _ => "Unknown"
        })
    }
}
<commit_msg>Update class file printer<commit_after>use super::classfile::*;

pub struct ClassfilePrinter;

impl ClassfilePrinter {
    pub fn render_lines(classfile: &Classfile) -> Vec<String> {
        let mut lines = vec![];

        lines.push(format!("class {}", ClassfilePrinter::resolve_class(&classfile.this_class, &classfile.constant_pool)));
        lines.push(format!("Minor version: {}", classfile.version.minor_version));
        lines.push(format!("Major version: {}", classfile.version.major_version));
        lines.push(format!("Flags: {}", ClassfilePrinter::render_flags(&classfile.access_flags)));
        lines.push(format!("Constant pool:"));

        let mut i: i32 = -1;

        let _: Vec<()> = ClassfilePrinter::render_constant_pool(&classfile.constant_pool).iter()
            .map(|constant| {
                i = i + 1;
                format!("{:>5} = {}", format!("#{}", i), constant)
            })
            .map(|line| lines.push(line))
            .collect();

        let _: Vec<()> = ClassfilePrinter::render_methods(&classfile).iter()
            .map(|method| {
                format!("{}", method)
            })
            .map(|line| lines.push(line))
            .collect();

        lines
    }

    pub fn render_flags(flags: &AccessFlags) -> String {
        let mut flag_vec = vec![];

        if flags.has_flag(ClassAccessFlags::Public as u16) {
            flag_vec.push("ACC_PUBLIC ");
        }

        if flags.has_flag(ClassAccessFlags::Super as u16) {
            flag_vec.push("ACC_SUPER ");
        }

        // TODO implement other access flags

        flag_vec.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc })
    }

    pub fn render_constant_pool(constant_pool: &ConstantPool) -> Vec<String> {
        constant_pool.constants.iter().map(|constant| {
            ClassfilePrinter::render_constant(&constant, constant_pool)
        }).collect()
    }

    pub fn render_constant(constant: &Constant, pool: &ConstantPool) -> String {
        match constant {
            &Constant::Utf8(ref content) => format!("Utf8               {}", String::from_utf8_lossy(content.as_slice())),
            &Constant::Integer(value) => format!("Integer            {}", value),
            &Constant::Float(value) => format!("Float               {}", value),
            &Constant::Long(value) => format!("Long               {}", value),
            &Constant::Double(value) => format!("Double              {}", value),
            &Constant::Class(ref index) => format!("Class              #{:<24}// {}", index.idx, ClassfilePrinter::resolve_utf8(index, pool)),
            &Constant::FieldRef { class_index: ref ci, name_and_type_index: ref ni } => format!("FieldRef           {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, &pool)),
            &Constant::MethodRef { class_index: ref ci, name_and_type_index: ref ni } => format!("MethodRef          {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::InterfaceMethodRef { class_index: ref ci, name_and_type_index: ref ni } => format!("InterfaceMethodRef {:<24} // {}.{}", format!("#{}.#{}", ci.idx, ni.idx), ClassfilePrinter::resolve_class(ci, pool), ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::String(ref cpi) => format!("String             #{:<24}// {}", cpi.idx, ClassfilePrinter::resolve_utf8(cpi, pool)),
            &Constant::NameAndType { name_index: ref ni, descriptor_index: ref dp } => format!("NameAndType        {:<24} // {}:{}", format!("#{}.#{}", ni.idx, dp.idx), ClassfilePrinter::resolve_utf8(ni, pool), ClassfilePrinter::resolve_utf8(dp, pool)),
            &Constant::MethodHandle { reference_kind: ref kind, reference_index: ref ri } => format!("MethodHandle       {} #{}", ClassfilePrinter::resolve_reference_kind(kind), ri.idx),
            &Constant::MethodType(ref cpi) => format!("MethodType         #{}", cpi.idx),
            &Constant::InvokeDynamic { bootstrap_method_attr_index: ref bi, name_and_type_index: ref ni } => format!("InvokeDynamic      #{}.{}", bi.idx, ClassfilePrinter::resolve_name_and_type(ni, pool)),
            &Constant::Unknown(value) => format!("Unknown constant        {}", value),
            &Constant::Placeholder => format!("Placeholder")
        }
    }

    pub fn render_utf8(index: &ConstantPoolIndex, cp: &ConstantPool) -> Option<String> {
        cp.get_utf8_string(index.idx as u16)
    }

    pub fn resolve_utf8(index: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        ClassfilePrinter::render_utf8(index, cp).unwrap_or(String::from("<Not UTF8>"))
    }

    pub fn resolve_class(index: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        cp.resolve_index(index).map(|constant| match constant {
            &Constant::Class(ref idx) => ClassfilePrinter::resolve_utf8(idx, cp),
            _ => String::from("<Not a class>")
        }).unwrap_or(String::from("<Not found>"))
    }

    pub fn resolve_name_and_type(nandt: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        cp.resolve_index(nandt).map(|constant| match constant {
            &Constant::NameAndType { name_index: ref ni, descriptor_index: ref di } => format!("{}:{}", ClassfilePrinter::resolve_utf8(ni, cp), ClassfilePrinter::resolve_utf8(di, cp)),
            _ => String::from("<Not a name and type index>")
        }).unwrap_or(String::from("<Not found>"))
    }

    pub fn resolve_method_reference(method_reference: &ConstantPoolIndex, cp: &ConstantPool) -> String {
        cp.resolve_index(method_reference).map(|constant| match constant {
            &Constant::MethodRef { class_index: ref ci, name_and_type_index: ref ni } => format!("{}:{}", ClassfilePrinter::resolve_class(ci, cp), ClassfilePrinter::resolve_name_and_type(ni, cp)),
            _ => String::from("Not a method reference>")
        }).unwrap_or(String::from("<Not found>"))
    }

    pub fn resolve_reference_kind(kind: &ReferenceKind) -> String {
        String::from(match kind {
            &ReferenceKind::GetField => "GetField",
            &ReferenceKind::GetStatic => "GetStatic",
            &ReferenceKind::InvokeInterface => "InvokeInterface",
            &ReferenceKind::InvokeSpecial => "InvokeSpecial",
            &ReferenceKind::InvokeStatic => "InvokeStatic",
            &ReferenceKind::InvokeVirtual => "InvokeVirtual",
            &ReferenceKind::NewInvokeSpecial => "NewInvokeSpecial",
            &ReferenceKind::PutField => "PutField",
            &ReferenceKind::PutStatic => "PutStatic",
            _ => "Unknown"
        })
    }

    pub fn render_methods(classfile: &Classfile) -> Vec<String> {
        classfile.methods.iter().flat_map(|method| ClassfilePrinter::render_method(method, &classfile.constant_pool)).collect()
    }

    pub fn render_method(method: &Method, cp: &ConstantPool) -> Vec<String> {
        let mut lines = vec![];

        lines.push(format!("  {}()", ClassfilePrinter::resolve_utf8(&method.name_index, cp)));
        lines.push(format!("    Descriptor: {}", ClassfilePrinter::resolve_utf8(&method.descriptor_index, cp)));
        // TODO display access flags
        lines.push(String::from("    Code: "));

        let _: Vec<()> = method.attributes.iter()/*.filter(|attr| match attr {
            &&Attribute::Code { max_stack: _, max_locals: _, code: _, exception_table: _, attributes: _ } => true,
            _ => false
        })*/.flat_map(|code_attr| ClassfilePrinter::render_attribute(code_attr, cp)).map(|line| lines.push(line)).collect();

        lines.push(String::from(""));

        lines
    }

    pub fn render_attribute(code: &Attribute, cp: &ConstantPool) -> Vec<String> {
        let mut lines = vec![];

        match code {
            &Attribute::Code { max_stack: ref ms, max_locals: ref ml, code: ref c, exception_table: ref et, attributes: ref at } => {
                let mut instr_pointer: usize = 0;

                lines.push(format!("      stack={} locals={} args={}", ms, ml, "???"));
                let _: Vec<()> = c.iter().map(|instr| (instr.len(), match instr {
                    &Instruction::AALOAD => format!("aaload"),
                    &Instruction::AASTORE => format!("aastore"),
                    &Instruction::ACONST_NULL => format!("aconst_null"),
                    &Instruction::ALOAD(value) => format!("aload {}", value),
                    &Instruction::ALOAD_0 => format!("aload_0"),
                    &Instruction::ALOAD_1 => format!("aload_1"),
                    &Instruction::ALOAD_2 => format!("aload_2"),
                    &Instruction::ALOAD_3 => format!("aload_3"),
                    &Instruction::ATHROW => format!("athrow"),
                    &Instruction::BALOAD => format!("baload"),
                    &Instruction::BASTORE => format!("bastore"),
                    &Instruction::BIPUSH(value) => format!("bipush {}", value),
                    &Instruction::CALOAD => format!("caload"),
                    &Instruction::CASTORE => format!("castore"),
                    &Instruction::CHECKCAST(value) => format!("checkcast {}", value),
                    &Instruction::D2F => format!("d2f"),
                    &Instruction::D2I => format!("d2i"),
                    &Instruction::D2L => format!("d2l"),
                    &Instruction::DADD => format!("dadd"),
                    &Instruction::DALOAD => format!("daload"),
                    &Instruction::DASTORE => format!("dastore"),
                    &Instruction::DCMPL => format!("dcmpl"),
                    &Instruction::DCMPG => format!("dcmpg"),
                    &Instruction::DCONST_0 => format!("dconst_0"),
                    &Instruction::DCONST_1 => format!("dconst_1"),
                    &Instruction::DDIV => format!("ddiv"),
                    &Instruction::DLOAD(value) => format!("dload {}", value),
                    &Instruction::DLOAD_0 => format!("dload_0"),
                    &Instruction::DLOAD_1 => format!("dload_1"),
                    &Instruction::DLOAD_2 => format!("dload_2"),
                    &Instruction::DLOAD_3 => format!("dload_3"),
                    &Instruction::DMUL => format!("dmul"),
                    &Instruction::DNEG => format!("dneg"),
                    &Instruction::DREM => format!("drem"),
                    &Instruction::DRETURN => format!("dreturn"),
                    &Instruction::DSTORE(value) => format!("dstore {}", value),
                    &Instruction::DSTORE_0 => format!("dstore_0"),
                    &Instruction::DSTORE_1 => format!("dstore_1"),
                    &Instruction::DSTORE_2 => format!("dstore_2"),
                    &Instruction::DSTORE_3 => format!("dstore_3"),
                    &Instruction::DSUB => format!("dsub"),
                    &Instruction::DUP => format!("dup"),
                    &Instruction::DUP_X1 => format!("dup_x1"),
                    &Instruction::DUP_X2 => format!("dup_x2"),
                    &Instruction::DUP2 => format!("dup2"),
                    &Instruction::DUP2_X1 => format!("dup2_x1"),
                    &Instruction::DUP2_X2 => format!("dup2_x2"),
                    &Instruction::F2D => format!("f2d"),
                    &Instruction::F2I => format!("f2i"),
                    &Instruction::F2L => format!("f2l"),
                    &Instruction::FADD => format!("fadd"),
                    &Instruction::FALOAD => format!("faload"),
                    &Instruction::FASTORE => format!("fastore"),
                    &Instruction::FCMPL => format!("fcmpl"),
                    &Instruction::FCMPG => format!("fcmpg"),
                    &Instruction::FCONST_0 => format!("fconst_0"),
                    &Instruction::FCONST_1 => format!("fconst_1"),
                    &Instruction::FCONST_2 => format!("fconst_2"),
                    &Instruction::FDIV => format!("fdiv"),
                    &Instruction::FLOAD(value) => format!("fload {}", value),
                    &Instruction::FLOAD_0 => format!("fload_0"),
                    &Instruction::FLOAD_1 => format!("fload_1"),
                    &Instruction::FLOAD_2 => format!("fload_2"),
                    &Instruction::FLOAD_3 => format!("fload_3"),
                    &Instruction::FMUL => format!("fmul"),
                    &Instruction::FNEG => format!("fneg"),
                    &Instruction::FREM => format!("frem"),
                    &Instruction::FRETURN => format!("freturn"),
                    &Instruction::FSTORE(value) => format!("fstore {}", value),
                    &Instruction::FSTORE_0 => format!("fstore_0"),
                    &Instruction::FSTORE_1 => format!("fstore_1"),
                    &Instruction::FSTORE_2 => format!("fstore_2"),
                    &Instruction::FSTORE_3 => format!("fstore_3"),
                    &Instruction::FSUB => format!("fsub"),
                    &Instruction::GETFIELD(value) => format!("getfield {}", value),
                    &Instruction::GETSTATIC(value) => format!("getstatic {}", value),
                    &Instruction::GOTO(value) => format!("goto {}", value),
                    &Instruction::GOTO_W(value) => format!("goto_w {}", value),
                    &Instruction::I2B => format!("i2b"),
                    &Instruction::I2C => format!("i2c"),
                    &Instruction::I2D => format!("i2d"),
                    &Instruction::I2F => format!("i2f"),
                    &Instruction::I2L => format!("i2l"),
                    &Instruction::I2S => format!("i2s"),
                    &Instruction::IADD => format!("iadd"),
                    &Instruction::IALOAD => format!("iaload"),
                    &Instruction::IAND => format!("iand"),
                    &Instruction::IASTORE => format!("iastore"),
                    &Instruction::ICONST_M1 => format!("iconst_m1"),
                    &Instruction::ICONST_0 => format!("iconst_0"),
                    &Instruction::ICONST_1 => format!("iconst_1"),
                    &Instruction::ICONST_2 => format!("iconst_2"),
                    &Instruction::ICONST_3 => format!("iconst_3"),
                    &Instruction::ICONST_4 => format!("iconst_4"),
                    &Instruction::ICONST_5 => format!("iconst_5"),
                    &Instruction::IDIV => format!("idiv"),
                    &Instruction::IF_ACMPEQ(value) => format!("if_acmpeq"),
                    &Instruction::IF_ACMPNE(value) => format!("if_acmpne"),
                    &Instruction::IF_ICMPEQ(value) => format!("if_icmpeq"),
                    &Instruction::IF_ICMPNE(value) => format!("if_icmpne"),
                    &Instruction::IF_ICMPLT(value) => format!("if_icmplt"),
                    &Instruction::IF_ICMPGE(value) => format!("if_icmpge"),
                    &Instruction::IF_ICMPGT(value) => format!("if_icmpgt"),
                    &Instruction::IF_ICMPLE(value) => format!("if_icmple"),
                    &Instruction::IFEQ(value) => format!("ifeq"),
                    &Instruction::IFNE(value) => format!("ifne"),
                    &Instruction::IFLT(value) => format!("iflt"),
                    &Instruction::IFGE(value) => format!("ifge"),
                    &Instruction::IFGT(value) => format!("ifgt"),
                    &Instruction::IFLE(value) => format!("ifle"),
                    &Instruction::IFNONNULL(value) => format!("ifnonnull"),
                    &Instruction::IFNULL(value) => format!("ifnull"),
                    &Instruction::IINC(value, increment) => format!("iinc"),
                    &Instruction::ILOAD(value) => format!("iload"),
                    &Instruction::ILOAD_0 => format!("iload_0"),
                    &Instruction::ILOAD_1 => format!("iload_1"),
                    &Instruction::ILOAD_2 => format!("iload_2"),
                    &Instruction::ILOAD_3 => format!("iload_3"),
                    &Instruction::IMUL => format!("imul"),
                    &Instruction::INEG => format!("ineg"),
                    &Instruction::INSTANCEOF(value) => format!("instanceof"),
                    &Instruction::INVOKEDYNAMIC(value) => format!("invokedynamic #{}", value),
                    &Instruction::INVOKEINTERFACE(value, index) => format!("invokeinterface #{}", value),
                    &Instruction::INVOKESPECIAL(value) => format!("invokespecial {}", ClassfilePrinter::resolve_method_reference(&ConstantPoolIndex::new(value as usize), cp)),
                    &Instruction::INVOKESTATIC(value) => format!("invokestatic {}", ClassfilePrinter::resolve_method_reference(&ConstantPoolIndex::new(value as usize), cp)),
                    &Instruction::INVOKEVIRTUAL(value) => format!("invokevirtual {}", ClassfilePrinter::resolve_method_reference(&ConstantPoolIndex::new(value as usize), cp)),
                    &Instruction::IOR => format!("ior"),
                    &Instruction::IREM => format!("irem"),
                    &Instruction::IRETURN => format!("ireturn"),
                    &Instruction::ISHL => format!("ishl"),
                    &Instruction::ISHR => format!("ishr"),
                    &Instruction::ISTORE(value) => format!("istore {}", value),
                    &Instruction::ISTORE_0 => format!("istore_0"),
                    &Instruction::ISTORE_1 => format!("istore_1"),
                    &Instruction::ISTORE_2 => format!("istore_2"),
                    &Instruction::ISTORE_3 => format!("istore_3"),
                    &Instruction::ISUB => format!("isub"),
                    &Instruction::IUSHR => format!("iushr"),
                    &Instruction::IXOR => format!("ixor"),
                    &Instruction::JSR(value) => format!("jsr"),
                    &Instruction::JSR_W(value) => format!("jsr_w"),
                    &Instruction::L2D => format!("l2d"),
                    &Instruction::L2F => format!("l2f"),
                    &Instruction::L2I => format!("l2i"),
                    &Instruction::LADD => format!("ladd"),
                    &Instruction::LALOAD => format!("laload"),
                    &Instruction::LAND => format!("land"),
                    &Instruction::LASTORE => format!("lastore"),
                    &Instruction::LCMP => format!("lcmp"),
                    &Instruction::LCONST_0 => format!("lconst_0"),
                    &Instruction::LCONST_1 => format!("lconst_1"),
                    &Instruction::LDC(value) => format!("ldc"),
                    &Instruction::LDC_W(value) => format!("ldc_w"),
                    &Instruction::LDC2_W(value) => format!("ldc2_w"),
                    &Instruction::LDIV => format!("ldiv"),
                    &Instruction::LLOAD(value) => format!("lload"),
                    &Instruction::LLOAD_0 => format!("lload_0"),
                    &Instruction::LLOAD_1 => format!("lload_1"),
                    &Instruction::LLOAD_2 => format!("lload_2"),
                    &Instruction::LLOAD_3 => format!("lload_3"),
                    &Instruction::LMUL => format!("lmul"),
                    &Instruction::LNEG => format!("lneg"),
                    &Instruction::LOOKUPSWITCH(value, ref table) => format!("lookupswitch"),
                    &Instruction::LOR => format!("lor"),
                    &Instruction::LREM => format!("lrem"),
                    &Instruction::LRETURN => format!("lreturn"),
                    &Instruction::LSHL => format!("lshl"),
                    &Instruction::LSHR => format!("lshr"),
                    &Instruction::LSTORE(value) => format!("lstore {}", value),
                    &Instruction::LSTORE_0 => format!("lstore_0"),
                    &Instruction::LSTORE_1 => format!("lstore_1"),
                    &Instruction::LSTORE_2 => format!("lstore_2"),
                    &Instruction::LSTORE_3 => format!("lstore_3"),
                    &Instruction::LSUB => format!("lsub"),
                    &Instruction::LUSHR => format!("lushr"),
                    &Instruction::LXOR => format!("lxor"),
                    &Instruction::MONITORENTER => format!("monitorenter"),
                    &Instruction::MONITOREXIT => format!("monitorexit"),
                    &Instruction::MULTIANEWARRAY(value, size) => format!("multianewarray"),
                    &Instruction::NEW(value) => format!("new"),
                    &Instruction::NEWARRAY(value) => format!("newarray"),
                    &Instruction::NOP => format!("nop"),
                    &Instruction::POP => format!("pop"),
                    &Instruction::POP2 => format!("pop2"),
                    &Instruction::PUTFIELD(value) => format!("putfield"),
                    &Instruction::PUTSTATIC(value) => format!("putstatic"),
                    &Instruction::RET(value) => format!("ret"),
                    &Instruction::RETURN => format!("return"),
                    &Instruction::SALOAD => format!("saload"),
                    &Instruction::SASTORE => format!("sastore"),
                    &Instruction::SIPUSH(value) => format!("sipush {}", value),
                    &Instruction::SWAP => format!("swap"),
                    &Instruction::TABLESWITCH(value, _, _, _) => format!("tableswitch"),
                    &Instruction::IINC_W(value, increment) => format!("iinc_w"),
                    &Instruction::ILOAD_W(value) => format!("iload_w {}", value),
                    &Instruction::FLOAD_W(value) => format!("fload_w {}", value),
                    &Instruction::ALOAD_W(value) => format!("aload_w {}", value),
                    &Instruction::LLOAD_W(value) => format!("lload_w {}", value),
                    &Instruction::DLOAD_W(value) => format!("dload_w {}", value),
                    &Instruction::ISTORE_W(value) => format!("istore_w {}", value),
                    &Instruction::FSTORE_W(value) => format!("fstore_w {}", value),
                    &Instruction::ASTORE_W(value) => format!("astore_w {}", value),
                    &Instruction::LSTORE_W(value) => format!("lstore_w {}", value),
                    &Instruction::DSTORE_W(value) => format!("dstore_w {}", value),
                    &Instruction::RET_W(value) => format!("ret_w {}", value),
                    &Instruction::PADDED_INSTRUCTION(value) => format!("padded_instruction {}", value),
                    &Instruction::WTF(value) => format!("wtf {}", value),
                    _ => format!("instr")
                })).map(|line| {
                    lines.push(format!("     {:>4} {}", instr_pointer, line.1));
                    instr_pointer = instr_pointer + line.0
                }).collect();
            },
            &Attribute::LineNumberTable(ref table) => {
                lines.push(String::from("    LineNumberTable"));

                let _: Vec<()> = ClassfilePrinter::render_line_number_table(table).iter().map(|line_number| lines.push(format!("  {}", line_number))).collect();
            },
            &Attribute::ConstantValue(ref cpi) => {
                lines.push(format!("    ConstantValue #{}", cpi.idx));
            },
            &Attribute::StackMapTable(_) => {
                lines.push(format!("    StackMapTable"));
            },
            &Attribute::AnnotationDefault(_) => {
                lines.push(format!("    AnnotationDefault"));
            },
            &Attribute::BootstrapMethods(_) => {
                lines.push(format!("    BootstrapMethods"));
            },
            _ => {
                lines.push(format!("RandomAttribute"));
                ()
            },
    //Code { max_stack: u16, max_locals: u16, code: Vec<Instruction>, exception_table: Vec<ExceptionHandler>, attributes: Vec<Attribute> },
        }

        lines
    }

    pub fn render_line_number_table(table: &Vec<LineNumberTable>) -> Vec<String> {
        table.iter().map(|line| format!("{} {}", line.start_pc, line.line_number)).collect()
    }
}
<|endoftext|>


<commit_before>use std::fs::{File, Permissions};
use std::fs::{remove_file, rename, create_dir_all, set_permissions};
use std::io::{stdout, stderr};
use std::io;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use argparse::{ArgumentParser};
use argparse::{Store, StoreOption, StoreTrue};
use sha2::{Digest, Sha256};
use unshare::{Command, Stdio};

use capsule::Context;
use capsule::packages as capsule;
use capsule::packages::State;
use file_util::check_stream_hashsum;
use process_util::{cmd_show};
use digest::hex;

pub fn download_file<S>(state: &mut State, urls: &[S], sha256: Option<String>,
    refresh: bool)
    -> Result<PathBuf, String>
    where S: AsRef<str>
{
    let urlpath = Path::new(urls[0].as_ref());
    let hash = match sha256 {
        Some(ref sha256) => sha256[..8].to_string(),
        None => {
            let mut hash = Sha256::new();
            hash.input(urls[0].as_ref().as_bytes());
            format!("{:.8x}", hex(&hash))
        },
    };
    let name = match urlpath.file_name().and_then(|x| x.to_str()) {
        Some(name) => name,
        None => "file.bin",
    };
    let name = hash[..8].to_string() + "-" + name;
    let dir = Path::new("/vagga/cache/downloads");
    if !dir.exists() {
        create_dir_all(&dir)
            .map_err(|e| format!("Error moving file: {}", e))?;
        set_permissions(&dir, Permissions::from_mode(0o755))
            .map_err(|e| format!("Can't chmod file: {}", e))?;
    }
    let filename = dir.join(&name);
    if !refresh && filename.exists() {
        return Ok(filename);
    }
    let https = urls.iter().any(|x| x.as_ref().starts_with("https:"));
    if https {
        capsule::ensure(state, &[capsule::Https])?;
    }
    for url in urls {
        let url = url.as_ref();
        info!("Downloading image {} -> {:?}", url, filename);
        let tmpfilename = filename.with_file_name(name.clone() + ".part");
        let mut cmd = Command::new(
            if https { "/usr/bin/wget" } else { "/vagga/bin/busybox" });
        cmd.stdin(Stdio::null());
        if !https {
            cmd.arg("wget");
        }
        cmd.arg("-O");
        cmd.arg(&tmpfilename);
        cmd.arg(url);

        debug!("Running: {}", cmd_show(&cmd));
        match cmd.status() {
            Ok(st) if st.success() => {
                if let Some(ref sha256) = sha256 {
                    let mut tmpfile = try_msg!(File::open(&tmpfilename),
                                            "Cannot open archive: {err}");
                    if let Err(e) = check_stream_hashsum(&mut tmpfile, sha256) {
                        remove_file(&filename)
                            .map_err(|e| error!(
                                "Error unlinking cache file: {}", e)).ok();
                        error!("Bad hashsum of {:?}", url);
                        return Err(e);
                    }
                }
                rename(&tmpfilename, &filename)
                    .map_err(|e| format!("Error moving file: {}", e))?;
                return Ok(filename);
            }
            Ok(val) => {
                match remove_file(&tmpfilename) {
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                        // Assume we got 404 and download have not even started
                        continue;
                    }
                    Err(e) => {
                        error!("Error unlinking cache file: {}", e);
                    }
                    Ok(_) => {}
                }
                error!("Error downloading {:?}, wget {:?}", url, val);
                continue;
            }
            Err(x) => {
                return Err(format!("Error starting wget: {}", x));
            }
        }
    }
    return Err(format!("Error downloading file {:?} from {:?}",
        filename, urls.iter().map(|x| x.as_ref()).collect::<Vec<_>>()));
}

fn check_if_local(url: &str, sha256: &Option<String>)
    -> Result<Option<PathBuf>, String>
{
    let path = if url.starts_with(".") {
        PathBuf::from("/work").join(url)
    } else if url.starts_with("/volumes/") {
        PathBuf::from(url)
    } else {
        return Ok(None);
    };
    if let Some(ref sha256) = *sha256 {
        let mut file = try_msg!(File::open(&path),
            "Cannot open file: {err}");
        check_stream_hashsum(&mut file, sha256)
            .map_err(|e| format!(
                "Error when checking hashsum for file {:?}: {}",
                &path, e))?;
    }
    Ok(Some(path))
}

pub fn maybe_download_and_check_hashsum(state: &mut State,
    url: &str, sha256: Option<String>, refresh: bool)
    -> Result<(PathBuf, bool), String>
{
    Ok(match check_if_local(url, &sha256)? {
        Some(path) => (path, false),
        None => (download_file(state, &[url], sha256, refresh)?, true),
    })
}

pub fn run_download(context: &Context, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut url = "".to_string();
    let mut sha256 = None;
    let mut refresh = false;
    {
        args.insert(0, "vagga _capsule script".to_string());
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Downloads file if not cached, puts it into a cache printing the
            path to the cached item to the stdout.
            ");
        ap.refer(&mut sha256)
            .add_option(&["--sha256"], StoreOption,
                "A SHA256 hashsum of a script (if you want to check)");
        ap.refer(&mut refresh)
            .add_option(&["--refresh"], StoreTrue,
                "Download file even if there is a cached item");
        ap.refer(&mut url)
            .add_argument("url", Store,
                "A file to download")
            .required();
        ap.stop_on_first_argument(true);
        match ap.parse(args.clone(), &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    // TODO(tailhook) wrap settings into Arc in the launcher's main
    let mut capsule = State::new(&Arc::new(context.settings.clone()));
    let (path, _) = maybe_download_and_check_hashsum(
        &mut capsule, &url, sha256, refresh)?;
    println!("{}", path.display());
    return Ok(0);
}
<commit_msg>Fix typo in usage<commit_after>use std::fs::{File, Permissions};
use std::fs::{remove_file, rename, create_dir_all, set_permissions};
use std::io::{stdout, stderr};
use std::io;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use argparse::{ArgumentParser};
use argparse::{Store, StoreOption, StoreTrue};
use sha2::{Digest, Sha256};
use unshare::{Command, Stdio};

use capsule::Context;
use capsule::packages as capsule;
use capsule::packages::State;
use file_util::check_stream_hashsum;
use process_util::{cmd_show};
use digest::hex;

pub fn download_file<S>(state: &mut State, urls: &[S], sha256: Option<String>,
    refresh: bool)
    -> Result<PathBuf, String>
    where S: AsRef<str>
{
    let urlpath = Path::new(urls[0].as_ref());
    let hash = match sha256 {
        Some(ref sha256) => sha256[..8].to_string(),
        None => {
            let mut hash = Sha256::new();
            hash.input(urls[0].as_ref().as_bytes());
            format!("{:.8x}", hex(&hash))
        },
    };
    let name = match urlpath.file_name().and_then(|x| x.to_str()) {
        Some(name) => name,
        None => "file.bin",
    };
    let name = hash[..8].to_string() + "-" + name;
    let dir = Path::new("/vagga/cache/downloads");
    if !dir.exists() {
        create_dir_all(&dir)
            .map_err(|e| format!("Error moving file: {}", e))?;
        set_permissions(&dir, Permissions::from_mode(0o755))
            .map_err(|e| format!("Can't chmod file: {}", e))?;
    }
    let filename = dir.join(&name);
    if !refresh && filename.exists() {
        return Ok(filename);
    }
    let https = urls.iter().any(|x| x.as_ref().starts_with("https:"));
    if https {
        capsule::ensure(state, &[capsule::Https])?;
    }
    for url in urls {
        let url = url.as_ref();
        info!("Downloading image {} -> {:?}", url, filename);
        let tmpfilename = filename.with_file_name(name.clone() + ".part");
        let mut cmd = Command::new(
            if https { "/usr/bin/wget" } else { "/vagga/bin/busybox" });
        cmd.stdin(Stdio::null());
        if !https {
            cmd.arg("wget");
        }
        cmd.arg("-O");
        cmd.arg(&tmpfilename);
        cmd.arg(url);

        debug!("Running: {}", cmd_show(&cmd));
        match cmd.status() {
            Ok(st) if st.success() => {
                if let Some(ref sha256) = sha256 {
                    let mut tmpfile = try_msg!(File::open(&tmpfilename),
                                            "Cannot open archive: {err}");
                    if let Err(e) = check_stream_hashsum(&mut tmpfile, sha256) {
                        remove_file(&filename)
                            .map_err(|e| error!(
                                "Error unlinking cache file: {}", e)).ok();
                        error!("Bad hashsum of {:?}", url);
                        return Err(e);
                    }
                }
                rename(&tmpfilename, &filename)
                    .map_err(|e| format!("Error moving file: {}", e))?;
                return Ok(filename);
            }
            Ok(val) => {
                match remove_file(&tmpfilename) {
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                        // Assume we got 404 and download have not even started
                        continue;
                    }
                    Err(e) => {
                        error!("Error unlinking cache file: {}", e);
                    }
                    Ok(_) => {}
                }
                error!("Error downloading {:?}, wget {:?}", url, val);
                continue;
            }
            Err(x) => {
                return Err(format!("Error starting wget: {}", x));
            }
        }
    }
    return Err(format!("Error downloading file {:?} from {:?}",
        filename, urls.iter().map(|x| x.as_ref()).collect::<Vec<_>>()));
}

fn check_if_local(url: &str, sha256: &Option<String>)
    -> Result<Option<PathBuf>, String>
{
    let path = if url.starts_with(".") {
        PathBuf::from("/work").join(url)
    } else if url.starts_with("/volumes/") {
        PathBuf::from(url)
    } else {
        return Ok(None);
    };
    if let Some(ref sha256) = *sha256 {
        let mut file = try_msg!(File::open(&path),
            "Cannot open file: {err}");
        check_stream_hashsum(&mut file, sha256)
            .map_err(|e| format!(
                "Error when checking hashsum for file {:?}: {}",
                &path, e))?;
    }
    Ok(Some(path))
}

pub fn maybe_download_and_check_hashsum(state: &mut State,
    url: &str, sha256: Option<String>, refresh: bool)
    -> Result<(PathBuf, bool), String>
{
    Ok(match check_if_local(url, &sha256)? {
        Some(path) => (path, false),
        None => (download_file(state, &[url], sha256, refresh)?, true),
    })
}

pub fn run_download(context: &Context, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut url = "".to_string();
    let mut sha256 = None;
    let mut refresh = false;
    {
        args.insert(0, "vagga _capsule download".to_string());
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Downloads file if not cached, puts it into a cache printing the
            path to the cached item to the stdout.
            ");
        ap.refer(&mut sha256)
            .add_option(&["--sha256"], StoreOption,
                "A SHA256 hashsum of a file (if you want to check)");
        ap.refer(&mut refresh)
            .add_option(&["--refresh"], StoreTrue,
                "Download file even if there is a cached item");
        ap.refer(&mut url)
            .add_argument("url", Store,
                "A file to download")
            .required();
        ap.stop_on_first_argument(true);
        match ap.parse(args.clone(), &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    // TODO(tailhook) wrap settings into Arc in the launcher's main
    let mut capsule = State::new(&Arc::new(context.settings.clone()));
    let (path, _) = maybe_download_and_check_hashsum(
        &mut capsule, &url, sha256, refresh)?;
    println!("{}", path.display());
    return Ok(0);
}
<|endoftext|>


<commit_before>use term;
use term::{Terminal,color};
use term::color::Color;
use term::attr::Attr;
use std::io::IoResult;

pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal<T> {
    NoColor(BasicTerminal<T>),
    Color(Box<Terminal<T>>)
}

pub struct Shell<T> {
    terminal: AdequateTerminal<T>,
    config: ShellConfig
}

impl<T: Writer + Send> Shell<T> {
    pub fn create(out: T, config: ShellConfig) -> Option<Shell<T>> {
        if config.tty && config.color {
            let term: Option<term::TerminfoTerminal<T>> = Terminal::new(out);
            term.map(|t| Shell { terminal: Color(box t as Box<Terminal<T>>), config: config })
        } else {
            Some(Shell { terminal: NoColor(BasicTerminal { writer: out }), config: config })
        }
    }

    pub fn verbose(&mut self, callback: |&mut Shell<T>| -> IoResult<()>) -> IoResult<()> {
        if self.config.verbose {
            return callback(self)
        }

        Ok(())
    }

    pub fn say<T: Str>(&mut self, message: T, color: Color) -> IoResult<()> {
        try!(self.reset());
        try!(self.fg(color));
        try!(self.write_line(message.as_slice()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }
}

impl<T: Writer + Send> Terminal<T> for Shell<T> {
    fn new(out: T) -> Option<Shell<T>> {
        Shell::create(out, ShellConfig { color: true, verbose: false, tty: false })
    }

    fn fg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.fg(color),
            NoColor(ref mut n) => n.fg(color)
        }
    }

    fn bg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.bg(color),
            NoColor(ref mut n) => n.bg(color)
        }
    }

    fn attr(&mut self, attr: Attr) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.attr(attr),
            NoColor(ref mut n) => n.attr(attr)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Color(ref c) => c.supports_attr(attr),
            NoColor(ref n) => n.supports_attr(attr)
        }
    }

    fn reset(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.reset(),
            NoColor(ref mut n) => n.reset()
        }
    }

    fn unwrap(self) -> T {
        fail!("Can't unwrap a Shell");
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        match self.terminal {
            Color(ref c) => c.get_ref(),
            NoColor(ref n) => n.get_ref()
        }
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        match self.terminal {
            Color(ref mut c) => c.get_mut(),
            NoColor(ref mut n) => n.get_mut()
        }
    }
}

impl<T: Writer + Send> Writer for Shell<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}

pub struct BasicTerminal<T> {
    writer: T
}

impl<T: Writer + Send> Terminal<T> for BasicTerminal<T> {
    fn new(out: T) -> Option<BasicTerminal<T>> {
        Some(BasicTerminal { writer: out })
    }

    fn fg(&mut self, _: Color) -> IoResult<bool> {
        Ok(false)
    }

    fn bg(&mut self, _: Color) -> IoResult<bool> {
        Ok(false)
    }

    fn attr(&mut self, _: Attr) -> IoResult<bool> {
        Ok(false)
    }

    fn supports_attr(&self, _: Attr) -> bool {
        false
    }

    fn reset(&mut self) -> IoResult<()> {
        Ok(())
    }

    fn unwrap(self) -> T {
        self.writer
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.writer
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.writer
    }
}

impl<T: Writer + Send> Writer for BasicTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.writer.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        self.writer.flush()
    }
}

<commit_msg>Remove unnecessary BasicTerminal<commit_after>use term;
use term::{Terminal,color};
use term::color::Color;
use term::attr::Attr;
use std::io::IoResult;

pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal<T> {
    NoColor(T),
    Color(Box<Terminal<T>>)
}

pub struct Shell<T> {
    terminal: AdequateTerminal<T>,
    config: ShellConfig
}

impl<T: Writer + Send> Shell<T> {
    pub fn create(out: T, config: ShellConfig) -> Option<Shell<T>> {
        if config.tty && config.color {
            let term: Option<term::TerminfoTerminal<T>> = Terminal::new(out);
            term.map(|t| Shell { terminal: Color(box t as Box<Terminal<T>>), config: config })
        } else {
            Some(Shell { terminal: NoColor(out), config: config })
        }
    }

    pub fn verbose(&mut self, callback: |&mut Shell<T>| -> IoResult<()>) -> IoResult<()> {
        if self.config.verbose {
            return callback(self)
        }

        Ok(())
    }

    pub fn say<T: Str>(&mut self, message: T, color: Color) -> IoResult<()> {
        try!(self.reset());
        try!(self.fg(color));
        try!(self.write_line(message.as_slice()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }
}

impl<T: Writer + Send> Terminal<T> for Shell<T> {
    fn new(out: T) -> Option<Shell<T>> {
        Shell::create(out, ShellConfig { color: true, verbose: false, tty: false })
    }

    fn fg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.fg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn bg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.bg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn attr(&mut self, attr: Attr) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.attr(attr),
            NoColor(_) => Ok(false)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Color(ref c) => c.supports_attr(attr),
            NoColor(_) => false
        }
    }

    fn reset(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.reset(),
            NoColor(_) => Ok(())
        }
    }

    fn unwrap(self) -> T {
        fail!("Can't unwrap a Shell");
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        match self.terminal {
            Color(ref c) => c.get_ref(),
            NoColor(ref w) => w
        }
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        match self.terminal {
            Color(ref mut c) => c.get_mut(),
            NoColor(ref mut w) => w
        }
    }
}

impl<T: Writer + Send> Writer for Shell<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}
<|endoftext|>


<commit_before>use std;
import str;

fn test(actual: str, expected: str) {
    log(debug, actual);
    log(debug, expected);
    assert (str::eq(actual, expected));
}

fn main() {
    test(#fmt["hello %d friends and %s things", 10, "formatted"],
         "hello 10 friends and formatted things");

    test(#fmt["test"], "test");

    // a quadratic optimization in LLVM (jump-threading) makes this test a
    // bit slow to compile unless we break it up
    part1();
    part2();
    part3();
    part4();
    part5();
    part6();
    percent();
}

fn part1() {
    // Simple tests for types

    test(#fmt["%d", 1], "1");
    test(#fmt["%i", 2], "2");
    test(#fmt["%i", -1], "-1");
    test(#fmt["%u", 10u], "10");
    test(#fmt["%s", "test"], "test");
    test(#fmt["%b", true], "true");
    test(#fmt["%b", false], "false");
    test(#fmt["%c", 'A'], "A");
    test(#fmt["%x", 0xff_u], "ff");
    test(#fmt["%X", 0x12ab_u], "12AB");
    test(#fmt["%o", 10u], "12");
    test(#fmt["%t", 0b11010101_u], "11010101");
    test(#fmt["%f", 5.82], "5.82");
    // 32-bit limits

    test(#fmt["%i", -2147483648], "-2147483648");
    test(#fmt["%i", 2147483647], "2147483647");
    test(#fmt["%u", 4294967295u], "4294967295");
    test(#fmt["%x", 0xffffffff_u], "ffffffff");
    test(#fmt["%o", 0xffffffff_u], "37777777777");
    test(#fmt["%t", 0xffffffff_u], "11111111111111111111111111111111");
}
fn part2() {
    // Widths

    test(#fmt["%1d", 500], "500");
    test(#fmt["%10d", 500], "       500");
    test(#fmt["%10d", -500], "      -500");
    test(#fmt["%10u", 500u], "       500");
    test(#fmt["%10s", "test"], "      test");
    test(#fmt["%10b", true], "      true");
    test(#fmt["%10x", 0xff_u], "        ff");
    test(#fmt["%10X", 0xff_u], "        FF");
    test(#fmt["%10o", 10u], "        12");
    test(#fmt["%10t", 0xff_u], "  11111111");
    test(#fmt["%10c", 'A'], "         A");
    test(#fmt["%10f", 5.82], "      5.82");
    // Left justify

    test(#fmt["%-10d", 500], "500       ");
    test(#fmt["%-10d", -500], "-500      ");
    test(#fmt["%-10u", 500u], "500       ");
    test(#fmt["%-10s", "test"], "test      ");
    test(#fmt["%-10b", true], "true      ");
    test(#fmt["%-10x", 0xff_u], "ff        ");
    test(#fmt["%-10X", 0xff_u], "FF        ");
    test(#fmt["%-10o", 10u], "12        ");
    test(#fmt["%-10t", 0xff_u], "11111111  ");
    test(#fmt["%-10c", 'A'], "A         ");
    test(#fmt["%-10f", 5.82], "5.82      ");
}

fn part3() {
    // Precision

    test(#fmt["%.d", 0], "");
    test(#fmt["%.u", 0u], "");
    test(#fmt["%.x", 0u], "");
    test(#fmt["%.t", 0u], "");
    test(#fmt["%.d", 10], "10");
    test(#fmt["%.d", -10], "-10");
    test(#fmt["%.u", 10u], "10");
    test(#fmt["%.s", "test"], "");
    test(#fmt["%.x", 127u], "7f");
    test(#fmt["%.o", 10u], "12");
    test(#fmt["%.t", 3u], "11");
    test(#fmt["%.c", 'A'], "A");
    test(#fmt["%.f", 5.82], "6");
    test(#fmt["%.0d", 0], "");
    test(#fmt["%.0u", 0u], "");
    test(#fmt["%.0x", 0u], "");
    test(#fmt["%.0t", 0u], "");
    test(#fmt["%.0d", 10], "10");
    test(#fmt["%.0d", -10], "-10");
    test(#fmt["%.0u", 10u], "10");
    test(#fmt["%.0s", "test"], "");
    test(#fmt["%.0x", 127u], "7f");
    test(#fmt["%.0o", 10u], "12");
    test(#fmt["%.0t", 3u], "11");
    test(#fmt["%.0c", 'A'], "A");
    test(#fmt["%.0f", 5.892], "6");
    test(#fmt["%.1d", 0], "0");
    test(#fmt["%.1u", 0u], "0");
    test(#fmt["%.1x", 0u], "0");
    test(#fmt["%.1t", 0u], "0");
    test(#fmt["%.1d", 10], "10");
    test(#fmt["%.1d", -10], "-10");
    test(#fmt["%.1u", 10u], "10");
    test(#fmt["%.1s", "test"], "t");
    test(#fmt["%.1x", 127u], "7f");
    test(#fmt["%.1o", 10u], "12");
    test(#fmt["%.1t", 3u], "11");
    test(#fmt["%.1c", 'A'], "A");
    test(#fmt["%.1f", 5.82], "5.8");
}
fn part4() {
    test(#fmt["%.5d", 0], "00000");
    test(#fmt["%.5u", 0u], "00000");
    test(#fmt["%.5x", 0u], "00000");
    test(#fmt["%.5t", 0u], "00000");
    test(#fmt["%.5d", 10], "00010");
    test(#fmt["%.5d", -10], "-00010");
    test(#fmt["%.5u", 10u], "00010");
    test(#fmt["%.5s", "test"], "test");
    test(#fmt["%.5x", 127u], "0007f");
    test(#fmt["%.5o", 10u], "00012");
    test(#fmt["%.5t", 3u], "00011");
    test(#fmt["%.5c", 'A'], "A");
    test(#fmt["%.5f", 5.82], "5.82000");
    test(#fmt["%.5f", 5.0], "5.00000");
    // Bool precision. I'm not sure if it's good or bad to have bool
    // conversions support precision - it's not standard printf so we
    // can do whatever. For now I'm making it behave the same as string
    // conversions.

    test(#fmt["%.b", true], "");
    test(#fmt["%.0b", true], "");
    test(#fmt["%.1b", true], "t");
}

fn part5() {
    // Explicit + sign. Only for signed conversions

    test(#fmt["%+d", 0], "+0");
    test(#fmt["%+d", 1], "+1");
    test(#fmt["%+d", -1], "-1");
    test(#fmt["%+f", 0.0], "+0");
    // Leave space for sign

    test(#fmt["% d", 0], " 0");
    test(#fmt["% d", 1], " 1");
    test(#fmt["% d", -1], "-1");
    test(#fmt["% f", 0.0], " 0");
    // Plus overrides space

    test(#fmt["% +d", 0], "+0");
    test(#fmt["%+ d", 0], "+0");
    test(#fmt["% +f", 0.0], "+0");
    test(#fmt["%+ f", 0.0], "+0");
    // 0-padding

    test(#fmt["%05d", 0], "00000");
    test(#fmt["%05d", 1], "00001");
    test(#fmt["%05d", -1], "-0001");
    test(#fmt["%05u", 1u], "00001");
    test(#fmt["%05x", 127u], "0007f");
    test(#fmt["%05X", 127u], "0007F");
    test(#fmt["%05o", 10u], "00012");
    test(#fmt["%05t", 3u], "00011");
    test(#fmt["%05f", 5.82], "05.82");
    // 0-padding a string is undefined but glibc does this:

    test(#fmt["%05s", "test"], " test");
    test(#fmt["%05c", 'A'], "    A");
    test(#fmt["%05b", true], " true");
    // Left-justify overrides 0-padding

    test(#fmt["%-05d", 0], "0    ");
    test(#fmt["%-05d", 1], "1    ");
    test(#fmt["%-05d", -1], "-1   ");
    test(#fmt["%-05u", 1u], "1    ");
    test(#fmt["%-05x", 127u], "7f   ");
    test(#fmt["%-05X", 127u], "7F   ");
    test(#fmt["%-05o", 10u], "12   ");
    test(#fmt["%-05t", 3u], "11   ");
    test(#fmt["%-05s", "test"], "test ");
    test(#fmt["%-05c", 'A'], "A    ");
    test(#fmt["%-05b", true], "true ");
    test(#fmt["%-05f", 5.82], "5.82 ");
}
fn part6() {
    // Precision overrides 0-padding
    // FIXME #2481: Recent gcc's report some of these as warnings

    test(#fmt["%06.5d", 0], " 00000");
    test(#fmt["%06.5u", 0u], " 00000");
    test(#fmt["%06.5x", 0u], " 00000");
    test(#fmt["%06.5d", 10], " 00010");
    test(#fmt["%06.5d", -10], "-00010");
    test(#fmt["%06.5u", 10u], " 00010");
    test(#fmt["%06.5s", "test"], "  test");
    test(#fmt["%06.5c", 'A'], "     A");
    test(#fmt["%06.5x", 127u], " 0007f");
    test(#fmt["%06.5X", 127u], " 0007F");
    test(#fmt["%06.5o", 10u], " 00012");

    // Precision does not override zero-padding for floats
    test(#fmt["%08.5f", 5.82], "05.82000");

    // Signed combinations

    test(#fmt["% 5d", 1], "    1");
    test(#fmt["% 5d", -1], "   -1");
    test(#fmt["%+5d", 1], "   +1");
    test(#fmt["%+5d", -1], "   -1");
    test(#fmt["% 05d", 1], " 0001");
    test(#fmt["% 05d", -1], "-0001");
    test(#fmt["%+05d", 1], "+0001");
    test(#fmt["%+05d", -1], "-0001");
    test(#fmt["%- 5d", 1], " 1   ");
    test(#fmt["%- 5d", -1], "-1   ");
    test(#fmt["%-+5d", 1], "+1   ");
    test(#fmt["%-+5d", -1], "-1   ");
    test(#fmt["%- 05d", 1], " 1   ");
    test(#fmt["%- 05d", -1], "-1   ");
    test(#fmt["%-+05d", 1], "+1   ");
    test(#fmt["%-+05d", -1], "-1   ");
}

fn percent() {
    let s = #fmt["ab%%cd"];
    assert(s == "ab%cd");
}
<commit_msg>Add more #fmt float tests<commit_after>use std;
import str;

fn test(actual: str, expected: str) {
    log(debug, actual);
    log(debug, expected);
    assert (str::eq(actual, expected));
}

fn main() {
    test(#fmt["hello %d friends and %s things", 10, "formatted"],
         "hello 10 friends and formatted things");

    test(#fmt["test"], "test");

    // a quadratic optimization in LLVM (jump-threading) makes this test a
    // bit slow to compile unless we break it up
    part1();
    part2();
    part3();
    part4();
    part5();
    part6();
    percent();
    more_floats();
}

fn part1() {
    // Simple tests for types

    test(#fmt["%d", 1], "1");
    test(#fmt["%i", 2], "2");
    test(#fmt["%i", -1], "-1");
    test(#fmt["%u", 10u], "10");
    test(#fmt["%s", "test"], "test");
    test(#fmt["%b", true], "true");
    test(#fmt["%b", false], "false");
    test(#fmt["%c", 'A'], "A");
    test(#fmt["%x", 0xff_u], "ff");
    test(#fmt["%X", 0x12ab_u], "12AB");
    test(#fmt["%o", 10u], "12");
    test(#fmt["%t", 0b11010101_u], "11010101");
    test(#fmt["%f", 5.82], "5.82");
    // 32-bit limits

    test(#fmt["%i", -2147483648], "-2147483648");
    test(#fmt["%i", 2147483647], "2147483647");
    test(#fmt["%u", 4294967295u], "4294967295");
    test(#fmt["%x", 0xffffffff_u], "ffffffff");
    test(#fmt["%o", 0xffffffff_u], "37777777777");
    test(#fmt["%t", 0xffffffff_u], "11111111111111111111111111111111");
}
fn part2() {
    // Widths

    test(#fmt["%1d", 500], "500");
    test(#fmt["%10d", 500], "       500");
    test(#fmt["%10d", -500], "      -500");
    test(#fmt["%10u", 500u], "       500");
    test(#fmt["%10s", "test"], "      test");
    test(#fmt["%10b", true], "      true");
    test(#fmt["%10x", 0xff_u], "        ff");
    test(#fmt["%10X", 0xff_u], "        FF");
    test(#fmt["%10o", 10u], "        12");
    test(#fmt["%10t", 0xff_u], "  11111111");
    test(#fmt["%10c", 'A'], "         A");
    test(#fmt["%10f", 5.82], "      5.82");
    // Left justify

    test(#fmt["%-10d", 500], "500       ");
    test(#fmt["%-10d", -500], "-500      ");
    test(#fmt["%-10u", 500u], "500       ");
    test(#fmt["%-10s", "test"], "test      ");
    test(#fmt["%-10b", true], "true      ");
    test(#fmt["%-10x", 0xff_u], "ff        ");
    test(#fmt["%-10X", 0xff_u], "FF        ");
    test(#fmt["%-10o", 10u], "12        ");
    test(#fmt["%-10t", 0xff_u], "11111111  ");
    test(#fmt["%-10c", 'A'], "A         ");
    test(#fmt["%-10f", 5.82], "5.82      ");
}

fn part3() {
    // Precision

    test(#fmt["%.d", 0], "");
    test(#fmt["%.u", 0u], "");
    test(#fmt["%.x", 0u], "");
    test(#fmt["%.t", 0u], "");
    test(#fmt["%.d", 10], "10");
    test(#fmt["%.d", -10], "-10");
    test(#fmt["%.u", 10u], "10");
    test(#fmt["%.s", "test"], "");
    test(#fmt["%.x", 127u], "7f");
    test(#fmt["%.o", 10u], "12");
    test(#fmt["%.t", 3u], "11");
    test(#fmt["%.c", 'A'], "A");
    test(#fmt["%.f", 5.82], "6");
    test(#fmt["%.0d", 0], "");
    test(#fmt["%.0u", 0u], "");
    test(#fmt["%.0x", 0u], "");
    test(#fmt["%.0t", 0u], "");
    test(#fmt["%.0d", 10], "10");
    test(#fmt["%.0d", -10], "-10");
    test(#fmt["%.0u", 10u], "10");
    test(#fmt["%.0s", "test"], "");
    test(#fmt["%.0x", 127u], "7f");
    test(#fmt["%.0o", 10u], "12");
    test(#fmt["%.0t", 3u], "11");
    test(#fmt["%.0c", 'A'], "A");
    test(#fmt["%.0f", 5.892], "6");
    test(#fmt["%.1d", 0], "0");
    test(#fmt["%.1u", 0u], "0");
    test(#fmt["%.1x", 0u], "0");
    test(#fmt["%.1t", 0u], "0");
    test(#fmt["%.1d", 10], "10");
    test(#fmt["%.1d", -10], "-10");
    test(#fmt["%.1u", 10u], "10");
    test(#fmt["%.1s", "test"], "t");
    test(#fmt["%.1x", 127u], "7f");
    test(#fmt["%.1o", 10u], "12");
    test(#fmt["%.1t", 3u], "11");
    test(#fmt["%.1c", 'A'], "A");
    test(#fmt["%.1f", 5.82], "5.8");
}
fn part4() {
    test(#fmt["%.5d", 0], "00000");
    test(#fmt["%.5u", 0u], "00000");
    test(#fmt["%.5x", 0u], "00000");
    test(#fmt["%.5t", 0u], "00000");
    test(#fmt["%.5d", 10], "00010");
    test(#fmt["%.5d", -10], "-00010");
    test(#fmt["%.5u", 10u], "00010");
    test(#fmt["%.5s", "test"], "test");
    test(#fmt["%.5x", 127u], "0007f");
    test(#fmt["%.5o", 10u], "00012");
    test(#fmt["%.5t", 3u], "00011");
    test(#fmt["%.5c", 'A'], "A");
    test(#fmt["%.5f", 5.82], "5.82000");
    test(#fmt["%.5f", 5.0], "5.00000");
    // Bool precision. I'm not sure if it's good or bad to have bool
    // conversions support precision - it's not standard printf so we
    // can do whatever. For now I'm making it behave the same as string
    // conversions.

    test(#fmt["%.b", true], "");
    test(#fmt["%.0b", true], "");
    test(#fmt["%.1b", true], "t");
}

fn part5() {
    // Explicit + sign. Only for signed conversions

    test(#fmt["%+d", 0], "+0");
    test(#fmt["%+d", 1], "+1");
    test(#fmt["%+d", -1], "-1");
    test(#fmt["%+f", 0.0], "+0");
    // Leave space for sign

    test(#fmt["% d", 0], " 0");
    test(#fmt["% d", 1], " 1");
    test(#fmt["% d", -1], "-1");
    test(#fmt["% f", 0.0], " 0");
    // Plus overrides space

    test(#fmt["% +d", 0], "+0");
    test(#fmt["%+ d", 0], "+0");
    test(#fmt["% +f", 0.0], "+0");
    test(#fmt["%+ f", 0.0], "+0");
    // 0-padding

    test(#fmt["%05d", 0], "00000");
    test(#fmt["%05d", 1], "00001");
    test(#fmt["%05d", -1], "-0001");
    test(#fmt["%05u", 1u], "00001");
    test(#fmt["%05x", 127u], "0007f");
    test(#fmt["%05X", 127u], "0007F");
    test(#fmt["%05o", 10u], "00012");
    test(#fmt["%05t", 3u], "00011");
    test(#fmt["%05f", 5.82], "05.82");
    // 0-padding a string is undefined but glibc does this:

    test(#fmt["%05s", "test"], " test");
    test(#fmt["%05c", 'A'], "    A");
    test(#fmt["%05b", true], " true");
    // Left-justify overrides 0-padding

    test(#fmt["%-05d", 0], "0    ");
    test(#fmt["%-05d", 1], "1    ");
    test(#fmt["%-05d", -1], "-1   ");
    test(#fmt["%-05u", 1u], "1    ");
    test(#fmt["%-05x", 127u], "7f   ");
    test(#fmt["%-05X", 127u], "7F   ");
    test(#fmt["%-05o", 10u], "12   ");
    test(#fmt["%-05t", 3u], "11   ");
    test(#fmt["%-05s", "test"], "test ");
    test(#fmt["%-05c", 'A'], "A    ");
    test(#fmt["%-05b", true], "true ");
    test(#fmt["%-05f", 5.82], "5.82 ");
}
fn part6() {
    // Precision overrides 0-padding
    // FIXME #2481: Recent gcc's report some of these as warnings

    test(#fmt["%06.5d", 0], " 00000");
    test(#fmt["%06.5u", 0u], " 00000");
    test(#fmt["%06.5x", 0u], " 00000");
    test(#fmt["%06.5d", 10], " 00010");
    test(#fmt["%06.5d", -10], "-00010");
    test(#fmt["%06.5u", 10u], " 00010");
    test(#fmt["%06.5s", "test"], "  test");
    test(#fmt["%06.5c", 'A'], "     A");
    test(#fmt["%06.5x", 127u], " 0007f");
    test(#fmt["%06.5X", 127u], " 0007F");
    test(#fmt["%06.5o", 10u], " 00012");

    // Precision does not override zero-padding for floats
    test(#fmt["%08.5f", 5.82], "05.82000");

    // Signed combinations

    test(#fmt["% 5d", 1], "    1");
    test(#fmt["% 5d", -1], "   -1");
    test(#fmt["%+5d", 1], "   +1");
    test(#fmt["%+5d", -1], "   -1");
    test(#fmt["% 05d", 1], " 0001");
    test(#fmt["% 05d", -1], "-0001");
    test(#fmt["%+05d", 1], "+0001");
    test(#fmt["%+05d", -1], "-0001");
    test(#fmt["%- 5d", 1], " 1   ");
    test(#fmt["%- 5d", -1], "-1   ");
    test(#fmt["%-+5d", 1], "+1   ");
    test(#fmt["%-+5d", -1], "-1   ");
    test(#fmt["%- 05d", 1], " 1   ");
    test(#fmt["%- 05d", -1], "-1   ");
    test(#fmt["%-+05d", 1], "+1   ");
    test(#fmt["%-+05d", -1], "-1   ");
}

fn percent() {
    let s = #fmt["ab%%cd"];
    assert(s == "ab%cd");
}

fn more_floats() {
    assert "3.1416"      == #fmt["%.4f", 3.14159];
    assert "3"           == #fmt["%.0f", 3.14159];
    assert "99"          == #fmt["%.0f", 98.5];
    assert "7.0000"      == #fmt["%.4f", 6.999999999];
    assert "3.141590000" == #fmt["%.9f", 3.14159];
}<|endoftext|>


<commit_before>use glium::backend::Facade;
use glium::{self, BackfaceCullingMode, DepthTest, DrawParameters, IndexBuffer, VertexBuffer};
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use base::math::*;
use base::gen::seeded_rng;
use base::prop::plant::{Plant, Tree};
use std::rc::Rc;
use super::PlantRenderer;
use base::prop::plant::ControlPoint;

/// Graphical representation of a 'base::Plant'
pub struct PlantView {
    branches: Vec<VertexBuffer<Vertex>>,
    indices: IndexBuffer<u32>,
    renderer: Rc<PlantRenderer>,
    pos: Point3f,
}

impl PlantView {
    pub fn from_plant<F: Facade>(pos: Point3f,
                                 plant: &Plant,
                                 renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {
        // FIXME handle other plant types
        let mut indices = Vec::new();
        let mut vertices = Vec::new();
        let branches = match *plant {
            Plant::Tree(Tree { ref branches, branch_color }) => {
                branches.iter()
                    .map(|branch| {

                        for i in 1..branch.points.len() {
                            get_vertices_for_branch(&branch.points[i - 1],
                                                    &branch.points[i],
                                                    &mut vertices,
                                                    &mut indices,
                                                    branch_color)
                        }
                        VertexBuffer::new(facade, &vertices).unwrap()
                    })
                    .collect()
            }
        };

        PlantView {
            branches: branches,
            indices: IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap(),
            renderer: renderer,
            pos: pos,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            offset: self.pos.to_arr(),
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        for vbuf in &self.branches {
            surface.draw(vbuf,
                      &self.indices,
                      &self.renderer.program(),
                      &uniforms,
                      &params)
                .unwrap();
        }
    }
}

/// Vertex type used to render plants/trees.
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, color, normal);

/// generates Vertexbuffer and indexbuffer for a branch
fn get_vertices_for_branch(start: &ControlPoint,
                           end: &ControlPoint,
                           vertices: &mut Vec<Vertex>,
                           indices: &mut Vec<u32>,
                           color: Vector3f) {

    let to_end: Vector3f = Vector3f::new(end.point.x - start.point.x,
                                         end.point.y - start.point.y,
                                         end.point.z - start.point.z);
    let ortho = get_points_from_vector(to_end);

    let mut cur_len = vertices.len() as u32;

    // Bottom
    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [start.point.x + vec.x * start.diameter,
                       start.point.y + vec.y * start.diameter,
                       start.point.z + vec.z * start.diameter],
            normal: (-to_end).normalize().to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 2, cur_len + 0, cur_len + 1]);

    // Top
    cur_len = vertices.len() as u32;

    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [end.point.x + vec.x * end.diameter,
                       end.point.y + vec.y * end.diameter,
                       end.point.z + vec.z * end.diameter],
            normal: to_end.normalize().to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 1, cur_len + 0, cur_len + 2]);

    side(vertices, indices, color, start, end, ortho[0], ortho[1]);
    side(vertices, indices, color, start, end, ortho[1], ortho[2]);
    side(vertices, indices, color, start, end, ortho[2], ortho[0]);
}

/// Creates Vertexbuffer and IndexBuffer for a Side of the plants
fn side(vertices: &mut Vec<Vertex>,
        indices: &mut Vec<u32>,
        color: Vector3f,
        start: &ControlPoint,
        end: &ControlPoint,
        first_normal: Vector3f,
        second_normal: Vector3f) {
    let cur_len = vertices.len() as u32;

    vertices.push(Vertex {
        position: [end.point.x + first_normal.x * end.diameter,
                   end.point.y + first_normal.y * end.diameter,
                   end.point.z + first_normal.z * end.diameter],
        normal: (first_normal + second_normal).normalize().to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [end.point.x + second_normal.x * end.diameter,
                   end.point.y + second_normal.y * end.diameter,
                   end.point.z + second_normal.z * end.diameter],
        normal: (first_normal + second_normal).normalize().to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + first_normal.x * start.diameter,
                   start.point.y + first_normal.y * start.diameter,
                   start.point.z + first_normal.z * start.diameter],
        normal: (first_normal + second_normal).normalize().to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + second_normal.x * start.diameter,
                   start.point.y + second_normal.y * start.diameter,
                   start.point.z + second_normal.z * start.diameter],
        normal: (first_normal + second_normal).normalize().to_arr(),
        color: color.to_arr(),
    });

    indices.append(&mut vec![cur_len + 2,
                             cur_len + 0,
                             cur_len + 1,
                             cur_len + 2,
                             cur_len + 1,
                             cur_len + 3]);
}

/// generates 3 normalized vectors  perpendicular to the given vector
fn get_points_from_vector(vector: Vector3f) -> [Vector3f; 3] {
    let ortho = random_vec_with_angle(&mut seeded_rng(0x2651aa465abded, (), ()), vector, 90.0);
    let rot = Basis3::from_axis_angle(vector, Deg::new(120.0).into());
    let v0 = rot.rotate_vector(ortho);
    let v1 = rot.rotate_vector(v0);

    [ortho.normalize(), v0.normalize(), v1.normalize()]
}
<commit_msg>Enhance tree view construction and rendering<commit_after>use glium::backend::Facade;
use glium::{self, BackfaceCullingMode, DepthTest, DrawParameters, IndexBuffer, VertexBuffer};
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use base::math::*;
use base::gen::seeded_rng;
use base::prop::plant::{Plant, Tree};
use std::rc::Rc;
use super::PlantRenderer;
use base::prop::plant::ControlPoint;

/// Graphical representation of a 'base::Plant'
pub struct PlantView {
    vertices: VertexBuffer<Vertex>,
    indices: IndexBuffer<u32>,
    renderer: Rc<PlantRenderer>,
    pos: Point3f,
}

impl PlantView {
    pub fn from_plant<F: Facade>(pos: Point3f,
                                 plant: &Plant,
                                 renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {
        let mut indices = Vec::new();
        let mut vertices = Vec::new();
        let branches = match *plant {
            Plant::Tree(Tree { ref branches, branch_color }) => {
                for branch in branches {
                    for i in 1..branch.points.len() {
                        get_vertices_for_branch(&branch.points[i - 1],
                                                &branch.points[i],
                                                &mut vertices,
                                                &mut indices,
                                                branch_color);
                    }
                }
            }
        };

        PlantView {
            vertices: VertexBuffer::new(facade, &vertices).unwrap(),
            indices: IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap(),
            renderer: renderer,
            pos: pos,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            offset: self.pos.to_arr(),
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        surface.draw(&self.vertices,
                  &self.indices,
                  &self.renderer.program(),
                  &uniforms,
                  &params)
            .unwrap();
    }
}

/// Vertex type used to render plants/trees.
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, color, normal);

/// generates Vertexbuffer and indexbuffer for a branch
fn get_vertices_for_branch(start: &ControlPoint,
                           end: &ControlPoint,
                           vertices: &mut Vec<Vertex>,
                           indices: &mut Vec<u32>,
                           color: Vector3f) {

    let to_end = end.point - start.point;
    let ortho = get_points_from_vector(to_end);

    let mut cur_len = vertices.len() as u32;

    // Bottom
    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: (start.point + vec * start.diameter).to_arr(),
            normal: (-to_end).normalize().to_arr(),
            color: color.to_arr(),
        });
    }
    indices.extend_from_slice(&[cur_len + 2, cur_len + 0, cur_len + 1]);

    // Top
    cur_len = vertices.len() as u32;

    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: (end.point + vec * end.diameter).to_arr(),
            normal: to_end.normalize().to_arr(),
            color: color.to_arr(),
        });
    }
    indices.extend_from_slice(&[cur_len + 1, cur_len + 0, cur_len + 2]);

    side(vertices, indices, color, start, end, ortho[0], ortho[1]);
    side(vertices, indices, color, start, end, ortho[1], ortho[2]);
    side(vertices, indices, color, start, end, ortho[2], ortho[0]);
}

/// Creates Vertexbuffer and IndexBuffer for a Side of the plants
fn side(vertices: &mut Vec<Vertex>,
        indices: &mut Vec<u32>,
        color: Vector3f,
        start: &ControlPoint,
        end: &ControlPoint,
        first_normal: Vector3f,
        second_normal: Vector3f) {
    let cur_len = vertices.len() as u32;

    vertices.extend_from_slice(&[Vertex {
                                     position: (end.point + first_normal * end.diameter).to_arr(),
                                     normal: (first_normal + second_normal).normalize().to_arr(),
                                     color: color.to_arr(),
                                 },
                                 Vertex {
                                     position: (end.point + second_normal * end.diameter).to_arr(),
                                     normal: (first_normal + second_normal).normalize().to_arr(),
                                     color: color.to_arr(),
                                 },
                                 Vertex {
                                     position: (start.point + first_normal * start.diameter)
                                         .to_arr(),
                                     normal: (first_normal + second_normal).normalize().to_arr(),
                                     color: color.to_arr(),
                                 },
                                 Vertex {
                                     position: (start.point + second_normal * start.diameter)
                                         .to_arr(),
                                     normal: (first_normal + second_normal).normalize().to_arr(),
                                     color: color.to_arr(),
                                 }]);

    indices.extend_from_slice(&[cur_len + 2,
                                cur_len + 0,
                                cur_len + 1,
                                cur_len + 2,
                                cur_len + 1,
                                cur_len + 3]);
}

/// generates 3 normalized vectors perpendicular to the given vector
fn get_points_from_vector(vector: Vector3f) -> [Vector3f; 3] {
    let ortho = random_vec_with_angle(&mut seeded_rng(0x2651aa465abded, (), ()), vector, 90.0);
    let rot = Basis3::from_axis_angle(vector, Deg::new(120.0).into());
    let v0 = rot.rotate_vector(ortho);
    let v1 = rot.rotate_vector(v0);

    [ortho.normalize(), v0.normalize(), v1.normalize()]
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{error, io, thread};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::mpsc;
use std::time::Duration;
use super::metrics::*;

use mio;

const MAX_SEND_RETRY_CNT: usize = 5;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Discard(reason: String) {
            description("message is discarded")
            display("{}", reason)
        }
        Closed {
            description("channel is closed")
            display("channel is closed")
        }
        Other(err: Box<error::Error + Send + Sync>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("unknown error {:?}", err)
        }
    }
}

impl<T: Debug> From<NotifyError<T>> for Error {
    fn from(e: NotifyError<T>) -> Error {
        match e {
            // ALLERT!! May cause sensitive data leak.
            NotifyError::Full(m) => Error::Discard(format!("Failed to send {:?} due to full", m)),
            NotifyError::Closed(..) => Error::Closed,
            _ => box_err!("{:?}", e),
        }
    }
}

#[derive(Debug)]
pub enum NotifyError<T> {
    Full(T),
    Closed(Option<T>),
    Io(io::Error),
}

pub trait Sender<T>: Clone {
    fn send(&self, t: T) -> Result<(), NotifyError<T>>;
}

impl<T: Send> Sender<T> for mio::Sender<T> {
    fn send(&self, t: T) -> Result<(), NotifyError<T>> {
        match mio::Sender::send(self, t) {
            Ok(()) => Ok(()),
            Err(mio::NotifyError::Closed(t)) => Err(NotifyError::Closed(t)),
            Err(mio::NotifyError::Full(t)) => Err(NotifyError::Full(t)),
            Err(mio::NotifyError::Io(e)) => Err(NotifyError::Io(e)),
        }
    }
}

impl<T> Sender<T> for mpsc::SyncSender<T> {
    fn send(&self, t: T) -> Result<(), NotifyError<T>> {
        match mpsc::SyncSender::try_send(self, t) {
            Ok(()) => Ok(()),
            Err(mpsc::TrySendError::Disconnected(t)) => Err(NotifyError::Closed(Some(t))),
            Err(mpsc::TrySendError::Full(t)) => Err(NotifyError::Full(t)),
        }
    }
}

/// A channel that handle error with retry automatically.
pub struct RetryableSendCh<T, C> {
    ch: C,
    name: &'static str,

    marker: PhantomData<T>,
}

// We only care about self.ch. When using built-in derive, the T will be
// also taken into consideration.
unsafe impl<T, C: Sender<T> + Send> Send for RetryableSendCh<T, C> {}
unsafe impl<T, C: Sender<T> + Sync> Sync for RetryableSendCh<T, C> {}

impl<T: Debug, C: Sender<T>> RetryableSendCh<T, C> {
    pub fn new(ch: C, name: &'static str) -> RetryableSendCh<T, C> {
        RetryableSendCh {
            ch: ch,
            name: name,
            marker: Default::default(),
        }
    }

    /// Try send t with default try times.
    pub fn send(&self, t: T) -> Result<(), Error> {
        self.send_with_try_times(t, MAX_SEND_RETRY_CNT)
    }

    pub fn try_send(&self, t: T) -> Result<(), Error> {
        self.send_with_try_times(t, 1)
    }

    fn send_with_try_times(&self, mut t: T, mut try_times: usize) -> Result<(), Error> {
        loop {
            t = match self.ch.send(t) {
                Ok(_) => return Ok(()),
                Err(NotifyError::Full(m)) => {
                    if try_times <= 1 {
                        CHANNEL_FULL_COUNTER_VEC
                            .with_label_values(&[self.name])
                            .inc();
                        return Err(NotifyError::Full(m).into());
                    }
                    try_times -= 1;
                    m
                }
                Err(e) => return Err(e.into()),
            };

            // ALLERT!! make cause sensitive data leak.
            warn!("notify queue is full, sleep and retry sending {:?}", t);
            thread::sleep(Duration::from_millis(100));
        }
    }
}

impl<T, C: Sender<T>> Clone for RetryableSendCh<T, C> {
    fn clone(&self) -> RetryableSendCh<T, C> {
        RetryableSendCh {
            ch: self.ch.clone(),
            name: self.name,
            marker: Default::default(),
        }
    }
}

pub type SendCh<T> = RetryableSendCh<T, mio::Sender<T>>;
pub type SyncSendCh<T> = RetryableSendCh<T, mpsc::SyncSender<T>>;

#[cfg(test)]
mod tests {
    use std::thread;
    use std::sync::mpsc::Receiver;
    use std::time::Duration;

    use mio::{EventLoop, EventLoopConfig, Handler};

    use super::*;

    #[derive(Debug)]
    enum Msg {
        Quit,
        Stop,
        Sleep(u64),
    }

    struct SenderHandler<S: Sender<Msg>> {
        ch: RetryableSendCh<Msg, S>,
    }

    impl<S: Sender<Msg>> SenderHandler<S> {
        fn run(&mut self, recv: Receiver<Msg>) {
            while let Ok(msg) = recv.recv() {
                if !self.on_msg(msg) {
                    break;
                }
            }
        }

        fn on_msg(&mut self, msg: Msg) -> bool {
            match msg {
                Msg::Quit => return false,
                Msg::Stop => self.ch.try_send(Msg::Quit).unwrap(),
                Msg::Sleep(millis) => thread::sleep(Duration::from_millis(millis)),
            }
            true
        }
    }

    impl<S: Sender<Msg>> Handler for SenderHandler<S> {
        type Timeout = ();
        type Message = Msg;

        fn notify(&mut self, event_loop: &mut EventLoop<SenderHandler<S>>, msg: Msg) {
            if !self.on_msg(msg) {
                event_loop.shutdown();
            }
        }
    }

    #[test]
    fn test_sendch() {
        let mut event_loop = EventLoop::new().unwrap();
        let ch = SendCh::new(event_loop.channel(), "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut sender = SenderHandler { ch: _ch };
            event_loop.run(&mut sender).unwrap();
        });

        ch.try_send(Msg::Stop).unwrap();

        h.join().unwrap();
    }

    #[test]
    fn test_sendch_full() {
        let mut config = EventLoopConfig::new();
        config.notify_capacity(2);
        let mut event_loop = EventLoop::configured(config).unwrap();
        let ch = SendCh::new(event_loop.channel(), "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut sender = SenderHandler { ch: _ch };
            event_loop.run(&mut sender).unwrap();
        });

        ch.send(Msg::Sleep(1000)).unwrap();
        ch.send(Msg::Stop).unwrap();
        ch.send(Msg::Stop).unwrap();
        match ch.send(Msg::Stop) {
            Err(Error::Discard(_)) => {}
            res => panic!("expect discard error, but found: {:?}", res),
        }

        h.join().unwrap();
    }

    #[test]
    fn test_sync_sendch() {
        let (tx, rx) = mpsc::sync_channel(10);
        let ch = SyncSendCh::new(tx, "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut handler = SenderHandler { ch: _ch };
            handler.run(rx);
        });

        ch.try_send(Msg::Stop).unwrap();

        h.join().unwrap();
    }

    #[test]
    fn test_sync_sendch_full() {
        let (tx, rx) = mpsc::sync_channel(2);
        let ch = SyncSendCh::new(tx, "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut handler = SenderHandler { ch: _ch };
            handler.run(rx);
        });

        ch.send(Msg::Sleep(1000)).unwrap();
        ch.send(Msg::Stop).unwrap();
        ch.send(Msg::Stop).unwrap();
        match ch.send(Msg::Stop) {
            Err(Error::Discard(_)) => {}
            res => panic!("expect discard error, but found: {:?}", res),
        }

        h.join().unwrap();
    }
}
<commit_msg>util/transport: Fix typos (#2396)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{error, io, thread};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::mpsc;
use std::time::Duration;
use super::metrics::*;

use mio;

const MAX_SEND_RETRY_CNT: usize = 5;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Discard(reason: String) {
            description("message is discarded")
            display("{}", reason)
        }
        Closed {
            description("channel is closed")
            display("channel is closed")
        }
        Other(err: Box<error::Error + Send + Sync>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("unknown error {:?}", err)
        }
    }
}

impl<T: Debug> From<NotifyError<T>> for Error {
    fn from(e: NotifyError<T>) -> Error {
        match e {
            // ALERT!! May cause sensitive data leak.
            NotifyError::Full(m) => Error::Discard(format!("Failed to send {:?} due to full", m)),
            NotifyError::Closed(..) => Error::Closed,
            _ => box_err!("{:?}", e),
        }
    }
}

#[derive(Debug)]
pub enum NotifyError<T> {
    Full(T),
    Closed(Option<T>),
    Io(io::Error),
}

pub trait Sender<T>: Clone {
    fn send(&self, t: T) -> Result<(), NotifyError<T>>;
}

impl<T: Send> Sender<T> for mio::Sender<T> {
    fn send(&self, t: T) -> Result<(), NotifyError<T>> {
        match mio::Sender::send(self, t) {
            Ok(()) => Ok(()),
            Err(mio::NotifyError::Closed(t)) => Err(NotifyError::Closed(t)),
            Err(mio::NotifyError::Full(t)) => Err(NotifyError::Full(t)),
            Err(mio::NotifyError::Io(e)) => Err(NotifyError::Io(e)),
        }
    }
}

impl<T> Sender<T> for mpsc::SyncSender<T> {
    fn send(&self, t: T) -> Result<(), NotifyError<T>> {
        match mpsc::SyncSender::try_send(self, t) {
            Ok(()) => Ok(()),
            Err(mpsc::TrySendError::Disconnected(t)) => Err(NotifyError::Closed(Some(t))),
            Err(mpsc::TrySendError::Full(t)) => Err(NotifyError::Full(t)),
        }
    }
}

/// A channel that handles errors with retry automatically.
pub struct RetryableSendCh<T, C> {
    ch: C,
    name: &'static str,

    marker: PhantomData<T>,
}

// We only care about self.ch. When using built-in derive, the T will be
// also taken into consideration.
unsafe impl<T, C: Sender<T> + Send> Send for RetryableSendCh<T, C> {}
unsafe impl<T, C: Sender<T> + Sync> Sync for RetryableSendCh<T, C> {}

impl<T: Debug, C: Sender<T>> RetryableSendCh<T, C> {
    pub fn new(ch: C, name: &'static str) -> RetryableSendCh<T, C> {
        RetryableSendCh {
            ch: ch,
            name: name,
            marker: Default::default(),
        }
    }

    /// Try send t with default try times.
    pub fn send(&self, t: T) -> Result<(), Error> {
        self.send_with_try_times(t, MAX_SEND_RETRY_CNT)
    }

    pub fn try_send(&self, t: T) -> Result<(), Error> {
        self.send_with_try_times(t, 1)
    }

    fn send_with_try_times(&self, mut t: T, mut try_times: usize) -> Result<(), Error> {
        loop {
            t = match self.ch.send(t) {
                Ok(_) => return Ok(()),
                Err(NotifyError::Full(m)) => {
                    if try_times <= 1 {
                        CHANNEL_FULL_COUNTER_VEC
                            .with_label_values(&[self.name])
                            .inc();
                        return Err(NotifyError::Full(m).into());
                    }
                    try_times -= 1;
                    m
                }
                Err(e) => return Err(e.into()),
            };

            // ALERT!! make cause sensitive data leak.
            warn!("notify queue is full, sleep and retry sending {:?}", t);
            thread::sleep(Duration::from_millis(100));
        }
    }
}

impl<T, C: Sender<T>> Clone for RetryableSendCh<T, C> {
    fn clone(&self) -> RetryableSendCh<T, C> {
        RetryableSendCh {
            ch: self.ch.clone(),
            name: self.name,
            marker: Default::default(),
        }
    }
}

pub type SendCh<T> = RetryableSendCh<T, mio::Sender<T>>;
pub type SyncSendCh<T> = RetryableSendCh<T, mpsc::SyncSender<T>>;

#[cfg(test)]
mod tests {
    use std::thread;
    use std::sync::mpsc::Receiver;
    use std::time::Duration;

    use mio::{EventLoop, EventLoopConfig, Handler};

    use super::*;

    #[derive(Debug)]
    enum Msg {
        Quit,
        Stop,
        Sleep(u64),
    }

    struct SenderHandler<S: Sender<Msg>> {
        ch: RetryableSendCh<Msg, S>,
    }

    impl<S: Sender<Msg>> SenderHandler<S> {
        fn run(&mut self, recv: Receiver<Msg>) {
            while let Ok(msg) = recv.recv() {
                if !self.on_msg(msg) {
                    break;
                }
            }
        }

        fn on_msg(&mut self, msg: Msg) -> bool {
            match msg {
                Msg::Quit => return false,
                Msg::Stop => self.ch.try_send(Msg::Quit).unwrap(),
                Msg::Sleep(millis) => thread::sleep(Duration::from_millis(millis)),
            }
            true
        }
    }

    impl<S: Sender<Msg>> Handler for SenderHandler<S> {
        type Timeout = ();
        type Message = Msg;

        fn notify(&mut self, event_loop: &mut EventLoop<SenderHandler<S>>, msg: Msg) {
            if !self.on_msg(msg) {
                event_loop.shutdown();
            }
        }
    }

    #[test]
    fn test_sendch() {
        let mut event_loop = EventLoop::new().unwrap();
        let ch = SendCh::new(event_loop.channel(), "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut sender = SenderHandler { ch: _ch };
            event_loop.run(&mut sender).unwrap();
        });

        ch.try_send(Msg::Stop).unwrap();

        h.join().unwrap();
    }

    #[test]
    fn test_sendch_full() {
        let mut config = EventLoopConfig::new();
        config.notify_capacity(2);
        let mut event_loop = EventLoop::configured(config).unwrap();
        let ch = SendCh::new(event_loop.channel(), "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut sender = SenderHandler { ch: _ch };
            event_loop.run(&mut sender).unwrap();
        });

        ch.send(Msg::Sleep(1000)).unwrap();
        ch.send(Msg::Stop).unwrap();
        ch.send(Msg::Stop).unwrap();
        match ch.send(Msg::Stop) {
            Err(Error::Discard(_)) => {}
            res => panic!("expect discard error, but found: {:?}", res),
        }

        h.join().unwrap();
    }

    #[test]
    fn test_sync_sendch() {
        let (tx, rx) = mpsc::sync_channel(10);
        let ch = SyncSendCh::new(tx, "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut handler = SenderHandler { ch: _ch };
            handler.run(rx);
        });

        ch.try_send(Msg::Stop).unwrap();

        h.join().unwrap();
    }

    #[test]
    fn test_sync_sendch_full() {
        let (tx, rx) = mpsc::sync_channel(2);
        let ch = SyncSendCh::new(tx, "test");
        let _ch = ch.clone();
        let h = thread::spawn(move || {
            let mut handler = SenderHandler { ch: _ch };
            handler.run(rx);
        });

        ch.send(Msg::Sleep(1000)).unwrap();
        ch.send(Msg::Stop).unwrap();
        ch.send(Msg::Stop).unwrap();
        match ch.send(Msg::Stop) {
            Err(Error::Discard(_)) => {}
            res => panic!("expect discard error, but found: {:?}", res),
        }

        h.join().unwrap();
    }
}
<|endoftext|>