<commit_before>//! Frame parsers and data handlers
//!
//! Parse various frame types and return a fully-constructed object. See individual functions for
//! their descriptions.

use structs::{Frame, Callsign, InformationField};
use constants;

pub fn parse_frame(raw_frame: &[u8]) -> Frame {
    for win in raw_frame.windows(2) {
        if win == constants::ADDR_INFO_DELIM {
            return parse_frame_ax25(raw_frame);
        }
    }
    parse_frame_text(raw_frame)
}

pub fn parse_frame_text(raw_frame: &[u8]) -> Frame {
    // src>dest[,path]:info
    let src_dest:  usize = raw_frame.iter().position(|x| *x == b'>').unwrap();
    let path_info: usize = raw_frame.iter().position(|x| *x == b':').unwrap();

    let source: Callsign = parse_callsign_text(&raw_frame[..src_dest]);
    let mut dest: Callsign = Callsign::new();
    let info: InformationField = parse_info_field(&raw_frame[(path_info + 1)..]);

    // Full path slice
    let mut _path: Vec<Callsign> = Vec::new();
    let _paths: &[u8] = &raw_frame[src_dest + 1..path_info];
    for (ind, path) in _paths.split(|x| *x == b',').enumerate() {
        if ind == 0 {
            dest = parse_callsign_text(path);
        } else {
            _path.push(parse_callsign_text(path));
        }
    }
    Frame {
        source: source,
        destination: dest,
        path: _path,
        info: info
    }
}

pub fn parse_frame_ax25(raw_frame: &[u8]) -> Frame {
    let mut _frame: &[u8] = raw_frame;
    let mut kiss_call: bool = false;

    if _frame.starts_with(&[constants::KISS_DATA_FRAME]) {
        let mut ind: usize = 0;
        for (id, byt) in _frame.iter().enumerate() {
            if byt == &constants::KISS_DATA_FRAME {
                continue;
            } else {
                ind = id;
                break;
            }
        }
        _frame = &_frame[ind..];
        kiss_call = true;
    }
    if _frame.ends_with(&[constants::KISS_DATA_FRAME]) {
        let mut ind: usize = 0;
        for (id, byt) in _frame.iter().rev().enumerate() {
            if byt == &constants::KISS_DATA_FRAME {
                continue;
            } else {
                ind = id;
                break;
            }
        }
        _frame = &_frame[..(_frame.len() - ind)];
        kiss_call = true;
    }

    let mut info_pos: usize = 0;
    for win in _frame.windows(2) {
        if win == constants::ADDR_INFO_DELIM {
            break;
        }
        info_pos += 1;
    }
    let (frame_addressing, _) = _frame.split_at(info_pos);
    let (_, mut info_field)   = _frame.split_at(info_pos + 2);
    if info_field.ends_with(&[0xff, 0x07]) {
        info_field = &info_field[..(info_field.len() - 2)];
    }
    let dest: Callsign = parse_callsign_ax25(&frame_addressing, kiss_call);
    let src: Callsign = parse_callsign_ax25(&frame_addressing[7..], kiss_call);

    let mut path: Vec<Callsign> = Vec::new();
    for chunk in frame_addressing[14..].chunks(7) {
        path.push(parse_callsign_ax25(chunk, false));
    }

    Frame {
        source: src,
        destination: dest,
        path: path,
        info: parse_info_field(info_field),
    }
}

pub fn parse_callsign(raw_callsign: &[u8]) -> Callsign {
    parse_callsign_text(raw_callsign)
}

pub fn parse_callsign_text(raw_callsign: &[u8]) -> Callsign {
    let mut _callsign: String = String::from_utf8(raw_callsign.to_vec()).unwrap();
    let mut ssid: u8 = 0;
    let mut digi: bool = false;

    if _callsign.contains('*') {
        _callsign.trim_matches('*');
        digi = true;
    }
    let pos: Option<usize> = _callsign.find('-');
    match pos {
        Some(x) => {
            _callsign = {
                let (call, id): (&str, &str) = _callsign.split_at(x);
                ssid = id[1..].parse::<u8>().unwrap();
                call.to_string()
            };
        },
        None => {}
    }
    Callsign {
        callsign: _callsign,
        ssid: ssid,
        digi: digi
    }
}

pub fn parse_callsign_ax25(raw_callsign: &[u8], kiss_call: bool) -> Callsign {
    // based on ax.25 v2.0 protocol, NOT v2.2
    let mut _callsign: Vec<u8> = Vec::new();
    let mut digi: bool = false;

    for chunk in raw_callsign[..6].iter() {
        let mut _chunk = chunk & 0xFF;
        // shift 1 bit
        _chunk = _chunk >> 1;
        if (_chunk as char).is_alphanumeric() {
            _callsign.push(_chunk);
        }
    }

    // seventh byte is ssid or digi
    let seven_chunk: u8 = raw_callsign[6] & 0xFF;
    let ssid = (seven_chunk >> 1) & 0x0F;

    // fixme - see python module
    if kiss_call {
        if (seven_chunk >> 1 & 0x80) == 1 {
            digi = true;
        }
    } else {
        if (seven_chunk & 0x80) == 1 {
            digi = true;
        }
    }
    Callsign {
        callsign: String::from_utf8(_callsign).unwrap(),
        ssid: ssid,
        digi: digi
    }
}

pub fn parse_info_field(raw_data: &[u8]) -> InformationField {
    let first_byte: &u8 = raw_data.first().unwrap();
    let data_type: String = constants::DATA_TYPE_MAP.get(first_byte).unwrap().to_string();
    InformationField {
        data: raw_data.to_vec(),
        data_type: data_type,
        safe: true
    }
}

pub fn default_data_handler(data: &[u8], data_type: u8) -> InformationField {
    let _data_type: String = constants::DATA_TYPE_MAP.get(&data_type).unwrap().to_string();
    InformationField {
        data: data.to_vec(),
        data_type: _data_type,
        safe: false
    }
}
<commit_msg>Simplify match<commit_after>//! Frame parsers and data handlers
//!
//! Parse various frame types and return a fully-constructed object. See individual functions for
//! their descriptions.

use structs::{Frame, Callsign, InformationField};
use constants;

pub fn parse_frame(raw_frame: &[u8]) -> Frame {
    for win in raw_frame.windows(2) {
        if win == constants::ADDR_INFO_DELIM {
            return parse_frame_ax25(raw_frame);
        }
    }
    parse_frame_text(raw_frame)
}

pub fn parse_frame_text(raw_frame: &[u8]) -> Frame {
    // src>dest[,path]:info
    let src_dest:  usize = raw_frame.iter().position(|x| *x == b'>').unwrap();
    let path_info: usize = raw_frame.iter().position(|x| *x == b':').unwrap();

    let source: Callsign = parse_callsign_text(&raw_frame[..src_dest]);
    let mut dest: Callsign = Callsign::new();
    let info: InformationField = parse_info_field(&raw_frame[(path_info + 1)..]);

    // Full path slice
    let mut _path: Vec<Callsign> = Vec::new();
    let _paths: &[u8] = &raw_frame[src_dest + 1..path_info];
    for (ind, path) in _paths.split(|x| *x == b',').enumerate() {
        if ind == 0 {
            dest = parse_callsign_text(path);
        } else {
            _path.push(parse_callsign_text(path));
        }
    }
    Frame {
        source: source,
        destination: dest,
        path: _path,
        info: info
    }
}

pub fn parse_frame_ax25(raw_frame: &[u8]) -> Frame {
    let mut _frame: &[u8] = raw_frame;
    let mut kiss_call: bool = false;

    if _frame.starts_with(&[constants::KISS_DATA_FRAME]) {
        let mut ind: usize = 0;
        for (id, byt) in _frame.iter().enumerate() {
            if byt == &constants::KISS_DATA_FRAME {
                continue;
            } else {
                ind = id;
                break;
            }
        }
        _frame = &_frame[ind..];
        kiss_call = true;
    }
    if _frame.ends_with(&[constants::KISS_DATA_FRAME]) {
        let mut ind: usize = 0;
        for (id, byt) in _frame.iter().rev().enumerate() {
            if byt == &constants::KISS_DATA_FRAME {
                continue;
            } else {
                ind = id;
                break;
            }
        }
        _frame = &_frame[..(_frame.len() - ind)];
        kiss_call = true;
    }

    let mut info_pos: usize = 0;
    for win in _frame.windows(2) {
        if win == constants::ADDR_INFO_DELIM {
            break;
        }
        info_pos += 1;
    }
    let (frame_addressing, _) = _frame.split_at(info_pos);
    let (_, mut info_field)   = _frame.split_at(info_pos + 2);
    if info_field.ends_with(&[0xff, 0x07]) {
        info_field = &info_field[..(info_field.len() - 2)];
    }
    let dest: Callsign = parse_callsign_ax25(&frame_addressing, kiss_call);
    let src: Callsign = parse_callsign_ax25(&frame_addressing[7..], kiss_call);

    let mut path: Vec<Callsign> = Vec::new();
    for chunk in frame_addressing[14..].chunks(7) {
        path.push(parse_callsign_ax25(chunk, false));
    }

    Frame {
        source: src,
        destination: dest,
        path: path,
        info: parse_info_field(info_field),
    }
}

pub fn parse_callsign(raw_callsign: &[u8]) -> Callsign {
    parse_callsign_text(raw_callsign)
}

pub fn parse_callsign_text(raw_callsign: &[u8]) -> Callsign {
    let mut _callsign: String = String::from_utf8(raw_callsign.to_vec()).unwrap();
    let mut ssid: u8 = 0;
    let mut digi: bool = false;

    if _callsign.contains('*') {
        _callsign.trim_matches('*');
        digi = true;
    }
    match _callsign.find('-') {
        Some(x) => {
            _callsign = {
                let (call, id): (&str, &str) = _callsign.split_at(x);
                ssid = id[1..].parse::<u8>().unwrap();
                call.to_string()
            };
        },
        None => {}
    }
    Callsign {
        callsign: _callsign,
        ssid: ssid,
        digi: digi
    }
}

pub fn parse_callsign_ax25(raw_callsign: &[u8], kiss_call: bool) -> Callsign {
    // based on ax.25 v2.0 protocol, NOT v2.2
    let mut _callsign: Vec<u8> = Vec::new();
    let mut digi: bool = false;

    for chunk in raw_callsign[..6].iter() {
        let mut _chunk = chunk & 0xFF;
        // shift 1 bit
        _chunk = _chunk >> 1;
        if (_chunk as char).is_alphanumeric() {
            _callsign.push(_chunk);
        }
    }

    // seventh byte is ssid or digi
    let seven_chunk: u8 = raw_callsign[6] & 0xFF;
    let ssid = (seven_chunk >> 1) & 0x0F;

    // fixme - see python module
    if kiss_call {
        if (seven_chunk >> 1 & 0x80) == 1 {
            digi = true;
        }
    } else {
        if (seven_chunk & 0x80) == 1 {
            digi = true;
        }
    }
    Callsign {
        callsign: String::from_utf8(_callsign).unwrap(),
        ssid: ssid,
        digi: digi
    }
}

pub fn parse_info_field(raw_data: &[u8]) -> InformationField {
    let first_byte: &u8 = raw_data.first().unwrap();
    let data_type: String = constants::DATA_TYPE_MAP.get(first_byte).unwrap().to_string();
    InformationField {
        data: raw_data.to_vec(),
        data_type: data_type,
        safe: true
    }
}

pub fn default_data_handler(data: &[u8], data_type: u8) -> InformationField {
    let _data_type: String = constants::DATA_TYPE_MAP.get(&data_type).unwrap().to_string();
    InformationField {
        data: data.to_vec(),
        data_type: _data_type,
        safe: false
    }
}
<|endoftext|>


<commit_before>use syntax::abi;
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub trait Generate {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item>;
}

impl Generate for Block {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        match self {
            Block::Describe(describe) => describe.generate(cx, up),
            Block::It(it) => it.generate(cx, up),
            Block::Bench(bench) => bench.generate(cx, up)
        }
    }
}

impl Generate for Describe {
    fn generate(mut self,
                cx: &mut ExtCtxt,
                up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        if let Some(ref up) = up {
            self.before = up.before.iter()
                .chain(self.before.iter())
                .cloned()
                .collect();
            self.after = self.after.iter()
                .chain(up.after.iter())
                .cloned()
                .collect();
        }

        let mut items = self.blocks.iter().map(|block| {
            block.clone().generate(cx, Some(&self))
        }).collect::<Vec<_>>();

        items.push(quote_item!(cx, pub use super::*;).unwrap());

        cx.item_mod(DUMMY_SP,
                    DUMMY_SP,
                    name,
                    vec![],
                    items)
    }
}

impl Generate for It {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);
        let attrs = vec![quote_attr!(cx, #[test])];

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();

        cx.item(DUMMY_SP, name, attrs,
                ast::ItemFn(
                    P(ast::FnDecl {
                        inputs: vec![],
                        output: ast::DefaultReturn(DUMMY_SP),
                        variadic: false
                    }),
                    ast::Unsafety::Normal,
                    ast::Constness::NotConst,
                    abi::Rust,
                    ast_util::empty_generics(),
                    block.fold(head, merge_blocks)
                ))
    }
}

impl Generate for Bench {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let attrs = vec![quote_attr!(cx, #[bench])];

        let args = vec![
            cx.arg(DUMMY_SP, self.ident, quote_ty!(cx, &mut ::test::Bencher))
        ];

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();

        cx.item(DUMMY_SP, name, attrs,
                ast::ItemFn(
                    P(ast::FnDecl {
                        inputs: args,
                        output: ast::DefaultReturn(DUMMY_SP),
                        variadic: false
                    }),
                    ast::Unsafety::Normal,
                    ast::Constness::NotConst,
                    abi::Rust,
                    ast_util::empty_generics(),
                    block.fold(head, merge_blocks)
                ))
    }
}

fn merge_blocks(left: P<ast::Block>, right: P<ast::Block>) -> P<ast::Block> {
    use std::ops::Deref;

    let mut stmts = left.stmts.clone();
    stmts.extend(right.stmts.clone());

    P(ast::Block {
        stmts: stmts,
        ..left.deref().clone()
    })
}
<commit_msg>Refactor `impl Generate for It` using `quote_item!`.<commit_after>use syntax::abi;
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub trait Generate {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item>;
}

impl Generate for Block {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        match self {
            Block::Describe(describe) => describe.generate(cx, up),
            Block::It(it) => it.generate(cx, up),
            Block::Bench(bench) => bench.generate(cx, up)
        }
    }
}

impl Generate for Describe {
    fn generate(mut self,
                cx: &mut ExtCtxt,
                up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        if let Some(ref up) = up {
            self.before = up.before.iter()
                .chain(self.before.iter())
                .cloned()
                .collect();
            self.after = self.after.iter()
                .chain(up.after.iter())
                .cloned()
                .collect();
        }

        let mut items = self.blocks.iter().map(|block| {
            block.clone().generate(cx, Some(&self))
        }).collect::<Vec<_>>();

        items.push(quote_item!(cx, pub use super::*;).unwrap());

        cx.item_mod(DUMMY_SP,
                    DUMMY_SP,
                    name,
                    vec![],
                    items)
    }
}

impl Generate for It {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();
        let block = block.fold(head, merge_blocks);

        quote_item!(cx, #[test] fn $name() { $block }).unwrap()
    }
}

impl Generate for Bench {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let attrs = vec![quote_attr!(cx, #[bench])];

        let args = vec![
            cx.arg(DUMMY_SP, self.ident, quote_ty!(cx, &mut ::test::Bencher))
        ];

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();

        cx.item(DUMMY_SP, name, attrs,
                ast::ItemFn(
                    P(ast::FnDecl {
                        inputs: args,
                        output: ast::DefaultReturn(DUMMY_SP),
                        variadic: false
                    }),
                    ast::Unsafety::Normal,
                    ast::Constness::NotConst,
                    abi::Rust,
                    ast_util::empty_generics(),
                    block.fold(head, merge_blocks)
                ))
    }
}

fn merge_blocks(left: P<ast::Block>, right: P<ast::Block>) -> P<ast::Block> {
    use std::ops::Deref;

    let mut stmts = left.stmts.clone();
    stmts.extend(right.stmts.clone());

    P(ast::Block {
        stmts: stmts,
        ..left.deref().clone()
    })
}
<|endoftext|>


<commit_before>use std::fmt;

use game_state::{GameState, GameMode};
use command::{CommandProcessor, HelpInfo};

pub enum Item {
  Torch,
}

impl Item {
  pub fn price(&self) -> i32 {
    match *self {
      Item::Torch => 15
    }
  }
}

impl fmt::Display for Item {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Item::Torch => "a flaming torch",
    })
  }
}

pub enum InventoryCommand {
  Buy(Item),
  Quit,
}

impl CommandProcessor<InventoryCommand> for InventoryCommand {
  fn prompt() -> &'static str { "What do you want to buy? " }

  fn get_help() -> Vec<HelpInfo> {
    let desc = |item: Item| format!("buy {} (${})", item, item.price());

    HelpInfo::string_list(vec![
      ('1', desc(Item::Torch)),
      ('0', String::from("continue adventure")),
    ])
  }

  fn from_char(c: char) -> Option<InventoryCommand> {
    match c {
      '1' => Some(InventoryCommand::Buy(Item::Torch)),
      _ => Some(InventoryCommand::Quit),
    }
  }
}

impl<'a> GameState<'a> {
  pub fn tick_inventory_mode(&mut self) {
    if self.show_desc {
      println!("Provisions & inventory\n");
      self.print_wealth();
      println!("");
      InventoryCommand::show_help();
      println!("");
      self.show_desc = false;
    }

    if let Some(cmd) = InventoryCommand::get() {
      match cmd {
        InventoryCommand::Buy(item) => {
          let price = item.price();
          if price > self.wealth {
            println!("You don't have enough money to buy that.");
          } else {
            self.wealth -= price;
            println!("You bought {}.", item);
            self.print_wealth();
            match item {
              Item::Torch => self.light = true,
            }
          }
          println!("");
        },
        InventoryCommand::Quit => { self.set_mode(GameMode::Primary) },
      }
    }
  }
}
<commit_msg>Minor readability improvement<commit_after>use std::fmt;

use game_state::{GameState, GameMode};
use command::{CommandProcessor, HelpInfo};

pub enum Item {
  Torch,
}

impl Item {
  pub fn price(&self) -> i32 {
    match *self {
      Item::Torch => 15
    }
  }
}

impl fmt::Display for Item {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Item::Torch => "a flaming torch",
    })
  }
}

pub enum InventoryCommand {
  Buy(Item),
  Quit,
}

impl CommandProcessor<InventoryCommand> for InventoryCommand {
  fn prompt() -> &'static str { "What do you want to buy? " }

  fn get_help() -> Vec<HelpInfo> {
    let buy = |item: Item| format!("buy {} (${})", item, item.price());

    HelpInfo::string_list(vec![
      ('1', buy(Item::Torch)),
      ('0', String::from("continue adventure")),
    ])
  }

  fn from_char(c: char) -> Option<InventoryCommand> {
    match c {
      '1' => Some(InventoryCommand::Buy(Item::Torch)),
      _ => Some(InventoryCommand::Quit),
    }
  }
}

impl<'a> GameState<'a> {
  pub fn tick_inventory_mode(&mut self) {
    if self.show_desc {
      println!("Provisions & inventory\n");
      self.print_wealth();
      println!("");
      InventoryCommand::show_help();
      println!("");
      self.show_desc = false;
    }

    if let Some(cmd) = InventoryCommand::get() {
      match cmd {
        InventoryCommand::Buy(item) => {
          let price = item.price();
          if price > self.wealth {
            println!("You don't have enough money to buy that.");
          } else {
            self.wealth -= price;
            println!("You bought {}.", item);
            self.print_wealth();
            match item {
              Item::Torch => self.light = true,
            }
          }
          println!("");
        },
        InventoryCommand::Quit => { self.set_mode(GameMode::Primary) },
      }
    }
  }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

// A network
#[derive(Debug)]
pub enum IpNetwork {
    V4(Ipv4Network),
    V6(Ipv6Network),
}

pub struct Ipv4Network {
    addr: Ipv4Addr,
    prefix: u8,
}

pub struct Ipv6Network {
    addr: Ipv6Addr,
    prefix: u8,
}

impl Ipv4Network {
    fn new(addr: Ipv4Addr, prefix: u8) -> Ipv4Network {
        Ipv4Network { addr: addr, prefix: prefix }
    }

    fn ip(&self) -> &Ipv4Addr {
        &(self.addr)
    }

    fn prefix(&self) -> u8 {
        self.prefix
    }

    fn mask_int(&self) -> u32 {
        let prefix = self.prefix;
        !(0xffffffff >> prefix)
    }

    fn mask(&self) -> Ipv4Addr {
        let mask = self.mask_int();
        Ipv4Network::int_to_ip(mask)
    }

    fn network_int(&self) -> u32 {
        Ipv4Network::ip_to_int(*(self.ip())) & self.mask_int()
    }

    fn network(&self) -> Ipv4Addr {
        Ipv4Network::int_to_ip(self.network_int())
    }

    fn ip_to_int(addr: Ipv4Addr) -> u32 {
        let ip = addr.octets();
        ((ip[0] as u32) << 24) + ((ip[1] as u32) << 16) + ((ip[2] as u32) << 8) + (ip[3] as u32)
    }

    fn int_to_ip(ip: u32) -> Ipv4Addr {
        Ipv4Addr::new(((ip >> 24) & 0xff) as u8, ((ip >> 16) & 0xff) as u8,
                     ((ip >> 8) & 0xff) as u8, (ip & 0xff) as u8)
    }
}

impl Ipv6Network {
    fn new(addr: Ipv6Addr, prefix: u8) -> Ipv6Network {
        Ipv6Network { addr: addr, prefix: prefix }
    }

    fn ip(&self) -> &Ipv6Addr {
        &(self.addr)
    }

    fn prefix(&self) -> u8 {
        self.prefix
    }
}

impl IpNetwork {
    pub fn new(ip: IpAddr, prefix: u8) -> IpNetwork {
        match ip {
            IpAddr::V4(a) => IpNetwork::V4(Ipv4Network::new(a, prefix)),
            IpAddr::V6(a) => IpNetwork::V6(Ipv6Network::new(a, prefix)),
        }
    }

    pub fn ip(&self) -> IpAddr {
        match *self {
            IpNetwork::V4(ref a) => IpAddr::V4(*a.ip()),
            IpNetwork::V6(ref a) => IpAddr::V6(*a.ip()),
        }
    }

    pub fn prefix(&self) -> u8 {
        match *self {
            IpNetwork::V4(ref a) => a.prefix(),
            IpNetwork::V6(ref a) => a.prefix(),
        }
    }
}

impl fmt::Debug for Ipv4Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

impl fmt::Debug for Ipv6Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

#[cfg(test)]
mod test {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use super::*;

    #[test]
    fn create_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(77, 88, 21, 11), 24);
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn create_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24);
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn mask_int_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 29);
        assert_eq!(cidr.mask_int(), 4294967288);
    }

    #[test]
    fn mask_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 29);
        assert_eq!(cidr.mask(), Ipv4Addr::new(255, 255, 255, 248));
    }

    #[test]
    fn network_int_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 25);
        assert_eq!(cidr.network_int(), 1249764096);
    }

    #[test]
    fn network_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 25);
        assert_eq!(cidr.network(), Ipv4Addr::new(74, 125, 227, 0));
    }
}
<commit_msg>Make everything pub<commit_after>use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

// A network
#[derive(Debug)]
pub enum IpNetwork {
    V4(Ipv4Network),
    V6(Ipv6Network),
}

pub struct Ipv4Network {
    addr: Ipv4Addr,
    prefix: u8,
}

pub struct Ipv6Network {
    addr: Ipv6Addr,
    prefix: u8,
}

impl Ipv4Network {
    pub fn new(addr: Ipv4Addr, prefix: u8) -> Ipv4Network {
        Ipv4Network { addr: addr, prefix: prefix }
    }

    pub fn ip(&self) -> &Ipv4Addr {
        &(self.addr)
    }

    pub fn prefix(&self) -> u8 {
        self.prefix
    }

    pub fn mask_int(&self) -> u32 {
        let prefix = self.prefix;
        !(0xffffffff >> prefix)
    }

    pub fn mask(&self) -> Ipv4Addr {
        let mask = self.mask_int();
        Ipv4Network::int_to_ip(mask)
    }

    pub fn network_int(&self) -> u32 {
        Ipv4Network::ip_to_int(*(self.ip())) & self.mask_int()
    }

    pub fn network(&self) -> Ipv4Addr {
        Ipv4Network::int_to_ip(self.network_int())
    }

    fn ip_to_int(addr: Ipv4Addr) -> u32 {
        let ip = addr.octets();
        ((ip[0] as u32) << 24) + ((ip[1] as u32) << 16) + ((ip[2] as u32) << 8) + (ip[3] as u32)
    }

    fn int_to_ip(ip: u32) -> Ipv4Addr {
        Ipv4Addr::new(((ip >> 24) & 0xff) as u8, ((ip >> 16) & 0xff) as u8,
                     ((ip >> 8) & 0xff) as u8, (ip & 0xff) as u8)
    }
}

impl Ipv6Network {
    pub fn new(addr: Ipv6Addr, prefix: u8) -> Ipv6Network {
        Ipv6Network { addr: addr, prefix: prefix }
    }

    pub fn ip(&self) -> &Ipv6Addr {
        &(self.addr)
    }

    pub fn prefix(&self) -> u8 {
        self.prefix
    }
}

impl IpNetwork {
    pub fn new(ip: IpAddr, prefix: u8) -> IpNetwork {
        match ip {
            IpAddr::V4(a) => IpNetwork::V4(Ipv4Network::new(a, prefix)),
            IpAddr::V6(a) => IpNetwork::V6(Ipv6Network::new(a, prefix)),
        }
    }

    pub fn ip(&self) -> IpAddr {
        match *self {
            IpNetwork::V4(ref a) => IpAddr::V4(*a.ip()),
            IpNetwork::V6(ref a) => IpAddr::V6(*a.ip()),
        }
    }

    pub fn prefix(&self) -> u8 {
        match *self {
            IpNetwork::V4(ref a) => a.prefix(),
            IpNetwork::V6(ref a) => a.prefix(),
        }
    }
}

impl fmt::Debug for Ipv4Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

impl fmt::Debug for Ipv6Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

#[cfg(test)]
mod test {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use super::*;

    #[test]
    fn create_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(77, 88, 21, 11), 24);
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn create_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24);
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn mask_int_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 29);
        assert_eq!(cidr.mask_int(), 4294967288);
    }

    #[test]
    fn mask_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 29);
        assert_eq!(cidr.mask(), Ipv4Addr::new(255, 255, 255, 248));
    }

    #[test]
    fn network_int_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 25);
        assert_eq!(cidr.network_int(), 1249764096);
    }

    #[test]
    fn network_v4() {
        let cidr = Ipv4Network::new(Ipv4Addr::new(74, 125, 227, 0), 25);
        assert_eq!(cidr.network(), Ipv4Addr::new(74, 125, 227, 0));
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! An Fleetspeak client connector library.
//!
//! This library exposes a set of functions for writing client-side Fleetspeak
//! services. Each of these functions operates on a global connection object
//! that is lazily established. If this global connection cannot be established,
//! the library will panic (because without this connection Fleetspeak will shut
//! down the service anyway).
//!
//! Note that each service should send startup information upon its inception
//! and continue to heartbeat from time to time to notify the Fleetspeak client
//! that it did not get stuck.

mod connection;
mod error;

use std::fs::File;
use std::sync::Mutex;
use std::time::Duration;

use lazy_static::lazy_static;

pub use self::connection::Packet;
pub use self::error::{ReadError, WriteError};

/// Sends a heartbeat information to the standard Fleetspeak client.
///
/// All client services should heartbeat from time to time. Otherwise, from the
/// Fleetspeak perspective, the service is unresponsive and should be restarted.
///
/// The exact frequency of the required heartbeat is defined in the service
/// configuration file.
pub fn heartbeat() -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::heartbeat(buf))
}

/// Sends the startup information to the standard Fleetspeak client.
///
/// All clients are required to send this information on startup. If the client
/// does not receive this information quickly enough, the service will be
/// killed.
///
/// The `version` string should contain a self-reported version of the service.
/// This data is used primarily for statistics.
pub fn startup(version: &str) -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::startup(buf, version))
}

/// Sends the message to the Fleetspeak server through the standard client.
///
/// The message is sent to the server-side `service` and tagged with the `kind`
/// type. Note that this message type is rather irrelevant for Fleetspeak and
/// it is up to the service what to do with this information.
pub fn send<M>(packet: Packet<M>) -> Result<(), WriteError>
where
    M: prost::Message,
{
    locked(&CONNECTION.output, |buf| self::connection::send(buf, packet))
}

/// Receives the message from the Fleetspeak server.
///
/// This function will block until there is a message to be read from the input.
/// Note that in particular it means your service will be unable to heartbeat
/// properly. If you are not expecting the message to arrive quickly, you should
/// use [`collect`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`collect`]: fn.collect.html
pub fn receive<M>() -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default,
{
    locked(&CONNECTION.input, |buf| self::connection::receive(buf))
}

/// Collects the message from the Fleetspeak server.
///
/// Unlike [`receive`], `collect` will send heartbeat signals while polling for
/// the message at the specified `rate`.
///
/// This function is useful in the main loop of your service when it is not
/// supposed to do anything until a request from the server arrives. If your
/// service is actually awaiting for a specific message to come, you should
/// use [`receive`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`receive`]: fn.receive.html
pub fn collect<M>(rate: Duration) -> Result<Packet<M>, std::io::Error>
where
    M: prost::Message + Default + 'static,
{
    let (sender, receiver) = std::sync::mpsc::channel();

    std::thread::spawn(move || {
        let packet = receive();

        // We do not care whether the packet was sent, because this call can
        // fail only if the receiver closed itself. Since the receiver loops
        // indefinitely, it closes only in case there was a heartbeat error. But
        // at that point we are no longer interested in the packet.
        let _ = sender.send(packet);
    });

    loop {
        use std::sync::mpsc::TryRecvError::*;

        // The sender will block indefinitely until there is a message to pick,
        // so the disconnection branch is not possible.
        match receiver.try_recv() {
            Ok(packet) => return Ok(packet?),
            Err(Empty) => (),
            Err(Disconnected) => panic!(),
        }

        heartbeat()?;
        std::thread::sleep(rate);
    }
}

struct Connection {
    input: Mutex<File>,
    output: Mutex<File>,
}

lazy_static! {
    static ref CONNECTION: Connection = {
        let mut input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let mut output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        use self::connection::handshake;
        handshake(&mut input, &mut output).expect("handshake failure");

        Connection {
            input: Mutex::new(input),
            output: Mutex::new(output),
        }
    };
}

/// Executes the given function with a file extracted from the mutex.
///
/// It might happen that the mutex becomes poisoned and this call will panic in
/// result. This should not be a problem in practice, because mutex poisoning
/// is a result of one of the threads being aborted. In case of a such scenario,
/// it is likely the service needs to be restarted anyway.
fn locked<F, T, E>(mutex: &Mutex<File>, f: F) -> Result<T, E>
where
    F: FnOnce(&mut File) -> Result<T, E>
{
    let mut file = mutex.lock().expect("poisoned connection mutex");
    f(&mut file)
}

/// Opens a file object pointed by an environment variable.
///
/// Note that this function will panic if the environment variable `var` is not
/// a valid file descriptor (in which case the library cannot be initialized and
/// the service is unlikely to work anyway).
fn open(var: &str) -> File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}
<commit_msg>Improve wording in the documentation comments.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! An Fleetspeak client connector library.
//!
//! This library exposes a set of functions for writing client-side Fleetspeak
//! services. Each of these functions operates on a global connection object
//! that is lazily established. If this global connection cannot be established,
//! the library will panic (because without this connection Fleetspeak will shut
//! the service down anyway).
//!
//! Note that each service should send startup information upon its inception
//! and continue to heartbeat from time to time to notify the Fleetspeak client
//! that it did not get stuck.

mod connection;
mod error;

use std::fs::File;
use std::sync::Mutex;
use std::time::Duration;

use lazy_static::lazy_static;

pub use self::connection::Packet;
pub use self::error::{ReadError, WriteError};

/// Sends a heartbeat signal to the Fleetspeak client.
///
/// All client services should heartbeat from time to time. Otherwise, from the
/// Fleetspeak perspective, the service is unresponsive and should be restarted.
///
/// The exact frequency of the required heartbeat is defined in the service
/// configuration file.
pub fn heartbeat() -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::heartbeat(buf))
}

/// Sends a system message with startup information to the Fleetspeak client.
///
/// All clients are required to send this information on startup. If the client
/// does not receive this information quickly enough, the service will be
/// killed.
///
/// The `version` string should contain a self-reported version of the service.
/// This data is used primarily for statistics.
pub fn startup(version: &str) -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::startup(buf, version))
}

/// Sends the message to the Fleetspeak server.
///
/// The message is delivered to the server-side service as specified by the
/// packet and optionally tagged with a type if specified. This optional message
/// type is irrelevant for Fleetspeak but might be useful for the service the
/// message is delivered to.
///
/// In case of any I/O failure or malformed message (e.g. due to encoding
/// problems), an error is reported.
pub fn send<M>(packet: Packet<M>) -> Result<(), WriteError>
where
    M: prost::Message,
{
    locked(&CONNECTION.output, |buf| self::connection::send(buf, packet))
}

/// Receives the message from the Fleetspeak server.
///
/// This function will block until there is a message to be read from the input.
/// Note that in particular it means your service will be unable to heartbeat
/// properly. If you are not expecting the message to arrive quickly, you should
/// use [`collect`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`collect`]: fn.collect.html
pub fn receive<M>() -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default,
{
    locked(&CONNECTION.input, |buf| self::connection::receive(buf))
}

/// Collects the message from the Fleetspeak server.
///
/// Unlike [`receive`], `collect` will send heartbeat signals while polling for
/// the message at the specified `rate`.
///
/// This function is useful in the main loop of your service when it is not
/// supposed to do anything until a request from the server arrives. If your
/// service is actually awaiting for a specific message to come, you should
/// use [`receive`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`receive`]: fn.receive.html
pub fn collect<M>(rate: Duration) -> Result<Packet<M>, std::io::Error>
where
    M: prost::Message + Default + 'static,
{
    let (sender, receiver) = std::sync::mpsc::channel();

    std::thread::spawn(move || {
        let packet = receive();

        // We do not care whether the packet was sent, because this call can
        // fail only if the receiver closed itself. Since the receiver loops
        // indefinitely, it closes only in case there was a heartbeat error. But
        // at that point we are no longer interested in the packet.
        let _ = sender.send(packet);
    });

    loop {
        use std::sync::mpsc::TryRecvError::*;

        // The sender will block indefinitely until there is a message to pick,
        // so the disconnection branch is not possible.
        match receiver.try_recv() {
            Ok(packet) => return Ok(packet?),
            Err(Empty) => (),
            Err(Disconnected) => panic!(),
        }

        heartbeat()?;
        std::thread::sleep(rate);
    }
}

struct Connection {
    input: Mutex<File>,
    output: Mutex<File>,
}

lazy_static! {
    static ref CONNECTION: Connection = {
        let mut input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let mut output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        use self::connection::handshake;
        handshake(&mut input, &mut output).expect("handshake failure");

        Connection {
            input: Mutex::new(input),
            output: Mutex::new(output),
        }
    };
}

/// Executes the given function with a file extracted from the mutex.
///
/// It might happen that the mutex becomes poisoned and this call will panic in
/// result. This should not be a problem in practice, because mutex poisoning
/// is a result of one of the threads being aborted. In case of a such scenario,
/// it is likely the service needs to be restarted anyway.
fn locked<F, T, E>(mutex: &Mutex<File>, f: F) -> Result<T, E>
where
    F: FnOnce(&mut File) -> Result<T, E>
{
    let mut file = mutex.lock().expect("poisoned connection mutex");
    f(&mut file)
}

/// Opens a file object pointed by an environment variable.
///
/// Note that this function will panic if the environment variable `var` is not
/// a valid file descriptor (in which case the library cannot be initialized and
/// the service is unlikely to work anyway).
fn open(var: &str) -> File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}
<|endoftext|>


<commit_before>use std::f64::NAN;

use js_types::js_var::JsVar;
use js_types::js_var::JsPtrEnum;
use js_types::js_var::JsType::*;

pub trait AsBool {
    fn as_bool(&self) -> bool;
}

impl AsBool for JsVar {
    fn as_bool(&self) -> bool {
        match self.t {
            JsBool(b) => b,
            JsUndef=> false,
            JsNull => false,
            JsNum(n) =>
                if n == 0.0f64 || n == -0.0f64 || n.is_nan() {
                    false
                } else {
                    true
                },
            JsPtr(_) => true, // TODO - this is incorrect
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        false
            //    } else {
            //        true
            //    },
            //JsSymbol(_) => true,
            //JsObject | JsError(_) | JsFunction(_, _, _) => true,
        }
    }
}

pub trait AsNumber {
    fn as_number(&self) -> f64;
}

impl AsNumber for JsVar {
    fn as_number(&self) -> f64 {
        match self.t {
            JsBool(b) => if b { 1f64 } else { 0f64 },
            JsUndef => NAN,
            JsNull => 0f64,
            JsNum(n) => n,
            JsPtr(_) => NAN,
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        JsNumber(0f64)
            //    } else {
            //        let num = s.parse();
            //        match num {
            //            Ok(n)  => JsNumber(n),
            //            Err(_) => JsNumber(NAN),
            //        }
            //    },
            //JsSymbol(_) => panic!("Cannot convert a Symbol to a number."),
            //JsObject | JsError(_) | JsFunction(_, _, _) => JsNumber(NAN),
        }
    }
}

fn ptr_as_str(ptr: &JsPtrEnum) -> String {
    match ptr {
        &JsPtrEnum::JsSym(ref s) => format!("Symbol({})", s),
        &JsPtrEnum::JsStr(ref s) => s.text.to_owned(),

        // TODO: Check object's `toString` method
        &JsPtrEnum::JsObj(ref s) => String::from("[object Object]"),

        // TODO: A function's string representation is apparently the string of source code that
        // created it; the AST doesn't currently support this, so we'll need to do some
        // restructuring before we can support this.
        &JsPtrEnum::JsFn(_) => String::from("[function]"),
    }
}

pub trait AsString {
    fn as_string(&self, ptr: Option<&JsPtrEnum>) -> String {
        let s = match self.t {
            JsBool(true) => "true"
            JsBool(false) => "false"
            JsUndef => "undefined",
            JsNull => "null",
            JsNum(n) => return format!("{}", n),
            JsPtr(_) => return ptr_as_str(ptr.expect("Invalid pointer")),
        };

        String::from(s)
    }
}
<commit_msg>fix mistakes in AsString<commit_after>use std::f64::NAN;

use js_types::js_var::JsVar;
use js_types::js_var::JsPtrEnum;
use js_types::js_var::JsType::*;

pub trait AsBool {
    fn as_bool(&self) -> bool;
}

impl AsBool for JsVar {
    fn as_bool(&self) -> bool {
        match self.t {
            JsBool(b) => b,
            JsUndef=> false,
            JsNull => false,
            JsNum(n) =>
                if n == 0.0f64 || n == -0.0f64 || n.is_nan() {
                    false
                } else {
                    true
                },
            JsPtr(_) => true, // TODO - this is incorrect
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        false
            //    } else {
            //        true
            //    },
            //JsSymbol(_) => true,
            //JsObject | JsError(_) | JsFunction(_, _, _) => true,
        }
    }
}

pub trait AsNumber {
    fn as_number(&self) -> f64;
}

impl AsNumber for JsVar {
    fn as_number(&self) -> f64 {
        match self.t {
            JsBool(b) => if b { 1f64 } else { 0f64 },
            JsUndef => NAN,
            JsNull => 0f64,
            JsNum(n) => n,
            JsPtr(_) => NAN,
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        JsNumber(0f64)
            //    } else {
            //        let num = s.parse();
            //        match num {
            //            Ok(n)  => JsNumber(n),
            //            Err(_) => JsNumber(NAN),
            //        }
            //    },
            //JsSymbol(_) => panic!("Cannot convert a Symbol to a number."),
            //JsObject | JsError(_) | JsFunction(_, _, _) => JsNumber(NAN),
        }
    }
}

pub trait AsString {
    fn as_string(&self, ptr: Option<&JsPtrEnum>) -> String;
}

fn ptr_as_str(ptr: &JsPtrEnum) -> String {
    match ptr {
        &JsPtrEnum::JsSym(ref s) => format!("Symbol({})", s),
        &JsPtrEnum::JsStr(ref s) => s.text.to_owned(),

        // TODO: Check object's `toString` method
        &JsPtrEnum::JsObj(ref s) => String::from("[object Object]"),

        // TODO: A function's string representation is apparently the string of source code that
        // created it; the AST doesn't currently support this, so we'll need to do some
        // restructuring before we can support this.
        &JsPtrEnum::JsFn(_) => String::from("[function]"),
    }
}

impl AsString for JsVar {
    fn as_string(&self, ptr: Option<&JsPtrEnum>) -> String {
        let s = match self.t {
            JsBool(true) => "true",
            JsBool(false) => "false",
            JsUndef => "undefined",
            JsNull => "null",
            JsNum(n) => return format!("{}", n),
            JsPtr(_) => return ptr_as_str(ptr.expect("Invalid pointer")),
        };

        String::from(s)
    }
}
<|endoftext|>


<commit_before>#![feature(inner_deref)]

extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::{self, Write};
use std::process::Command;
use std::fs::{self, File};

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [subcommand] [options] [--] [<opts>...]

Subcommands:
    run                      Run binaries (default)
    test                     Run tests
    setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MiriCommand {
    Run,
    Test,
    Setup,
}

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn show_error(msg: String) -> ! {
    eprintln!("fatal error: {}", msg);
    std::process::exit(1)
}

fn get_arg_flag_value(name: &str) -> Option<String> {
    // stop searching at `--`
    let mut args = std::env::args().skip_while(|val| !(val.starts_with(name) || val == "--"));

    match args.next() {
        Some(ref p) if p == "--" => None,
        Some(ref p) if p == name => args.next(),
        Some(p) => {
            // Make sure this really starts with `$name=`, we didn't test for the `=` yet.
            let v = &p[name.len()..]; // strip leading `$name`
            if v.starts_with('=') {
                Some(v[1..].to_owned()) // strip leading `=`
            } else {
                None
            }
        },
        None => None,
    }
}

fn list_targets() -> impl Iterator<Item=cargo_metadata::Target> {
    // We need to get the manifest, and then the metadata, to enumerate targets.
    let manifest_path = get_arg_flag_value("--manifest-path").map(|m|
        Path::new(&m).canonicalize().unwrap()
    );

    let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
        manifest_path.as_ref().map(AsRef::as_ref),
    )
    {
        metadata
    } else {
        show_error(format!("error: Could not obtain cargo metadata."));
    };

    let current_dir = std::env::current_dir();

    let package_index = metadata
        .packages
        .iter()
        .position(|package| {
            let package_manifest_path = Path::new(&package.manifest_path);
            if let Some(ref manifest_path) = manifest_path {
                package_manifest_path == manifest_path
            } else {
                let current_dir = current_dir.as_ref().expect(
                    "could not read current directory",
                );
                let package_manifest_directory = package_manifest_path.parent().expect(
                    "could not find parent directory of package manifest",
                );
                package_manifest_directory == current_dir
            }
        })
        .expect("could not find matching package");
    let package = metadata.packages.remove(package_index);

    // Finally we got the list of targets to build
    package.targets.into_iter()
}

fn ask(question: &str) {
    let mut buf = String::new();
    print!("{} [Y/n] ", question);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut buf).unwrap();
    match buf.trim().to_lowercase().as_ref() {
        "" | "y" | "yes" => {}, // proceed
        "n" | "no" => show_error(format!("Aborting as per your request")),
        a => show_error(format!("I do not understand `{}`", a))
    };
}

/// Perform the setup requires to make `cargo miri` work: Getting a custom-built libstd. Then sets MIRI_SYSROOT.
/// Skipped if MIRI_SYSROOT is already set, in that case we expect the user has done all this already.
fn setup(ask_user: bool) {
    if std::env::var("MIRI_SYSROOT").is_ok() {
        return;
    }

    // First, we need xargo
    if Command::new("xargo").arg("--version").output().is_err()
    {
        if ask_user {
            ask("It seems you do not have xargo installed. I will run `cargo install xargo`. Proceed?");
        } else {
            println!("Installing xargo: `cargo install xargo`");
        }
        if !Command::new("cargo").args(&["install", "xargo"]).status().unwrap().success() {
            show_error(format!("Failed to install xargo"));
        }
    }

    // Then, we also need rust-src.  Let's see if it is already installed.
    let sysroot = Command::new("rustc").args(&["--print", "sysroot"]).output().unwrap().stdout;
    let sysroot = std::str::from_utf8(&sysroot[..]).unwrap();
    let src = Path::new(sysroot.trim_end_matches('\n')).join("lib").join("rustlib").join("src");
    if !src.exists() {
        if ask_user {
            ask("It seems you do not have the rust-src component installed. I will run `rustup component add rust-src`. Proceed?");
        } else {
            println!("Installing rust-src component: `rustup component add rust-src`");
        }
        if !Command::new("rustup").args(&["component", "add", "rust-src"]).status().unwrap().success() {
            show_error(format!("Failed to install rust-src component"));
        }
    }

    // Next, we need our own libstd. We will do this work in whatever is a good cache dir for this platform.
    let dirs = directories::ProjectDirs::from("miri", "miri", "miri").unwrap();
    let dir = dirs.cache_dir();
    if !dir.exists() {
        fs::create_dir_all(&dir).unwrap();
    }
    // The interesting bit: Xargo.toml
    File::create(dir.join("Xargo.toml")).unwrap()
        .write_all(br#"
[dependencies.std]
default_features = false
# We need the `panic_unwind` feature because we use the `unwind` panic strategy.
# Using `abort` works for libstd, but then libtest will not compile.
features = ["panic_unwind"]

[dependencies.test]
stage = 1
        "#).unwrap();
    // The boring bits: A dummy project for xargo
    File::create(dir.join("Cargo.toml")).unwrap()
        .write_all(br#"
[package]
name = "miri-xargo"
description = "A dummy project for building libstd with xargo."
version = "0.0.0"

[lib]
path = "lib.rs"
        "#).unwrap();
    File::create(dir.join("lib.rs")).unwrap();
    // Run xargo
    let target = get_arg_flag_value("--target");
    let mut command = Command::new("xargo");
    command.arg("build").arg("-q")
        .current_dir(&dir)
        .env("RUSTFLAGS", miri::miri_default_args().join(" "))
        .env("XARGO_HOME", dir.to_str().unwrap());
    if let Some(ref target) = target {
        command.arg("--target").arg(&target);
    }
    if !command.status().unwrap().success()
    {
        show_error(format!("Failed to run xargo"));
    }

    // That should be it!  But we need to figure out where xargo built stuff.
    // Unfortunately, it puts things into a different directory when the
    // architecture matches the host.
    let is_host = match target {
        None => true,
        Some(target) => target == rustc_version::version_meta().unwrap().host,
    };
    let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
    std::env::set_var("MIRI_SYSROOT", &sysroot);
    if !ask_user {
        println!("A libstd for miri is now available in `{}`", sysroot.display());
    }
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called.  We call `cargo rustc` for
        // each applicable target, but with the RUSTC env var set to the `cargo-miri`
        // binary so that we come back in the other branch, and dispatch
        // the invocations to rustc and miri, respectively.

        let (subcommand, skip) = match std::env::args().nth(2).deref() {
            Some("test") => (MiriCommand::Test, 3),
            Some("run") => (MiriCommand::Run, 3),
            Some("setup") => (MiriCommand::Setup, 3),
            // Default command, if there is an option or nothing
            Some(s) if s.starts_with("-") => (MiriCommand::Run, 2),
            None => (MiriCommand::Run, 2),
            // Unvalid command
            Some(s) => {
                show_error(format!("Unknown command `{}`", s))
            }
        };

        // We always setup
        let ask = subcommand != MiriCommand::Setup;
        setup(ask);
        if subcommand == MiriCommand::Setup {
            // Stop here.
            return;
        }

        // Now run the command.
        for target in list_targets() {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            match (subcommand, &kind[..]) {
                (MiriCommand::Test, "test") => {
                    // For test binaries we call `cargo rustc --test target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--test".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Test, "lib") => {
                    // For libraries we call `cargo rustc -- --test <rustc args>`
                    // Notice now that `--test` is a rustc arg rather than a cargo arg. This tells
                    // rustc to build a test harness which calls all #[test] functions. We don't
                    // use the harness since we execute each #[test] function's MIR ourselves before
                    // compilation even completes, but this option is necessary to build the library.
                    if let Err(code) = process(
                        vec!["--".to_string(), "--test".to_string()].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Run, "bin") => {
                    // For ordinary binaries we call `cargo rustc --bin target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--bin".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                _ => {}
            }
        }
    } else if let Some("rustc") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // This arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC_WRAPPER` env var set to itself:
        // Dependencies get dispatched to rustc, the final test/binary to miri.

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let rustc_args = std::env::args().skip(2);
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            rustc_args.collect()
        } else {
            rustc_args
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };
        args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");
        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };
        command.args(&args);

        match command.status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    } else {
        show_error!("Must be called with either `miri` or `rustc` as first argument.")
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = Command::new("cargo")
        .args(&args)
        .env("RUSTC_WRAPPER", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>*oops*<commit_after>#![feature(inner_deref)]

extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::{self, Write};
use std::process::Command;
use std::fs::{self, File};

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [subcommand] [options] [--] [<opts>...]

Subcommands:
    run                      Run binaries (default)
    test                     Run tests
    setup                    Only perform automatic setup, but without asking questions (for getting a proper libstd)

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MiriCommand {
    Run,
    Test,
    Setup,
}

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn show_error(msg: String) -> ! {
    eprintln!("fatal error: {}", msg);
    std::process::exit(1)
}

fn get_arg_flag_value(name: &str) -> Option<String> {
    // stop searching at `--`
    let mut args = std::env::args().skip_while(|val| !(val.starts_with(name) || val == "--"));

    match args.next() {
        Some(ref p) if p == "--" => None,
        Some(ref p) if p == name => args.next(),
        Some(p) => {
            // Make sure this really starts with `$name=`, we didn't test for the `=` yet.
            let v = &p[name.len()..]; // strip leading `$name`
            if v.starts_with('=') {
                Some(v[1..].to_owned()) // strip leading `=`
            } else {
                None
            }
        },
        None => None,
    }
}

fn list_targets() -> impl Iterator<Item=cargo_metadata::Target> {
    // We need to get the manifest, and then the metadata, to enumerate targets.
    let manifest_path = get_arg_flag_value("--manifest-path").map(|m|
        Path::new(&m).canonicalize().unwrap()
    );

    let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
        manifest_path.as_ref().map(AsRef::as_ref),
    )
    {
        metadata
    } else {
        show_error(format!("error: Could not obtain cargo metadata."));
    };

    let current_dir = std::env::current_dir();

    let package_index = metadata
        .packages
        .iter()
        .position(|package| {
            let package_manifest_path = Path::new(&package.manifest_path);
            if let Some(ref manifest_path) = manifest_path {
                package_manifest_path == manifest_path
            } else {
                let current_dir = current_dir.as_ref().expect(
                    "could not read current directory",
                );
                let package_manifest_directory = package_manifest_path.parent().expect(
                    "could not find parent directory of package manifest",
                );
                package_manifest_directory == current_dir
            }
        })
        .expect("could not find matching package");
    let package = metadata.packages.remove(package_index);

    // Finally we got the list of targets to build
    package.targets.into_iter()
}

fn ask(question: &str) {
    let mut buf = String::new();
    print!("{} [Y/n] ", question);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut buf).unwrap();
    match buf.trim().to_lowercase().as_ref() {
        "" | "y" | "yes" => {}, // proceed
        "n" | "no" => show_error(format!("Aborting as per your request")),
        a => show_error(format!("I do not understand `{}`", a))
    };
}

/// Perform the setup requires to make `cargo miri` work: Getting a custom-built libstd. Then sets MIRI_SYSROOT.
/// Skipped if MIRI_SYSROOT is already set, in that case we expect the user has done all this already.
fn setup(ask_user: bool) {
    if std::env::var("MIRI_SYSROOT").is_ok() {
        return;
    }

    // First, we need xargo
    if Command::new("xargo").arg("--version").output().is_err()
    {
        if ask_user {
            ask("It seems you do not have xargo installed. I will run `cargo install xargo`. Proceed?");
        } else {
            println!("Installing xargo: `cargo install xargo`");
        }
        if !Command::new("cargo").args(&["install", "xargo"]).status().unwrap().success() {
            show_error(format!("Failed to install xargo"));
        }
    }

    // Then, we also need rust-src.  Let's see if it is already installed.
    let sysroot = Command::new("rustc").args(&["--print", "sysroot"]).output().unwrap().stdout;
    let sysroot = std::str::from_utf8(&sysroot[..]).unwrap();
    let src = Path::new(sysroot.trim_end_matches('\n')).join("lib").join("rustlib").join("src");
    if !src.exists() {
        if ask_user {
            ask("It seems you do not have the rust-src component installed. I will run `rustup component add rust-src`. Proceed?");
        } else {
            println!("Installing rust-src component: `rustup component add rust-src`");
        }
        if !Command::new("rustup").args(&["component", "add", "rust-src"]).status().unwrap().success() {
            show_error(format!("Failed to install rust-src component"));
        }
    }

    // Next, we need our own libstd. We will do this work in whatever is a good cache dir for this platform.
    let dirs = directories::ProjectDirs::from("miri", "miri", "miri").unwrap();
    let dir = dirs.cache_dir();
    if !dir.exists() {
        fs::create_dir_all(&dir).unwrap();
    }
    // The interesting bit: Xargo.toml
    File::create(dir.join("Xargo.toml")).unwrap()
        .write_all(br#"
[dependencies.std]
default_features = false
# We need the `panic_unwind` feature because we use the `unwind` panic strategy.
# Using `abort` works for libstd, but then libtest will not compile.
features = ["panic_unwind"]

[dependencies.test]
stage = 1
        "#).unwrap();
    // The boring bits: A dummy project for xargo
    File::create(dir.join("Cargo.toml")).unwrap()
        .write_all(br#"
[package]
name = "miri-xargo"
description = "A dummy project for building libstd with xargo."
version = "0.0.0"

[lib]
path = "lib.rs"
        "#).unwrap();
    File::create(dir.join("lib.rs")).unwrap();
    // Run xargo
    let target = get_arg_flag_value("--target");
    let mut command = Command::new("xargo");
    command.arg("build").arg("-q")
        .current_dir(&dir)
        .env("RUSTFLAGS", miri::miri_default_args().join(" "))
        .env("XARGO_HOME", dir.to_str().unwrap());
    if let Some(ref target) = target {
        command.arg("--target").arg(&target);
    }
    if !command.status().unwrap().success()
    {
        show_error(format!("Failed to run xargo"));
    }

    // That should be it!  But we need to figure out where xargo built stuff.
    // Unfortunately, it puts things into a different directory when the
    // architecture matches the host.
    let is_host = match target {
        None => true,
        Some(target) => target == rustc_version::version_meta().unwrap().host,
    };
    let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
    std::env::set_var("MIRI_SYSROOT", &sysroot);
    if !ask_user {
        println!("A libstd for miri is now available in `{}`", sysroot.display());
    }
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called.  We call `cargo rustc` for
        // each applicable target, but with the RUSTC env var set to the `cargo-miri`
        // binary so that we come back in the other branch, and dispatch
        // the invocations to rustc and miri, respectively.

        let (subcommand, skip) = match std::env::args().nth(2).deref() {
            Some("test") => (MiriCommand::Test, 3),
            Some("run") => (MiriCommand::Run, 3),
            Some("setup") => (MiriCommand::Setup, 3),
            // Default command, if there is an option or nothing
            Some(s) if s.starts_with("-") => (MiriCommand::Run, 2),
            None => (MiriCommand::Run, 2),
            // Unvalid command
            Some(s) => {
                show_error(format!("Unknown command `{}`", s))
            }
        };

        // We always setup
        let ask = subcommand != MiriCommand::Setup;
        setup(ask);
        if subcommand == MiriCommand::Setup {
            // Stop here.
            return;
        }

        // Now run the command.
        for target in list_targets() {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            match (subcommand, &kind[..]) {
                (MiriCommand::Test, "test") => {
                    // For test binaries we call `cargo rustc --test target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--test".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Test, "lib") => {
                    // For libraries we call `cargo rustc -- --test <rustc args>`
                    // Notice now that `--test` is a rustc arg rather than a cargo arg. This tells
                    // rustc to build a test harness which calls all #[test] functions. We don't
                    // use the harness since we execute each #[test] function's MIR ourselves before
                    // compilation even completes, but this option is necessary to build the library.
                    if let Err(code) = process(
                        vec!["--".to_string(), "--test".to_string()].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (MiriCommand::Run, "bin") => {
                    // For ordinary binaries we call `cargo rustc --bin target -- <rustc args>`
                    if let Err(code) = process(
                        vec!["--bin".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                _ => {}
            }
        }
    } else if let Some("rustc") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // This arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC_WRAPPER` env var set to itself:
        // Dependencies get dispatched to rustc, the final test/binary to miri.

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let rustc_args = std::env::args().skip(2);
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            rustc_args.collect()
        } else {
            rustc_args
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };
        args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");
        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };
        command.args(&args);

        match command.status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    } else {
        show_error(format!("Must be called with either `miri` or `rustc` as first argument."))
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = Command::new("cargo")
        .args(&args)
        .env("RUSTC_WRAPPER", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>