<commit_before>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use std::io::{IoResult, TimedOut};
use std::comm::{Disconnected, Empty};
use std::collections::TreeMap;
use packet::{Packet, PacketType, PacketConnect, PacketDisconnect, PacketMessage, Command, Disconnect};
use shared::ConnectionConfig;
use time::now;

//FIXME: Ew ew ew
fn hash_sender(address: &SocketAddr) -> String {
    match address.ip {
        Ipv4Addr(a, b, c, d) => {
            format!("{}-{}-{}-{}:{}", a, b, c, d, address.port)
        },
        Ipv6Addr(a, b, c, d, e, f, g, h) => {
            format!("{}-{}-{}-{}-{}-{}-{}-{}:{}", a, b, c, d, e, f, g, h, address.port)
        }
    }
}

#[deriving(Clone)]
struct ClientInstance {
    addr: SocketAddr,
    timeout: i64
}

impl ClientInstance {
    pub fn new(addr: SocketAddr, timeout: i64) -> ClientInstance {
        ClientInstance {
            addr: addr,
            timeout: timeout
        }
    }
}

pub enum PacketOrCommand <T> {
    UserPacket(T),
    Command(PacketType)
}

fn reader_process(mut reader: UdpSocket, reader_sub_out: Sender<(Packet, SocketAddr)>, reader_sub_in: Receiver<Command>, protocol_id: u32) {
    let mut buf = [0, ..255];
    reader.set_timeout(Some(1000));
    loop {
        match reader.recv_from(buf) {
            Ok((amt, src)) => {
                match Packet::deserialize(buf.slice_to(amt)) {
                    Ok(packet) => {
                        if packet.protocol_id == protocol_id {
                            reader_sub_out.send((packet, src));
                        }
                    },
                    Err(_) => ()
                }
            },
            Err(e) => {
                match e.kind {
                    TimedOut => {
                        match reader_sub_in.try_recv() {
                            Ok(Disconnect) => {
                                break;
                            },
                            Err(Disconnected) => {
                                break;
                            },
                            Err(Empty) => ()
                        }
                    },
                    _ => ()
                }
            }
        }
    }
}

fn writer_process(mut writer: UdpSocket, _writer_sub_out: Sender<Command>, writer_sub_in: Receiver<(Packet, SocketAddr)>) {
    for (msg, target_addr) in writer_sub_in.iter() {
        match msg.serialize() {
            Ok(msg) => {
                match writer.send_to(msg.as_slice(), target_addr) {
                    Ok(()) => (),
                    Err(e) => println!("Error sending data - {}", e)
                }
            },
            Err(_) => ()
        }
    }
}

pub struct Server <T> {
    pub addr: SocketAddr,
    pub config: ConnectionConfig<T>,

    reader_send: Sender<Command>,
    reader_receive: Receiver<(Packet, SocketAddr)>,
    writer_send: Sender<(Packet, SocketAddr)>,
    writer_receive: Receiver<Command>,

    connections: TreeMap<String, ClientInstance>
}

impl <T> Server <T> {
    pub fn new(addr: SocketAddr, config: ConnectionConfig<T>) -> IoResult<Server<T>> {
        match UdpSocket::bind(addr) {
            Ok(reader) => {
                let writer = reader.clone();
                let (reader_out, reader_sub_in) = channel();
                let (reader_sub_out, reader_in) = channel();

                let protocol_id = config.protocol_id;

                spawn(proc() {
                    reader_process(reader, reader_sub_out, reader_sub_in, protocol_id);
                });

                let (writer_out, writer_sub_in) = channel();
                let (writer_sub_out, writer_in) = channel();

                spawn(proc() {
                    writer_process(writer, writer_sub_out, writer_sub_in);
                });

                Ok(Server {
                    addr: addr,
                    config: config,
                    reader_send: reader_out,
                    reader_receive: reader_in,
                    writer_send: writer_out,
                    writer_receive: writer_in,
                    connections: TreeMap::new()
                })
            }
            Err(e) => Err(e)
        }
    }

    pub fn poll(&mut self) -> Option<(PacketOrCommand<T>, SocketAddr)> {
        let mut out = None;
        loop {
            match self.reader_receive.try_recv() {
                Ok((packet, src)) => {
                    //Handle any new connections
                    match packet.packet_type {
                        PacketConnect => {
                            self.connections.insert(hash_sender(&src), ClientInstance::new(src, now().to_timespec().sec + self.config.timeout_period as i64));
                            self.writer_send.send((Packet::accept(self.config.protocol_id), src));
                            out = Some((Command(PacketConnect), src));
                            break
                        },
                        PacketDisconnect => {
                            let hash = hash_sender(&src);
                            if self.connections.contains_key(&hash) {
                                out = Some((Command(PacketConnect), src));
                                self.connections.remove(&hash);
                                break
                            }
                        },
                        PacketMessage => {
                            let hash = hash_sender(&src);
                            match self.connections.find_mut(&hash) {
                                Some(ref mut comms) => {
                                    out = Some((UserPacket((self.config.packet_deserializer)(&packet.packet_content.unwrap())), src));
                                    //Update our timeout
                                    comms.timeout = now().to_timespec().sec + self.config.timeout_period as i64;
                                    break
                                },
                                None => ()
                            }
                        },
                        _ => ()
                    };
                },
                _ => {
                    break
                }
            };
        };
        out
    }

    pub fn cull(&mut self) -> Vec<SocketAddr> {
        let mut keep_alive = TreeMap::new();
        let mut culled = vec![];

        let now = now().to_timespec().sec;

        for (hash, connection) in self.connections.iter() {
            if connection.timeout >= now {
                keep_alive.insert(hash.clone(), connection.clone());
            } else {
                culled.push(connection.addr);
            }
        };

        self.connections = keep_alive;
        culled
    }

    pub fn send_to(&mut self, packet: &T, addr: &SocketAddr) {
        let hashed = hash_sender(addr);
        match self.connections.find(&hashed) {
            Some(_) => self.writer_send.send((Packet::message(self.config.protocol_id, (self.config.packet_serializer)(packet)), addr.clone())),
            None => (),
        }
    }

    pub fn send_to_many(&mut self, packet: &T, addrs: &Vec<SocketAddr>) {
        for addr in addrs.iter() {
            self.send_to(packet, addr)
        }
    }

    pub fn send_to_all(&mut self, packet: &T) {
        for addr in self.connections.clone().values() { //FIXME: Urgh
            self.send_to(packet, &addr.addr)
        }
    }
}

#[unsafe_destructor]
impl <T> Drop for Server <T> {

    fn drop(&mut self) {
        self.reader_send.send(Disconnect);
    }
}
<commit_msg>Fixed build error<commit_after>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use std::io::{IoResult, TimedOut};
use std::comm::{Disconnected, Empty};
use std::collections::TreeMap;
use packet::{Packet, PacketType, PacketConnect, PacketDisconnect, PacketMessage, Command, Disconnect};
use shared::ConnectionConfig;
use time::now;

//FIXME: Ew ew ew
fn hash_sender(address: &SocketAddr) -> String {
    match address.ip {
        Ipv4Addr(a, b, c, d) => {
            format!("{}-{}-{}-{}:{}", a, b, c, d, address.port)
        },
        Ipv6Addr(a, b, c, d, e, f, g, h) => {
            format!("{}-{}-{}-{}-{}-{}-{}-{}:{}", a, b, c, d, e, f, g, h, address.port)
        }
    }
}

#[deriving(Clone)]
struct ClientInstance {
    addr: SocketAddr,
    timeout: i64
}

impl ClientInstance {
    pub fn new(addr: SocketAddr, timeout: i64) -> ClientInstance {
        ClientInstance {
            addr: addr,
            timeout: timeout
        }
    }
}

pub enum PacketOrCommand <T> {
    UserPacket(T),
    IsCommand(PacketType)
}

fn reader_process(mut reader: UdpSocket, reader_sub_out: Sender<(Packet, SocketAddr)>, reader_sub_in: Receiver<Command>, protocol_id: u32) {
    let mut buf = [0, ..255];
    reader.set_timeout(Some(1000));
    loop {
        match reader.recv_from(buf) {
            Ok((amt, src)) => {
                match Packet::deserialize(buf.slice_to(amt)) {
                    Ok(packet) => {
                        if packet.protocol_id == protocol_id {
                            reader_sub_out.send((packet, src));
                        }
                    },
                    Err(_) => ()
                }
            },
            Err(e) => {
                match e.kind {
                    TimedOut => {
                        match reader_sub_in.try_recv() {
                            Ok(Disconnect) => {
                                break;
                            },
                            Err(Disconnected) => {
                                break;
                            },
                            Err(Empty) => ()
                        }
                    },
                    _ => ()
                }
            }
        }
    }
}

fn writer_process(mut writer: UdpSocket, _writer_sub_out: Sender<Command>, writer_sub_in: Receiver<(Packet, SocketAddr)>) {
    for (msg, target_addr) in writer_sub_in.iter() {
        match msg.serialize() {
            Ok(msg) => {
                match writer.send_to(msg.as_slice(), target_addr) {
                    Ok(()) => (),
                    Err(e) => println!("Error sending data - {}", e)
                }
            },
            Err(_) => ()
        }
    }
}

pub struct Server <T> {
    pub addr: SocketAddr,
    pub config: ConnectionConfig<T>,

    reader_send: Sender<Command>,
    reader_receive: Receiver<(Packet, SocketAddr)>,
    writer_send: Sender<(Packet, SocketAddr)>,
    writer_receive: Receiver<Command>,

    connections: TreeMap<String, ClientInstance>
}

impl <T> Server <T> {
    pub fn new(addr: SocketAddr, config: ConnectionConfig<T>) -> IoResult<Server<T>> {
        match UdpSocket::bind(addr) {
            Ok(reader) => {
                let writer = reader.clone();
                let (reader_out, reader_sub_in) = channel();
                let (reader_sub_out, reader_in) = channel();

                let protocol_id = config.protocol_id;

                spawn(proc() {
                    reader_process(reader, reader_sub_out, reader_sub_in, protocol_id);
                });

                let (writer_out, writer_sub_in) = channel();
                let (writer_sub_out, writer_in) = channel();

                spawn(proc() {
                    writer_process(writer, writer_sub_out, writer_sub_in);
                });

                Ok(Server {
                    addr: addr,
                    config: config,
                    reader_send: reader_out,
                    reader_receive: reader_in,
                    writer_send: writer_out,
                    writer_receive: writer_in,
                    connections: TreeMap::new()
                })
            }
            Err(e) => Err(e)
        }
    }

    pub fn poll(&mut self) -> Option<(PacketOrCommand<T>, SocketAddr)> {
        let mut out = None;
        loop {
            match self.reader_receive.try_recv() {
                Ok((packet, src)) => {
                    //Handle any new connections
                    match packet.packet_type {
                        PacketConnect => {
                            self.connections.insert(hash_sender(&src), ClientInstance::new(src, now().to_timespec().sec + self.config.timeout_period as i64));
                            self.writer_send.send((Packet::accept(self.config.protocol_id), src));
                            out = Some((IsCommand(PacketConnect), src));
                            break
                        },
                        PacketDisconnect => {
                            let hash = hash_sender(&src);
                            if self.connections.contains_key(&hash) {
                                out = Some((IsCommand(PacketConnect), src));
                                self.connections.remove(&hash);
                                break
                            }
                        },
                        PacketMessage => {
                            let hash = hash_sender(&src);
                            match self.connections.find_mut(&hash) {
                                Some(ref mut comms) => {
                                    out = Some((UserPacket((self.config.packet_deserializer)(&packet.packet_content.unwrap())), src));
                                    //Update our timeout
                                    comms.timeout = now().to_timespec().sec + self.config.timeout_period as i64;
                                    break
                                },
                                None => ()
                            }
                        },
                        _ => ()
                    };
                },
                _ => {
                    break
                }
            };
        };
        out
    }

    pub fn cull(&mut self) -> Vec<SocketAddr> {
        let mut keep_alive = TreeMap::new();
        let mut culled = vec![];

        let now = now().to_timespec().sec;

        for (hash, connection) in self.connections.iter() {
            if connection.timeout >= now {
                keep_alive.insert(hash.clone(), connection.clone());
            } else {
                culled.push(connection.addr);
            }
        };

        self.connections = keep_alive;
        culled
    }

    pub fn send_to(&mut self, packet: &T, addr: &SocketAddr) {
        let hashed = hash_sender(addr);
        match self.connections.find(&hashed) {
            Some(_) => self.writer_send.send((Packet::message(self.config.protocol_id, (self.config.packet_serializer)(packet)), addr.clone())),
            None => (),
        }
    }

    pub fn send_to_many(&mut self, packet: &T, addrs: &Vec<SocketAddr>) {
        for addr in addrs.iter() {
            self.send_to(packet, addr)
        }
    }

    pub fn send_to_all(&mut self, packet: &T) {
        for addr in self.connections.clone().values() { //FIXME: Urgh
            self.send_to(packet, &addr.addr)
        }
    }
}

#[unsafe_destructor]
impl <T> Drop for Server <T> {

    fn drop(&mut self) {
        self.reader_send.send(Disconnect);
    }
}
<|endoftext|>


<commit_before><commit_msg>Avoid using format! when writing.<commit_after><|endoftext|>


<commit_before>extern crate arguments;
extern crate log;
extern crate mcpat;
extern crate term;

#[macro_use]
extern crate recorder;

mod dynamic;
mod logger;
mod statik;

use recorder::{Error, Result};

const USAGE: &'static str = "
Usage: recorder <command> [options]

Commands:
    dynamic        Record dynamic characteristics.
    static         Record static characteristics.
";

fn main() {
    logger::setup();
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.orphans.len() != 1 {
        help(USAGE);
    }
    match &arguments.orphans[0][..] {
        "dynamic" => dynamic::execute(&arguments),
        "static" => statik::execute(&arguments),
        _ => raise!("the command is unknown"),
    }
}

fn help(message: &str) -> ! {
    println!("{}", message.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::{stderr, Write};
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<commit_msg>Eliminate a warning<commit_after>extern crate arguments;
extern crate log;
extern crate mcpat;
extern crate term;

#[macro_use]
extern crate recorder;

mod dynamic;
mod logger;
mod statik;

use recorder::{Error, Result};

const USAGE: &'static str = "
Usage: recorder <command> [options]

Commands:
    dynamic        Record dynamic characteristics.
    static         Record static characteristics.
";

fn main() {
    logger::setup();
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.orphans.len() != 1 {
        help(USAGE);
    }
    match &arguments.orphans[0][..] {
        "dynamic" => dynamic::execute(&arguments),
        "static" => statik::execute(&arguments),
        _ => raise!("the command is unknown"),
    }
}

fn help(message: &str) -> ! {
    println!("{}", message.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::Write;
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<|endoftext|>


<commit_before>use mimirsbrunn::utils::logger::logger_init;
use snafu::{ResultExt, Snafu};
use std::net::ToSocketAddrs;
use tokio::runtime;
use tracing::{info, instrument};
use warp::Filter;

use super::settings::{Error as SettingsError, Opts, Settings};
use mimir::adapters::primary::bragi::prometheus_handler::update_metrics;
use mimir::{
    adapters::primary::bragi::api::{
        features, forward_geocoder, forward_geocoder_explain, reverse_geocoder, status,
    },
    adapters::primary::bragi::{handlers, routes},
    adapters::secondary::elasticsearch::remote::connection_pool_url,
    domain::ports::secondary::remote::{Error as PortRemoteError, Remote},
    metrics,
};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Could not establish Elasticsearch Connection: {}", source))]
    ElasticsearchConnection { source: PortRemoteError },

    #[snafu(display("Could not generate settings: {}", source))]
    SettingsProcessing { source: SettingsError },

    #[snafu(display("Socket Addr Error with host {} / port {}: {}", host, port, source))]
    SockAddr {
        host: String,
        port: u16,
        source: std::io::Error,
    },

    #[snafu(display("Addr Resolution Error {}", msg))]
    AddrResolution { msg: String },

    #[snafu(display("Could not init logger: {}", source))]
    InitLog {
        source: mimirsbrunn::utils::logger::Error,
    },
}

pub fn run(opts: &Opts) -> Result<(), Error> {
    let settings = Settings::new(opts).context(SettingsProcessing)?;

    let _log_guard = logger_init().map_err(|err| Error::InitLog { source: err })?;

    let runtime = runtime::Builder::new_multi_thread()
        .worker_threads(settings.nb_threads.unwrap_or_else(num_cpus::get))
        .enable_all()
        .build()
        .expect("Failed to build tokio runtime.");

    runtime.block_on(run_server(settings))
}

pub fn config(opts: &Opts) -> Result<(), Error> {
    let settings = Settings::new(opts).context(SettingsProcessing)?;
    println!("{}", serde_json::to_string_pretty(&settings).unwrap());
    Ok(())
}

#[instrument(skip(settings))]
pub async fn run_server(settings: Settings) -> Result<(), Error> {
    info!(
        "Connecting to Elasticsearch at {}",
        &settings.elasticsearch.url
    );

    let client = connection_pool_url(&settings.elasticsearch.url)
        .conn(settings.elasticsearch.clone())
        .await
        .context(ElasticsearchConnection)?;

    // Here I place reverse_geocoder first because its most likely to get hit.
    let api = reverse_geocoder!(client.clone(), settings.query.clone())
        .or(forward_geocoder!(client.clone(), settings.query.clone()))
        .or(features!(client.clone(), settings.query.clone()))
        .or(forward_geocoder_explain!(client.clone(), settings.query))
        .or(status!(client.clone(), &settings.elasticsearch.url))
        .or(metrics!())
        .recover(routes::report_invalid)
        .with(warp::wrap_fn(|filter| {
            routes::cache_filter(filter, settings.http_cache_duration)
        }))
        .with(warp::log::custom(move |log| update_metrics(log)));
        .with(warp::trace(|info| {
            // Create a span using tracing macros
            tracing::info_span!(
                "request",
                method = %info.method(),
                path = %info.path(),
            )
        }));

    info!("api ready");

    let host = settings.service.host;
    let port = settings.service.port;
    let addr = (host.as_str(), port);
    let addr = addr
        .to_socket_addrs()
        .context(SockAddr { host, port })?
        .next()
        .ok_or(Error::AddrResolution {
            msg: String::from("Cannot resolve bragi addr."),
        })?;

    info!("Serving bragi on {}", addr);

    warp::serve(api).run(addr).await;

    Ok(())
}
<commit_msg>fix after rebase<commit_after>use mimirsbrunn::utils::logger::logger_init;
use snafu::{ResultExt, Snafu};
use std::net::ToSocketAddrs;
use tokio::runtime;
use tracing::{info, instrument};
use warp::Filter;

use super::settings::{Error as SettingsError, Opts, Settings};
use mimir::adapters::primary::bragi::prometheus_handler::update_metrics;
use mimir::{
    adapters::primary::bragi::api::{
        features, forward_geocoder, forward_geocoder_explain, reverse_geocoder, status,
    },
    adapters::primary::bragi::{handlers, routes},
    adapters::secondary::elasticsearch::remote::connection_pool_url,
    domain::ports::secondary::remote::{Error as PortRemoteError, Remote},
    metrics,
};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Could not establish Elasticsearch Connection: {}", source))]
    ElasticsearchConnection { source: PortRemoteError },

    #[snafu(display("Could not generate settings: {}", source))]
    SettingsProcessing { source: SettingsError },

    #[snafu(display("Socket Addr Error with host {} / port {}: {}", host, port, source))]
    SockAddr {
        host: String,
        port: u16,
        source: std::io::Error,
    },

    #[snafu(display("Addr Resolution Error {}", msg))]
    AddrResolution { msg: String },

    #[snafu(display("Could not init logger: {}", source))]
    InitLog {
        source: mimirsbrunn::utils::logger::Error,
    },
}

pub fn run(opts: &Opts) -> Result<(), Error> {
    let settings = Settings::new(opts).context(SettingsProcessing)?;

    let _log_guard = logger_init().map_err(|err| Error::InitLog { source: err })?;

    let runtime = runtime::Builder::new_multi_thread()
        .worker_threads(settings.nb_threads.unwrap_or_else(num_cpus::get))
        .enable_all()
        .build()
        .expect("Failed to build tokio runtime.");

    runtime.block_on(run_server(settings))
}

pub fn config(opts: &Opts) -> Result<(), Error> {
    let settings = Settings::new(opts).context(SettingsProcessing)?;
    println!("{}", serde_json::to_string_pretty(&settings).unwrap());
    Ok(())
}

#[instrument(skip(settings))]
pub async fn run_server(settings: Settings) -> Result<(), Error> {
    info!(
        "Connecting to Elasticsearch at {}",
        &settings.elasticsearch.url
    );

    let client = connection_pool_url(&settings.elasticsearch.url)
        .conn(settings.elasticsearch.clone())
        .await
        .context(ElasticsearchConnection)?;

    // Here I place reverse_geocoder first because its most likely to get hit.
    let api = reverse_geocoder!(client.clone(), settings.query.clone())
        .or(forward_geocoder!(client.clone(), settings.query.clone()))
        .or(features!(client.clone(), settings.query.clone()))
        .or(forward_geocoder_explain!(client.clone(), settings.query))
        .or(status!(client.clone(), &settings.elasticsearch.url))
        .or(metrics!())
        .recover(routes::report_invalid)
        .with(warp::wrap_fn(|filter| {
            routes::cache_filter(filter, settings.http_cache_duration)
        }))
        .with(warp::log::custom(move |log| update_metrics(log)))
        .with(warp::trace(|info| {
            // Create a span using tracing macros
            tracing::info_span!(
                "request",
                method = %info.method(),
                path = %info.path(),
            )
        }));

    info!("api ready");

    let host = settings.service.host;
    let port = settings.service.port;
    let addr = (host.as_str(), port);
    let addr = addr
        .to_socket_addrs()
        .context(SockAddr { host, port })?
        .next()
        .ok_or(Error::AddrResolution {
            msg: String::from("Cannot resolve bragi addr."),
        })?;

    info!("Serving bragi on {}", addr);

    warp::serve(api).run(addr).await;

    Ok(())
}
<|endoftext|>


<commit_before>//! Intrinsics are instructions we cannot model with Falcon.

use il::*;
use std::fmt;

/// An Instrinsic is a lifted instruction we cannot model.
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Intrinsic {
    mnemonic: String,
    instruction_str: String,
    written_expressions: Option<Vec<Expression>>,
    read_expressions: Option<Vec<Expression>>,
    bytes: Vec<u8>
}

impl Intrinsic {
    pub fn new<S: Into<String>, SS: Into<String>>(
        mnemonic: S,
        instruction_str: SS,
        written_expressions: Option<Vec<Expression>>,
        read_expressions: Option<Vec<Expression>>,
        bytes: Vec<u8>
    ) -> Intrinsic {
        Intrinsic {
            mnemonic: mnemonic.into(),
            instruction_str: instruction_str.into(),
            written_expressions: written_expressions,
            read_expressions: read_expressions,
            bytes: bytes.clone()
        }
    }

    pub fn mnemonic(&self) -> &str {
        &self.mnemonic
    }

    pub fn instruction_str(&self) -> &str {
        &self.instruction_str
    }

    pub fn written_expressions(&self) -> Option<&[Expression]> {
        self.written_expressions.as_ref().map(|x| x.as_slice())
    }

    pub fn written_expressions_mut(&mut self) -> Option<&mut [Expression]> {
        self.written_expressions.as_mut().map(|x| x.as_mut_slice())
    }

    pub fn read_expressions(&self) -> Option<&[Expression]> {
        self.read_expressions.as_ref().map(|x| x.as_slice())
    }

    pub fn read_expressions_mut(&mut self) -> Option<&mut [Expression]> {
        self.read_expressions.as_mut().map(|x| x.as_mut_slice())
    }

    pub fn scalars_written(&self) -> Vec<&Scalar> {
        self.written_expressions()
            .map(|written_expressions|
                written_expressions
                    .iter()
                    .flat_map(|expression| expression.scalars())
                    .collect::<Vec<&Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_written_mut(&mut self) -> Vec<&mut Scalar> {
        self.written_expressions_mut()
            .map(|written_expressions|
                written_expressions
                    .iter_mut()
                    .flat_map(|expression| expression.scalars_mut())
                    .collect::<Vec<&mut Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_read(&self) -> Vec<&Scalar> {
        self.read_expressions()
            .map(|read_expressions|
                read_expressions
                    .iter()
                    .flat_map(|expression| expression.scalars())
                    .collect::<Vec<&Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_read_mut(&mut self) -> Vec<&mut Scalar> {
        self.read_expressions_mut()
            .map(|read_expressions|
                read_expressions
                    .iter_mut()
                    .flat_map(|expression| expression.scalars_mut())
                    .collect::<Vec<&mut Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }
}

impl fmt::Display for Intrinsic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let bytes = self.bytes()
            .into_iter()
            .map(|byte| format!("{:02x}", byte))
            .collect::<Vec<String>>()
            .join("");
        write!(f, "{} {}", bytes, self.instruction_str())
    }
}<commit_msg>add arguments field to intrinsics<commit_after>//! Intrinsics are instructions we cannot model with Falcon.

use il::*;
use std::fmt;

/// An Instrinsic is a lifted instruction we cannot model.
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Intrinsic {
    mnemonic: String,
    instruction_str: String,
    arguments: Vec<Expression>,
    written_expressions: Option<Vec<Expression>>,
    read_expressions: Option<Vec<Expression>>,
    bytes: Vec<u8>
}

impl Intrinsic {
    pub fn new<S: Into<String>, SS: Into<String>>(
        mnemonic: S,
        instruction_str: SS,
        arguments: Vec<Expression>,
        written_expressions: Option<Vec<Expression>>,
        read_expressions: Option<Vec<Expression>>,
        bytes: Vec<u8>
    ) -> Intrinsic {
        Intrinsic {
            mnemonic: mnemonic.into(),
            instruction_str: instruction_str.into(),
            arguments: arguments,
            written_expressions: written_expressions,
            read_expressions: read_expressions,
            bytes: bytes.clone()
        }
    }

    pub fn mnemonic(&self) -> &str {
        &self.mnemonic
    }

    pub fn instruction_str(&self) -> &str {
        &self.instruction_str
    }

    pub fn arguments(&self) -> &[Expression] {
        &self.arguments
    }

    pub fn written_expressions(&self) -> Option<&[Expression]> {
        self.written_expressions.as_ref().map(|x| x.as_slice())
    }

    pub fn written_expressions_mut(&mut self) -> Option<&mut [Expression]> {
        self.written_expressions.as_mut().map(|x| x.as_mut_slice())
    }

    pub fn read_expressions(&self) -> Option<&[Expression]> {
        self.read_expressions.as_ref().map(|x| x.as_slice())
    }

    pub fn read_expressions_mut(&mut self) -> Option<&mut [Expression]> {
        self.read_expressions.as_mut().map(|x| x.as_mut_slice())
    }

    pub fn scalars_written(&self) -> Vec<&Scalar> {
        self.written_expressions()
            .map(|written_expressions|
                written_expressions
                    .iter()
                    .flat_map(|expression| expression.scalars())
                    .collect::<Vec<&Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_written_mut(&mut self) -> Vec<&mut Scalar> {
        self.written_expressions_mut()
            .map(|written_expressions|
                written_expressions
                    .iter_mut()
                    .flat_map(|expression| expression.scalars_mut())
                    .collect::<Vec<&mut Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_read(&self) -> Vec<&Scalar> {
        self.read_expressions()
            .map(|read_expressions|
                read_expressions
                    .iter()
                    .flat_map(|expression| expression.scalars())
                    .collect::<Vec<&Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn scalars_read_mut(&mut self) -> Vec<&mut Scalar> {
        self.read_expressions_mut()
            .map(|read_expressions|
                read_expressions
                    .iter_mut()
                    .flat_map(|expression| expression.scalars_mut())
                    .collect::<Vec<&mut Scalar>>())
            .unwrap_or(Vec::new())
    }

    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }
}

impl fmt::Display for Intrinsic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let bytes = self.bytes()
            .into_iter()
            .map(|byte| format!("{:02x}", byte))
            .collect::<Vec<String>>()
            .join("");
        write!(f, "{} {}", bytes, self.instruction_str())
    }
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

pub type BitCount0 = usize; // `BitCount` clashes with a standard trait
pub type BitCount8 = u8;
pub type BitCount16 = u16;
pub type BitCount32 = u32;
pub type BitCount64 = u64;

pub type ByteCount = usize;
pub type ByteCount8 = u8;
pub type ByteCount16 = u16;
pub type ByteCount32 = u32;
pub type ByteCount64 = u64;

pub type WordCount = usize;
pub type WordCount8 = u8;
pub type WordCount16 = u16;
pub type WordCount32 = u32;
pub type WordCount64 = u64;

pub type ElementCount = usize;
pub type ElementCount8 = u8;
pub type ElementCount16 = u16;
pub type ElementCount32 = u32;
pub type ElementCount64 = u64;

pub type WirePointerCount = usize;
pub type WirePointerCount8 = u8;
pub type WirePointerCount16 = u16;
pub type WirePointerCount32 = u32;
pub type WirePointerCount64 = u64;

#[derive(Copy)]
pub struct Word {_x : u64}

pub const BITS_PER_BYTE : BitCount0 = 8;
pub const BITS_PER_WORD : BitCount0 = 64;
pub const BYTES_PER_WORD : ByteCount = 8;

pub const BITS_PER_POINTER : BitCount0 = 64;
pub const BYTES_PER_POINTER : ByteCount = 8;
pub const WORDS_PER_POINTER : WordCount = 1;

pub const POINTER_SIZE_IN_WORDS : WordCount = 1;

pub fn bytes_per_element<T>() -> ByteCount {
    ::std::mem::size_of::<T>()
}

pub fn bits_per_element<T>() -> BitCount0 {
    8 * ::std::mem::size_of::<T>()
}

#[inline]
pub fn allocate_zeroed_words(size : WordCount) -> ::std::vec::Vec<Word> {

//    Do this, but faster:
//    return ::std::vec::Vec::from_elem(size, 0);

    let mut result : ::std::vec::Vec<Word> = ::std::vec::Vec::with_capacity(size);
    unsafe {
        result.set_len(size);
        let p : *mut u8 = ::std::mem::transmute(result.as_mut_slice().as_mut_ptr());
        ::std::ptr::set_memory(p, 0, size * BYTES_PER_WORD);
    }
    return result;
}

#[derive(Copy)]
pub struct MessageSize {
    //# Size of a message. Every struct type has a method `.total_size()` that returns this.
    pub word_count : u64,
    pub cap_count : u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

pub trait PtrUint<T> {
    fn as_uint(self) -> usize;
}

impl <T> PtrUint<T> for *const T {
    fn as_uint(self) -> usize {
        self as usize
    }
}

impl <T> PtrUint<T> for *mut T {
    fn as_uint(self) -> usize {
        self as usize
    }
}

#[inline]
pub fn ptr_sub<T, U: PtrUint<T>, V: PtrUint<T>>(p1 : U, p2 : V) -> usize {
    return (p1.as_uint() - p2.as_uint()) / ::std::mem::size_of::<T>();
}
<commit_msg>PtrUint -> PtrUsize<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

pub type BitCount0 = usize; // `BitCount` clashes with a standard trait
pub type BitCount8 = u8;
pub type BitCount16 = u16;
pub type BitCount32 = u32;
pub type BitCount64 = u64;

pub type ByteCount = usize;
pub type ByteCount8 = u8;
pub type ByteCount16 = u16;
pub type ByteCount32 = u32;
pub type ByteCount64 = u64;

pub type WordCount = usize;
pub type WordCount8 = u8;
pub type WordCount16 = u16;
pub type WordCount32 = u32;
pub type WordCount64 = u64;

pub type ElementCount = usize;
pub type ElementCount8 = u8;
pub type ElementCount16 = u16;
pub type ElementCount32 = u32;
pub type ElementCount64 = u64;

pub type WirePointerCount = usize;
pub type WirePointerCount8 = u8;
pub type WirePointerCount16 = u16;
pub type WirePointerCount32 = u32;
pub type WirePointerCount64 = u64;

#[derive(Copy)]
pub struct Word {_x : u64}

pub const BITS_PER_BYTE : BitCount0 = 8;
pub const BITS_PER_WORD : BitCount0 = 64;
pub const BYTES_PER_WORD : ByteCount = 8;

pub const BITS_PER_POINTER : BitCount0 = 64;
pub const BYTES_PER_POINTER : ByteCount = 8;
pub const WORDS_PER_POINTER : WordCount = 1;

pub const POINTER_SIZE_IN_WORDS : WordCount = 1;

pub fn bytes_per_element<T>() -> ByteCount {
    ::std::mem::size_of::<T>()
}

pub fn bits_per_element<T>() -> BitCount0 {
    8 * ::std::mem::size_of::<T>()
}

#[inline]
pub fn allocate_zeroed_words(size : WordCount) -> ::std::vec::Vec<Word> {

//    Do this, but faster:
//    return ::std::vec::Vec::from_elem(size, 0);

    let mut result : ::std::vec::Vec<Word> = ::std::vec::Vec::with_capacity(size);
    unsafe {
        result.set_len(size);
        let p : *mut u8 = ::std::mem::transmute(result.as_mut_slice().as_mut_ptr());
        ::std::ptr::set_memory(p, 0, size * BYTES_PER_WORD);
    }
    return result;
}

#[derive(Copy)]
pub struct MessageSize {
    //# Size of a message. Every struct type has a method `.total_size()` that returns this.
    pub word_count : u64,
    pub cap_count : u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

pub trait PtrUsize<T> {
    fn as_usize(self) -> usize;
}

impl <T> PtrUsize<T> for *const T {
    fn as_usize(self) -> usize {
        self as usize
    }
}

impl <T> PtrUsize<T> for *mut T {
    fn as_usize(self) -> usize {
        self as usize
    }
}

#[inline]
pub fn ptr_sub<T, U: PtrUsize<T>, V: PtrUsize<T>>(p1 : U, p2 : V) -> usize {
    return (p1.as_usize() - p2.as_usize()) / ::std::mem::size_of::<T>();
}
<|endoftext|>


<commit_before>use failure::Error;
use std::str::FromStr;
use std::net::IpAddr;
use std::collections::BTreeMap;

use quire::validate::{Structure, Scalar, Numeric, Mapping, Sequence};
use quire::{Options, parse_string};

#[derive(Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ChildKind {
    Daemon,
    Command,
}

// Note everything here should be stable-serializable
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct ChildInstance {
    pub instances: usize,  // legacy maybe remove somehow?
    pub image: String,
    pub config: String,
    #[serde(skip_serializing_if="BTreeMap::is_empty", default)]
    pub variables: BTreeMap<String, String>,
    #[serde(skip_serializing_if="Option::is_none", default)]
    pub ip_address: Option<IpAddr>,
    pub kind: ChildKind,
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct ChildConfig {
    pub instances: usize,
    pub image: String,
    pub config: String,
    #[serde(skip_serializing_if="BTreeMap::is_empty", default)]
    pub variables: BTreeMap<String, String>,
    #[serde(skip_serializing_if="Vec::is_empty", default)]
    pub ip_addresses: Vec<IpAddr>,
    pub kind: ChildKind,
}

impl ChildConfig {
    pub fn instantiate(&self, instance: usize) -> Result<ChildInstance, Error>
    {
        let cfg = ChildInstance {
            instances: 1,  // TODO(tailhook) legacy, find a way to remove
            image: self.image.clone(),
            config: self.config.clone(),
            variables: self.variables.clone(),
            ip_address: if self.ip_addresses.len() > 0 {
                if let Some(addr) = self.ip_addresses.get(instance) {
                    Some(*addr)
                } else {
                    bail!("Instance no {}, but there's only {} ip addresses",
                        instance, self.ip_addresses.len());
                }
            } else {
                None
            },
            kind: self.kind,
        };
        return Ok(cfg);
    }
    pub fn mapping_validator<'x>() -> Mapping<'x> {
        return Mapping::new(
            Scalar::new(),
            ChildConfig::validator());
    }
    pub fn validator<'x>() -> Structure<'x> {
        Structure::new()
        .member("instances", Numeric::new().default(1))
        .member("image", Scalar::new())
        .member("config", Scalar::new())
        .member("variables", Mapping::new(Scalar::new(), Scalar::new()))
        .member("kind", Scalar::new().default("Daemon"))
        .member("ip_addresses", Sequence::new(Scalar::new()))
    }
}

impl FromStr for ChildInstance {
    type Err = ();
    fn from_str(body: &str) -> Result<ChildInstance, ()> {
        parse_string("<command-line>", body,
            &ChildConfig::validator(), &Options::default())
            .map_err(|_| ())
    }
}

#[cfg(test)]
mod test {
    use std::collections::BTreeMap;
    use std::str::FromStr;
    use super::ChildConfig;
    use super::ChildKind::Daemon;
    use serde_json::{to_string, from_str};

    #[test]
    fn deserialize_compat() {
        let data = r#"{
            "instances":1,
            "image":"myproj.4a20772b",
            "config":"/config/staging/myproj.yaml",
            "kind":"Daemon"}"#;
        let cc = ChildConfig::from_str(data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        });

        let cc: ChildConfig = from_str(&data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        });
    }

    #[test]
    fn dedeserialize_vars() {
        let data = r#"{
            "instances":1,
            "image":"myproj.4a20772b",
            "config":"/config/staging/myproj.yaml",
            "variables": {"a": "b"},
            "kind":"Daemon"}"#;
        let cc = ChildConfig::from_str(data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: vec![
                (String::from("a"), String::from("b")),
            ].into_iter().collect(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        })
    }

    #[test]
    fn serialize_compat() {
        let data = to_string(&ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        }).unwrap();
        assert_eq!(data, "{\
            \"instances\":1,\
            \"image\":\"myproj.4a20772b\",\
            \"config\":\"/config/staging/myproj.yaml\",\
            \"kind\":\"Daemon\"}");
    }

    #[test]
    fn serialize_vars() {
        let data = to_string(&ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: vec![
                (String::from("a"), String::from("b")),
                (String::from("c"), String::from("d")),
            ].into_iter().collect(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        }).unwrap();
        assert_eq!(data, "{\
            \"instances\":1,\
            \"image\":\"myproj.4a20772b\",\
            \"config\":\"/config/staging/myproj.yaml\",\
            \"variables\":{\"a\":\"b\",\"c\":\"d\"},\
            \"kind\":\"Daemon\"}");
    }
}
<commit_msg>Fix child config parsing<commit_after>use failure::Error;
use std::str::FromStr;
use std::net::IpAddr;
use std::collections::BTreeMap;

use quire::validate::{Structure, Scalar, Numeric, Mapping, Sequence};
use quire::{Options, parse_string};

#[derive(Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ChildKind {
    Daemon,
    Command,
}

// Note everything here should be stable-serializable
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct ChildInstance {
    pub instances: usize,  // legacy maybe remove somehow?
    pub image: String,
    pub config: String,
    #[serde(skip_serializing_if="BTreeMap::is_empty", default)]
    pub variables: BTreeMap<String, String>,
    #[serde(skip_serializing_if="Option::is_none", default)]
    pub ip_address: Option<IpAddr>,
    pub kind: ChildKind,
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct ChildConfig {
    pub instances: usize,
    pub image: String,
    pub config: String,
    #[serde(skip_serializing_if="BTreeMap::is_empty", default)]
    pub variables: BTreeMap<String, String>,
    #[serde(skip_serializing_if="Vec::is_empty", default)]
    pub ip_addresses: Vec<IpAddr>,
    pub kind: ChildKind,
}

impl ChildConfig {
    pub fn instantiate(&self, instance: usize) -> Result<ChildInstance, Error>
    {
        let cfg = ChildInstance {
            instances: 1,  // TODO(tailhook) legacy, find a way to remove
            image: self.image.clone(),
            config: self.config.clone(),
            variables: self.variables.clone(),
            ip_address: if self.ip_addresses.len() > 0 {
                if let Some(addr) = self.ip_addresses.get(instance) {
                    Some(*addr)
                } else {
                    bail!("Instance no {}, but there's only {} ip addresses",
                        instance, self.ip_addresses.len());
                }
            } else {
                None
            },
            kind: self.kind,
        };
        return Ok(cfg);
    }
    pub fn mapping_validator<'x>() -> Mapping<'x> {
        return Mapping::new(
            Scalar::new(),
            ChildConfig::validator());
    }
    pub fn validator<'x>() -> Structure<'x> {
        Structure::new()
        .member("instances", Numeric::new().default(1))
        .member("image", Scalar::new())
        .member("config", Scalar::new())
        .member("variables", Mapping::new(Scalar::new(), Scalar::new()))
        .member("kind", Scalar::new().default("Daemon"))
        .member("ip_addresses", Sequence::new(Scalar::new()))
    }
}
impl ChildInstance {
    pub fn validator<'x>() -> Structure<'x> {
        Structure::new()
        .member("instances", Numeric::new().default(1))
        .member("image", Scalar::new())
        .member("config", Scalar::new())
        .member("variables", Mapping::new(Scalar::new(), Scalar::new()))
        .member("kind", Scalar::new().default("Daemon"))
        .member("ip_address", Scalar::new().optional())
    }
}

impl FromStr for ChildInstance {
    type Err = ();
    fn from_str(body: &str) -> Result<ChildInstance, ()> {
        parse_string("<command-line>", body,
            &Self::validator(), &Options::default())
            .map_err(|_| ())
    }
}

#[cfg(test)]
mod test {
    use std::collections::BTreeMap;
    use std::str::FromStr;
    use super::ChildConfig;
    use super::ChildKind::Daemon;
    use serde_json::{to_string, from_str};

    #[test]
    fn deserialize_compat() {
        let data = r#"{
            "instances":1,
            "image":"myproj.4a20772b",
            "config":"/config/staging/myproj.yaml",
            "kind":"Daemon"}"#;
        let cc = ChildConfig::from_str(data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        });

        let cc: ChildConfig = from_str(&data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        });
    }

    #[test]
    fn dedeserialize_vars() {
        let data = r#"{
            "instances":1,
            "image":"myproj.4a20772b",
            "config":"/config/staging/myproj.yaml",
            "variables": {"a": "b"},
            "kind":"Daemon"}"#;
        let cc = ChildConfig::from_str(data).unwrap();
        assert_eq!(cc, ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: vec![
                (String::from("a"), String::from("b")),
            ].into_iter().collect(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        })
    }

    #[test]
    fn serialize_compat() {
        let data = to_string(&ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: BTreeMap::new(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        }).unwrap();
        assert_eq!(data, "{\
            \"instances\":1,\
            \"image\":\"myproj.4a20772b\",\
            \"config\":\"/config/staging/myproj.yaml\",\
            \"kind\":\"Daemon\"}");
    }

    #[test]
    fn serialize_vars() {
        let data = to_string(&ChildConfig {
            instances: 1,
            image: String::from("myproj.4a20772b"),
            config: String::from("/config/staging/myproj.yaml"),
            variables: vec![
                (String::from("a"), String::from("b")),
                (String::from("c"), String::from("d")),
            ].into_iter().collect(),
            ip_addresses: Vec::new(),
            kind: Daemon,
        }).unwrap();
        assert_eq!(data, "{\
            \"instances\":1,\
            \"image\":\"myproj.4a20772b\",\
            \"config\":\"/config/staging/myproj.yaml\",\
            \"variables\":{\"a\":\"b\",\"c\":\"d\"},\
            \"kind\":\"Daemon\"}");
    }
}
<|endoftext|>


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

//! Types and traits shared between services.

use super::super::session::Session;
use super::super::Result;

/// Trait representing something that can be refreshed.
pub trait Refresh {
    /// Refresh the resource representation.
    fn refresh(&mut self) -> Result<()>;
}

/// A type that can be converted into a verified representation.
pub trait IntoVerified {
    /// Conver this object into the same object with verification.
    fn into_verified(self, session: &Session) -> Result<Self>
    where
        Self: Sized;
}

macro_rules! opaque_resource_type {
    ($(#[$attr:meta])* $name:ident ? $service:expr) => (
        $(#[$attr])*
        #[derive(Debug, Clone, PartialEq, Eq)]
        pub struct $name {
            pub(crate) value: String,
            pub(crate) verified: bool
        }

        impl From<String> for $name {
            fn from(value: String) -> $name {
                $name {
                    value: value,
                    verified: false
                }
            }
        }

        impl<'s> From<&'s str> for $name {
            fn from(value: &'s str) -> $name {
                $name {
                    value: String::from(value),
                    verified: false
                }
            }
        }

        impl From<$name> for String {
            fn from(value: $name) -> String {
                value.value
            }
        }

        impl From<$name> for ::serde_json::Value {
            fn from(value: $name) -> ::serde_json::Value {
                value.value.into()
            }
        }

        impl AsRef<str> for $name {
            fn as_ref(&self) -> &str {
                &self.value
            }
        }

        impl ::std::fmt::Display for $name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                self.value.fmt(f)
            }
        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
                    where S: ::serde::ser::Serializer {
                serializer.serialize_str(&self.value)
            }
        }

        impl<'de> ::serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D)
                    -> ::std::result::Result<$name, D::Error>
                    where D: ::serde::de::Deserializer<'de> {
                Ok($name {
                    value: String::deserialize(deserializer)?,
                    // Assume that values coming from network are valid.
                    verified: true
                })
            }
        }

        impl $name {
            /// Create a reference that was previously verified.
            #[allow(dead_code)]
            pub(crate) fn new_verified(value: String) -> $name {
                $name {
                    value: value,
                    verified: true
                }
            }
        }

        #[cfg(not(feature = $service))]
        #[allow(dead_code)]
        impl $crate::common::IntoVerified for $name {
            fn into_verified(self, _session: &$crate::session::Session)
                    -> $crate::Result<$name> {
                Ok(self)
            }
        }
    )
}

opaque_resource_type!(#[doc = "An ID of a `Flavor`"] FlavorRef ? "compute");

opaque_resource_type!(#[doc = "An ID of an `Image`"] ImageRef ? "image");

opaque_resource_type!(#[doc = "An ID of a `KeyPair`"] KeyPairRef ? "compute");

opaque_resource_type!(#[doc = "An ID of a `Network`"] NetworkRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Project`"] ProjectRef ? "identity");

opaque_resource_type!(#[doc = "An ID of a `Port`"] PortRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Router`"] RouterRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `SecurityGroup`"] SecurityGroupRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Snapshot`"] SnapshotRef ? "volume");

opaque_resource_type!(#[doc = "An ID of a `Subnet`"] SubnetRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `User`"] UserRef ? "identity");

opaque_resource_type!(#[doc = "An ID of a `Volume`"] VolumeRef ? "volume");

#[cfg(test)]
mod test {
    use serde_json;

    opaque_resource_type!(TestId ? "test");

    #[test]
    fn test_opaque_type_basics() {
        let id = TestId::from("foo");
        assert_eq!(id.as_ref(), "foo");
        assert_eq!(&id.to_string(), "foo");
        assert_eq!(id, TestId::from("foo"));
        assert!(id != TestId::from("bar"));
        let s: String = id.into();
        assert_eq!(&s, "foo");
    }

    #[test]
    fn test_opaque_type_serde() {
        let id: TestId = serde_json::from_str("\"foo\"").unwrap();
        assert_eq!(id.as_ref(), "foo");
        assert_eq!(serde_json::to_string(&id).unwrap(), "\"foo\"");
    }
}
<commit_msg>chore(common): default implementation for IntoVerified<commit_after>// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Types and traits shared between services.

use super::super::session::Session;
use super::super::Result;

/// Trait representing something that can be refreshed.
pub trait Refresh {
    /// Refresh the resource representation.
    fn refresh(&mut self) -> Result<()>;
}

/// A type that can be converted into a verified representation.
pub trait IntoVerified {
    /// Convert this object into the same object with verification.
    fn into_verified(self, _session: &Session) -> Result<Self>
    where
        Self: Sized,
    {
        Ok(self)
    }
}

macro_rules! opaque_resource_type {
    ($(#[$attr:meta])* $name:ident ? $service:expr) => (
        $(#[$attr])*
        #[derive(Debug, Clone, PartialEq, Eq)]
        pub struct $name {
            pub(crate) value: String,
            pub(crate) verified: bool
        }

        impl From<String> for $name {
            fn from(value: String) -> $name {
                $name {
                    value: value,
                    verified: false
                }
            }
        }

        impl<'s> From<&'s str> for $name {
            fn from(value: &'s str) -> $name {
                $name {
                    value: String::from(value),
                    verified: false
                }
            }
        }

        impl From<$name> for String {
            fn from(value: $name) -> String {
                value.value
            }
        }

        impl From<$name> for ::serde_json::Value {
            fn from(value: $name) -> ::serde_json::Value {
                value.value.into()
            }
        }

        impl AsRef<str> for $name {
            fn as_ref(&self) -> &str {
                &self.value
            }
        }

        impl ::std::fmt::Display for $name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                self.value.fmt(f)
            }
        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
                    where S: ::serde::ser::Serializer {
                serializer.serialize_str(&self.value)
            }
        }

        impl<'de> ::serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D)
                    -> ::std::result::Result<$name, D::Error>
                    where D: ::serde::de::Deserializer<'de> {
                Ok($name {
                    value: String::deserialize(deserializer)?,
                    // Assume that values coming from network are valid.
                    verified: true
                })
            }
        }

        impl $name {
            /// Create a reference that was previously verified.
            #[allow(dead_code)]
            pub(crate) fn new_verified(value: String) -> $name {
                $name {
                    value: value,
                    verified: true
                }
            }
        }

        #[cfg(not(feature = $service))]
        #[allow(dead_code)]
        impl $crate::common::IntoVerified for $name {}
    )
}

opaque_resource_type!(#[doc = "An ID of a `Flavor`"] FlavorRef ? "compute");

opaque_resource_type!(#[doc = "An ID of an `Image`"] ImageRef ? "image");

opaque_resource_type!(#[doc = "An ID of a `KeyPair`"] KeyPairRef ? "compute");

opaque_resource_type!(#[doc = "An ID of a `Network`"] NetworkRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Project`"] ProjectRef ? "identity");

opaque_resource_type!(#[doc = "An ID of a `Port`"] PortRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Router`"] RouterRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `SecurityGroup`"] SecurityGroupRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `Snapshot`"] SnapshotRef ? "volume");

opaque_resource_type!(#[doc = "An ID of a `Subnet`"] SubnetRef ? "network");

opaque_resource_type!(#[doc = "An ID of a `User`"] UserRef ? "identity");

opaque_resource_type!(#[doc = "An ID of a `Volume`"] VolumeRef ? "volume");

#[cfg(test)]
mod test {
    use serde_json;

    opaque_resource_type!(TestId ? "test");

    #[test]
    fn test_opaque_type_basics() {
        let id = TestId::from("foo");
        assert_eq!(id.as_ref(), "foo");
        assert_eq!(&id.to_string(), "foo");
        assert_eq!(id, TestId::from("foo"));
        assert!(id != TestId::from("bar"));
        let s: String = id.into();
        assert_eq!(&s, "foo");
    }

    #[test]
    fn test_opaque_type_serde() {
        let id: TestId = serde_json::from_str("\"foo\"").unwrap();
        assert_eq!(id.as_ref(), "foo");
        assert_eq!(serde_json::to_string(&id).unwrap(), "\"foo\"");
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// 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.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use proc_macro2;
use syn;
use syn::punctuated;
use syn::token;

use derives;
use derives::attrs::{Attrs, Kind, Parser, Ty};
use derives::from_argmatches;
use derives::into_app;

/// Generate a block of code to add arguments/subcommands corresponding to
/// the `fields` to an app.
fn gen_app_augmentation(
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
    app_var: &syn::Ident,
) -> proc_macro2::TokenStream {
    let subcmds: Vec<_> = fields
        .iter()
        .filter_map(|field| {
            let attrs = Attrs::from_field(&field);
            if let Kind::Subcommand(ty) = attrs.kind() {
                let subcmd_type = match (ty, derives::sub_type(&field.ty)) {
                    (Ty::Option, Some(sub_type)) => sub_type,
                    _ => &field.ty,
                };
                let required = if ty == Ty::Option {
                    quote!()
                } else {
                    quote! {
                        let #app_var = #app_var.setting(
                            ::clap::AppSettings::SubcommandRequiredElseHelp
                        );
                    }
                };

                Some(quote! {
                    let #app_var = <#subcmd_type>::augment_app( #app_var );
                    #required
                })
            } else {
                None
            }
        })
        .collect();

    assert!(
        subcmds.len() <= 1,
        "cannot have more than one nested subcommand"
    );

    let args = fields.iter().filter_map(|field| {
        let attrs = Attrs::from_field(field);
        match attrs.kind() {
            Kind::Subcommand(_) => None,
            Kind::FlattenStruct => {
                let ty = &field.ty;
                Some(quote! {
                    let #app_var = <#ty>::augment_app(#app_var);
                    let #app_var = if <#ty>::is_subcommand() {
                        #app_var.setting(::clap::AppSettings::SubcommandRequiredElseHelp)
                    } else {
                        #app_var
                    };
                })
            }
            Kind::Arg(ty) => {
                let convert_type = match ty {
                    Ty::Vec | Ty::Option => derives::sub_type(&field.ty).unwrap_or(&field.ty),
                    _ => &field.ty,
                };

                let occurences = attrs.parser().0 == Parser::FromOccurrences;

                let validator = match *attrs.parser() {
                    (Parser::TryFromStr, ref f) => quote! {
                        .validator(|s| {
                            #f(&s)
                            .map(|_: #convert_type| ())
                            .map_err(|e| e.to_string())
                        })
                    },
                    (Parser::TryFromOsStr, ref f) => quote! {
                        .validator_os(|s| #f(&s).map(|_: #convert_type| ()))
                    },
                    _ => quote!(),
                };

                // @TODO remove unneccessary builders
                let modifier = match ty {
                    Ty::Bool => quote!(),
                    Ty::Option => quote!( .takes_value(true) #validator ),
                    Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ),
                    Ty::Other if occurences => quote!( .multiple_occurrences(true) ),
                    Ty::Other => {
                        let required = !attrs.has_method("default_value");
                        quote!( .takes_value(true).required(#required) #validator )
                    }
                };
                let methods = attrs.methods();
                let name = attrs.name();
                Some(quote! {
                    let #app_var = #app_var.arg(
                        ::clap::Arg::with_name(#name)
                            #modifier
                            #methods
                    );
                })
            }
        }
    });

    quote! {{
        #( #args )*
        #( #subcmds )*
        #app_var
    }}
}

fn gen_augment_app_fn(
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
) -> proc_macro2::TokenStream {
    let app_var = syn::Ident::new("app", proc_macro2::Span::call_site());
    let augmentation = gen_app_augmentation(fields, &app_var);
    quote! {
        pub fn augment_app<'b>(
            #app_var: ::clap::App<'b>
        ) -> ::clap::App<'b> {
            #augmentation
        }
    }
}

fn gen_augment_app_for_enum(
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
) -> proc_macro2::TokenStream {
    use syn::Fields::*;

    let subcommands = variants.iter().map(|variant| {
        let name = variant.ident.to_string();
        let attrs = Attrs::from_struct(&variant.attrs, name);
        let app_var = syn::Ident::new("subcommand", proc_macro2::Span::call_site());
        let arg_block = match variant.fields {
            Named(ref fields) => gen_app_augmentation(&fields.named, &app_var),
            Unit => quote!( #app_var ),
            Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
                let ty = &unnamed[0];
                quote! {
                    {
                        let #app_var = <#ty>::augment_app(#app_var);
                        if <#ty>::is_subcommand() {
                            #app_var.setting(
                                ::clap::AppSettings::SubcommandRequiredElseHelp
                            )
                        } else {
                            #app_var
                        }
                    }
                }
            }
            Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident),
        };

        let name = attrs.name();
        let from_attrs = attrs.methods();
        quote! {
            .subcommand({
                let #app_var = ::clap::App::new(#name);
                let #app_var = #arg_block;
                #app_var#from_attrs
            })
        }
    });

    quote! {
        pub fn augment_app<'b>(
            app: ::clap::App<'b>
        ) -> ::clap::App<'b> {
            app #( #subcommands )*
        }
    }
}

fn gen_from_subcommand(
    name: &syn::Ident,
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
) -> proc_macro2::TokenStream {
    use syn::Fields::*;

    let match_arms = variants.iter().map(|variant| {
        let attrs = Attrs::from_struct(&variant.attrs, variant.ident.to_string());
        let sub_name = attrs.name();
        let variant_name = &variant.ident;
        let constructor_block = match variant.fields {
            Named(ref fields) => from_argmatches::gen_constructor(&fields.named),
            Unit => quote!(),
            Unnamed(ref fields) if fields.unnamed.len() == 1 => {
                let ty = &fields.unnamed[0];
                quote!( ( <#ty as ::clap::FromArgMatches>::from_argmatches(matches) ) )
            }
            Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident),
        };

        quote! {
            (#sub_name, Some(matches)) =>
                Some(#name :: #variant_name #constructor_block)
        }
    });

    quote! {
        pub fn from_subcommand<'b>(
            sub: (&'b str, Option<&'b ::clap::ArgMatches>)
        ) -> Option<Self> {
            match sub {
                #( #match_arms ),*,
                _ => None
            }
        }
    }
}

fn clap_impl_for_struct(
    name: &syn::Ident,
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
    attrs: &[syn::Attribute],
) -> proc_macro2::TokenStream {
    let into_app_impl = into_app::gen_into_app_impl_for_struct(name, attrs);
    let augment_app_fn = gen_augment_app_fn(fields);
    let from_argmatches_impl = from_argmatches::gen_from_argmatches_impl_for_struct(name, fields);
    let parse_fns = gen_parse_fns(name);

    quote! {
        #[allow(unused_variables)]
        impl ::clap::Clap for #name { }

        #into_app_impl

        #from_argmatches_impl

        #[allow(dead_code, unreachable_code)]
        #[doc(hidden)]
        impl #name {
            #augment_app_fn

            #parse_fns

            pub fn is_subcommand() -> bool { false }
        }
    }
}

fn clap_impl_for_enum(
    name: &syn::Ident,
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
    attrs: &[syn::Attribute],
) -> proc_macro2::TokenStream {
    let into_app_impl = into_app::gen_into_app_impl_for_enum(name, attrs);
    let augment_app_fn = gen_augment_app_for_enum(variants);
    let from_argmatches_impl = from_argmatches::gen_from_argmatches_impl_for_enum(name);
    let from_subcommand = gen_from_subcommand(name, variants);
    let parse_fns = gen_parse_fns(name);

    quote! {
        #[allow(unused_variables)]
        impl ::clap::Clap for #name { }

        #into_app_impl

        #from_argmatches_impl

        #[allow(unused_variables, dead_code, unreachable_code)]
        #[doc(hidden)]
        impl #name {
            #augment_app_fn

            #from_subcommand

            #parse_fns

            pub fn is_subcommand() -> bool { true }
        }
    }
}

pub fn derive_clap(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
    use syn::Data::*;

    let struct_name = &input.ident;
    let inner_impl = match input.data {
        Struct(syn::DataStruct {
            fields: syn::Fields::Named(ref fields),
            ..
        }) => clap_impl_for_struct(struct_name, &fields.named, &input.attrs),
        Enum(ref e) => clap_impl_for_enum(struct_name, &e.variants, &input.attrs),
        _ => panic!("clap_derive only supports non-tuple structs and enums"),
    };

    quote!(#inner_impl)
}

fn gen_parse_fns(name: &syn::Ident) -> proc_macro2::TokenStream {
    quote! {
        pub fn parse() -> #name {
            use ::clap::{FromArgMatches, IntoApp};
            #name::from_argmatches(&#name::into_app().get_matches())
        }

        pub fn try_parse() -> ::std::result::Result<#name, ::clap::Error> {
            use ::clap::{FromArgMatches, IntoApp};
            Ok(#name::from_argmatches(&#name::into_app().try_get_matches()?))
        }

        pub fn parse_from<I, T>(itr: I) -> #name
        where
            I: ::std::iter::IntoIterator<Item = T>,
            T: Into<::std::ffi::OsString> + Clone {
            use ::clap::{FromArgMatches, IntoApp};
            #name::from_argmatches(&#name::into_app().get_matches_from(itr))
        }

        pub fn try_parse_from<I, T>(itr: I) -> ::std::result::Result<#name, ::clap::Error>
        where
            I: ::std::iter::IntoIterator<Item = T>,
            T: Into<::std::ffi::OsString> + Clone {
            use ::clap::{FromArgMatches, IntoApp};
            Ok(#name::from_argmatches(&#name::into_app().try_get_matches_from(itr)?))
        }
    }
}
<commit_msg>Added unreachable pub attribute to the parsing functions<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// 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.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use proc_macro2;
use syn;
use syn::punctuated;
use syn::token;

use derives;
use derives::attrs::{Attrs, Kind, Parser, Ty};
use derives::from_argmatches;
use derives::into_app;

/// Generate a block of code to add arguments/subcommands corresponding to
/// the `fields` to an app.
fn gen_app_augmentation(
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
    app_var: &syn::Ident,
) -> proc_macro2::TokenStream {
    let subcmds: Vec<_> = fields
        .iter()
        .filter_map(|field| {
            let attrs = Attrs::from_field(&field);
            if let Kind::Subcommand(ty) = attrs.kind() {
                let subcmd_type = match (ty, derives::sub_type(&field.ty)) {
                    (Ty::Option, Some(sub_type)) => sub_type,
                    _ => &field.ty,
                };
                let required = if ty == Ty::Option {
                    quote!()
                } else {
                    quote! {
                        let #app_var = #app_var.setting(
                            ::clap::AppSettings::SubcommandRequiredElseHelp
                        );
                    }
                };

                Some(quote! {
                    let #app_var = <#subcmd_type>::augment_app( #app_var );
                    #required
                })
            } else {
                None
            }
        })
        .collect();

    assert!(
        subcmds.len() <= 1,
        "cannot have more than one nested subcommand"
    );

    let args = fields.iter().filter_map(|field| {
        let attrs = Attrs::from_field(field);
        match attrs.kind() {
            Kind::Subcommand(_) => None,
            Kind::FlattenStruct => {
                let ty = &field.ty;
                Some(quote! {
                    let #app_var = <#ty>::augment_app(#app_var);
                    let #app_var = if <#ty>::is_subcommand() {
                        #app_var.setting(::clap::AppSettings::SubcommandRequiredElseHelp)
                    } else {
                        #app_var
                    };
                })
            }
            Kind::Arg(ty) => {
                let convert_type = match ty {
                    Ty::Vec | Ty::Option => derives::sub_type(&field.ty).unwrap_or(&field.ty),
                    _ => &field.ty,
                };

                let occurences = attrs.parser().0 == Parser::FromOccurrences;

                let validator = match *attrs.parser() {
                    (Parser::TryFromStr, ref f) => quote! {
                        .validator(|s| {
                            #f(&s)
                            .map(|_: #convert_type| ())
                            .map_err(|e| e.to_string())
                        })
                    },
                    (Parser::TryFromOsStr, ref f) => quote! {
                        .validator_os(|s| #f(&s).map(|_: #convert_type| ()))
                    },
                    _ => quote!(),
                };

                // @TODO remove unneccessary builders
                let modifier = match ty {
                    Ty::Bool => quote!(),
                    Ty::Option => quote!( .takes_value(true) #validator ),
                    Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ),
                    Ty::Other if occurences => quote!( .multiple_occurrences(true) ),
                    Ty::Other => {
                        let required = !attrs.has_method("default_value");
                        quote!( .takes_value(true).required(#required) #validator )
                    }
                };
                let methods = attrs.methods();
                let name = attrs.name();
                Some(quote! {
                    let #app_var = #app_var.arg(
                        ::clap::Arg::with_name(#name)
                            #modifier
                            #methods
                    );
                })
            }
        }
    });

    quote! {{
        #( #args )*
        #( #subcmds )*
        #app_var
    }}
}

fn gen_augment_app_fn(
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
) -> proc_macro2::TokenStream {
    let app_var = syn::Ident::new("app", proc_macro2::Span::call_site());
    let augmentation = gen_app_augmentation(fields, &app_var);
    quote! {
        pub fn augment_app<'b>(
            #app_var: ::clap::App<'b>
        ) -> ::clap::App<'b> {
            #augmentation
        }
    }
}

fn gen_augment_app_for_enum(
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
) -> proc_macro2::TokenStream {
    use syn::Fields::*;

    let subcommands = variants.iter().map(|variant| {
        let name = variant.ident.to_string();
        let attrs = Attrs::from_struct(&variant.attrs, name);
        let app_var = syn::Ident::new("subcommand", proc_macro2::Span::call_site());
        let arg_block = match variant.fields {
            Named(ref fields) => gen_app_augmentation(&fields.named, &app_var),
            Unit => quote!( #app_var ),
            Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
                let ty = &unnamed[0];
                quote! {
                    {
                        let #app_var = <#ty>::augment_app(#app_var);
                        if <#ty>::is_subcommand() {
                            #app_var.setting(
                                ::clap::AppSettings::SubcommandRequiredElseHelp
                            )
                        } else {
                            #app_var
                        }
                    }
                }
            }
            Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident),
        };

        let name = attrs.name();
        let from_attrs = attrs.methods();
        quote! {
            .subcommand({
                let #app_var = ::clap::App::new(#name);
                let #app_var = #arg_block;
                #app_var#from_attrs
            })
        }
    });

    quote! {
        pub fn augment_app<'b>(
            app: ::clap::App<'b>
        ) -> ::clap::App<'b> {
            app #( #subcommands )*
        }
    }
}

fn gen_from_subcommand(
    name: &syn::Ident,
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
) -> proc_macro2::TokenStream {
    use syn::Fields::*;

    let match_arms = variants.iter().map(|variant| {
        let attrs = Attrs::from_struct(&variant.attrs, variant.ident.to_string());
        let sub_name = attrs.name();
        let variant_name = &variant.ident;
        let constructor_block = match variant.fields {
            Named(ref fields) => from_argmatches::gen_constructor(&fields.named),
            Unit => quote!(),
            Unnamed(ref fields) if fields.unnamed.len() == 1 => {
                let ty = &fields.unnamed[0];
                quote!( ( <#ty as ::clap::FromArgMatches>::from_argmatches(matches) ) )
            }
            Unnamed(..) => panic!("{}: tuple enum are not supported", variant.ident),
        };

        quote! {
            (#sub_name, Some(matches)) =>
                Some(#name :: #variant_name #constructor_block)
        }
    });

    quote! {
        pub fn from_subcommand<'b>(
            sub: (&'b str, Option<&'b ::clap::ArgMatches>)
        ) -> Option<Self> {
            match sub {
                #( #match_arms ),*,
                _ => None
            }
        }
    }
}

fn clap_impl_for_struct(
    name: &syn::Ident,
    fields: &punctuated::Punctuated<syn::Field, token::Comma>,
    attrs: &[syn::Attribute],
) -> proc_macro2::TokenStream {
    let into_app_impl = into_app::gen_into_app_impl_for_struct(name, attrs);
    let augment_app_fn = gen_augment_app_fn(fields);
    let from_argmatches_impl = from_argmatches::gen_from_argmatches_impl_for_struct(name, fields);
    let parse_fns = gen_parse_fns(name);

    quote! {
        #[allow(unused_variables)]
        impl ::clap::Clap for #name { }

        #into_app_impl

        #from_argmatches_impl

        #[allow(dead_code, unreachable_code)]
        #[doc(hidden)]
        impl #name {
            #augment_app_fn

            #parse_fns

            pub fn is_subcommand() -> bool { false }
        }
    }
}

fn clap_impl_for_enum(
    name: &syn::Ident,
    variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
    attrs: &[syn::Attribute],
) -> proc_macro2::TokenStream {
    let into_app_impl = into_app::gen_into_app_impl_for_enum(name, attrs);
    let augment_app_fn = gen_augment_app_for_enum(variants);
    let from_argmatches_impl = from_argmatches::gen_from_argmatches_impl_for_enum(name);
    let from_subcommand = gen_from_subcommand(name, variants);
    let parse_fns = gen_parse_fns(name);

    quote! {
        #[allow(unused_variables)]
        impl ::clap::Clap for #name { }

        #into_app_impl

        #from_argmatches_impl

        #[allow(unused_variables, dead_code, unreachable_code)]
        #[doc(hidden)]
        impl #name {
            #augment_app_fn

            #from_subcommand

            #parse_fns

            pub fn is_subcommand() -> bool { true }
        }
    }
}

pub fn derive_clap(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
    use syn::Data::*;

    let struct_name = &input.ident;
    let inner_impl = match input.data {
        Struct(syn::DataStruct {
            fields: syn::Fields::Named(ref fields),
            ..
        }) => clap_impl_for_struct(struct_name, &fields.named, &input.attrs),
        Enum(ref e) => clap_impl_for_enum(struct_name, &e.variants, &input.attrs),
        _ => panic!("clap_derive only supports non-tuple structs and enums"),
    };

    quote!(#inner_impl)
}

fn gen_parse_fns(name: &syn::Ident) -> proc_macro2::TokenStream {
    quote! {
        #[allow(unreachable_pub)]
        pub fn parse() -> #name {
            use ::clap::{FromArgMatches, IntoApp};
            #name::from_argmatches(&#name::into_app().get_matches())
        }
        #[allow(unreachable_pub)]
        pub fn try_parse() -> ::std::result::Result<#name, ::clap::Error> {
            use ::clap::{FromArgMatches, IntoApp};
            Ok(#name::from_argmatches(&#name::into_app().try_get_matches()?))
        }
        #[allow(unreachable_pub)]
        pub fn parse_from<I, T>(itr: I) -> #name
        where
            I: ::std::iter::IntoIterator<Item = T>,
            T: Into<::std::ffi::OsString> + Clone {
            use ::clap::{FromArgMatches, IntoApp};
            #name::from_argmatches(&#name::into_app().get_matches_from(itr))
        }
        #[allow(unreachable_pub)]
        pub fn try_parse_from<I, T>(itr: I) -> ::std::result::Result<#name, ::clap::Error>
        where
            I: ::std::iter::IntoIterator<Item = T>,
            T: Into<::std::ffi::OsString> + Clone {
            use ::clap::{FromArgMatches, IntoApp};
            Ok(#name::from_argmatches(&#name::into_app().try_get_matches_from(itr)?))
        }
    }
}
<|endoftext|>