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

extern crate test;

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

use std::fmt::Write;

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

    #[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(Debug)]
pub struct DbccOpt {
    /// Should tokio SocketCan BCM streams be generated.
    /// This requires the `tokio-socketcan-bcm` crate.
    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) -> String {
    format!(
        "#[allow(dead_code)]\npub const MESSAGE_ID_{}: u32 = {};",
        message.message_name().to_shouty_snake_case(),
        message.message_id().0
    )
}

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: &DbccOpt, 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() {

        if *signal.multiplexer_indicator() != MultiplexIndicator::Plain {
            warn!("Multiplexed signals are currently not supported, the message `{}` signal `{}` will be skipped", message.message_name(), signal.name());
            continue;
        }

        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!(
        "std::io::Result<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)?;");
    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())?;");
    stream_fn.line(format!(
        "let f = frame_stream.map(|frame| {}::new(frame.data().to_vec()));",
        message.message_name().to_camel_case()
    ));
    stream_fn.line("Ok(Box::new(f))");

    stream_fn
}

pub fn can_reader(opt: &DbccOpt, 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.raw(&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>Add signal unit comment if not empty<commit_after>#![feature(no_panic_pow)]
#![feature(test)]

extern crate test;

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

use std::fmt::Write;

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

    #[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(Debug)]
pub struct DbccOpt {
    /// Should tokio SocketCan BCM streams be generated.
    /// This requires the `tokio-socketcan-bcm` crate.
    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);

    let signal_unit = if !signal.unit().is_empty() {
        format!("\nUnit: {}", signal.unit())
    } else {
        String::default()
    };

    signal_fn.doc(&format!("{}{}", signal_comment, signal_unit));

    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) -> String {
    format!(
        "#[allow(dead_code)]\npub const MESSAGE_ID_{}: u32 = {};",
        message.message_name().to_shouty_snake_case(),
        message.message_id().0
    )
}

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: &DbccOpt, 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() {

        if *signal.multiplexer_indicator() != MultiplexIndicator::Plain {
            warn!("Multiplexed signals are currently not supported, the message `{}` signal `{}` will be skipped", message.message_name(), signal.name());
            continue;
        }

        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!(
        "std::io::Result<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)?;");
    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())?;");
    stream_fn.line(format!(
        "let f = frame_stream.map(|frame| {}::new(frame.data().to_vec()));",
        message.message_name().to_camel_case()
    ));
    stream_fn.line("Ok(Box::new(f))");

    stream_fn
}

pub fn can_reader(opt: &DbccOpt, 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.raw(&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>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use libc::{c_void, c_int, c_char, c_double, c_ulong, c_uint};

#[derive(Copy)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum op_stat_t {
    OP_STAT_UNKNOWN = 0,
    OP_STAT_OPERATIONAL,    // Operational           [+]
    OP_STAT_NONOP,          // Nonoperational        [-]
    OP_STAT_PARTIAL,        // Partially operational [P]
    OP_STAT_STDBY,          // Backup/Standby        [B]
    OP_STAT_SPARE,          // Spare                 [S]
    OP_STAT_EXTENDED,       // Extended Mission      [X]
}

#[derive(Copy)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum orbit_type_t {
    ORBIT_TYPE_UNKNOWN = 0,
    ORBIT_TYPE_LEO,         // Low Earth orbit, up to 1200 km
    ORBIT_TYPE_ICO,         // Intermediate Circular Orbit, up to 1400 km
    ORBIT_TYPE_GEO,         // Geostationary
    ORBIT_TYPE_GSO,         // Geosynchronuous
    ORBIT_TYPE_MOLNIYA,
    ORBIT_TYPE_TUNDRA,
    ORBIT_TYPE_POLAR,
    ORBIT_TYPE_SUNSYNC,
    ORBIT_TYPE_DECAYED
}

#[repr(C)]
#[allow(dead_code)]
pub struct qth_t {
    pub name:       *const c_char,  // Name, eg. callsign
    pub loc:        *const c_char,  // Location, eg City, Country
    pub desc:       *const c_char,  // Short description
    pub lat:        c_double,       // Latitude in dec. deg. North
    pub lon:        c_double,       // Longitude in dec. deg. East
    pub alt:        c_int,          // Altitude above sea level in meters
    pub qra:        *const c_char,  // QRA locator
    pub wx:         *const c_char,  // Weather station code (4 chars)
}

#[repr(C)]
#[allow(dead_code)]
pub struct tle_t {
    pub epoch:      c_double,       // Epoch Time in NORAD TLE format YYDDD.FFFFFFFF
    pub epoch_year: c_uint,         // Epoch: year
    pub epoch_day:  c_uint,         // Epoch: day of year
    pub epoch_fod:  c_double,       // Epoch: Fraction of day
    pub xndt2o:     c_double,       // 1. time derivative of mean motion
    pub xndd6o:     c_double,       // 2. time derivative of mean motion
    pub bstar:      c_double,       // Bstar drag coefficient
    pub xincl:      c_double,       // Inclination
    pub xnodeo:     c_double,       // R.A.A.N.
    pub eo:         c_double,       // Eccentricity
    pub omegao:     c_double,       // argument of perigee
    pub xmo:        c_double,       // mean anomaly
    pub xno:        c_double,       // mean motion

    pub catnr:      c_int,          // Catalogue Number
    pub elset:      c_int,          // Element Set number
    pub revnum:     c_int,          // Revolution Number at epoch

    pub sat_name:   [c_char; 25],   // Satellite name string
    pub idesg:      [c_char; 9],    // International Designator
    pub status:     op_stat_t,      // Operational status

    // values needed for squint calculations
    pub xincl1:     c_double,
    pub xnodeo1:    c_double,
    pub omegao1:    c_double,
}

#[repr(C)]
#[derive(Default)]
#[allow(dead_code)]
pub struct vector_t {
    pub x:          c_double,   // X component
    pub y:          c_double,   // Y component
    pub z:          c_double,   // Z component
    pub w:          c_double,   // Magnitude
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// Common arguments between deep-space functions
pub struct deep_arg_t {
    // Used by dpinit part of Deep()
    pub eosq:       c_double,
    pub sinio:      c_double,
    pub cosio:      c_double,
    pub betao:      c_double,
    pub aodp:       c_double,
    pub theta2:     c_double,
    pub sing:       c_double,
    pub cosg:       c_double,
    pub betao2:     c_double,
    pub xmdot:      c_double,
    pub omgdot:     c_double,
    pub xnodot:     c_double,
    pub xnodp:      c_double,

    // Used by dpsec and dpper parts of Deep()
    pub xll:        c_double,
    pub omgadf:     c_double,
    pub xnode:      c_double,
    pub em:         c_double,
    pub xinc:       c_double,
    pub xn:         c_double,
    pub t:          c_double,

    // Used by thetg and Deep()
    pub ds50:       c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// static data for SGP4 and SDP4
pub struct sgpsdp_static_t {
    pub aodp:       c_double,
    pub aycof:      c_double,
    pub c1:         c_double,
    pub c4:         c_double,
    pub c5:         c_double,
    pub cosio:      c_double,
    pub d2:         c_double,
    pub d3:         c_double,
    pub d4:         c_double,
    pub delmo:      c_double,
    pub omgcof:     c_double,

    pub eta:        c_double,
    pub omgdot:     c_double,
    pub sinio:      c_double,
    pub xnodp:      c_double,
    pub sinmo:      c_double,
    pub t2cof:      c_double,
    pub t3cof:      c_double,
    pub t4cof:      c_double,
    pub t5cof:      c_double,

    pub x1mth2:     c_double,
    pub x3thm1:     c_double,
    pub x7thm1:     c_double,
    pub xmcof:      c_double,
    pub xmdot:      c_double,
    pub xnodcf:     c_double,
    pub xnodot:     c_double,
    pub xlcof:      c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// static data for DEEP
pub struct deep_static_t {
    pub thgr:       c_double,
    pub xnq:        c_double,
    pub xqncl:      c_double,
    pub omegaq:     c_double,
    pub zmol:       c_double,
    pub zmos:       c_double,
    pub savtsn:     c_double,
    pub ee2:        c_double,
    pub e3:         c_double,
    pub xi2:        c_double,

    pub xl2:        c_double,
    pub xl3:        c_double,
    pub xl4:        c_double,
    pub xgh2:       c_double,
    pub xgh3:       c_double,
    pub xgh4:       c_double,
    pub xh2:        c_double,
    pub xh3:        c_double,
    pub sse:        c_double,
    pub ssi:        c_double,
    pub ssg:        c_double,
    pub xi3:        c_double,

    pub se2:        c_double,
    pub si2:        c_double,
    pub sl2:        c_double,
    pub sgh2:       c_double,
    pub sh2:        c_double,
    pub se3:        c_double,
    pub si3:        c_double,
    pub sl3:        c_double,
    pub sgh3:       c_double,
    pub sh3:        c_double,
    pub sl4:        c_double,
    pub sgh4:       c_double,

    pub ssl:        c_double,
    pub ssh:        c_double,
    pub d3210:      c_double,
    pub d3222:      c_double,
    pub d4410:      c_double,
    pub d4422:      c_double,
    pub d5220:      c_double,
    pub d5232:      c_double,
    pub d5421:      c_double,

    pub d5433:      c_double,
    pub del1:       c_double,
    pub del2:       c_double,
    pub del3:       c_double,
    pub fasx2:      c_double,
    pub fasx4:      c_double,
    pub fasx6:      c_double,
    pub xlamo:      c_double,
    pub xfact:      c_double,

    pub xni:        c_double,
    pub atime:      c_double,
    pub stepp:      c_double,
    pub stepn:      c_double,
    pub step2:      c_double,
    pub preep:      c_double,
    pub pl:         c_double,
    pub sghs:       c_double,
    pub xli:        c_double,

    pub d2201:      c_double,
    pub d2211:      c_double,
    pub sghl:       c_double,
    pub sh1:        c_double,
    pub pinc:       c_double,
    pub pe:         c_double,
    pub shs:        c_double,
    pub zsingl:     c_double,
    pub zcosgl:     c_double,

    pub zsinhl:     c_double,
    pub zcoshl:     c_double,
    pub zsinil:     c_double,
    pub zcosil:     c_double,

}

#[repr(C)]
pub struct sat_t {
    pub name:       *const c_char,
    pub nickname:   *const c_char,
    pub website:    *const c_char,
    pub tle:        tle_t,          // Keplerian elements
    pub flags:      c_int,          // Flags for algo ctrl
    pub sgps:       sgpsdp_static_t,
    pub dps:        deep_static_t,
    pub deep_arg:   deep_arg_t,
    pub pos:        vector_t,       // Raw position and range
    pub vel:        vector_t,       // Raw velocity

    pub jul_epoch:  c_double,
    pub jul_utc:    c_double,
    pub tsince:     c_double,
    pub aos:        c_double,       // Next AOS
    pub los:        c_double,       // Next LO

    pub az:         c_double,       // Azimuth [deg]
    pub el:         c_double,       // Elevation [deg]
    pub range:      c_double,       // Range [km]
    pub range_rate: c_double,       // Range Rate [km/sec]
    pub ra:         c_double,       // Right Ascension [deg]
    pub dec:        c_double,       // Declination [deg]
    pub ssplat:     c_double,       // SSP latitude [deg]
    pub ssplon:     c_double,       // SSP longitude [deg]
    pub alt:        c_double,       // altitude [km]
    pub velo:       c_double,       // velocity [km/s]
    pub ma:         c_double,       // mean anomaly
    pub footprint:  c_double,       // footprint
    pub phase:      c_double,       // orbit phase
    pub meanmo:     c_double,       // mean motion kept in rev/day
    pub orbit:      c_ulong,        // orbit number
    pub otype:      orbit_type_t    // orbit type
}

#[link(name = "gpredict")]
extern {
    pub fn get_current_daynum() -> c_double;
    pub fn predict_calc(sat: *mut sat_t, qth: *mut qth_t, t: c_double) -> c_void;

    pub fn Get_Next_Tle_Set(line: *const c_char, tle: *mut tle_t) -> c_int;
    pub fn select_ephemeris(sat: *mut sat_t) -> c_void;
    pub fn gtk_sat_data_init_sat(sat: *mut sat_t, sat: *mut qth_t) -> c_void;
}
<commit_msg>annotate C enums with #[repr(C)]<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use libc::{c_void, c_int, c_char, c_double, c_ulong, c_uint};

#[repr(C)]
#[derive(Copy)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum op_stat_t {
    OP_STAT_UNKNOWN = 0,
    OP_STAT_OPERATIONAL,    // Operational           [+]
    OP_STAT_NONOP,          // Nonoperational        [-]
    OP_STAT_PARTIAL,        // Partially operational [P]
    OP_STAT_STDBY,          // Backup/Standby        [B]
    OP_STAT_SPARE,          // Spare                 [S]
    OP_STAT_EXTENDED,       // Extended Mission      [X]
}

#[repr(C)]
#[derive(Copy)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum orbit_type_t {
    ORBIT_TYPE_UNKNOWN = 0,
    ORBIT_TYPE_LEO,         // Low Earth orbit, up to 1200 km
    ORBIT_TYPE_ICO,         // Intermediate Circular Orbit, up to 1400 km
    ORBIT_TYPE_GEO,         // Geostationary
    ORBIT_TYPE_GSO,         // Geosynchronuous
    ORBIT_TYPE_MOLNIYA,
    ORBIT_TYPE_TUNDRA,
    ORBIT_TYPE_POLAR,
    ORBIT_TYPE_SUNSYNC,
    ORBIT_TYPE_DECAYED
}

#[repr(C)]
#[allow(dead_code)]
pub struct qth_t {
    pub name:       *const c_char,  // Name, eg. callsign
    pub loc:        *const c_char,  // Location, eg City, Country
    pub desc:       *const c_char,  // Short description
    pub lat:        c_double,       // Latitude in dec. deg. North
    pub lon:        c_double,       // Longitude in dec. deg. East
    pub alt:        c_int,          // Altitude above sea level in meters
    pub qra:        *const c_char,  // QRA locator
    pub wx:         *const c_char,  // Weather station code (4 chars)
}

#[repr(C)]
#[allow(dead_code)]
pub struct tle_t {
    pub epoch:      c_double,       // Epoch Time in NORAD TLE format YYDDD.FFFFFFFF
    pub epoch_year: c_uint,         // Epoch: year
    pub epoch_day:  c_uint,         // Epoch: day of year
    pub epoch_fod:  c_double,       // Epoch: Fraction of day
    pub xndt2o:     c_double,       // 1. time derivative of mean motion
    pub xndd6o:     c_double,       // 2. time derivative of mean motion
    pub bstar:      c_double,       // Bstar drag coefficient
    pub xincl:      c_double,       // Inclination
    pub xnodeo:     c_double,       // R.A.A.N.
    pub eo:         c_double,       // Eccentricity
    pub omegao:     c_double,       // argument of perigee
    pub xmo:        c_double,       // mean anomaly
    pub xno:        c_double,       // mean motion

    pub catnr:      c_int,          // Catalogue Number
    pub elset:      c_int,          // Element Set number
    pub revnum:     c_int,          // Revolution Number at epoch

    pub sat_name:   [c_char; 25],   // Satellite name string
    pub idesg:      [c_char; 9],    // International Designator
    pub status:     op_stat_t,      // Operational status

    // values needed for squint calculations
    pub xincl1:     c_double,
    pub xnodeo1:    c_double,
    pub omegao1:    c_double,
}

#[repr(C)]
#[derive(Default)]
#[allow(dead_code)]
pub struct vector_t {
    pub x:          c_double,   // X component
    pub y:          c_double,   // Y component
    pub z:          c_double,   // Z component
    pub w:          c_double,   // Magnitude
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// Common arguments between deep-space functions
pub struct deep_arg_t {
    // Used by dpinit part of Deep()
    pub eosq:       c_double,
    pub sinio:      c_double,
    pub cosio:      c_double,
    pub betao:      c_double,
    pub aodp:       c_double,
    pub theta2:     c_double,
    pub sing:       c_double,
    pub cosg:       c_double,
    pub betao2:     c_double,
    pub xmdot:      c_double,
    pub omgdot:     c_double,
    pub xnodot:     c_double,
    pub xnodp:      c_double,

    // Used by dpsec and dpper parts of Deep()
    pub xll:        c_double,
    pub omgadf:     c_double,
    pub xnode:      c_double,
    pub em:         c_double,
    pub xinc:       c_double,
    pub xn:         c_double,
    pub t:          c_double,

    // Used by thetg and Deep()
    pub ds50:       c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// static data for SGP4 and SDP4
pub struct sgpsdp_static_t {
    pub aodp:       c_double,
    pub aycof:      c_double,
    pub c1:         c_double,
    pub c4:         c_double,
    pub c5:         c_double,
    pub cosio:      c_double,
    pub d2:         c_double,
    pub d3:         c_double,
    pub d4:         c_double,
    pub delmo:      c_double,
    pub omgcof:     c_double,

    pub eta:        c_double,
    pub omgdot:     c_double,
    pub sinio:      c_double,
    pub xnodp:      c_double,
    pub sinmo:      c_double,
    pub t2cof:      c_double,
    pub t3cof:      c_double,
    pub t4cof:      c_double,
    pub t5cof:      c_double,

    pub x1mth2:     c_double,
    pub x3thm1:     c_double,
    pub x7thm1:     c_double,
    pub xmcof:      c_double,
    pub xmdot:      c_double,
    pub xnodcf:     c_double,
    pub xnodot:     c_double,
    pub xlcof:      c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default)]
/// static data for DEEP
pub struct deep_static_t {
    pub thgr:       c_double,
    pub xnq:        c_double,
    pub xqncl:      c_double,
    pub omegaq:     c_double,
    pub zmol:       c_double,
    pub zmos:       c_double,
    pub savtsn:     c_double,
    pub ee2:        c_double,
    pub e3:         c_double,
    pub xi2:        c_double,

    pub xl2:        c_double,
    pub xl3:        c_double,
    pub xl4:        c_double,
    pub xgh2:       c_double,
    pub xgh3:       c_double,
    pub xgh4:       c_double,
    pub xh2:        c_double,
    pub xh3:        c_double,
    pub sse:        c_double,
    pub ssi:        c_double,
    pub ssg:        c_double,
    pub xi3:        c_double,

    pub se2:        c_double,
    pub si2:        c_double,
    pub sl2:        c_double,
    pub sgh2:       c_double,
    pub sh2:        c_double,
    pub se3:        c_double,
    pub si3:        c_double,
    pub sl3:        c_double,
    pub sgh3:       c_double,
    pub sh3:        c_double,
    pub sl4:        c_double,
    pub sgh4:       c_double,

    pub ssl:        c_double,
    pub ssh:        c_double,
    pub d3210:      c_double,
    pub d3222:      c_double,
    pub d4410:      c_double,
    pub d4422:      c_double,
    pub d5220:      c_double,
    pub d5232:      c_double,
    pub d5421:      c_double,

    pub d5433:      c_double,
    pub del1:       c_double,
    pub del2:       c_double,
    pub del3:       c_double,
    pub fasx2:      c_double,
    pub fasx4:      c_double,
    pub fasx6:      c_double,
    pub xlamo:      c_double,
    pub xfact:      c_double,

    pub xni:        c_double,
    pub atime:      c_double,
    pub stepp:      c_double,
    pub stepn:      c_double,
    pub step2:      c_double,
    pub preep:      c_double,
    pub pl:         c_double,
    pub sghs:       c_double,
    pub xli:        c_double,

    pub d2201:      c_double,
    pub d2211:      c_double,
    pub sghl:       c_double,
    pub sh1:        c_double,
    pub pinc:       c_double,
    pub pe:         c_double,
    pub shs:        c_double,
    pub zsingl:     c_double,
    pub zcosgl:     c_double,

    pub zsinhl:     c_double,
    pub zcoshl:     c_double,
    pub zsinil:     c_double,
    pub zcosil:     c_double,

}

#[repr(C)]
pub struct sat_t {
    pub name:       *const c_char,
    pub nickname:   *const c_char,
    pub website:    *const c_char,
    pub tle:        tle_t,          // Keplerian elements
    pub flags:      c_int,          // Flags for algo ctrl
    pub sgps:       sgpsdp_static_t,
    pub dps:        deep_static_t,
    pub deep_arg:   deep_arg_t,
    pub pos:        vector_t,       // Raw position and range
    pub vel:        vector_t,       // Raw velocity

    pub jul_epoch:  c_double,
    pub jul_utc:    c_double,
    pub tsince:     c_double,
    pub aos:        c_double,       // Next AOS
    pub los:        c_double,       // Next LO

    pub az:         c_double,       // Azimuth [deg]
    pub el:         c_double,       // Elevation [deg]
    pub range:      c_double,       // Range [km]
    pub range_rate: c_double,       // Range Rate [km/sec]
    pub ra:         c_double,       // Right Ascension [deg]
    pub dec:        c_double,       // Declination [deg]
    pub ssplat:     c_double,       // SSP latitude [deg]
    pub ssplon:     c_double,       // SSP longitude [deg]
    pub alt:        c_double,       // altitude [km]
    pub velo:       c_double,       // velocity [km/s]
    pub ma:         c_double,       // mean anomaly
    pub footprint:  c_double,       // footprint
    pub phase:      c_double,       // orbit phase
    pub meanmo:     c_double,       // mean motion kept in rev/day
    pub orbit:      c_ulong,        // orbit number
    pub otype:      orbit_type_t    // orbit type
}

#[link(name = "gpredict")]
extern {
    pub fn get_current_daynum() -> c_double;
    pub fn predict_calc(sat: *mut sat_t, qth: *mut qth_t, t: c_double) -> c_void;

    pub fn Get_Next_Tle_Set(line: *const c_char, tle: *mut tle_t) -> c_int;
    pub fn select_ephemeris(sat: *mut sat_t) -> c_void;
    pub fn gtk_sat_data_init_sat(sat: *mut sat_t, sat: *mut qth_t) -> c_void;
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-linux see joyent/libuv#1189
// ignore-fast
// ignore-android needs extra network permissions
// exec-env:RUST_LOG=debug

use std::libc;
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::tcp::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};

fn main() {
    // This test has a chance to time out, try to not let it time out
    spawn(proc() {
        use std::io::timer;
        timer::sleep(30 * 1000);
        println!("timed out!");
        unsafe { libc::exit(1) }
    });

    let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 0 };
    let (p, c) = Chan::new();
    spawn(proc() {
        let mut listener = TcpListener::bind(addr).unwrap();
        c.send(listener.socket_name().unwrap());
        let mut acceptor = listener.listen();
        loop {
            let mut stream = match acceptor.accept() {
                Ok(stream) => stream,
                Err(error) => {
                    debug!("accept failed: {:?}", error);
                    continue;
                }
            };
            stream.read_byte();
            stream.write([2]);
        }
    });
    let addr = p.recv();

    let (p, c) = Chan::new();
    for _ in range(0, 1000) {
        let c = c.clone();
        spawn(proc() {
            match TcpStream::connect(addr) {
                Ok(stream) => {
                    let mut stream = stream;
                    stream.write([1]);
                    let mut buf = [0];
                    stream.read(buf);
                },
                Err(e) => debug!("{:?}", e)
            }
            c.send(());
        });
    }

    // Wait for all clients to exit, but don't wait for the server to exit. The
    // server just runs infinitely.
    drop(c);
    for _ in range(0, 1000) {
        p.recv();
    }
    unsafe { libc::exit(0) }
}
<commit_msg>fix a test that was missed in the chan/port renaming (PR #12815)<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-linux see joyent/libuv#1189
// ignore-fast
// ignore-android needs extra network permissions
// exec-env:RUST_LOG=debug

use std::libc;
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::tcp::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};

fn main() {
    // This test has a chance to time out, try to not let it time out
    spawn(proc() {
        use std::io::timer;
        timer::sleep(30 * 1000);
        println!("timed out!");
        unsafe { libc::exit(1) }
    });

    let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 0 };
    let (tx, rx) = channel();
    spawn(proc() {
        let mut listener = TcpListener::bind(addr).unwrap();
        tx.send(listener.socket_name().unwrap());
        let mut acceptor = listener.listen();
        loop {
            let mut stream = match acceptor.accept() {
                Ok(stream) => stream,
                Err(error) => {
                    debug!("accept failed: {:?}", error);
                    continue;
                }
            };
            stream.read_byte();
            stream.write([2]);
        }
    });
    let addr = rx.recv();

    let (tx, rx) = channel();
    for _ in range(0, 1000) {
        let tx = tx.clone();
        spawn(proc() {
            match TcpStream::connect(addr) {
                Ok(stream) => {
                    let mut stream = stream;
                    stream.write([1]);
                    let mut buf = [0];
                    stream.read(buf);
                },
                Err(e) => debug!("{:?}", e)
            }
            tx.send(());
        });
    }

    // Wait for all clients to exit, but don't wait for the server to exit. The
    // server just runs infinitely.
    drop(tx);
    for _ in range(0, 1000) {
        rx.recv();
    }
    unsafe { libc::exit(0) }
}
<|endoftext|>


<commit_before>use parser::{Cond, Expr, Node, Target, WS};
use std::str;
use std::collections::HashSet;
use syn;

fn annotations(generics: &syn::Generics) -> String {
    if generics.lifetimes.len() < 1 {
        return String::new();
    }
    let mut res = String::new();
    res.push('<');
    for lt in &generics.lifetimes {
        res.push_str(lt.lifetime.ident.as_ref());
    }
    res.push('>');
    res
}

fn path_as_identifier(s: &str) -> String {
    let mut res = String::new();
    for c in s.chars() {
        if c.is_alphanumeric() {
            res.push(c);
        } else {
            res.push_str(&format!("{:x}", c as u32));
        }
    }
    res
}

struct Generator<'a> {
    buf: String,
    indent: u8,
    start: bool,
    locals: HashSet<String>,
    next_ws: Option<&'a str>,
    skip_ws: bool,
    loop_vars: bool,
    loop_index: u8,
}

impl<'a> Generator<'a> {

    fn new() -> Generator<'a> {
        Generator {
            buf: String::new(),
            indent: 0,
            start: true,
            locals: HashSet::new(),
            next_ws: None,
            skip_ws: false,
            loop_vars: false,
            loop_index: 0,
        }
    }

    fn indent(&mut self) {
        self.indent += 1;
    }

    fn dedent(&mut self) {
        self.indent -= 1;
    }

    fn write(&mut self, s: &str) {
        if self.start {
            for _ in 0..(self.indent * 4) {
                self.buf.push(' ');
            }
            self.start = false;
        }
        self.buf.push_str(s);
    }

    fn writeln(&mut self, s: &str) {
        if s.is_empty() {
            return;
        }
        if s == "}" {
            self.dedent();
        }
        self.write(s);
        if s.ends_with('{') {
            self.indent();
        }
        self.buf.push('\n');
        self.start = true;
    }

    fn flush_ws(&mut self, ws: &WS) {
        if self.next_ws.is_some() && !ws.0 {
            let val = self.next_ws.unwrap();
            if !val.is_empty() {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      val));
            }
        }
        self.next_ws = None;
    }

    fn prepare_ws(&mut self, ws: &WS) {
        self.skip_ws = ws.1;
    }

    fn handle_ws(&mut self, ws: &WS) {
        self.flush_ws(ws);
        self.prepare_ws(ws);
    }

    fn visit_num_lit(&mut self, s: &str) {
        self.write(s);
    }

    fn visit_str_lit(&mut self, s: &str) {
        self.write(&format!("\"{}\"", s));
    }

    fn visit_var(&mut self, s: &str) {
        if self.locals.contains(s) {
            self.write(s);
        } else {
            self.write(&format!("self.{}", s));
        }
    }

    fn visit_attr(&mut self, obj: &Expr, attr: &str) {
        if let Expr::Var(name) = *obj {
            if name == "loop" {
                self.write("_loop_indexes[_loop_cur]");
                if attr == "index" {
                    return;
                } else if attr == "index0" {
                    self.write(" - 1");
                    return;
                } else {
                    panic!("unknown loop variable");
                }
            }
        }
        self.visit_expr(obj);
        self.write(&format!(".{}", attr));
    }

    fn visit_filter(&mut self, name: &str, args: &[Expr]) {
        if name == "format" {
            self.write("format!(");
        } else {
            self.write(&format!("askama::filters::{}(&", name));
        }

        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", &");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) {
        self.visit_expr(left);
        self.write(&format!(" {} ", op));
        self.visit_expr(right);
    }

    fn visit_group(&mut self, inner: &Expr) {
        self.write("(");
        self.visit_expr(inner);
        self.write(")");
    }

    fn visit_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) {
        self.visit_expr(obj);
        self.write(&format!(".{}(", method));
        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", ");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_expr(&mut self, expr: &Expr) {
        match *expr {
            Expr::NumLit(s) => self.visit_num_lit(s),
            Expr::StrLit(s) => self.visit_str_lit(s),
            Expr::Var(s) => self.visit_var(s),
            Expr::Attr(ref obj, name) => self.visit_attr(obj, name),
            Expr::Filter(name, ref args) => self.visit_filter(name, args),
            Expr::BinOp(op, ref left, ref right) =>
                self.visit_binop(op, left, right),
            Expr::Group(ref inner) => self.visit_group(inner),
            Expr::Call(ref obj, method, ref args) =>
                self.visit_call(obj, method, args),
        }
    }

    fn visit_target_single(&mut self, name: &str) -> Vec<String> {
        vec![name.to_string()]
    }

    fn visit_target(&mut self, target: &Target) -> Vec<String> {
        match *target {
            Target::Name(s) => { self.visit_target_single(s) },
        }
    }

    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) {
        assert!(self.next_ws.is_none());
        if !lws.is_empty() {
            if self.skip_ws {
                self.skip_ws = false;
            } else if val.is_empty() {
                assert!(rws.is_empty());
                self.next_ws = Some(lws);
            } else {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      lws));
            }
        }
        if !val.is_empty() {
            self.writeln(&format!("writer.write_str({:#?}).unwrap();", val));
        }
        if !rws.is_empty() {
            self.next_ws = Some(rws);
        }
    }

    fn write_expr(&mut self, ws: &WS, s: &Expr) {
        self.handle_ws(ws);
        self.write("writer.write_fmt(format_args!(\"{}\", ");
        self.visit_expr(s);
        self.writeln(")).unwrap();");
    }

    fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) {
        for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() {
            self.handle_ws(cws);
            match *cond {
                Some(ref expr) => {
                    if i == 0 {
                        self.write("if ");
                    } else {
                        self.dedent();
                        self.write("} else if ");
                    }
                    self.visit_expr(expr);
                },
                None => {
                    self.dedent();
                    self.write("} else");
                },
            }
            self.writeln(" {");
            self.handle(nodes);
        }
        self.handle_ws(ws);
        self.writeln("}");
    }

    fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr,
                  body: &'a [Node], ws2: &WS) {

        self.handle_ws(ws1);
        if !self.loop_vars {
            self.writeln("let mut _loop_indexes = Vec::new();");
            self.writeln("let mut _loop_cur = 0;");
            self.loop_vars = true;
        }

        self.writeln("_loop_indexes.push(0);");
        let cur_index = self.loop_index;
        self.loop_index += 1;
        self.writeln(&format!("_loop_cur = {};", cur_index));
        self.write("for ");
        let targets = self.visit_target(var);
        for name in &targets {
            self.locals.insert(name.clone());
            self.write(name);
        }
        self.write(" in &");
        self.visit_expr(iter);
        self.writeln(" {");

        self.writeln("_loop_indexes[_loop_cur] += 1;");
        self.handle(body);
        self.handle_ws(ws2);
        self.writeln("}");
        self.loop_index -= 1;
        self.writeln("_loop_indexes.pop();");
        for name in &targets {
            self.locals.remove(name);
        }
    }

    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) {
        self.flush_ws(ws1);
        self.writeln(&format!("timpl.render_block_{}_to(writer);", name));
        self.prepare_ws(ws2);
    }

    fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node],
                       ws2: &WS) {
        self.writeln("#[allow(unused_variables)]");
        self.writeln(&format!(
            "fn render_block_{}_to(&self, writer: &mut std::fmt::Write) {{",
            name));
        self.prepare_ws(ws1);
        self.handle(nodes);
        self.flush_ws(ws2);
        self.writeln("}");
    }

    fn handle(&mut self, nodes: &'a [Node]) {
        for n in nodes {
            match *n {
                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); }
                Node::Comment() => {},
                Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); },
                Node::Cond(ref conds, ref ws) => {
                    self.write_cond(conds, ws);
                },
                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
                    self.write_loop(ws1, var, iter, body, ws2);
                },
                Node::Block(ref ws1, name, ref ws2) => {
                    self.write_block(ws1, name, ws2);
                },
                Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => {
                    self.write_block_def(ws1, name, block_nodes, ws2);
                }
                Node::Extends(_) => {
                    panic!("no extends or block definition allowed in content");
                },
            }
        }
    }

    fn impl_template(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} askama::Template for {}{} {{",
                              anno, ast.ident.as_ref(), anno));

        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        self.handle(nodes);
        self.flush_ws(&WS(false, false));
        self.writeln("}");
        self.writeln("}");
    }

    fn impl_trait(&mut self, ast: &syn::DeriveInput, base: &str,
                  blocks: &'a [Node], nodes: Option<&'a [Node]>) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} TraitFrom{} for {}{} {{",
                              anno, path_as_identifier(base),
                              ast.ident.as_ref(), anno));
        self.handle(blocks);

        self.writeln("#[allow(unused_variables)]");
        let trait_name = format!("TraitFrom{}", path_as_identifier(base));
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write) {{",
            trait_name));

        if let Some(nodes) = nodes {
            self.handle(nodes);
            self.flush_ws(&WS(false, false));
        } else {
            self.writeln("self._parent.render_trait_to(self, writer);");
        }

        self.writeln("}");
        self.flush_ws(&WS(false, false));
        self.writeln("}");
    }

    fn impl_template_for_trait(&mut self, ast: &syn::DeriveInput, derived: bool) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} askama::Template for {}{} {{",
                              anno, ast.ident.as_ref(), anno));
        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        if derived {
            self.writeln("self._parent.render_trait_to(self, writer);");
        } else {
            self.writeln("self.render_trait_to(self, writer);");
        }
        self.writeln("}");
        self.writeln("}");
    }

    fn define_trait(&mut self, path: &str, block_names: &[&str]) {
        let trait_name = format!("TraitFrom{}", path_as_identifier(path));
        self.writeln(&format!("trait {} {{", &trait_name));

        for bname in block_names {
            self.writeln(&format!(
                "fn render_block_{}_to(&self, writer: &mut std::fmt::Write);",
                bname));
        }
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write);",
            trait_name));

        self.writeln("}");
    }

    fn result(self) -> String {
        self.buf
    }

}

pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String {
    let mut base: Option<Expr> = None;
    let mut blocks = Vec::new();
    let mut block_names = Vec::new();
    let mut content = Vec::new();
    for n in nodes.drain(..) {
        match n {
            Node::Extends(path) => {
                match base {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => { base = Some(path); },
                }
            },
            Node::BlockDef(ws1, name, _, ws2) => {
                blocks.push(n);
                block_names.push(name);
                content.push(Node::Block(ws1, name, ws2));
            },
            _ => { content.push(n); },
        }
    }

    let mut gen = Generator::new();
    if !blocks.is_empty() {
        if base.is_none() {
            gen.define_trait(path, &block_names);
        }
        let tmpl_path = match base {
            Some(Expr::StrLit(base_path)) => { base_path },
            _ => { path },
        };
        let trait_nodes = if base.is_none() { Some(&content[..]) } else { None };
        gen.impl_trait(ast, tmpl_path, &blocks, trait_nodes);
        gen.impl_template_for_trait(ast, base.is_some());
    } else {
        gen.impl_template(ast, &content);
    }
    gen.result()
}
<commit_msg>Better support for Iterator variants in for loop (fixes #8)<commit_after>use parser::{Cond, Expr, Node, Target, WS};
use std::str;
use std::collections::HashSet;
use syn;

fn annotations(generics: &syn::Generics) -> String {
    if generics.lifetimes.len() < 1 {
        return String::new();
    }
    let mut res = String::new();
    res.push('<');
    for lt in &generics.lifetimes {
        res.push_str(lt.lifetime.ident.as_ref());
    }
    res.push('>');
    res
}

fn path_as_identifier(s: &str) -> String {
    let mut res = String::new();
    for c in s.chars() {
        if c.is_alphanumeric() {
            res.push(c);
        } else {
            res.push_str(&format!("{:x}", c as u32));
        }
    }
    res
}

struct Generator<'a> {
    buf: String,
    indent: u8,
    start: bool,
    locals: HashSet<String>,
    next_ws: Option<&'a str>,
    skip_ws: bool,
    loop_vars: bool,
    loop_index: u8,
}

impl<'a> Generator<'a> {

    fn new() -> Generator<'a> {
        Generator {
            buf: String::new(),
            indent: 0,
            start: true,
            locals: HashSet::new(),
            next_ws: None,
            skip_ws: false,
            loop_vars: false,
            loop_index: 0,
        }
    }

    fn indent(&mut self) {
        self.indent += 1;
    }

    fn dedent(&mut self) {
        self.indent -= 1;
    }

    fn write(&mut self, s: &str) {
        if self.start {
            for _ in 0..(self.indent * 4) {
                self.buf.push(' ');
            }
            self.start = false;
        }
        self.buf.push_str(s);
    }

    fn writeln(&mut self, s: &str) {
        if s.is_empty() {
            return;
        }
        if s == "}" {
            self.dedent();
        }
        self.write(s);
        if s.ends_with('{') {
            self.indent();
        }
        self.buf.push('\n');
        self.start = true;
    }

    fn flush_ws(&mut self, ws: &WS) {
        if self.next_ws.is_some() && !ws.0 {
            let val = self.next_ws.unwrap();
            if !val.is_empty() {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      val));
            }
        }
        self.next_ws = None;
    }

    fn prepare_ws(&mut self, ws: &WS) {
        self.skip_ws = ws.1;
    }

    fn handle_ws(&mut self, ws: &WS) {
        self.flush_ws(ws);
        self.prepare_ws(ws);
    }

    fn visit_num_lit(&mut self, s: &str) {
        self.write(s);
    }

    fn visit_str_lit(&mut self, s: &str) {
        self.write(&format!("\"{}\"", s));
    }

    fn visit_var(&mut self, s: &str) {
        if self.locals.contains(s) {
            self.write(s);
        } else {
            self.write(&format!("self.{}", s));
        }
    }

    fn visit_attr(&mut self, obj: &Expr, attr: &str) {
        if let Expr::Var(name) = *obj {
            if name == "loop" {
                self.write("_loop_indexes[_loop_cur]");
                if attr == "index" {
                    return;
                } else if attr == "index0" {
                    self.write(" - 1");
                    return;
                } else {
                    panic!("unknown loop variable");
                }
            }
        }
        self.visit_expr(obj);
        self.write(&format!(".{}", attr));
    }

    fn visit_filter(&mut self, name: &str, args: &[Expr]) {
        if name == "format" {
            self.write("format!(");
        } else {
            self.write(&format!("askama::filters::{}(&", name));
        }

        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", &");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) {
        self.visit_expr(left);
        self.write(&format!(" {} ", op));
        self.visit_expr(right);
    }

    fn visit_group(&mut self, inner: &Expr) {
        self.write("(");
        self.visit_expr(inner);
        self.write(")");
    }

    fn visit_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) {
        self.visit_expr(obj);
        self.write(&format!(".{}(", method));
        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", ");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_expr(&mut self, expr: &Expr) {
        match *expr {
            Expr::NumLit(s) => self.visit_num_lit(s),
            Expr::StrLit(s) => self.visit_str_lit(s),
            Expr::Var(s) => self.visit_var(s),
            Expr::Attr(ref obj, name) => self.visit_attr(obj, name),
            Expr::Filter(name, ref args) => self.visit_filter(name, args),
            Expr::BinOp(op, ref left, ref right) =>
                self.visit_binop(op, left, right),
            Expr::Group(ref inner) => self.visit_group(inner),
            Expr::Call(ref obj, method, ref args) =>
                self.visit_call(obj, method, args),
        }
    }

    fn visit_target_single(&mut self, name: &str) -> Vec<String> {
        vec![name.to_string()]
    }

    fn visit_target(&mut self, target: &Target) -> Vec<String> {
        match *target {
            Target::Name(s) => { self.visit_target_single(s) },
        }
    }

    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) {
        assert!(self.next_ws.is_none());
        if !lws.is_empty() {
            if self.skip_ws {
                self.skip_ws = false;
            } else if val.is_empty() {
                assert!(rws.is_empty());
                self.next_ws = Some(lws);
            } else {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      lws));
            }
        }
        if !val.is_empty() {
            self.writeln(&format!("writer.write_str({:#?}).unwrap();", val));
        }
        if !rws.is_empty() {
            self.next_ws = Some(rws);
        }
    }

    fn write_expr(&mut self, ws: &WS, s: &Expr) {
        self.handle_ws(ws);
        self.write("writer.write_fmt(format_args!(\"{}\", ");
        self.visit_expr(s);
        self.writeln(")).unwrap();");
    }

    fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) {
        for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() {
            self.handle_ws(cws);
            match *cond {
                Some(ref expr) => {
                    if i == 0 {
                        self.write("if ");
                    } else {
                        self.dedent();
                        self.write("} else if ");
                    }
                    self.visit_expr(expr);
                },
                None => {
                    self.dedent();
                    self.write("} else");
                },
            }
            self.writeln(" {");
            self.handle(nodes);
        }
        self.handle_ws(ws);
        self.writeln("}");
    }

    fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr,
                  body: &'a [Node], ws2: &WS) {

        self.handle_ws(ws1);
        if !self.loop_vars {
            self.writeln("let mut _loop_indexes = Vec::new();");
            self.writeln("let mut _loop_cur = 0;");
            self.loop_vars = true;
        }

        self.writeln("_loop_indexes.push(0);");
        let cur_index = self.loop_index;
        self.loop_index += 1;
        self.writeln(&format!("_loop_cur = {};", cur_index));
        self.write("for ");
        let targets = self.visit_target(var);
        for name in &targets {
            self.locals.insert(name.clone());
            self.write(name);
        }
        self.write(" in (&");
        self.visit_expr(iter);
        self.writeln(").into_iter() {");

        self.writeln("_loop_indexes[_loop_cur] += 1;");
        self.handle(body);
        self.handle_ws(ws2);
        self.writeln("}");
        self.loop_index -= 1;
        self.writeln("_loop_indexes.pop();");
        for name in &targets {
            self.locals.remove(name);
        }
    }

    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) {
        self.flush_ws(ws1);
        self.writeln(&format!("timpl.render_block_{}_to(writer);", name));
        self.prepare_ws(ws2);
    }

    fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node],
                       ws2: &WS) {
        self.writeln("#[allow(unused_variables)]");
        self.writeln(&format!(
            "fn render_block_{}_to(&self, writer: &mut std::fmt::Write) {{",
            name));
        self.prepare_ws(ws1);
        self.handle(nodes);
        self.flush_ws(ws2);
        self.writeln("}");
    }

    fn handle(&mut self, nodes: &'a [Node]) {
        for n in nodes {
            match *n {
                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); }
                Node::Comment() => {},
                Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); },
                Node::Cond(ref conds, ref ws) => {
                    self.write_cond(conds, ws);
                },
                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
                    self.write_loop(ws1, var, iter, body, ws2);
                },
                Node::Block(ref ws1, name, ref ws2) => {
                    self.write_block(ws1, name, ws2);
                },
                Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => {
                    self.write_block_def(ws1, name, block_nodes, ws2);
                }
                Node::Extends(_) => {
                    panic!("no extends or block definition allowed in content");
                },
            }
        }
    }

    fn impl_template(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} askama::Template for {}{} {{",
                              anno, ast.ident.as_ref(), anno));

        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        self.handle(nodes);
        self.flush_ws(&WS(false, false));
        self.writeln("}");
        self.writeln("}");
    }

    fn impl_trait(&mut self, ast: &syn::DeriveInput, base: &str,
                  blocks: &'a [Node], nodes: Option<&'a [Node]>) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} TraitFrom{} for {}{} {{",
                              anno, path_as_identifier(base),
                              ast.ident.as_ref(), anno));
        self.handle(blocks);

        self.writeln("#[allow(unused_variables)]");
        let trait_name = format!("TraitFrom{}", path_as_identifier(base));
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write) {{",
            trait_name));

        if let Some(nodes) = nodes {
            self.handle(nodes);
            self.flush_ws(&WS(false, false));
        } else {
            self.writeln("self._parent.render_trait_to(self, writer);");
        }

        self.writeln("}");
        self.flush_ws(&WS(false, false));
        self.writeln("}");
    }

    fn impl_template_for_trait(&mut self, ast: &syn::DeriveInput, derived: bool) {
        let anno = annotations(&ast.generics);
        self.writeln(&format!("impl{} askama::Template for {}{} {{",
                              anno, ast.ident.as_ref(), anno));
        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        if derived {
            self.writeln("self._parent.render_trait_to(self, writer);");
        } else {
            self.writeln("self.render_trait_to(self, writer);");
        }
        self.writeln("}");
        self.writeln("}");
    }

    fn define_trait(&mut self, path: &str, block_names: &[&str]) {
        let trait_name = format!("TraitFrom{}", path_as_identifier(path));
        self.writeln(&format!("trait {} {{", &trait_name));

        for bname in block_names {
            self.writeln(&format!(
                "fn render_block_{}_to(&self, writer: &mut std::fmt::Write);",
                bname));
        }
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write);",
            trait_name));

        self.writeln("}");
    }

    fn result(self) -> String {
        self.buf
    }

}

pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String {
    let mut base: Option<Expr> = None;
    let mut blocks = Vec::new();
    let mut block_names = Vec::new();
    let mut content = Vec::new();
    for n in nodes.drain(..) {
        match n {
            Node::Extends(path) => {
                match base {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => { base = Some(path); },
                }
            },
            Node::BlockDef(ws1, name, _, ws2) => {
                blocks.push(n);
                block_names.push(name);
                content.push(Node::Block(ws1, name, ws2));
            },
            _ => { content.push(n); },
        }
    }

    let mut gen = Generator::new();
    if !blocks.is_empty() {
        if base.is_none() {
            gen.define_trait(path, &block_names);
        }
        let tmpl_path = match base {
            Some(Expr::StrLit(base_path)) => { base_path },
            _ => { path },
        };
        let trait_nodes = if base.is_none() { Some(&content[..]) } else { None };
        gen.impl_trait(ast, tmpl_path, &blocks, trait_nodes);
        gen.impl_template_for_trait(ast, base.is_some());
    } else {
        gen.impl_template(ast, &content);
    }
    gen.result()
}
<|endoftext|>


<commit_before>use async_trait::async_trait;
use crate::{
    ast::{Directive, FromInputValue, InputValue, Selection},
    value::{Object, ScalarRefValue, ScalarValue, Value},
};

use crate::{
    executor::{ExecutionResult, Executor},
    parser::Spanning,
};

use crate::BoxFuture;

use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType};

#[async_trait]
pub trait GraphQLTypeAsync<S>: GraphQLType<S> + Send + Sync
where
    Self::Context: Send + Sync,
    Self::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    async fn resolve_field_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        field_name: &'a str,
        arguments: &'a Arguments<'a, S>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> ExecutionResult<S> {
        panic!("resolve_field must be implemented by object types");
    }

    async fn resolve_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        selection_set: Option<&'a [Selection<'a, S>]>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> Value<S> {
        if let Some(selection_set) = selection_set {
            resolve_selection_set_into_async(self, info, selection_set, executor).await
        } else {
            panic!("resolve() must be implemented by non-object output types");
        }
    }

    async fn resolve_into_type_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        type_name: &str,
        selection_set: Option<&'a [Selection<'a, S>]>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> ExecutionResult<S> {
        if Self::name(info).unwrap() == type_name {
            Ok(self.resolve_async(info, selection_set, executor).await)
        } else {
            panic!("resolve_into_type_async must be implemented by unions and interfaces");
        }
    }
}

// Wrapper function around resolve_selection_set_into_async_recursive.
// This wrapper is necessary because async fns can not be recursive.
#[cfg(feature = "async")]
pub(crate) fn resolve_selection_set_into_async<'a, 'e, T, CtxT, S>(
    instance: &'a T,
    info: &'a T::TypeInfo,
    selection_set: &'e [Selection<'e, S>],
    executor: &'e Executor<'e, CtxT, S>,
) -> BoxFuture<'a, Value<S>>
where
    T: GraphQLTypeAsync<S, Context = CtxT>,
    T::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    CtxT: Send + Sync,
    'e: 'a,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    Box::pin(resolve_selection_set_into_async_recursive(
        instance,
        info,
        selection_set,
        executor,
    ))
}

struct AsyncField<S> {
    name: String,
    value: Option<Value<S>>,
}

enum AsyncValue<S> {
    Field(AsyncField<S>),
    Nested(Value<S>),
}

#[cfg(feature = "async")]
pub(crate) async fn resolve_selection_set_into_async_recursive<'a, T, CtxT, S>(
    instance: &'a T,
    info: &'a T::TypeInfo,
    selection_set: &'a [Selection<'a, S>],
    executor: &'a Executor<'a, CtxT, S>,
) -> Value<S>
where
    T: GraphQLTypeAsync<S, Context = CtxT> + Send + Sync,
    T::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    CtxT: Send + Sync,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    use futures::stream::{FuturesOrdered, StreamExt};

    let mut object = Object::with_capacity(selection_set.len());

    let mut async_values = FuturesOrdered::<BoxFuture<'a, AsyncValue<S>>>::new();

    let meta_type = executor
        .schema()
        .concrete_type_by_name(
            T::name(info)
                .expect("Resolving named type's selection set")
                .as_ref(),
        )
        .expect("Type not found in schema");

    for selection in selection_set {
        match *selection {
            Selection::Field(Spanning {
                item: ref f,
                start: ref start_pos,
                ..
            }) => {
                if is_excluded(&f.directives, executor.variables()) {
                    continue;
                }

                let response_name = f.alias.as_ref().unwrap_or(&f.name).item;

                if f.name.item == "__typename" {
                    object.add_field(
                        response_name,
                        Value::scalar(instance.concrete_type_name(executor.context(), info)),
                    );
                    continue;
                }

                let meta_field = meta_type.field_by_name(f.name.item).unwrap_or_else(|| {
                    panic!(format!(
                        "Field {} not found on type {:?}",
                        f.name.item,
                        meta_type.name()
                    ))
                });

                let exec_vars = executor.variables();

                let sub_exec = executor.field_sub_executor(
                    &response_name,
                    f.name.item,
                    start_pos.clone(),
                    f.selection_set.as_ref().map(|v| &v[..]),
                );
                let args = Arguments::new(
                    f.arguments.as_ref().map(|m| {
                        m.item
                            .iter()
                            .map(|&(ref k, ref v)| (k.item, v.item.clone().into_const(exec_vars)))
                            .collect()
                    }),
                    &meta_field.arguments,
                );

                let pos = start_pos.clone();
                let is_non_null = meta_field.field_type.is_non_null();

                let response_name = response_name.to_string();
                let field_future = async move {
                    // TODO: implement custom future type instead of
                    //       two-level boxing.
                    let res = instance
                        .resolve_field_async(info, f.name.item, &args, &sub_exec)
                        .await;

                    let value = match res {
                        Ok(Value::Null) if is_non_null => None,
                        Ok(v) => Some(v),
                        Err(e) => {
                            sub_exec.push_error_at(e, pos);

                            if is_non_null {
                                None
                            } else {
                                Some(Value::null())
                            }
                        }
                    };
                    AsyncValue::Field(AsyncField {
                        name: response_name,
                        value,
                    })
                };
                async_values.push(Box::pin(field_future));
            }
            Selection::FragmentSpread(Spanning {
                item: ref spread, ..
            }) => {
                if is_excluded(&spread.directives, executor.variables()) {
                    continue;
                }

                // TODO: prevent duplicate boxing.
                let f = async move {
                    let fragment = &executor
                        .fragment_by_name(spread.name.item)
                        .expect("Fragment could not be found");
                    let value = resolve_selection_set_into_async(
                        instance,
                        info,
                        &fragment.selection_set[..],
                        executor,
                    )
                    .await;
                    AsyncValue::Nested(value)
                };
                async_values.push(Box::pin(f));
            }
            Selection::InlineFragment(Spanning {
                item: ref fragment,
                start: ref start_pos,
                ..
            }) => {
                if is_excluded(&fragment.directives, executor.variables()) {
                    continue;
                }

                let sub_exec = executor.type_sub_executor(
                    fragment.type_condition.as_ref().map(|c| c.item),
                    Some(&fragment.selection_set[..]),
                );

                if let Some(ref type_condition) = fragment.type_condition {
                    // FIXME: implement async version.

                    let sub_result = instance.resolve_into_type_async(
                        info,
                        type_condition.item,
                        Some(&fragment.selection_set[..]),
                        &sub_exec,
                    ).await;

                    if let Ok(Value::Object(obj)) = sub_result {
                        for (k, v) in obj {
                            merge_key_into(&mut object, &k, v);
                        }
                    } else if let Err(e) = sub_result {
                        sub_exec.push_error_at(e, start_pos.clone());
                    }
                } else {
                    let f = async move {
                        let value = resolve_selection_set_into_async(
                            instance,
                            info,
                            &fragment.selection_set[..],
                            &sub_exec,
                        )
                        .await;
                        AsyncValue::Nested(value)
                    };
                    async_values.push(Box::pin(f));
                }
            }
        }
    }

    while let Some(item) = async_values.next().await {
        match item {
            AsyncValue::Field(AsyncField { name, value }) => {
                if let Some(value) = value {
                    object.add_field(&name, value);
                } else {
                    return Value::null();
                }
            }
            AsyncValue::Nested(obj) => match obj {
                v @ Value::Null => {
                    return v;
                }
                Value::Object(obj) => {
                    for (k, v) in obj {
                        merge_key_into(&mut object, &k, v);
                    }
                }
                _ => unreachable!(),
            },
        }
    }

    Value::Object(object)
}
<commit_msg>Remove useless todo<commit_after>use async_trait::async_trait;
use crate::{
    ast::{Directive, FromInputValue, InputValue, Selection},
    value::{Object, ScalarRefValue, ScalarValue, Value},
};

use crate::{
    executor::{ExecutionResult, Executor},
    parser::Spanning,
};

use crate::BoxFuture;

use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType};

#[async_trait]
pub trait GraphQLTypeAsync<S>: GraphQLType<S> + Send + Sync
where
    Self::Context: Send + Sync,
    Self::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    async fn resolve_field_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        field_name: &'a str,
        arguments: &'a Arguments<'a, S>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> ExecutionResult<S> {
        panic!("resolve_field must be implemented by object types");
    }

    async fn resolve_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        selection_set: Option<&'a [Selection<'a, S>]>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> Value<S> {
        if let Some(selection_set) = selection_set {
            resolve_selection_set_into_async(self, info, selection_set, executor).await
        } else {
            panic!("resolve() must be implemented by non-object output types");
        }
    }

    async fn resolve_into_type_async<'a>(
        &'a self,
        info: &'a Self::TypeInfo,
        type_name: &str,
        selection_set: Option<&'a [Selection<'a, S>]>,
        executor: &'a Executor<'a, Self::Context, S>,
    ) -> ExecutionResult<S> {
        if Self::name(info).unwrap() == type_name {
            Ok(self.resolve_async(info, selection_set, executor).await)
        } else {
            panic!("resolve_into_type_async must be implemented by unions and interfaces");
        }
    }
}

// Wrapper function around resolve_selection_set_into_async_recursive.
// This wrapper is necessary because async fns can not be recursive.
#[cfg(feature = "async")]
pub(crate) fn resolve_selection_set_into_async<'a, 'e, T, CtxT, S>(
    instance: &'a T,
    info: &'a T::TypeInfo,
    selection_set: &'e [Selection<'e, S>],
    executor: &'e Executor<'e, CtxT, S>,
) -> BoxFuture<'a, Value<S>>
where
    T: GraphQLTypeAsync<S, Context = CtxT>,
    T::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    CtxT: Send + Sync,
    'e: 'a,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    Box::pin(resolve_selection_set_into_async_recursive(
        instance,
        info,
        selection_set,
        executor,
    ))
}

struct AsyncField<S> {
    name: String,
    value: Option<Value<S>>,
}

enum AsyncValue<S> {
    Field(AsyncField<S>),
    Nested(Value<S>),
}

#[cfg(feature = "async")]
pub(crate) async fn resolve_selection_set_into_async_recursive<'a, T, CtxT, S>(
    instance: &'a T,
    info: &'a T::TypeInfo,
    selection_set: &'a [Selection<'a, S>],
    executor: &'a Executor<'a, CtxT, S>,
) -> Value<S>
where
    T: GraphQLTypeAsync<S, Context = CtxT> + Send + Sync,
    T::TypeInfo: Send + Sync,
    S: ScalarValue + Send + Sync,
    CtxT: Send + Sync,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    use futures::stream::{FuturesOrdered, StreamExt};

    let mut object = Object::with_capacity(selection_set.len());

    let mut async_values = FuturesOrdered::<BoxFuture<'a, AsyncValue<S>>>::new();

    let meta_type = executor
        .schema()
        .concrete_type_by_name(
            T::name(info)
                .expect("Resolving named type's selection set")
                .as_ref(),
        )
        .expect("Type not found in schema");

    for selection in selection_set {
        match *selection {
            Selection::Field(Spanning {
                item: ref f,
                start: ref start_pos,
                ..
            }) => {
                if is_excluded(&f.directives, executor.variables()) {
                    continue;
                }

                let response_name = f.alias.as_ref().unwrap_or(&f.name).item;

                if f.name.item == "__typename" {
                    object.add_field(
                        response_name,
                        Value::scalar(instance.concrete_type_name(executor.context(), info)),
                    );
                    continue;
                }

                let meta_field = meta_type.field_by_name(f.name.item).unwrap_or_else(|| {
                    panic!(format!(
                        "Field {} not found on type {:?}",
                        f.name.item,
                        meta_type.name()
                    ))
                });

                let exec_vars = executor.variables();

                let sub_exec = executor.field_sub_executor(
                    &response_name,
                    f.name.item,
                    start_pos.clone(),
                    f.selection_set.as_ref().map(|v| &v[..]),
                );
                let args = Arguments::new(
                    f.arguments.as_ref().map(|m| {
                        m.item
                            .iter()
                            .map(|&(ref k, ref v)| (k.item, v.item.clone().into_const(exec_vars)))
                            .collect()
                    }),
                    &meta_field.arguments,
                );

                let pos = start_pos.clone();
                let is_non_null = meta_field.field_type.is_non_null();

                let response_name = response_name.to_string();
                let field_future = async move {
                    // TODO: implement custom future type instead of
                    //       two-level boxing.
                    let res = instance
                        .resolve_field_async(info, f.name.item, &args, &sub_exec)
                        .await;

                    let value = match res {
                        Ok(Value::Null) if is_non_null => None,
                        Ok(v) => Some(v),
                        Err(e) => {
                            sub_exec.push_error_at(e, pos);

                            if is_non_null {
                                None
                            } else {
                                Some(Value::null())
                            }
                        }
                    };
                    AsyncValue::Field(AsyncField {
                        name: response_name,
                        value,
                    })
                };
                async_values.push(Box::pin(field_future));
            }
            Selection::FragmentSpread(Spanning {
                item: ref spread, ..
            }) => {
                if is_excluded(&spread.directives, executor.variables()) {
                    continue;
                }

                // TODO: prevent duplicate boxing.
                let f = async move {
                    let fragment = &executor
                        .fragment_by_name(spread.name.item)
                        .expect("Fragment could not be found");
                    let value = resolve_selection_set_into_async(
                        instance,
                        info,
                        &fragment.selection_set[..],
                        executor,
                    )
                    .await;
                    AsyncValue::Nested(value)
                };
                async_values.push(Box::pin(f));
            }
            Selection::InlineFragment(Spanning {
                item: ref fragment,
                start: ref start_pos,
                ..
            }) => {
                if is_excluded(&fragment.directives, executor.variables()) {
                    continue;
                }

                let sub_exec = executor.type_sub_executor(
                    fragment.type_condition.as_ref().map(|c| c.item),
                    Some(&fragment.selection_set[..]),
                );

                if let Some(ref type_condition) = fragment.type_condition {
                    let sub_result = instance.resolve_into_type_async(
                        info,
                        type_condition.item,
                        Some(&fragment.selection_set[..]),
                        &sub_exec,
                    ).await;

                    if let Ok(Value::Object(obj)) = sub_result {
                        for (k, v) in obj {
                            merge_key_into(&mut object, &k, v);
                        }
                    } else if let Err(e) = sub_result {
                        sub_exec.push_error_at(e, start_pos.clone());
                    }
                } else {
                    let f = async move {
                        let value = resolve_selection_set_into_async(
                            instance,
                            info,
                            &fragment.selection_set[..],
                            &sub_exec,
                        )
                        .await;
                        AsyncValue::Nested(value)
                    };
                    async_values.push(Box::pin(f));
                }
            }
        }
    }

    while let Some(item) = async_values.next().await {
        match item {
            AsyncValue::Field(AsyncField { name, value }) => {
                if let Some(value) = value {
                    object.add_field(&name, value);
                } else {
                    return Value::null();
                }
            }
            AsyncValue::Nested(obj) => match obj {
                v @ Value::Null => {
                    return v;
                }
                Value::Object(obj) => {
                    for (k, v) in obj {
                        merge_key_into(&mut object, &k, v);
                    }
                }
                _ => unreachable!(),
            },
        }
    }

    Value::Object(object)
}
<|endoftext|>