<commit_before>#![feature(no_panic_pow)]
#![feature(test)]

extern crate test;
#[macro_use]
extern crate structopt;

use codegen::{Const, Enum, Function, Struct, Scope, Impl};
use can_dbc::{DBC, ByteOrder, Message, MessageId, Signal, ValueDescription};
use heck::{CamelCase, ShoutySnakeCase};
use log::warn;

use std::fmt::Write;
use std::path::PathBuf;

#[cfg(test)]
mod tests {
    use test::{Bencher, black_box};
    use byteorder;
    use byteorder::{ByteOrder, LE};

    #[bench]
    fn bench_read_signal(b: &mut Bencher) {
        const byte_payload: &[u8] = &[0x4, 0x2, 0xA, 0xA, 0xF, 0xF, 0xE, 0xE, 0xD, 0xD, 0xA, 0xA, 0xF, 0xF, 0xD, 0xD];

        b.iter(|| {
            let frame_payload: u64 = LE::read_u64(byte_payload);
            let bit_msk_const = 2u64.saturating_pow(8 as u32) - 1;
            let factor: f64 = test::black_box(2.0);
            let start_bit: u64 = test::black_box(8);
            let offset: f64 = test::black_box(10.0);

            (((frame_payload >> start_bit) & bit_msk_const) as f64) * factor + offset
        });
    }
}

/// Character that is prefixed before type names that are
/// are not starting with an alphabetic char.
const PREFIX_CHAR: char = 'X';

/// Character that is used to replace invalid characters
/// in type names.
const REPLACEMENT_CHAR: char = 'X';

/// Suffix that is append to the raw signal function
const RAW_FN_SUFFIX: &str = "raw_value";

type Result<T> = std::result::Result<T, std::fmt::Error>;

#[derive(StructOpt, Debug)]
#[structopt(name = "dbcc", about = "DBC to rust code compiler")]
pub struct Opt {
    #[structopt(short = "i", long = "input", parse(from_os_str), help = "DBC file")]
    pub input: PathBuf,

    #[structopt(long = "with-tokio", help = "Generate Tokio streams.")]
    pub with_tokio: bool,
}

pub trait TypeName: ToOwned {
    fn to_type_name(&self) -> Self::Owned;
}

impl TypeName for str {
    fn to_type_name(&self) -> String {
        let mut out = String::with_capacity(self.len() + 1);
        let mut chars = self.chars();
        if let Some(first) = chars.next() {
            if !first.is_alphabetic() && first != '_' {
                warn!("string: {} is prefixed with `{}`", self, PREFIX_CHAR);
                out.push(PREFIX_CHAR);
            }
            out.push(first);
        }

        while let Some(chr) = chars.next() {
            if chr.is_digit(10) || chr.is_alphabetic() || chr == '_' {
                out.push(chr);
            } else {
                warn!("`{}` character in string: {} is replaced by `{}`", chr, self, REPLACEMENT_CHAR);
                out.push(REPLACEMENT_CHAR);
            }
        }

        out
    }
}

fn to_enum_name(message_id: &MessageId, signal_name: &str) -> String {
     format!("{}{}", &signal_name.to_camel_case(), message_id.0)
}

pub fn signal_enum(val_desc: &ValueDescription) -> Option<Enum> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {

        let mut sig_enum = Enum::new(&to_enum_name(message_id, signal_name));
        sig_enum.allow("dead_code");
        sig_enum.vis("pub");
        sig_enum.repr("u64");
        sig_enum.derive("Debug");
        sig_enum.derive("Clone");
        sig_enum.derive("Copy");
        sig_enum.derive("PartialEq");
        sig_enum.derive("Eq");
        for desc in value_descriptions {
            sig_enum.new_variant(&desc.b().to_camel_case().to_type_name());
        }
        sig_enum.new_variant("XValue(u64)");
        return Some(sig_enum);
    }
    None
}

pub fn signal_enum_impl_from(val_desc: &ValueDescription) -> Option<Impl> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {
        let enum_name = to_enum_name(message_id, signal_name);
        let mut enum_impl = Impl::new(codegen::Type::new(&enum_name));
        enum_impl.impl_trait("From<u64>");

        let from_fn = enum_impl.new_fn("from");
        from_fn.allow("dead_code");
        from_fn.arg("val", codegen::Type::new("u64"));
        from_fn.ret(codegen::Type::new("Self"));

        let mut matching = String::new();
        write!(&mut matching, "match val {{\n").unwrap();
        for value_description in value_descriptions {
            write!(&mut matching, "    {} => {}::{},\n", value_description.a(), enum_name, value_description.b().to_camel_case().to_type_name()).unwrap();
        }
        write!(&mut matching, "    value => {}::XValue(value),\n", enum_name).unwrap();
        write!(&mut matching, "}}").unwrap();

        from_fn.line(matching);

        return Some(enum_impl);
    }
    None
}

pub fn signal_fn_raw(dbc: &DBC, signal: &Signal, message_id: &MessageId) -> Result<Function> {
    let raw_fn_name = format!("{}_{}", signal.name().to_lowercase(), RAW_FN_SUFFIX);

    let mut signal_fn = codegen::Function::new(&raw_fn_name);
    signal_fn.allow("dead_code");
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    let signal_return_type = signal_return_type(signal);
    signal_fn.ret(codegen::Type::new(&signal_return_type));

    let default_signal_comment = format!("Read {} signal from can frame", signal.name());
    let signal_comment = dbc.signal_comment(message_id, signal.name()).unwrap_or(&default_signal_comment);
    signal_fn.doc(signal_comment);

    let read_byte_order = match signal.byte_order() {
        ByteOrder::LittleEndian => "let frame_payload: u64 = LE::read_u64(&self.frame_payload);",
        ByteOrder::BigEndian => "let  frame_payload: u64 = BE::read_u64(&self.frame_payload);",
    };
    signal_fn.line(read_byte_order);

    let bit_msk_const = 2u64.saturating_pow(*signal.signal_size() as u32) - 1;
    let signal_shift = shift_amount(*signal.byte_order(), *signal.start_bit(), *signal.signal_size());

    let calc = calc_raw(signal, signal_return_type, signal_shift, bit_msk_const)?;
    signal_fn.line(calc);

    Ok(signal_fn)
}

pub fn signal_fn_enum(signal: &Signal, enum_type: String) -> Result<Function> {
    let mut signal_fn = codegen::Function::new(&signal.name().to_lowercase());
    signal_fn.allow("dead_code");
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    signal_fn.ret(enum_type.clone());

    let raw_fn_name = format!("{}_{}", signal.name().to_lowercase(), RAW_FN_SUFFIX);

    signal_fn.line(format!("{}::from(self.{}() as u64)", enum_type, raw_fn_name));

    Ok(signal_fn)
}

fn calc_raw(signal: &Signal, signal_return_type: String, signal_shift: u64, bit_msk_const: u64) -> Result<String> {
    let mut calc = String::new();

    // No shift required if start_bit == 0
    let shift = if signal_shift != 0 {
        format!("(frame_payload >> {})", signal_shift)
    } else {
        format!("frame_payload")
    };

    write!(&mut calc, "({} & {:#X})", shift, bit_msk_const)?;

    if *signal.signal_size() != 1 {
        write!(&mut calc, " as {}", signal_return_type)?;
    }

    if *signal.factor() != 1.0 {
        write!(&mut calc, " * {:.6}", signal.factor())?;
    }

    if *signal.offset() != 0.0 && *signal.signal_size() <= 32 {
        write!(&mut calc, " + {}f32", signal.offset())?;
    } else if *signal.offset() != 0.0 {
        write!(&mut calc, " + {}f64", signal.offset())?;
    }

    // boolean signal
    if *signal.signal_size() == 1 {
        write!(&mut calc, " == 1")?;
    }

    Ok(calc)
}

fn signal_return_type(signal: &Signal) -> String {
    match signal.signal_size() {
        _ if *signal.signal_size() == 1 => "bool".to_string(),
        _ if *signal.signal_size() > 1 && *signal.signal_size() <= 32 => "f32".to_string(),
        _ => "f64".to_string(),
    }
}

fn shift_amount(byte_order: ByteOrder, start_bit: u64, signal_size: u64) -> u64 {
    match byte_order {
        ByteOrder::LittleEndian => start_bit,
        ByteOrder::BigEndian => 64 - signal_size - ((start_bit / 8) * 8 + (7 - (start_bit % 8))),
    }
}

fn message_const(message: &Message) -> Const {
    let const_name = format!("MESSAGE_ID_{}", message.message_name().to_shouty_snake_case());
    let mut c = Const::new(&const_name, codegen::Type::new("u32"), message.message_id().0.to_string());
    c.allow("dead_code");
    c.vis("pub");
    c
}

fn message_struct(dbc: &DBC, message: &Message) -> Struct {
    let mut message_struct = Struct::new(&message.message_name().to_camel_case());
    if let Some(message_comment) = dbc.message_comment(message.message_id()) {
        message_struct.doc(message_comment);
    }
    message_struct.allow("dead_code");
    message_struct.derive("Debug");
    message_struct.vis("pub");
    message_struct.field("frame_payload", "Vec<u8>");
    message_struct
}

fn message_impl(opt: &Opt, dbc: &DBC, message: &Message) -> Result<Impl> {

    let mut msg_impl = Impl::new(codegen::Type::new(&message.message_name().to_camel_case()));

    let new_fn = msg_impl.new_fn("new");
    new_fn.allow("dead_code");
    new_fn.vis("pub");
    new_fn.arg("frame_payload", codegen::Type::new("Vec<u8>"));

    new_fn.line(format!("{} {{ frame_payload }}", message.message_name().to_camel_case()));
    new_fn.ret(codegen::Type::new(&message.message_name().to_camel_case()));

    if opt.with_tokio {
        msg_impl.push_fn(message_stream(message));
    }

    for signal in message.signals() {

        msg_impl.push_fn(signal_fn_raw(dbc, signal, message.message_id())?);

        // Check if this signal can be turned into an enum
        let enum_type = dbc.value_descriptions_for_signal(message.message_id(), signal.name()).map(|_| to_enum_name(message.message_id(), signal.name()));
        if let Some(enum_type) = enum_type {
            msg_impl.push_fn(signal_fn_enum(signal, enum_type)?);
        }
    }

    Ok(msg_impl)
}

/// Generate message stream using socketcan's Broadcast Manager filters via socketcan-tokio.
fn message_stream(message: &Message) -> Function {
    let mut stream_fn = codegen::Function::new("stream");
    stream_fn.allow("dead_code");
    stream_fn.vis("pub");

    stream_fn.arg("can_interface", codegen::Type::new("&str"));
    stream_fn.arg("ival1", codegen::Type::new("&std::time::Duration"));
    stream_fn.arg("ival2", codegen::Type::new("&std::time::Duration"));

    let ret = format!("Box<Stream<Item = {}, Error = std::io::Error> + Sync + Send>", message.message_name().to_camel_case());
    stream_fn.ret(ret);

    stream_fn.line("let socket = BCMSocket::open_nb(&can_interface).unwrap();");
    stream_fn.line(format!("let message_id = CANMessageId::try_from({} as u32).unwrap();", message.message_id().0.to_string()));
    stream_fn.line("let frame_stream = socket.filter_id_incoming_frames(message_id, ival1.clone(), ival2.clone()).unwrap();");
    stream_fn.line(format!("let f = frame_stream.map(|frame| {}::new(frame.data().to_vec()));",  message.message_name().to_camel_case()));
    stream_fn.line("Box::new(f)");

    stream_fn
}

pub fn can_reader(opt: &Opt, dbc: &DBC) -> Result<Scope> {

    let mut scope = Scope::new();
    scope.import("byteorder", "{ByteOrder, LE, BE}");

    if opt.with_tokio {
        scope.import("tokio_socketcan_bcm", "{CANMessageId, BCMSocket}");
        scope.import("futures", "Stream");
        scope.import("std::convert", "TryFrom");
    }

    for message in dbc.messages() {
        scope.push_const(message_const(message));
    }

    for value_description in dbc.value_descriptions() {
        if let Some(signal_enum) = signal_enum(value_description) {
            scope.push_enum(signal_enum);
        }

        if let Some(enum_impl) = signal_enum_impl_from(value_description) {
            scope.push_impl(enum_impl);
        }
    }

    for message in dbc.messages() {
        scope.push_struct(message_struct(&dbc, message));
        scope.push_impl(message_impl(opt, &dbc, message)?);
    }

    Ok(scope)
}<commit_msg>Avoid panics when frame payload is smaller than 8 bytes<commit_after>#![feature(no_panic_pow)]
#![feature(test)]

extern crate test;
#[macro_use]
extern crate structopt;

use codegen::{Const, Enum, Function, Struct, Scope, Impl};
use can_dbc::{DBC, ByteOrder, Message, MessageId, Signal, ValueDescription};
use heck::{CamelCase, ShoutySnakeCase};
use log::warn;

use std::fmt::Write;
use std::path::PathBuf;

#[cfg(test)]
mod tests {
    use test::{Bencher, black_box};
    use byteorder;
    use byteorder::{ByteOrder, LE};

    #[bench]
    fn bench_read_signal(b: &mut Bencher) {
        const byte_payload: &[u8] = &[0x4, 0x2, 0xA, 0xA, 0xF, 0xF, 0xE, 0xE, 0xD, 0xD, 0xA, 0xA, 0xF, 0xF, 0xD, 0xD];

        b.iter(|| {
            let frame_payload: u64 = LE::read_u64(byte_payload);
            let bit_msk_const = 2u64.saturating_pow(8 as u32) - 1;
            let factor: f64 = test::black_box(2.0);
            let start_bit: u64 = test::black_box(8);
            let offset: f64 = test::black_box(10.0);

            (((frame_payload >> start_bit) & bit_msk_const) as f64) * factor + offset
        });
    }
}

/// Character that is prefixed before type names that are
/// are not starting with an alphabetic char.
const PREFIX_CHAR: char = 'X';

/// Character that is used to replace invalid characters
/// in type names.
const REPLACEMENT_CHAR: char = 'X';

/// Suffix that is append to the raw signal function
const RAW_FN_SUFFIX: &str = "raw_value";

type Result<T> = std::result::Result<T, std::fmt::Error>;

#[derive(StructOpt, Debug)]
#[structopt(name = "dbcc", about = "DBC to rust code compiler")]
pub struct Opt {
    #[structopt(short = "i", long = "input", parse(from_os_str), help = "DBC file")]
    pub input: PathBuf,

    #[structopt(long = "with-tokio", help = "Generate Tokio streams.")]
    pub with_tokio: bool,
}

pub trait TypeName: ToOwned {
    fn to_type_name(&self) -> Self::Owned;
}

impl TypeName for str {
    fn to_type_name(&self) -> String {
        let mut out = String::with_capacity(self.len() + 1);
        let mut chars = self.chars();
        if let Some(first) = chars.next() {
            if !first.is_alphabetic() && first != '_' {
                warn!("string: {} is prefixed with `{}`", self, PREFIX_CHAR);
                out.push(PREFIX_CHAR);
            }
            out.push(first);
        }

        while let Some(chr) = chars.next() {
            if chr.is_digit(10) || chr.is_alphabetic() || chr == '_' {
                out.push(chr);
            } else {
                warn!("`{}` character in string: {} is replaced by `{}`", chr, self, REPLACEMENT_CHAR);
                out.push(REPLACEMENT_CHAR);
            }
        }

        out
    }
}

fn to_enum_name(message_id: &MessageId, signal_name: &str) -> String {
     format!("{}{}", &signal_name.to_camel_case(), message_id.0)
}

pub fn signal_enum(val_desc: &ValueDescription) -> Option<Enum> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {

        let mut sig_enum = Enum::new(&to_enum_name(message_id, signal_name));
        sig_enum.allow("dead_code");
        sig_enum.vis("pub");
        sig_enum.repr("u64");
        sig_enum.derive("Debug");
        sig_enum.derive("Clone");
        sig_enum.derive("Copy");
        sig_enum.derive("PartialEq");
        sig_enum.derive("Eq");
        for desc in value_descriptions {
            sig_enum.new_variant(&desc.b().to_camel_case().to_type_name());
        }
        sig_enum.new_variant("XValue(u64)");
        return Some(sig_enum);
    }
    None
}

pub fn signal_enum_impl_from(val_desc: &ValueDescription) -> Option<Impl> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {
        let enum_name = to_enum_name(message_id, signal_name);
        let mut enum_impl = Impl::new(codegen::Type::new(&enum_name));
        enum_impl.impl_trait("From<u64>");

        let from_fn = enum_impl.new_fn("from");
        from_fn.allow("dead_code");
        from_fn.arg("val", codegen::Type::new("u64"));
        from_fn.ret(codegen::Type::new("Self"));

        let mut matching = String::new();
        write!(&mut matching, "match val {{\n").unwrap();
        for value_description in value_descriptions {
            write!(&mut matching, "    {} => {}::{},\n", value_description.a(), enum_name, value_description.b().to_camel_case().to_type_name()).unwrap();
        }
        write!(&mut matching, "    value => {}::XValue(value),\n", enum_name).unwrap();
        write!(&mut matching, "}}").unwrap();

        from_fn.line(matching);

        return Some(enum_impl);
    }
    None
}

pub fn signal_fn_raw(dbc: &DBC, signal: &Signal, message_id: &MessageId) -> Result<Function> {
    let raw_fn_name = format!("{}_{}", signal.name().to_lowercase(), RAW_FN_SUFFIX);

    let mut signal_fn = codegen::Function::new(&raw_fn_name);
    signal_fn.allow("dead_code");
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    let signal_return_type = signal_return_type(signal);
    signal_fn.ret(codegen::Type::new(&signal_return_type));

    let default_signal_comment = format!("Read {} signal from can frame", signal.name());
    let signal_comment = dbc.signal_comment(message_id, signal.name()).unwrap_or(&default_signal_comment);
    signal_fn.doc(signal_comment);

    let read_byte_order = match signal.byte_order() {
        ByteOrder::LittleEndian => "let frame_payload: u64 = LE::read_u64(&self.frame_payload);",
        ByteOrder::BigEndian => "let  frame_payload: u64 = BE::read_u64(&self.frame_payload);",
    };
    signal_fn.line(read_byte_order);

    let bit_msk_const = 2u64.saturating_pow(*signal.signal_size() as u32) - 1;
    let signal_shift = shift_amount(*signal.byte_order(), *signal.start_bit(), *signal.signal_size());

    let calc = calc_raw(signal, signal_return_type, signal_shift, bit_msk_const)?;
    signal_fn.line(calc);

    Ok(signal_fn)
}

pub fn signal_fn_enum(signal: &Signal, enum_type: String) -> Result<Function> {
    let mut signal_fn = codegen::Function::new(&signal.name().to_lowercase());
    signal_fn.allow("dead_code");
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    signal_fn.ret(enum_type.clone());

    let raw_fn_name = format!("{}_{}", signal.name().to_lowercase(), RAW_FN_SUFFIX);

    signal_fn.line(format!("{}::from(self.{}() as u64)", enum_type, raw_fn_name));

    Ok(signal_fn)
}

fn calc_raw(signal: &Signal, signal_return_type: String, signal_shift: u64, bit_msk_const: u64) -> Result<String> {
    let mut calc = String::new();

    // No shift required if start_bit == 0
    let shift = if signal_shift != 0 {
        format!("(frame_payload >> {})", signal_shift)
    } else {
        format!("frame_payload")
    };

    write!(&mut calc, "({} & {:#X})", shift, bit_msk_const)?;

    if *signal.signal_size() != 1 {
        write!(&mut calc, " as {}", signal_return_type)?;
    }

    if *signal.factor() != 1.0 {
        write!(&mut calc, " * {:.6}", signal.factor())?;
    }

    if *signal.offset() != 0.0 && *signal.signal_size() <= 32 {
        write!(&mut calc, " + {}f32", signal.offset())?;
    } else if *signal.offset() != 0.0 {
        write!(&mut calc, " + {}f64", signal.offset())?;
    }

    // boolean signal
    if *signal.signal_size() == 1 {
        write!(&mut calc, " == 1")?;
    }

    Ok(calc)
}

fn signal_return_type(signal: &Signal) -> String {
    match signal.signal_size() {
        _ if *signal.signal_size() == 1 => "bool".to_string(),
        _ if *signal.signal_size() > 1 && *signal.signal_size() <= 32 => "f32".to_string(),
        _ => "f64".to_string(),
    }
}

fn shift_amount(byte_order: ByteOrder, start_bit: u64, signal_size: u64) -> u64 {
    match byte_order {
        ByteOrder::LittleEndian => start_bit,
        ByteOrder::BigEndian => 64 - signal_size - ((start_bit / 8) * 8 + (7 - (start_bit % 8))),
    }
}

fn message_const(message: &Message) -> Const {
    let const_name = format!("MESSAGE_ID_{}", message.message_name().to_shouty_snake_case());
    let mut c = Const::new(&const_name, codegen::Type::new("u32"), message.message_id().0.to_string());
    c.allow("dead_code");
    c.vis("pub");
    c
}

fn message_struct(dbc: &DBC, message: &Message) -> Struct {
    let mut message_struct = Struct::new(&message.message_name().to_camel_case());
    if let Some(message_comment) = dbc.message_comment(message.message_id()) {
        message_struct.doc(message_comment);
    }
    message_struct.allow("dead_code");
    message_struct.derive("Debug");
    message_struct.vis("pub");
    message_struct.field("frame_payload", "Vec<u8>");
    message_struct
}

fn message_impl(opt: &Opt, dbc: &DBC, message: &Message) -> Result<Impl> {

    let mut msg_impl = Impl::new(codegen::Type::new(&message.message_name().to_camel_case()));

    let new_fn = msg_impl.new_fn("new");
    new_fn.allow("dead_code");
    new_fn.vis("pub");
    new_fn.arg("mut frame_payload", codegen::Type::new("Vec<u8>"));
    new_fn.line("frame_payload.resize(8, 0);");
    new_fn.line(format!("{} {{ frame_payload }}", message.message_name().to_camel_case()));
    new_fn.ret(codegen::Type::new(&message.message_name().to_camel_case()));

    if opt.with_tokio {
        msg_impl.push_fn(message_stream(message));
    }

    for signal in message.signals() {

        msg_impl.push_fn(signal_fn_raw(dbc, signal, message.message_id())?);

        // Check if this signal can be turned into an enum
        let enum_type = dbc.value_descriptions_for_signal(message.message_id(), signal.name()).map(|_| to_enum_name(message.message_id(), signal.name()));
        if let Some(enum_type) = enum_type {
            msg_impl.push_fn(signal_fn_enum(signal, enum_type)?);
        }
    }

    Ok(msg_impl)
}

/// Generate message stream using socketcan's Broadcast Manager filters via socketcan-tokio.
fn message_stream(message: &Message) -> Function {
    let mut stream_fn = codegen::Function::new("stream");
    stream_fn.allow("dead_code");
    stream_fn.vis("pub");

    stream_fn.arg("can_interface", codegen::Type::new("&str"));
    stream_fn.arg("ival1", codegen::Type::new("&std::time::Duration"));
    stream_fn.arg("ival2", codegen::Type::new("&std::time::Duration"));

    let ret = format!("Box<Stream<Item = {}, Error = std::io::Error> + Sync + Send>", message.message_name().to_camel_case());
    stream_fn.ret(ret);

    stream_fn.line("let socket = BCMSocket::open_nb(&can_interface).unwrap();");
    stream_fn.line(format!("let message_id = CANMessageId::try_from({} as u32).unwrap();", message.message_id().0.to_string()));
    stream_fn.line("let frame_stream = socket.filter_id_incoming_frames(message_id, ival1.clone(), ival2.clone()).unwrap();");
    stream_fn.line(format!("let f = frame_stream.map(|frame| {}::new(frame.data().to_vec()));",  message.message_name().to_camel_case()));
    stream_fn.line("Box::new(f)");

    stream_fn
}

pub fn can_reader(opt: &Opt, dbc: &DBC) -> Result<Scope> {

    let mut scope = Scope::new();
    scope.import("byteorder", "{ByteOrder, LE, BE}");

    if opt.with_tokio {
        scope.import("tokio_socketcan_bcm", "{CANMessageId, BCMSocket}");
        scope.import("futures", "Stream");
        scope.import("std::convert", "TryFrom");
    }

    for message in dbc.messages() {
        scope.push_const(message_const(message));
    }

    for value_description in dbc.value_descriptions() {
        if let Some(signal_enum) = signal_enum(value_description) {
            scope.push_enum(signal_enum);
        }

        if let Some(enum_impl) = signal_enum_impl_from(value_description) {
            scope.push_impl(enum_impl);
        }
    }

    for message in dbc.messages() {
        scope.push_struct(message_struct(&dbc, message));
        scope.push_impl(message_impl(opt, &dbc, message)?);
    }

    Ok(scope)
}<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::sync::mpsc::Sender;

use errors::*;
use messages::*;

/// Subscription
struct Subscription {
  /// Subscription
  subscriber: Sender<SerialResponse>,
  /// The ports it is subscribed to
  ports: Vec<String>,
}

impl Subscription {
  /// Send a message to a subscriber
  fn send_message(&self, msg: SerialResponse) -> Result<()> {
    self
      .subscriber
      .send(msg)
      .map_err(|e| ErrorKind::SendResponse(e).into())
  }

  // Register interest in a port
  fn add_port(&mut self, port_name: &String) {
    match self.ports.iter().position(|p| p == port_name) {
      None => self.ports.push(port_name.to_string()),
      Some(_) => debug!("Port already subscribed to"),
    }
  }

  // Remove interest in a port
  fn remove_port(&mut self, port_name: &String) {
    self.ports.retain(|p| *p != *port_name);
  }

  // Remove interest in a port
  fn remove_all_ports(&mut self) {
    self.ports.clear();
  }
}

/// The subscription manager maintains a registry of
/// subscriptions vs ports
/// and provides methods for sending
/// messages to subscribers
pub struct SubscriptionManager {
  /// The subscriptions
  subscriptions: HashMap<String, Subscription>,
}

impl SubscriptionManager {
  /// Create a new SubscriptionManager instance
  pub fn new() -> SubscriptionManager {
    SubscriptionManager { subscriptions: HashMap::new() }
  }

  /// Add a port to a subscription
  pub fn add_port(&mut self, sub_id: &String, port_name: &String) -> Result<()> {
    match self.subscriptions.get_mut(sub_id) {
      Some(sub) => {
        sub.add_port(port_name);
        Ok(())
      }
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// Remove a single port from a given subscription or all subscriptions
  pub fn remove_port(&mut self, sub_id: &String, port_name: &String) -> Result<()> {
    match self.subscriptions.get_mut(sub_id) {
      Some(sub) => {
        sub.ports.retain(|p| p != port_name);
        Ok(())
      }
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// Remove a port from all subscriptions
  pub fn remove_port_from_all(&mut self, port_name: &String) {
    for (_, sub) in self.subscriptions.iter_mut() {
      sub.ports.retain(|p| p != port_name);
    }
  }

  /// Remove all ports from a single subscription or all subscriptions
  pub fn clear_ports(&mut self, sub_id: Option<&String>) {
    match sub_id {
      Some(sid) => {
        match self.subscriptions.get_mut(sid) {
          Some(sub) => {
            sub.ports.clear();
          }
          None => {}
        }
      }
      None => {
        for (_, sub) in self.subscriptions.iter_mut() {
          sub.ports.clear();
        }
      }
    }
  }

  /// Add a subscription
  pub fn add_subscription(&mut self, sub: SubscriptionRequest) {
    self
      .subscriptions
      .entry(sub.sub_id)
      .or_insert(
        Subscription {
          subscriber: sub.subscriber,
          ports: Vec::new(),
        },
      );
  }

  /// Check if subscription exists, otherwise return error
  pub fn check_subscription_exists(&self, sub_id: &String) -> Result<()> {
    match self.subscriptions.contains_key(sub_id) {
      true => Ok(()),
      false => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// End a subscription
  pub fn end_subscription(&mut self, sub_id: &String) {
    self.subscriptions.remove(sub_id);
  }

  /// Send a message to the given subscription
  pub fn send_message(&self, sub_id: &String, msg: SerialResponse) -> Result<()> {
    match self.subscriptions.get(sub_id) {
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
      Some(sub) => {
        sub
          .subscriber
          .send(msg)
          .map_err(|e| ErrorKind::SendResponse(e).into())
      }
    }
  }

  /// Broadcast a messages to all subscribers, returning
  /// a vec of (sub_id,ErrorKind::SendResponse) failures if some sends fail
  pub fn broadcast_message(&self, msg: SerialResponse) -> Vec<Error> {
    debug!("Broadcasting '{}' to all subscribers", &msg);
    let mut res = Vec::new();
    for (sub_id, sub) in self.subscriptions.iter() {
      match self.send_message(sub_id, msg.clone()) {
        Err(e) => res.push(e),
        _ => debug!("  Broadcast to '{}' was successful", sub_id),
      }
    }
    res
  }

  /// Broadcast message to all subscribers registered for a given port
  pub fn broadcast_message_for_port(&self, port_name: &String, msg: SerialResponse) -> Vec<Error> {
    debug!(
      "Broadcasting '{}' to all subscribers registered on port {}",
      &msg,
      port_name
    );
    let mut res = Vec::new();
    for (sub_id, sub) in self.subscriptions.iter() {
      if sub
           .ports
           .iter()
           .position(|p| *p == *port_name)
           .is_some() {
        match self.send_message(sub_id, msg.clone()) {
          Err(e) => res.push(e),
          _ => debug!("  Broadcast to '{}' was successful", sub_id),
        }
      }
    }
    res
  }

  /// Get a list of ports that currently have subscriptions
  pub fn subscribed_ports(&mut self) -> HashSet<String> {
    let mut subscribed_ports = HashSet::<String>::new();
    for subs in self.subscriptions.values() {
      subscribed_ports.extend(subs.ports.clone());
    }
    subscribed_ports
  }
}

#[cfg(test)]
mod tests {

  use std::collections::HashSet;
  use std::iter::FromIterator;
  use std::sync::mpsc::{Receiver, Sender, channel};

  use super::*;
  use errors::*;
  use messages::*;

  #[test]
  fn test_subscriptions() {

    fn should_get_msg(
      rcvr: &Receiver<SerialResponse>,
      serial_resp: &SerialResponse,
      fail_tag: &str,
    ) {
      if let Ok(resp) = rcvr.try_recv() {
        assert_eq!(
          resp,
          serial_resp.clone(),
          "{} messages '{:?}' '{:?}' should be equal",
          fail_tag,
          resp,
          serial_resp.clone()
        )
      } else {
        panic!("{} should not have recieved anything", fail_tag);
      }
    }

    fn should_not_get_a_msg(rcvr: &Receiver<SerialResponse>, fail_tag: &str) {
      if let Ok(resp) = rcvr.try_recv() {
        panic!(
          "{} should not have recieved anything, got {:?}",
          fail_tag,
          resp
        );
      }
    }

    // manager
    let mut sub_manager = SubscriptionManager::new();
    // ports
    let ports = vec!["/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2"];
    let ports_set = HashSet::from_iter(ports.clone().into_iter().map(|p| p.to_string()));
    // subscriber1
    let sub1_id = "SUB1";
    let sub1_channel = channel::<SerialResponse>();
    let sub1_req = SubscriptionRequest {
      sub_id: sub1_id.to_string(),
      subscriber: sub1_channel.0,
    };
    // subscriber 2
    let sub2_id = "SUB2";
    let sub2_channel = channel::<SerialResponse>();
    let sub2_req = SubscriptionRequest {
      sub_id: sub2_id.to_string(),
      subscriber: sub2_channel.0,
    };
    // Add subscriber 1
    sub_manager.add_subscription(sub1_req);
    for port in ports.iter() {
      sub_manager
        .add_port(&sub1_id.to_string(), &port.to_string())
        .unwrap();
    }
    // Sub1 should be subscribed to all ports
    assert_eq!(
      sub_manager.subscribed_ports(),
      ports_set,
      "Port sets should be equal",
    );
    // Add subscriber2, but provide no ports
    sub_manager.add_subscription(sub2_req);
    // Broadcast a message to everyone
    let all_subscribers_msg = SerialResponse::Ok { msg: "Broadcast all!".to_string() };
    let mut all_res = sub_manager.broadcast_message(all_subscribers_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_get_msg(&sub1_channel.1, &all_subscribers_msg, "Subscriber 1");
    should_get_msg(&sub2_channel.1, &all_subscribers_msg, "Subscriber 2");
    // Send message to one subscriber
    let sub1_msg = SerialResponse::Ok { msg: "Only subscriber 1 should see this!".to_string() };
    sub_manager
      .send_message(&sub1_id.to_string(), sub1_msg.clone())
      .expect("Send to subscriber 1 should work!");
    should_get_msg(&sub1_channel.1, &sub1_msg, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
    // Send message to subscribers of a given port
    all_res = sub_manager.broadcast_message_for_port(&"/dev/ttyUSB2".to_string(), sub1_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_get_msg(&sub1_channel.1, &sub1_msg, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
    // unsubscribe sub1 from /dev/ttyUSB2
    sub_manager.remove_port(&sub1_id.to_string(), &"/dev/ttyUSB2".to_string()).expect("remvoving sub1 from port should not fail.");
    // No one should get message on ttyUSB2 now
    all_res = sub_manager.broadcast_message_for_port(&"/dev/ttyUSB2".to_string(), sub1_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_not_get_a_msg(&sub1_channel.1, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
  }

}<commit_msg>Formatting Tweak<commit_after>use std::collections::{HashMap, HashSet};
use std::sync::mpsc::Sender;

use errors::*;
use messages::*;

/// Subscription
struct Subscription {
  /// Subscription
  subscriber: Sender<SerialResponse>,
  /// The ports it is subscribed to
  ports: Vec<String>,
}

impl Subscription {
  /// Send a message to a subscriber
  fn send_message(&self, msg: SerialResponse) -> Result<()> {
    self
      .subscriber
      .send(msg)
      .map_err(|e| ErrorKind::SendResponse(e).into())
  }

  // Register interest in a port
  fn add_port(&mut self, port_name: &String) {
    match self.ports.iter().position(|p| p == port_name) {
      None => self.ports.push(port_name.to_string()),
      Some(_) => debug!("Port already subscribed to"),
    }
  }

  // Remove interest in a port
  fn remove_port(&mut self, port_name: &String) {
    self.ports.retain(|p| *p != *port_name);
  }

  // Remove interest in a port
  fn remove_all_ports(&mut self) {
    self.ports.clear();
  }
}

/// The subscription manager maintains a registry of
/// subscriptions vs ports
/// and provides methods for sending
/// messages to subscribers
pub struct SubscriptionManager {
  /// The subscriptions
  subscriptions: HashMap<String, Subscription>,
}

impl SubscriptionManager {
  /// Create a new SubscriptionManager instance
  pub fn new() -> SubscriptionManager {
    SubscriptionManager { subscriptions: HashMap::new() }
  }

  /// Add a port to a subscription
  pub fn add_port(&mut self, sub_id: &String, port_name: &String) -> Result<()> {
    match self.subscriptions.get_mut(sub_id) {
      Some(sub) => {
        sub.add_port(port_name);
        Ok(())
      }
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// Remove a single port from a given subscription or all subscriptions
  pub fn remove_port(&mut self, sub_id: &String, port_name: &String) -> Result<()> {
    match self.subscriptions.get_mut(sub_id) {
      Some(sub) => {
        sub.ports.retain(|p| p != port_name);
        Ok(())
      }
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// Remove a port from all subscriptions
  pub fn remove_port_from_all(&mut self, port_name: &String) {
    for (_, sub) in self.subscriptions.iter_mut() {
      sub.ports.retain(|p| p != port_name);
    }
  }

  /// Remove all ports from a single subscription or all subscriptions
  pub fn clear_ports(&mut self, sub_id: Option<&String>) {
    match sub_id {
      Some(sid) => {
        match self.subscriptions.get_mut(sid) {
          Some(sub) => {
            sub.ports.clear();
          }
          None => {}
        }
      }
      None => {
        for (_, sub) in self.subscriptions.iter_mut() {
          sub.ports.clear();
        }
      }
    }
  }

  /// Add a subscription
  pub fn add_subscription(&mut self, sub: SubscriptionRequest) {
    self
      .subscriptions
      .entry(sub.sub_id)
      .or_insert(
        Subscription {
          subscriber: sub.subscriber,
          ports: Vec::new(),
        },
      );
  }

  /// Check if subscription exists, otherwise return error
  pub fn check_subscription_exists(&self, sub_id: &String) -> Result<()> {
    match self.subscriptions.contains_key(sub_id) {
      true => Ok(()),
      false => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
    }
  }

  /// End a subscription
  pub fn end_subscription(&mut self, sub_id: &String) {
    self.subscriptions.remove(sub_id);
  }

  /// Send a message to the given subscription
  pub fn send_message(&self, sub_id: &String, msg: SerialResponse) -> Result<()> {
    match self.subscriptions.get(sub_id) {
      None => Err(ErrorKind::SubscriptionNotFound(sub_id.to_string()).into()),
      Some(sub) => {
        sub
          .subscriber
          .send(msg)
          .map_err(|e| ErrorKind::SendResponse(e).into())
      }
    }
  }

  /// Broadcast a messages to all subscribers, returning
  /// a vec of (sub_id,ErrorKind::SendResponse) failures if some sends fail
  pub fn broadcast_message(&self, msg: SerialResponse) -> Vec<Error> {
    debug!("Broadcasting '{}' to all subscribers", &msg);
    let mut res = Vec::new();
    for (sub_id, sub) in self.subscriptions.iter() {
      match self.send_message(sub_id, msg.clone()) {
        Err(e) => res.push(e),
        _ => debug!("  Broadcast to '{}' was successful", sub_id),
      }
    }
    res
  }

  /// Broadcast message to all subscribers registered for a given port
  pub fn broadcast_message_for_port(&self, port_name: &String, msg: SerialResponse) -> Vec<Error> {
    debug!(
      "Broadcasting '{}' to all subscribers registered on port {}",
      &msg,
      port_name
    );
    let mut res = Vec::new();
    for (sub_id, sub) in self.subscriptions.iter() {
      if sub
           .ports
           .iter()
           .position(|p| *p == *port_name)
           .is_some() {
        match self.send_message(sub_id, msg.clone()) {
          Err(e) => res.push(e),
          _ => debug!("  Broadcast to '{}' was successful", sub_id),
        }
      }
    }
    res
  }

  /// Get a list of ports that currently have subscriptions
  pub fn subscribed_ports(&mut self) -> HashSet<String> {
    let mut subscribed_ports = HashSet::<String>::new();
    for subs in self.subscriptions.values() {
      subscribed_ports.extend(subs.ports.clone());
    }
    subscribed_ports
  }
}

#[cfg(test)]
mod tests {

  use std::collections::HashSet;
  use std::iter::FromIterator;
  use std::sync::mpsc::{Receiver, Sender, channel};

  use super::*;
  use errors::*;
  use messages::*;

  #[test]
  fn test_subscriptions() {

    fn should_get_msg(
      rcvr: &Receiver<SerialResponse>,
      serial_resp: &SerialResponse,
      fail_tag: &str,
    ) {
      if let Ok(resp) = rcvr.try_recv() {
        assert_eq!(
          resp,
          serial_resp.clone(),
          "{} messages '{:?}' '{:?}' should be equal",
          fail_tag,
          resp,
          serial_resp.clone()
        )
      } else {
        panic!("{} should not have recieved anything", fail_tag);
      }
    }

    fn should_not_get_a_msg(rcvr: &Receiver<SerialResponse>, fail_tag: &str) {
      if let Ok(resp) = rcvr.try_recv() {
        panic!(
          "{} should not have recieved anything, got {:?}",
          fail_tag,
          resp
        );
      }
    }

    // manager
    let mut sub_manager = SubscriptionManager::new();
    // ports
    let ports = vec!["/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2"];
    let ports_set = HashSet::from_iter(ports.clone().into_iter().map(|p| p.to_string()));
    // subscriber1
    let sub1_id = "SUB1";
    let sub1_channel = channel::<SerialResponse>();
    let sub1_req = SubscriptionRequest {
      sub_id: sub1_id.to_string(),
      subscriber: sub1_channel.0,
    };
    // subscriber 2
    let sub2_id = "SUB2";
    let sub2_channel = channel::<SerialResponse>();
    let sub2_req = SubscriptionRequest {
      sub_id: sub2_id.to_string(),
      subscriber: sub2_channel.0,
    };
    // Add subscriber 1
    sub_manager.add_subscription(sub1_req);
    for port in ports.iter() {
      sub_manager
        .add_port(&sub1_id.to_string(), &port.to_string())
        .unwrap();
    }
    // Sub1 should be subscribed to all ports
    assert_eq!(
      sub_manager.subscribed_ports(),
      ports_set,
      "Port sets should be equal",
    );
    // Add subscriber2, but provide no ports
    sub_manager.add_subscription(sub2_req);
    // Broadcast a message to everyone
    let all_subscribers_msg = SerialResponse::Ok { msg: "Broadcast all!".to_string() };
    let mut all_res = sub_manager.broadcast_message(all_subscribers_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_get_msg(&sub1_channel.1, &all_subscribers_msg, "Subscriber 1");
    should_get_msg(&sub2_channel.1, &all_subscribers_msg, "Subscriber 2");
    // Send message to one subscriber
    let sub1_msg = SerialResponse::Ok { msg: "Only subscriber 1 should see this!".to_string() };
    sub_manager
      .send_message(&sub1_id.to_string(), sub1_msg.clone())
      .expect("Send to subscriber 1 should work!");
    should_get_msg(&sub1_channel.1, &sub1_msg, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
    // Send message to subscribers of a given port
    all_res = sub_manager.broadcast_message_for_port(&"/dev/ttyUSB2".to_string(), sub1_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_get_msg(&sub1_channel.1, &sub1_msg, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
    // unsubscribe sub1 from /dev/ttyUSB2
    sub_manager
      .remove_port(&sub1_id.to_string(), &"/dev/ttyUSB2".to_string())
      .expect("remvoving sub1 from port should not fail.");
    // No one should get message on ttyUSB2 now
    all_res = sub_manager.broadcast_message_for_port(&"/dev/ttyUSB2".to_string(), sub1_msg.clone());
    assert!(all_res.len() == 0, "There should be no errors");
    should_not_get_a_msg(&sub1_channel.1, "Subscriber 1");
    should_not_get_a_msg(&sub2_channel.1, "Subscriber 2");
  }

}<|endoftext|>


<commit_before>use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};
use output::Colours;
use output::cell::TextCellContents;

pub struct FileName<'a, 'dir: 'a> {
    file:    &'a File<'dir>,
    colours: &'a Colours,
}

impl<'a, 'dir> FileName<'a, 'dir> {
    pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
        FileName {
            file: file,
            colours: colours,
        }
    }

    pub fn file_name(&self, links: bool, classify: bool) -> TextCellContents {
        let mut bits = Vec::new();

        if self.file.dir.is_none() {
            if let Some(parent) = self.file.path.parent() {
                let coconut = parent.components().count();

                if coconut == 1 && parent.has_root() {
                    bits.push(self.colours.symlink_path.paint("/"));
                }
                else if coconut >= 1 {
                    bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                    bits.push(self.colours.symlink_path.paint("/"));
                }
            }
        }

        if !self.file.name.is_empty() {
            for bit in self.coloured_file_name() {
                bits.push(bit);
            }
        }

        if links && self.file.is_link() {
            match self.file.link_target() {
                FileTarget::Ok(target) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.punctuation.paint("->"));
                    bits.push(Style::default().paint(" "));

                    if let Some(parent) = target.path.parent() {
                        let coconut = parent.components().count();

                        if coconut == 1 && parent.has_root() {
                            bits.push(self.colours.symlink_path.paint("/"));
                        }
                        else if coconut >= 1 {
                            bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                            bits.push(self.colours.symlink_path.paint("/"));
                        }
                    }

                    if !target.name.is_empty() {
                        bits.push(FileName::new(&target, self.colours).style().paint(target.name));
                    }
                },

                FileTarget::Broken(broken_path) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_arrow.paint("->"));
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_filename.paint(broken_path.display().to_string()));
                },

                FileTarget::Err(_) => {
                    // Do nothing -- the error gets displayed on the next line
                }
            }
        }
        else if classify {
            if let Some(class) = self.classify_char() {
                bits.push(Style::default().paint(class));
            }
        }

        bits.into()
    }

    fn classify_char(&self) -> Option<&'static str> {
        if self.file.is_executable_file() {
            Some("*")
        } else if self.file.is_directory() {
            Some("/")
        } else if self.file.is_pipe() {
            Some("|")
        } else if self.file.is_link() {
            Some("@")
        } else if self.file.is_socket() {
            Some("=")
        } else {
            None
        }
    }

    /// Returns at least one ANSI-highlighted string representing this file’s
    /// name using the given set of colours.
    ///
    /// Ordinarily, this will be just one string: the file’s complete name,
    /// coloured according to its file type. If the name contains control
    /// characters such as newlines or escapes, though, we can’t just print them
    /// to the screen directly, because then there’ll be newlines in weird places.
    ///
    /// So in that situation, those characters will be escaped and highlighted in
    /// a different colour.
    fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
        let file_style = self.style();
        let mut bits = Vec::new();

        if self.file.name.chars().all(|c| c >= 0x20 as char) {
            bits.push(file_style.paint(self.file.name.clone()));
        }
        else {
            for c in self.file.name.chars() {
                // The `escape_default` method on `char` is *almost* what we want here, but
                // it still escapes non-ASCII UTF-8 characters, which are still printable.

                if c >= 0x20 as char {
                    // TODO: This allocates way too much,
                    // hence the `all` check above.
                    let mut s = String::new();
                    s.push(c);
                    bits.push(file_style.paint(s));
                } else {
                    let s = c.escape_default().collect::<String>();
                    bits.push(self.colours.control_char.paint(s));
                }
            }
        }

        bits
    }

    pub fn style(&self) -> Style {
        match self.file {
            f if f.is_directory()        => self.colours.filetypes.directory,
            f if f.is_executable_file()  => self.colours.filetypes.executable,
            f if f.is_link()             => self.colours.filetypes.symlink,
            f if f.is_pipe()             => self.colours.filetypes.pipe,
            f if f.is_char_device()
               | f.is_block_device()     => self.colours.filetypes.device,
            f if f.is_socket()           => self.colours.filetypes.socket,
            f if !f.is_file()            => self.colours.filetypes.special,
            f if f.is_immediate()        => self.colours.filetypes.immediate,
            f if f.is_image()            => self.colours.filetypes.image,
            f if f.is_video()            => self.colours.filetypes.video,
            f if f.is_music()            => self.colours.filetypes.music,
            f if f.is_lossless()         => self.colours.filetypes.lossless,
            f if f.is_crypto()           => self.colours.filetypes.crypto,
            f if f.is_document()         => self.colours.filetypes.document,
            f if f.is_compressed()       => self.colours.filetypes.compressed,
            f if f.is_temp()             => self.colours.filetypes.temp,
            f if f.is_compiled()         => self.colours.filetypes.compiled,
            _                            => self.colours.filetypes.normal,
        }
    }
}
<commit_msg>Remember to escape characters in link targets<commit_after>use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};
use output::Colours;
use output::cell::TextCellContents;

pub struct FileName<'a, 'dir: 'a> {
    file:    &'a File<'dir>,
    colours: &'a Colours,
}

impl<'a, 'dir> FileName<'a, 'dir> {
    pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
        FileName {
            file: file,
            colours: colours,
        }
    }

    pub fn file_name(&self, links: bool, classify: bool) -> TextCellContents {
        let mut bits = Vec::new();

        if self.file.dir.is_none() {
            if let Some(parent) = self.file.path.parent() {
                let coconut = parent.components().count();

                if coconut == 1 && parent.has_root() {
                    bits.push(self.colours.symlink_path.paint("/"));
                }
                else if coconut >= 1 {
                    bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                    bits.push(self.colours.symlink_path.paint("/"));
                }
            }
        }

        if !self.file.name.is_empty() {
            for bit in self.coloured_file_name() {
                bits.push(bit);
            }
        }

        if links && self.file.is_link() {
            match self.file.link_target() {
                FileTarget::Ok(target) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.punctuation.paint("->"));
                    bits.push(Style::default().paint(" "));

                    if let Some(parent) = target.path.parent() {
                        let coconut = parent.components().count();

                        if coconut == 1 && parent.has_root() {
                            bits.push(self.colours.symlink_path.paint("/"));
                        }
                        else if coconut >= 1 {
                            bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                            bits.push(self.colours.symlink_path.paint("/"));
                        }
                    }

                    if !target.name.is_empty() {
                        let target = FileName::new(&target, self.colours);
                        for bit in target.coloured_file_name() {
                            bits.push(bit);
                        }
                    }
                },

                FileTarget::Broken(broken_path) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_arrow.paint("->"));
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_filename.paint(broken_path.display().to_string()));
                },

                FileTarget::Err(_) => {
                    // Do nothing -- the error gets displayed on the next line
                }
            }
        }
        else if classify {
            if let Some(class) = self.classify_char() {
                bits.push(Style::default().paint(class));
            }
        }

        bits.into()
    }

    fn classify_char(&self) -> Option<&'static str> {
        if self.file.is_executable_file() {
            Some("*")
        } else if self.file.is_directory() {
            Some("/")
        } else if self.file.is_pipe() {
            Some("|")
        } else if self.file.is_link() {
            Some("@")
        } else if self.file.is_socket() {
            Some("=")
        } else {
            None
        }
    }

    /// Returns at least one ANSI-highlighted string representing this file’s
    /// name using the given set of colours.
    ///
    /// Ordinarily, this will be just one string: the file’s complete name,
    /// coloured according to its file type. If the name contains control
    /// characters such as newlines or escapes, though, we can’t just print them
    /// to the screen directly, because then there’ll be newlines in weird places.
    ///
    /// So in that situation, those characters will be escaped and highlighted in
    /// a different colour.
    fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
        let file_style = self.style();
        let mut bits = Vec::new();

        if self.file.name.chars().all(|c| c >= 0x20 as char) {
            bits.push(file_style.paint(self.file.name.clone()));
        }
        else {
            for c in self.file.name.chars() {
                // The `escape_default` method on `char` is *almost* what we want here, but
                // it still escapes non-ASCII UTF-8 characters, which are still printable.

                if c >= 0x20 as char {
                    // TODO: This allocates way too much,
                    // hence the `all` check above.
                    let mut s = String::new();
                    s.push(c);
                    bits.push(file_style.paint(s));
                } else {
                    let s = c.escape_default().collect::<String>();
                    bits.push(self.colours.control_char.paint(s));
                }
            }
        }

        bits
    }

    pub fn style(&self) -> Style {
        match self.file {
            f if f.is_directory()        => self.colours.filetypes.directory,
            f if f.is_executable_file()  => self.colours.filetypes.executable,
            f if f.is_link()             => self.colours.filetypes.symlink,
            f if f.is_pipe()             => self.colours.filetypes.pipe,
            f if f.is_char_device()
               | f.is_block_device()     => self.colours.filetypes.device,
            f if f.is_socket()           => self.colours.filetypes.socket,
            f if !f.is_file()            => self.colours.filetypes.special,
            f if f.is_immediate()        => self.colours.filetypes.immediate,
            f if f.is_image()            => self.colours.filetypes.image,
            f if f.is_video()            => self.colours.filetypes.video,
            f if f.is_music()            => self.colours.filetypes.music,
            f if f.is_lossless()         => self.colours.filetypes.lossless,
            f if f.is_crypto()           => self.colours.filetypes.crypto,
            f if f.is_document()         => self.colours.filetypes.document,
            f if f.is_compressed()       => self.colours.filetypes.compressed,
            f if f.is_temp()             => self.colours.filetypes.temp,
            f if f.is_compiled()         => self.colours.filetypes.compiled,
            _                            => self.colours.filetypes.normal,
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::collections::graph::*;
use adapton::engine::*;

mod graph_add_edge {
    use super::*;

    fn doit<G: GraphIntro<usize> + GraphElim<usize>>(g: G, (i, j): (usize, usize)) -> G {
        ns(name_of_str("add_edge"),
           || G::add_edge(g, name_pair(name_of_usize(i), name_of_usize(j)), i, j))
    }

    fn run_bench<G: GraphIntro<usize> + GraphElim<usize>, GraphBuilder>(b: &mut Bencher,
                                                                        builder: GraphBuilder)
        where GraphBuilder: Fn() -> G
    {
        let mut input = builder();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;

            b.iter(|| doit(input.clone(), (i, j)));
            input = GraphIntro::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j)
        }
    }

    #[bench]
    fn benchmark_naive_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_dcg_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_naive_adj_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, AdjacencyGraph::empty);
    }

    #[bench]
    fn benchmark_dcg_adj_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, AdjacencyGraph::empty);
    }
}

mod graph_conversion {
    use super::*;

    fn convert_to_adj(g: &Graph<usize>) -> AdjacencyGraph<usize> {
        ns(name_of_str("convert_to_adj"), || adjacency_of_edge_list(g))
    }

    fn convert_to_edge_list(g: &AdjacencyGraph<usize>) -> Graph<usize> {
        ns(name_of_str("convert_to_edge_list"),
           || edge_list_of_adjacency(g))
    }

    fn run_graph_bench<From: GraphIntro<usize> + GraphElim<usize>,
                       To: GraphIntro<usize> + GraphElim<usize>,
                       Runner>
        (b: &mut Bencher,
         f: Runner)
        where Runner: Fn(&From) -> To
    {
        let mut input = From::empty();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;
            input = From::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j);

            b.iter(|| f(&input));
        }
    }

    #[bench]
    fn benchmark_naive_graph_to_adj(b: &mut Bencher) {
        init_naive();
        run_graph_bench(b, convert_to_adj);
    }

    #[bench]
    fn benchmark_dcg_graph_to_adj(b: &mut Bencher) {
        init_dcg();
        run_graph_bench(b, convert_to_adj);
    }

    #[bench]
    fn benchmark_naive_adj_to_graph(b: &mut Bencher) {
        init_naive();
        run_graph_bench(b, convert_to_edge_list);
    }

    #[bench]
    fn benchmark_dcg_adj_to_graph(b: &mut Bencher) {
        init_dcg();
        run_graph_bench(b, convert_to_edge_list);
    }
}

mod graph_reverse {
    use super::*;

    fn reverse<G: GraphIntro<usize> + GraphElim<usize>>(g: &G) -> G {
        ns(name_of_str("reverse_graph"), || G::reverse_edges(g))
    }

    fn run_bench<G: GraphIntro<usize> + GraphElim<usize>, GraphBuilder>(b: &mut Bencher,
                                                                        builder: GraphBuilder)
        where GraphBuilder: Fn() -> G
    {
        let mut input = builder();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;
            input = G::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j);

            b.iter(|| reverse(&input));
        }
    }

    #[bench]
    fn benchmark_naive_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_dcg_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_naive_adj_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, AdjacencyGraph::empty);
    }

    #[bench]
    fn benchmark_dcg_adj_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, AdjacencyGraph::empty);
    }
}
<commit_msg>Fix bench build after engine::manage refactor<commit_after>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::collections::graph::*;
use adapton::engine::*;
use adapton::engine::manage::*;

mod graph_add_edge {
    use super::*;

    fn doit<G: GraphIntro<usize> + GraphElim<usize>>(g: G, (i, j): (usize, usize)) -> G {
        ns(name_of_str("add_edge"),
           || G::add_edge(g, name_pair(name_of_usize(i), name_of_usize(j)), i, j))
    }

    fn run_bench<G: GraphIntro<usize> + GraphElim<usize>, GraphBuilder>(b: &mut Bencher,
                                                                        builder: GraphBuilder)
        where GraphBuilder: Fn() -> G
    {
        let mut input = builder();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;

            b.iter(|| doit(input.clone(), (i, j)));
            input = GraphIntro::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j)
        }
    }

    #[bench]
    fn benchmark_naive_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_dcg_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_naive_adj_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, AdjacencyGraph::empty);
    }

    #[bench]
    fn benchmark_dcg_adj_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, AdjacencyGraph::empty);
    }
}

mod graph_conversion {
    use super::*;

    fn convert_to_adj(g: &Graph<usize>) -> AdjacencyGraph<usize> {
        ns(name_of_str("convert_to_adj"), || adjacency_of_edge_list(g))
    }

    fn convert_to_edge_list(g: &AdjacencyGraph<usize>) -> Graph<usize> {
        ns(name_of_str("convert_to_edge_list"),
           || edge_list_of_adjacency(g))
    }

    fn run_graph_bench<From: GraphIntro<usize> + GraphElim<usize>,
                       To: GraphIntro<usize> + GraphElim<usize>,
                       Runner>
        (b: &mut Bencher,
         f: Runner)
        where Runner: Fn(&From) -> To
    {
        let mut input = From::empty();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;
            input = From::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j);

            b.iter(|| f(&input));
        }
    }

    #[bench]
    fn benchmark_naive_graph_to_adj(b: &mut Bencher) {
        init_naive();
        run_graph_bench(b, convert_to_adj);
    }

    #[bench]
    fn benchmark_dcg_graph_to_adj(b: &mut Bencher) {
        init_dcg();
        run_graph_bench(b, convert_to_adj);
    }

    #[bench]
    fn benchmark_naive_adj_to_graph(b: &mut Bencher) {
        init_naive();
        run_graph_bench(b, convert_to_edge_list);
    }

    #[bench]
    fn benchmark_dcg_adj_to_graph(b: &mut Bencher) {
        init_dcg();
        run_graph_bench(b, convert_to_edge_list);
    }
}

mod graph_reverse {
    use super::*;

    fn reverse<G: GraphIntro<usize> + GraphElim<usize>>(g: &G) -> G {
        ns(name_of_str("reverse_graph"), || G::reverse_edges(g))
    }

    fn run_bench<G: GraphIntro<usize> + GraphElim<usize>, GraphBuilder>(b: &mut Bencher,
                                                                        builder: GraphBuilder)
        where GraphBuilder: Fn() -> G
    {
        let mut input = builder();

        let max = 100;

        for i in (1..max / 2 - 1).into_iter() {
            let j = max - i;
            input = G::add_edge(input, name_pair(name_of_usize(i), name_of_usize(j)), i, j);

            b.iter(|| reverse(&input));
        }
    }

    #[bench]
    fn benchmark_naive_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_dcg_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, Graph::empty);
    }

    #[bench]
    fn benchmark_naive_adj_graph(b: &mut Bencher) {
        init_naive();
        run_bench(b, AdjacencyGraph::empty);
    }

    #[bench]
    fn benchmark_dcg_adj_graph(b: &mut Bencher) {
        init_dcg();
        run_bench(b, AdjacencyGraph::empty);
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;

use bincode::Options as _;
use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $x:expr, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str($x).unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "2.01", "1");
bench_decimal_op!(add_two, +, "2.01", "2");
bench_decimal_op!(add_one_hundred, +, "2.01", "100");
bench_decimal_op!(add_point_zero_one, +, "2.01", "0.01");
bench_decimal_op!(add_negative_point_five, +, "2.01", "-0.5");
bench_decimal_op!(add_pi, +, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "2.01", "1");
bench_decimal_op!(sub_two, -, "2.01", "2");
bench_decimal_op!(sub_one_hundred, -, "2.01", "100");
bench_decimal_op!(sub_point_zero_one, -, "2.01", "0.01");
bench_decimal_op!(sub_negative_point_five, -, "2.01", "-0.5");
bench_decimal_op!(sub_pi, -, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "2.01", "1");
bench_decimal_op!(mul_two, *, "2.01", "2");
bench_decimal_op!(mul_one_hundred, *, "2.01", "100");
bench_decimal_op!(mul_point_zero_one, *, "2.01", "0.01");
bench_decimal_op!(mul_negative_point_five, *, "2.01", "-0.5");
bench_decimal_op!(mul_pi, *, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "2.01", "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "2.01", "1");
bench_decimal_op!(div_two, /, "2.01", "2");
bench_decimal_op!(div_one_hundred, /, "2.01", "100");
bench_decimal_op!(div_point_zero_one, /, "2.01", "0.01");
bench_decimal_op!(div_negative_point_five, /, "2.01", "-0.5");
bench_decimal_op!(div_pi, /, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "2.01", "-3.1415926535897932384626433832");
bench_decimal_op!(div_no_underflow, /, "1.02343545345", "0.35454343453");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

const SAMPLE_STRS: &[&str] = &[
    "3950.123456",
    "3950",
    "0.1",
    "0.01",
    "0.001",
    "0.0001",
    "0.00001",
    "0.000001",
    "1",
    "-100",
    "-123.456",
    "119996.25",
    "1000000",
    "9999999.99999",
    "12340.56789",
];

#[bench]
fn serialize_bincode(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for d in &decimals {
            let bytes = bincode::options().serialize(d).unwrap();
            test::black_box(bytes);
        }
    })
}

#[cfg(feature = "serde-str")]
#[bench]
fn deserialize_bincode(b: &mut test::Bencher) {
    let payloads: Vec<Vec<u8>> = SAMPLE_STRS
        .iter()
        .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap())
        .collect();

    b.iter(|| {
        for payload in &payloads {
            let decimal: Decimal = bincode::options().deserialize(payload).unwrap();
            test::black_box(decimal);
        }
    })
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    b.iter(|| {
        for s in SAMPLE_STRS {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[bench]
fn decimal_to_string(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for s in decimals.iter() {
            let string = s.to_string();
            test::black_box(string);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use bytes::BytesMut;
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples: Vec<Decimal> = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}

#[bench]
fn powi(b: &mut ::test::Bencher) {
    // These exponents have to be fairly small because multiplcation overflows easily
    let samples = &[
        (Decimal::from_str("36.7").unwrap(), 5),
        (Decimal::from_str("0.00000007").unwrap(), 5),
        (Decimal::from(2), 64),
        (Decimal::from_str("8819287.19276555").unwrap(), 3),
        (Decimal::from_str("-8819287.19276555").unwrap(), 3),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.0.powi(sample.1);
            ::test::black_box(result);
        }
    });
}

#[bench]
fn sqrt(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19276555").unwrap(),
        Decimal::from_str("-8819287.19276555").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.sqrt();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn exp(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8.19").unwrap(),
        Decimal::from_str("-8.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.exp();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_cdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_cdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_pdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_pdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn ln(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19").unwrap(),
        Decimal::from_str("-8819287.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.ln();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn erf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from(0),
        Decimal::from(1),
        Decimal::from_str("-0.98717").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from_str("0.1111").unwrap(),
        Decimal::from_str("0.4").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.erf();
            ::test::black_box(result);
        }
    });
}
<commit_msg>Fixes benchmarks to support mathematics feature flag<commit_after>#![feature(test)]

extern crate test;

use bincode::Options as _;
use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $x:expr, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str($x).unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "2.01", "1");
bench_decimal_op!(add_two, +, "2.01", "2");
bench_decimal_op!(add_one_hundred, +, "2.01", "100");
bench_decimal_op!(add_point_zero_one, +, "2.01", "0.01");
bench_decimal_op!(add_negative_point_five, +, "2.01", "-0.5");
bench_decimal_op!(add_pi, +, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "2.01", "1");
bench_decimal_op!(sub_two, -, "2.01", "2");
bench_decimal_op!(sub_one_hundred, -, "2.01", "100");
bench_decimal_op!(sub_point_zero_one, -, "2.01", "0.01");
bench_decimal_op!(sub_negative_point_five, -, "2.01", "-0.5");
bench_decimal_op!(sub_pi, -, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "2.01", "1");
bench_decimal_op!(mul_two, *, "2.01", "2");
bench_decimal_op!(mul_one_hundred, *, "2.01", "100");
bench_decimal_op!(mul_point_zero_one, *, "2.01", "0.01");
bench_decimal_op!(mul_negative_point_five, *, "2.01", "-0.5");
bench_decimal_op!(mul_pi, *, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "2.01", "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "2.01", "1");
bench_decimal_op!(div_two, /, "2.01", "2");
bench_decimal_op!(div_one_hundred, /, "2.01", "100");
bench_decimal_op!(div_point_zero_one, /, "2.01", "0.01");
bench_decimal_op!(div_negative_point_five, /, "2.01", "-0.5");
bench_decimal_op!(div_pi, /, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "2.01", "-3.1415926535897932384626433832");
bench_decimal_op!(div_no_underflow, /, "1.02343545345", "0.35454343453");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

const SAMPLE_STRS: &[&str] = &[
    "3950.123456",
    "3950",
    "0.1",
    "0.01",
    "0.001",
    "0.0001",
    "0.00001",
    "0.000001",
    "1",
    "-100",
    "-123.456",
    "119996.25",
    "1000000",
    "9999999.99999",
    "12340.56789",
];

#[bench]
fn serialize_bincode(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for d in &decimals {
            let bytes = bincode::options().serialize(d).unwrap();
            test::black_box(bytes);
        }
    })
}

#[cfg(feature = "serde-str")]
#[bench]
fn deserialize_bincode(b: &mut test::Bencher) {
    let payloads: Vec<Vec<u8>> = SAMPLE_STRS
        .iter()
        .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap())
        .collect();

    b.iter(|| {
        for payload in &payloads {
            let decimal: Decimal = bincode::options().deserialize(payload).unwrap();
            test::black_box(decimal);
        }
    })
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    b.iter(|| {
        for s in SAMPLE_STRS {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[bench]
fn decimal_to_string(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for s in decimals.iter() {
            let string = s.to_string();
            test::black_box(string);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use bytes::BytesMut;
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples: Vec<Decimal> = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}

#[cfg(feature = "maths")]
mod maths {
    use rust_decimal::prelude::*;

    #[bench]
    fn powi(b: &mut ::test::Bencher) {
        // These exponents have to be fairly small because multiplcation overflows easily
        let samples = &[
            (Decimal::from_str("36.7").unwrap(), 5),
            (Decimal::from_str("0.00000007").unwrap(), 5),
            (Decimal::from(2), 64),
            (Decimal::from_str("8819287.19276555").unwrap(), 3),
            (Decimal::from_str("-8819287.19276555").unwrap(), 3),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.0.powi(sample.1);
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn sqrt(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from_str("36.7").unwrap(),
            Decimal::from_str("0.00000007").unwrap(),
            Decimal::from(2),
            Decimal::from_str("8819287.19276555").unwrap(),
            Decimal::from_str("-8819287.19276555").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.sqrt();
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn exp(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from_str("3.7").unwrap(),
            Decimal::from_str("0.07").unwrap(),
            Decimal::from(2),
            Decimal::from_str("8.19").unwrap(),
            Decimal::from_str("-8.19").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.exp();
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn norm_cdf(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from_str("3.7").unwrap(),
            Decimal::from_str("0.007").unwrap(),
            Decimal::from(2),
            Decimal::from_str("1.19").unwrap(),
            Decimal::from_str("-1.19").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.norm_cdf();
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn norm_pdf(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from_str("3.7").unwrap(),
            Decimal::from_str("0.007").unwrap(),
            Decimal::from(2),
            Decimal::from_str("1.19").unwrap(),
            Decimal::from_str("-1.19").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.norm_pdf();
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn ln(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from_str("36.7").unwrap(),
            Decimal::from_str("0.00000007").unwrap(),
            Decimal::from(2),
            Decimal::from_str("8819287.19").unwrap(),
            Decimal::from_str("-8819287.19").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.ln();
                ::test::black_box(result);
            }
        });
    }

    #[bench]
    fn erf(b: &mut ::test::Bencher) {
        let samples = &[
            Decimal::from(0),
            Decimal::from(1),
            Decimal::from_str("-0.98717").unwrap(),
            Decimal::from_str("0.07").unwrap(),
            Decimal::from_str("0.1111").unwrap(),
            Decimal::from_str("0.4").unwrap(),
        ];
        b.iter(|| {
            for sample in samples.iter() {
                let result = sample.erf();
                ::test::black_box(result);
            }
        });
    }
}
<|endoftext|>


<commit_before><commit_msg>rename variable<commit_after><|endoftext|>


<commit_before>use itertools::Itertools;

type NArray = Vec<Vec<Vec<usize>>>;

fn generate_partitions(args: &[usize]) -> NArray {
    // calculate the sum of all partitions
    let max = args.iter().sum();

    // generate combinations with the given lengths
    // for each partition
    let c = args.iter().fold(vec![], |mut acc, arg| {
        acc.push((1..=max).combinations(*arg).collect::<Vec<_>>());
        acc
    });

    // create a cartesian product of all individual combinations
    // filter/keep only where all the elements are there and exactly once
    c.iter()
        .map(|i| i.iter().cloned())
        .multi_cartesian_product()
        .unique()
        .filter(|x| x.iter().cloned().flatten().unique().count() == max)
        .collect::<Vec<_>>()
}

#[allow(clippy::clippy::ptr_arg)]
fn print_partitions(result: &NArray) {
    println!("Partitions:");
    for partition in result {
        println!("{:?}", partition);
    }
}
fn main() {
    print_partitions(generate_partitions(&[2, 0, 2]).as_ref());
    print_partitions(generate_partitions(&[1, 1, 1]).as_ref());
    print_partitions(generate_partitions(&[2, 3]).as_ref());
    print_partitions(generate_partitions(&[0]).as_ref());
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_partitions() {
        itertools::assert_equal(generate_partitions(&[0]), vec![vec![vec![]]]);
        itertools::assert_equal(
            generate_partitions(&[2, 0, 2]),
            vec![
                vec![vec![1, 2], vec![], vec![3, 4]],
                vec![vec![1, 3], vec![], vec![2, 4]],
                vec![vec![1, 4], vec![], vec![2, 3]],
                vec![vec![2, 3], vec![], vec![1, 4]],
                vec![vec![2, 4], vec![], vec![1, 3]],
                vec![vec![3, 4], vec![], vec![1, 2]],
            ],
        );
    }
}
<commit_msg>Add second test<commit_after>use itertools::Itertools;

type NArray = Vec<Vec<Vec<usize>>>;

fn generate_partitions(args: &[usize]) -> NArray {
    // calculate the sum of all partitions
    let max = args.iter().sum();

    // generate combinations with the given lengths
    // for each partition
    let c = args.iter().fold(vec![], |mut acc, arg| {
        acc.push((1..=max).combinations(*arg).collect::<Vec<_>>());
        acc
    });

    // create a cartesian product of all individual combinations
    // filter/keep only where all the elements are there and exactly once
    c.iter()
        .map(|i| i.iter().cloned())
        .multi_cartesian_product()
        .unique()
        .filter(|x| x.iter().cloned().flatten().unique().count() == max)
        .collect::<Vec<_>>()
}

#[allow(clippy::clippy::ptr_arg)]
fn print_partitions(result: &NArray) {
    println!("Partitions:");
    for partition in result {
        println!("{:?}", partition);
    }
}
fn main() {
    print_partitions(generate_partitions(&[2, 0, 2]).as_ref());
    print_partitions(generate_partitions(&[1, 1, 1]).as_ref());
    print_partitions(generate_partitions(&[2, 3]).as_ref());
    print_partitions(generate_partitions(&[0]).as_ref());
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_partitions() {
        itertools::assert_equal(generate_partitions(&[0]), vec![vec![vec![]]]);
        itertools::assert_equal(
            generate_partitions(&[2, 0, 2]),
            vec![
                vec![vec![1, 2], vec![], vec![3, 4]],
                vec![vec![1, 3], vec![], vec![2, 4]],
                vec![vec![1, 4], vec![], vec![2, 3]],
                vec![vec![2, 3], vec![], vec![1, 4]],
                vec![vec![2, 4], vec![], vec![1, 3]],
                vec![vec![3, 4], vec![], vec![1, 2]],
            ],
        );

        itertools::assert_equal(
            generate_partitions(&[1, 1, 1]),
            vec![
                vec![vec![1], vec![2], vec![3]],
                vec![vec![1], vec![3], vec![2]],
                vec![vec![2], vec![1], vec![3]],
                vec![vec![2], vec![3], vec![1]],
                vec![vec![3], vec![1], vec![2]],
                vec![vec![3], vec![2], vec![1]],
            ],
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;

use super::KeyValueStore;
use super::in_memory::InMemoryKeyValueStore;

const KEY_VALUE_SEPARATOR: char = ' ';

pub struct PersistedKeyValueStore {
    base: InMemoryKeyValueStore,
    file: File,
}

impl PersistedKeyValueStore {
    pub fn new(filename: String) -> Self {
        let file = OpenOptions::new().read(true).write(true).create(true).open(filename).unwrap();

        let mut file_data: HashMap<String, String> = HashMap::new();
        {
            let buffered_reader = BufReader::new(&file);
            for line in buffered_reader.lines() {
                let line = line.unwrap();
                match line.find(KEY_VALUE_SEPARATOR) {
                    Some(separator_index) => {
                        let key = line[0..separator_index].to_owned();
                        let value = line[separator_index + 1..].to_owned();
                        file_data.insert(key, value);
                    }
                    None => println!("Ignoring line because of invalid format: '{}'", line),
                }
            }
        }

        PersistedKeyValueStore {
            base: InMemoryKeyValueStore::with_initial_data(file_data),
            file: file,
        }
    }
}

impl KeyValueStore for PersistedKeyValueStore {
    fn insert(&mut self, value: String) -> String {
        let key = self.base.insert(value.clone());
        self.file
            .write_all(format!("{}{}{}\n", &key, KEY_VALUE_SEPARATOR, &value).as_bytes())
            .unwrap();
        key
    }

    fn get(&self, key: &str) -> Option<String> {
        self.base.get(key)
    }
}
<commit_msg>Refactor PersistedKeyValueStore constructor<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;

use super::KeyValueStore;
use super::in_memory::InMemoryKeyValueStore;

const KEY_VALUE_SEPARATOR: char = ' ';

pub struct PersistedKeyValueStore {
    base: InMemoryKeyValueStore,
    file: File,
}

impl PersistedKeyValueStore {
    pub fn new(filename: String) -> Self {
        let file = OpenOptions::new().read(true).write(true).create(true).open(filename).unwrap();
        let file_data = PersistedKeyValueStore::extract_data(&file);

        PersistedKeyValueStore {
            base: InMemoryKeyValueStore::with_initial_data(file_data),
            file: file,
        }
    }

    fn extract_data(file: &File) -> HashMap<String, String> {
        BufReader::new(file)
            .lines()
            .map(|l| l.unwrap())
            .flat_map(|line| match line.find(KEY_VALUE_SEPARATOR) {
                Some(separator_index) => {
                    let key = line[0..separator_index].to_owned();
                    let value = line[separator_index + 1..].to_owned();
                    Some((key, value))
                }
                None => {
                    println!("Ignoring line because of invalid format: '{}'", line);
                    None
                }
            })
            .fold(HashMap::new(), |mut acc, (key, value)| {
                acc.insert(key, value);
                acc
            })
    }
}

impl KeyValueStore for PersistedKeyValueStore {
    fn insert(&mut self, value: String) -> String {
        let key = self.base.insert(value.clone());
        self.file
            .write_all(format!("{}{}{}\n", &key, KEY_VALUE_SEPARATOR, &value).as_bytes())
            .unwrap();
        key
    }

    fn get(&self, key: &str) -> Option<String> {
        self.base.get(key)
    }
}
<|endoftext|>