<commit_before>mod crypt;

extern crate rustc_serialize;
use rustc_serialize::json;

extern crate uuid;
use uuid::Uuid;

use std::io::prelude::*;
use std::fs;
use std::path;

use std::sync::mpsc::channel;
use std::thread;

const KEYS_PATH: &'static str = "/home/andrey/Documents/storages/keys";
const DATA_PATH: &'static str = "/home/andrey/Documents/storages/data";
const MAPS_PATH: &'static str = "/home/andrey/Documents/storages/maps";

#[derive(RustcDecodable, RustcEncodable)]
struct StorableKey {
    key: Box<[u8]>,
    iv: Box<[u8]>
}

#[derive(RustcDecodable, RustcEncodable)]
struct StorableData {
    ciphertext: Vec<u8>
}

#[derive(RustcDecodable, RustcEncodable)]
struct StorableMap {
    key_id: Uuid,
    data_id: Uuid,
    tag: Box<[u8]>
}

pub fn dump(external_id: String, data: Vec<u8>) -> Result<(), String>  {
    let result = crypt::encrypt(external_id.as_bytes(), &data);

    let (key_tx, key_rx) = channel();
    let storable_key = StorableKey { key: result.key, iv: result.iv };
    thread::spawn(move || {
        key_tx.send(store_key(KEYS_PATH.to_string(), storable_key).unwrap()).unwrap();
    });

    let (data_tx, data_rx) = channel();
    let storable_data = StorableData { ciphertext: result.ciphertext };
    thread::spawn(move || {
        data_tx.send(store_data(DATA_PATH.to_string(), storable_data).unwrap()).unwrap();
    });

    let key_id = key_rx.recv().unwrap();
    let data_id = data_rx.recv().unwrap();
    let external_uuid = Uuid::parse_str(&external_id).unwrap();
    store_map(MAPS_PATH.to_string(), external_uuid, StorableMap { key_id: key_id, data_id: data_id, tag: result.tag })
}

fn construct_store_path(path_prefix: &String, path_key_string: &String) -> path::PathBuf {
    let mut path = path::PathBuf::from(path_prefix);
    path.push(path_key_string[0..2].to_string());
    path.push(path_key_string[2..4].to_string());
    path.push(path_key_string[4..6].to_string());
    path
}

fn construct_storable_path(path_prefix: &path::PathBuf, path_key: &String) -> path::PathBuf {
    let mut path = path::PathBuf::from(path_prefix);
    path.push(path_key);
    path.set_extension("json");
    path
}

fn store_json_string(path_prefix: String, path_key: Uuid, json: String) -> Result<(), String> {
    let store_path = construct_store_path(&path_prefix, &path_key.to_simple_string());
    fs::create_dir_all(&store_path).ok();
    let mut file = fs::File::create(construct_storable_path(&store_path, &path_key.to_string())).ok().expect("Cannot create file");
    match file.write_all(json.as_bytes()) {
        Ok(_) => Ok(()),
        Err(error) => Err(format!("Error: {}", error))
    }
}

fn store_data(path_prefix: String, storable: StorableData) -> Result<Uuid, String> {
    let id = Uuid::new_v4();
    let json = json::encode(&storable).unwrap();
    store_json_string(path_prefix, id, json).ok();
    Ok(id)
}

fn store_key(path_prefix: String, storable: StorableKey) -> Result<Uuid, String> {
    let id = Uuid::new_v4();
    let json = json::encode(&storable).unwrap();
    store_json_string(path_prefix, id, json).ok();
    Ok(id)
}

fn store_map(path_prefix: String, external_id: Uuid, storable: StorableMap) -> Result<(), String> {
    let id = external_id;
    let json = json::encode(&storable).unwrap();
    store_json_string(path_prefix, id, json).ok();
    Ok(())
}

fn read_from_storage(prefix: &String, id: &String) -> Result<String, String> {
    let path = construct_storable_path(&construct_store_path(prefix, id), id);
    let mut file = fs::File::open(&path).unwrap();
    let mut string = String::new();
    match file.read_to_string(&mut string) {
        Ok(_) => Ok(string),
        Err(error) => Err(format!("Error: {}", error))
    }
}

pub fn load(external_id: &String) -> Vec<u8> {
    let map: StorableMap = json::decode(&read_from_storage(&MAPS_PATH.to_string(), external_id).unwrap()).unwrap();

    let (key_tx, key_rx) = channel();
    let key_id = map.key_id.to_string();
    thread::spawn(move || {
        key_tx.send(json::decode(&read_from_storage(&KEYS_PATH.to_string(), &key_id).unwrap()).unwrap()).unwrap();
    });

    let (data_tx, data_rx) = channel();
    let data_id = map.data_id.to_string();
    thread::spawn(move || {
        data_tx.send(json::decode(&read_from_storage(&DATA_PATH.to_string(), &data_id).unwrap()).unwrap()).unwrap();
    });

    let key: StorableKey = key_rx.recv().unwrap();
    let data: StorableData = data_rx.recv().unwrap();

    let result = crypt::decrypt(external_id.as_bytes(), &key.key, &key.iv, &data.ciphertext, &map.tag);

    result.plaintext
}
<commit_msg>Public interface was cleaned significantly.<commit_after>mod crypt;

extern crate rustc_serialize;
use rustc_serialize::json;

extern crate uuid;
use uuid::Uuid;

use std::io::prelude::*;
use std::fs;
use std::path;

use std::sync::mpsc::channel;
use std::thread;

const KEYS_PATH: &'static str = "/home/andrey/Documents/storages/keys";
const DATA_PATH: &'static str = "/home/andrey/Documents/storages/data";
const MAPS_PATH: &'static str = "/home/andrey/Documents/storages/maps";

#[derive(RustcDecodable, RustcEncodable)]
struct StorableKey {
    key: Box<[u8]>,
    iv: Box<[u8]>
}

#[derive(RustcDecodable, RustcEncodable)]
struct StorableData {
    ciphertext: Vec<u8>
}

#[derive(RustcDecodable, RustcEncodable)]
struct StorableMap {
    key_id: Uuid,
    data_id: Uuid,
    tag: Box<[u8]>
}

pub fn dump(external_id: String, data: Vec<u8>) -> Result<(), String>  {
    let result = crypt::encrypt(external_id.as_bytes(), &data);

    let (key_tx, key_rx) = channel();
    let storable_key = StorableKey { key: result.key, iv: result.iv };
    thread::spawn(move || {
        key_tx.send(store_key(storable_key).unwrap()).unwrap();
    });

    let (data_tx, data_rx) = channel();
    let storable_data = StorableData { ciphertext: result.ciphertext };
    thread::spawn(move || {
        data_tx.send(store_data(storable_data).unwrap()).unwrap();
    });

    let key_id = key_rx.recv().unwrap();
    let data_id = data_rx.recv().unwrap();
    let external_uuid = Uuid::parse_str(&external_id).unwrap();
    store_map(external_uuid, StorableMap { key_id: key_id, data_id: data_id, tag: result.tag })
}

fn construct_store_path(path_prefix: &String, path_key_string: &String) -> path::PathBuf {
    let mut path = path::PathBuf::from(path_prefix);
    path.push(path_key_string[0..2].to_string());
    path.push(path_key_string[2..4].to_string());
    path.push(path_key_string[4..6].to_string());
    path
}

fn construct_storable_path(path_prefix: &path::PathBuf, path_key: &String) -> path::PathBuf {
    let mut path = path::PathBuf::from(path_prefix);
    path.push(path_key);
    path.set_extension("json");
    path
}

fn store_json_string(path_prefix: String, path_key: Uuid, json: String) -> Result<(), String> {
    let store_path = construct_store_path(&path_prefix, &path_key.to_simple_string());
    fs::create_dir_all(&store_path).ok();
    let mut file = fs::File::create(construct_storable_path(&store_path, &path_key.to_string())).ok().expect("Cannot create file");
    match file.write_all(json.as_bytes()) {
        Ok(_) => Ok(()),
        Err(error) => Err(format!("Error: {}", error))
    }
}

fn store_data(storable: StorableData) -> Result<Uuid, String> {
    let id = Uuid::new_v4();
    let json = json::encode(&storable).unwrap();
    store_json_string(DATA_PATH.to_string(), id, json).ok();
    Ok(id)
}

fn store_key(storable: StorableKey) -> Result<Uuid, String> {
    let id = Uuid::new_v4();
    let json = json::encode(&storable).unwrap();
    store_json_string(KEYS_PATH.to_string(), id, json).ok();
    Ok(id)
}

fn store_map(external_id: Uuid, storable: StorableMap) -> Result<(), String> {
    let id = external_id;
    let json = json::encode(&storable).unwrap();
    store_json_string(MAPS_PATH.to_string(), id, json).ok();
    Ok(())
}

fn read_from_storage(prefix: &String, id: &String) -> Result<String, String> {
    let path = construct_storable_path(&construct_store_path(prefix, id), id);
    let mut file = fs::File::open(&path).unwrap();
    let mut string = String::new();
    match file.read_to_string(&mut string) {
        Ok(_) => Ok(string),
        Err(error) => Err(format!("Error: {}", error))
    }
}

pub fn load(external_id: &String) -> Vec<u8> {
    let map: StorableMap = json::decode(&read_from_storage(&MAPS_PATH.to_string(), external_id).unwrap()).unwrap();

    let (key_tx, key_rx) = channel();
    let key_id = map.key_id.to_string();
    thread::spawn(move || {
        key_tx.send(json::decode(&read_from_storage(&KEYS_PATH.to_string(), &key_id).unwrap()).unwrap()).unwrap();
    });

    let (data_tx, data_rx) = channel();
    let data_id = map.data_id.to_string();
    thread::spawn(move || {
        data_tx.send(json::decode(&read_from_storage(&DATA_PATH.to_string(), &data_id).unwrap()).unwrap()).unwrap();
    });

    let key: StorableKey = key_rx.recv().unwrap();
    let data: StorableData = data_rx.recv().unwrap();

    let result = crypt::decrypt(external_id.as_bytes(), &key.key, &key.iv, &data.ciphertext, &map.tag);

    result.plaintext
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate "posix-ipc" as ipc;

use std::os;
use std::ptr;
use std::default::Default;
use std::vec::Vec;
use std::mem;
use std::iter;
use std::num::FromPrimitive;

pub type Address = u64;
pub type Word = u64;

pub enum Action {
  Allow,
  Kill
}

#[derive(Show)]
pub enum Request {
  TraceMe = 0,
  PeekText = 1,
  PeekData = 2,
  PeekUser = 3,
  PokeText = 4,
  PokeData = 5,
  PokeUser = 6,
  Continue = 7,
  Kill = 8,
  SingleStep = 9,
  GetRegs = 12,
  SetRegs = 13,
  Attach = 16,
  Detatch = 17,
  SetOptions = 0x4200,
  Seize = 0x4206
}

#[derive(Copy, Show, FromPrimitive)]
pub enum Event {
  Fork = 1,
  VFork = 2,
  Clone = 3,
  Exec = 4,
  VForkDone = 5,
  Exit = 6,
  Seccomp = 7,
  Stop = 128
}

impl Event {
    pub fn from_wait_status(st: i32) -> Option<Event> {
        let e: Option<Event> = FromPrimitive::from_i32(((st >> 8) & !5) >> 8);
        return e;
    }
}

#[derive(Copy, Default, Show)]
pub struct Registers {
  pub r15: Word,
  pub r14: Word,
  pub r13: Word,
  pub r12: Word,
  pub rbp: Word,
  pub rbx: Word,
  pub r11: Word,
  pub r10: Word,
  pub r9: Word,
  pub r8: Word,
  pub rax: Word,
  pub rcx: Word,
  pub rdx: Word,
  pub rsi: Word,
  pub rdi: Word,
  pub orig_rax: Word,
  pub rip: Word,
  pub cs: Word,
  pub eflags: Word,
  pub rsp: Word,
  pub ss: Word,
  pub fs_base: Word,
  pub gs_base: Word,
  pub ds: Word,
  pub es: Word,
  pub fs: Word,
  pub gs: Word
}

bitflags! {
  flags Options: u32 {
    const SysGood = 1,
    const TraceFork = 1 << 1,
    const TraceVFork = 1 << 2,
    const TraceClone = 1 << 3,
    const TraceExec = 1 << 4,
    const TraceVForkDone = 1 << 5,
    const TraceExit = 1 << 6,
    const TraceSeccomp = 1 << 7,
    const ExitKill = 1 << 20
  }
}

pub fn setoptions(pid: libc::pid_t, opts: Options) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::SetOptions, pid, ptr::null_mut(), opts.bits as *mut
    libc::c_void)
  }
}

pub fn getregs(pid: libc::pid_t) -> Registers {
  let mut buf: Registers = Default::default();
  let buf_mut: *mut Registers = &mut buf;

  unsafe {
    raw (Request::GetRegs, pid, ptr::null_mut(), buf_mut as *mut libc::c_void);
  }

  return buf;
}

pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result<libc::c_long, usize> {
    unsafe {
        let mut buf: *mut libc::c_void = mem::transmute(regs);
        raw (Request::SetRegs, pid, ptr::null_mut(), buf)
    }
}

pub fn seize(pid: libc::pid_t) -> Result<libc::c_long, usize> {
    unsafe {
        raw (Request::Seize, pid, ptr::null_mut(), ptr::null_mut())
    }
}

pub fn attach(pid: libc::pid_t) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::Attach, pid, ptr::null_mut(), ptr::null_mut())
  }
}

pub fn release(pid: libc::pid_t, signal: ipc::signals::Signal) {
  unsafe {
    raw (Request::Detatch, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void);
  }
}

pub fn cont(pid: libc::pid_t, signal: ipc::signals::Signal) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::Continue, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void)
  }
}

pub fn traceme() -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::TraceMe, 0, ptr::null_mut(), ptr::null_mut())
  }
}

unsafe fn raw(request: Request,
       pid: libc::pid_t,
       addr: *mut libc::c_void,
       data: *mut libc::c_void) -> Result<libc::c_long, usize> {
  let v = ptrace (request as libc::c_int, pid, addr, data);
  match v {
      -1 => Result::Err(os::errno()),
      _ => Result::Ok(v)
  }
}

extern {
  fn ptrace(request: libc::c_int,
            pid: libc::pid_t,
            addr: *mut libc::c_void,
            data: *mut libc::c_void) -> libc::c_long;
}

#[derive(Copy, Show)]
pub struct Syscall {
  pub args: [Word; 6],
  pub call: u64,
  pub pid: libc::pid_t,
  pub returnVal: Word
}

impl Syscall {
  pub fn from_pid(pid: libc::pid_t) -> Syscall {
    let regs = getregs (pid);
    Syscall {
      pid: pid,
      call: regs.orig_rax,
      args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9],
      returnVal: 0
    }
  }

  pub fn write(&self) -> Result<libc::c_long, usize> {
      let mut regs = getregs(self.pid);
      regs.rdi = self.args[0];
      regs.rsi = self.args[1];
      regs.rdx = self.args[2];
      regs.rcx = self.args[3];
      regs.r8 = self.args[4];
      regs.r9 = self.args[5];
      setregs(self.pid, &regs)
  }
}

pub struct Reader {
  pub pid: libc::pid_t
}

impl Reader {
  pub fn new(pid: libc::pid_t) -> Reader {
    Reader {
      pid: pid
    }
  }

    pub fn peek_data(&self, address: Address) -> Result<Word, usize> {
        let l;
        unsafe {
            l = raw (Request::PeekData, self.pid, address as *mut libc::c_void, ptr::null_mut())
        }
        match l {
            Result::Err(e) => Result::Err(e),
            _ => Result::Ok(l.unwrap() as Word)
        }
    }

    pub fn read_string(&self, address: Address) -> Vec<u8> {
        let mut end_of_str = false;
        let mut buf: Vec<u8> = Vec::with_capacity(1024);
        let max_addr = address + buf.capacity() as Address;
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        'finish: for read_addr in iter::range_step(address, align_end, mem::size_of::<Word>() as Address) {
            let d = self.peek_data(read_addr).ok().expect("Could not read");
            for word_idx in iter::range(0, mem::size_of::<Word>()) {
                let chr = ((d >> (word_idx*8) as uint) & 0xff) as u8;
                buf.push (chr);
                if chr == 0 {
                    end_of_str = true;
                    break 'finish;
                }
            }
        }
        if !end_of_str {
            let d = self.peek_data(align_end).ok().expect("Could not read");
            for word_idx in range(0, mem::size_of::<Word>()) {
                let chr = ((d >> (word_idx*8) as uint) & 0xff) as u8;
                buf.push (chr);
                if chr == 0 {
                    break;
                }
            }
        }
        return buf;
    }
}
<commit_msg>fix up syscall id and return value<commit_after>extern crate libc;
extern crate "posix-ipc" as ipc;

use std::os;
use std::ptr;
use std::default::Default;
use std::vec::Vec;
use std::mem;
use std::iter;
use std::num::FromPrimitive;

pub type Address = u64;
pub type Word = u64;

pub enum Action {
  Allow,
  Kill
}

#[derive(Show)]
pub enum Request {
  TraceMe = 0,
  PeekText = 1,
  PeekData = 2,
  PeekUser = 3,
  PokeText = 4,
  PokeData = 5,
  PokeUser = 6,
  Continue = 7,
  Kill = 8,
  SingleStep = 9,
  GetRegs = 12,
  SetRegs = 13,
  Attach = 16,
  Detatch = 17,
  SetOptions = 0x4200,
  Seize = 0x4206
}

#[derive(Copy, Show, FromPrimitive)]
pub enum Event {
  Fork = 1,
  VFork = 2,
  Clone = 3,
  Exec = 4,
  VForkDone = 5,
  Exit = 6,
  Seccomp = 7,
  Stop = 128
}

impl Event {
    pub fn from_wait_status(st: i32) -> Option<Event> {
        let e: Option<Event> = FromPrimitive::from_i32(((st >> 8) & !5) >> 8);
        return e;
    }
}

#[derive(Copy, Default, Show)]
pub struct Registers {
  pub r15: Word,
  pub r14: Word,
  pub r13: Word,
  pub r12: Word,
  pub rbp: Word,
  pub rbx: Word,
  pub r11: Word,
  pub r10: Word,
  pub r9: Word,
  pub r8: Word,
  pub rax: Word,
  pub rcx: Word,
  pub rdx: Word,
  pub rsi: Word,
  pub rdi: Word,
  pub orig_rax: Word,
  pub rip: Word,
  pub cs: Word,
  pub eflags: Word,
  pub rsp: Word,
  pub ss: Word,
  pub fs_base: Word,
  pub gs_base: Word,
  pub ds: Word,
  pub es: Word,
  pub fs: Word,
  pub gs: Word
}

bitflags! {
  flags Options: u32 {
    const SysGood = 1,
    const TraceFork = 1 << 1,
    const TraceVFork = 1 << 2,
    const TraceClone = 1 << 3,
    const TraceExec = 1 << 4,
    const TraceVForkDone = 1 << 5,
    const TraceExit = 1 << 6,
    const TraceSeccomp = 1 << 7,
    const ExitKill = 1 << 20
  }
}

pub fn setoptions(pid: libc::pid_t, opts: Options) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::SetOptions, pid, ptr::null_mut(), opts.bits as *mut
    libc::c_void)
  }
}

pub fn getregs(pid: libc::pid_t) -> Registers {
  let mut buf: Registers = Default::default();
  let buf_mut: *mut Registers = &mut buf;

  unsafe {
    raw (Request::GetRegs, pid, ptr::null_mut(), buf_mut as *mut libc::c_void);
  }

  return buf;
}

pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result<libc::c_long, usize> {
    unsafe {
        let mut buf: *mut libc::c_void = mem::transmute(regs);
        raw (Request::SetRegs, pid, ptr::null_mut(), buf)
    }
}

pub fn seize(pid: libc::pid_t) -> Result<libc::c_long, usize> {
    unsafe {
        raw (Request::Seize, pid, ptr::null_mut(), ptr::null_mut())
    }
}

pub fn attach(pid: libc::pid_t) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::Attach, pid, ptr::null_mut(), ptr::null_mut())
  }
}

pub fn release(pid: libc::pid_t, signal: ipc::signals::Signal) {
  unsafe {
    raw (Request::Detatch, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void);
  }
}

pub fn cont(pid: libc::pid_t, signal: ipc::signals::Signal) -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::Continue, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void)
  }
}

pub fn traceme() -> Result<libc::c_long, usize> {
  unsafe {
    raw (Request::TraceMe, 0, ptr::null_mut(), ptr::null_mut())
  }
}

unsafe fn raw(request: Request,
       pid: libc::pid_t,
       addr: *mut libc::c_void,
       data: *mut libc::c_void) -> Result<libc::c_long, usize> {
  let v = ptrace (request as libc::c_int, pid, addr, data);
  match v {
      -1 => Result::Err(os::errno()),
      _ => Result::Ok(v)
  }
}

extern {
  fn ptrace(request: libc::c_int,
            pid: libc::pid_t,
            addr: *mut libc::c_void,
            data: *mut libc::c_void) -> libc::c_long;
}

#[derive(Copy, Show)]
pub struct Syscall {
  pub args: [Word; 6],
  pub call: u64,
  pub pid: libc::pid_t,
  pub returnVal: Word
}

impl Syscall {
  pub fn from_pid(pid: libc::pid_t) -> Syscall {
    let regs = getregs (pid);
    Syscall {
      pid: pid,
      call: regs.orig_rax,
      args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9],
      returnVal: 0
    }
  }

  pub fn write(&self) -> Result<libc::c_long, usize> {
      let mut regs = getregs(self.pid);
      regs.rdi = self.args[0];
      regs.rsi = self.args[1];
      regs.rdx = self.args[2];
      regs.rcx = self.args[3];
      regs.r8 = self.args[4];
      regs.r9 = self.args[5];
      regs.orig_rax = self.call;
      regs.rax = self.returnVal;
      setregs(self.pid, &regs)
  }
}

pub struct Reader {
  pub pid: libc::pid_t
}

impl Reader {
  pub fn new(pid: libc::pid_t) -> Reader {
    Reader {
      pid: pid
    }
  }

    pub fn peek_data(&self, address: Address) -> Result<Word, usize> {
        let l;
        unsafe {
            l = raw (Request::PeekData, self.pid, address as *mut libc::c_void, ptr::null_mut())
        }
        match l {
            Result::Err(e) => Result::Err(e),
            _ => Result::Ok(l.unwrap() as Word)
        }
    }

    pub fn read_string(&self, address: Address) -> Vec<u8> {
        let mut end_of_str = false;
        let mut buf: Vec<u8> = Vec::with_capacity(1024);
        let max_addr = address + buf.capacity() as Address;
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        'finish: for read_addr in iter::range_step(address, align_end, mem::size_of::<Word>() as Address) {
            let d = self.peek_data(read_addr).ok().expect("Could not read");
            for word_idx in iter::range(0, mem::size_of::<Word>()) {
                let chr = ((d >> (word_idx*8) as uint) & 0xff) as u8;
                buf.push (chr);
                if chr == 0 {
                    end_of_str = true;
                    break 'finish;
                }
            }
        }
        if !end_of_str {
            let d = self.peek_data(align_end).ok().expect("Could not read");
            for word_idx in range(0, mem::size_of::<Word>()) {
                let chr = ((d >> (word_idx*8) as uint) & 0xff) as u8;
                buf.push (chr);
                if chr == 0 {
                    break;
                }
            }
        }
        return buf;
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(unix, feature(fs_ext))]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

pub mod error;

pub use error::*;

use std::convert::From;
use std::env::{self, home_dir, split_paths};
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::result;

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_DATA_HOME", ".local/share")
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> Result<PathBuf> {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_DATA_DIRS", "/usr/local/share:/usr/share")
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CONFIG_HOME", ".config")
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> Result<PathBuf> {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_CONFIG_DIRS", "/etc/xdg")
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CACHE_HOME", ".cache")
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> Result<PathBuf> {
    get_cache_home_from_env(&env::var_os)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application.
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<PathBuf> {
    get_env_path(&env::var_os, "XDG_RUNTIME_DIR")
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification.
///
/// Returns Ok(true) if permissions are correct, Ok(false) if permissions are incorrect, or propogates any errors that occurred while checking permissions.
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(unix)]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> result::Result<bool, io::Error> {
    fs::metadata(path).map(|attr| (attr.permissions().mode() == 0o700))
}

#[cfg(not(unix))]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> result::Result<bool, io::Error> {
    Ok(true)
}

/// Get path from environment variable's value or a default path relative to home_dir
fn get_env_path_or_default<F>(get_env_var: &F, env_var: &str, default: &str) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    match get_env_path(get_env_var, env_var) {
        Some(p) => Ok(p),
        None => {
            match home_dir().map(|p| p.join(default)) {
                Some(p) => Ok(p),
                None => Err(From::from(XdgError::NoHomeDir)),
            }
        }
    }
}

/// Get an environment variable's value as a PathBuf.
fn get_env_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    let path = (*get_env_var)(env_var);
    let path = path.map(PathBuf::from);
    match path.iter().next() {
        Some(p) if p.is_absolute() => Some(p.clone()),
        _ => None
    }
}

fn get_env_paths_or_default<F>(get_env_var: &F, env_var: &str, default: &str) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    let default_paths = OsString::from(default);

    let paths = match (*get_env_var)(env_var).iter().next() {
        Some(p) if p != "" => p.clone(),
        _ => default_paths
    };

    split_paths(&paths).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashMap;
    use std::env::{self, home_dir, join_paths, split_paths};
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(&f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(&f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(&f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(&f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(&f)          == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(&f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(&f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(&f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(&f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(&f)          == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();

        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").into_os_string());
        custom_env.insert("XDG_DATA_DIRS", join_paths(vec![cwd.join("share/data"), cwd.join("local/data")]).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").into_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", join_paths(vec![cwd.join("config"), cwd.join("local/config")]).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").into_os_string());

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(get_data_dirs_from_env(&f)            == split_paths(&custom_env["XDG_DATA_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_config_home_from_env(&f).unwrap() == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(get_config_dirs_from_env(&f)          == split_paths(&custom_env["XDG_CONFIG_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_cache_home_from_env(&f).unwrap()  == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<commit_msg>address #7<commit_after>#![cfg_attr(unix, feature(fs_ext))]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

pub mod error;

pub use error::*;

use std::convert::From;
use std::env::{self, home_dir, split_paths};
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::result;

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_DATA_HOME", ".local/share")
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> Result<PathBuf> {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_DATA_DIRS", "/usr/local/share:/usr/share")
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CONFIG_HOME", ".config")
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> Result<PathBuf> {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_CONFIG_DIRS", "/etc/xdg")
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CACHE_HOME", ".cache")
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> Result<PathBuf> {
    get_cache_home_from_env(&env::var_os)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application.
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    get_env_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<PathBuf> {
    get_runtime_dir_from_env(&env::var_os)
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification.
///
/// Returns Ok(true) if permissions are correct, Ok(false) if permissions are incorrect, or propogates any errors that occurred while checking permissions.
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(unix)]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> result::Result<bool, io::Error> {
    fs::metadata(path).map(|attr| (attr.permissions().mode() == 0o700))
}

#[cfg(not(unix))]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> result::Result<bool, io::Error> {
    Ok(true)
}

/// Get path from environment variable's value or a default path relative to home_dir
fn get_env_path_or_default<F>(get_env_var: &F, env_var: &str, default: &str) -> Result<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    match get_env_path(get_env_var, env_var) {
        Some(p) => Ok(p),
        None => {
            match home_dir().map(|p| p.join(default)) {
                Some(p) => Ok(p),
                None => Err(From::from(XdgError::NoHomeDir)),
            }
        }
    }
}

/// Get an environment variable's value as a PathBuf.
fn get_env_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    let path = (*get_env_var)(env_var);
    let path = path.map(PathBuf::from);
    match path.iter().next() {
        Some(p) if p.is_absolute() => Some(p.clone()),
        _ => None
    }
}

fn get_env_paths_or_default<F>(get_env_var: &F, env_var: &str, default: &str) -> Vec<PathBuf>
    where F: Fn(&str) -> Option<OsString>
{
    let default_paths = OsString::from(default);

    let paths = match (*get_env_var)(env_var).iter().next() {
        Some(p) if p != "" => p.clone(),
        _ => default_paths
    };

    split_paths(&paths).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashMap;
    use std::env::{self, home_dir, join_paths, split_paths};
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(&f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(&f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(&f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(&f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(&f)          == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(&f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(&f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(&f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(&f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(&f)          == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();

        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").into_os_string());
        custom_env.insert("XDG_DATA_DIRS", join_paths(vec![cwd.join("share/data"), cwd.join("local/data")]).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").into_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", join_paths(vec![cwd.join("config"), cwd.join("local/config")]).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").into_os_string());

        let f = |var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(&f).unwrap()   == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(get_data_dirs_from_env(&f)            == split_paths(&custom_env["XDG_DATA_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_config_home_from_env(&f).unwrap() == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(get_config_dirs_from_env(&f)          == split_paths(&custom_env["XDG_CONFIG_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_cache_home_from_env(&f).unwrap()  == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<|endoftext|>


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

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do Cap'n Proto code generation within a Cargo build.
//!
//! In your Cargo.toml:
//!
//! ```ignore
//!   [build-dependencies.capnpc]
//!   git = "https://github.com/dwrensha/capnpc-rust.git"
//! ```
//!
//! In your build.rs, call
//!
//! ```ignore
//! ::capnpc::compile(Path::new("schema"),
//!                   [Path::new("schema/foo.capnp"),
//!                    Path::new("schema/bar.capnp")]);
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

#![crate_name="capnpc"]
#![crate_type = "lib"]
#![feature(box_syntax, collections, core, io, os, path, std_misc)]

extern crate capnp;

pub mod schema_capnp;
pub mod codegen;

pub fn compile(prefix : Path, files : &[Path]) -> ::std::old_io::IoResult<()> {
    let out_dir = Path::new(::std::os::getenv("OUT_DIR").unwrap());
    let cwd = try!(::std::os::getcwd());
    try!(::std::os::change_dir(&out_dir));

    let mut command = ::std::old_io::Command::new("capnp");
    command
        .arg("compile")
        .arg("-o/bin/cat")
        .arg(format!("--src-prefix={}", cwd.join(prefix).display()));

    for file in files.iter() {
        command.arg(format!("{}", cwd.join(file).display()));
    }

    command.stdout(::std::old_io::process::CreatePipe(false, true));
    command.stderr(::std::old_io::process::InheritFd(2));

    let mut p =  try!(command.spawn());
    let mut child_stdout = p.stdout.take().unwrap();
    try!(::codegen::main(&mut child_stdout));
    try!(p.wait());
    return Ok(());
}

<commit_msg>Remove feature preventing compilation<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do Cap'n Proto code generation within a Cargo build.
//!
//! In your Cargo.toml:
//!
//! ```ignore
//!   [build-dependencies.capnpc]
//!   git = "https://github.com/dwrensha/capnpc-rust.git"
//! ```
//!
//! In your build.rs, call
//!
//! ```ignore
//! ::capnpc::compile(Path::new("schema"),
//!                   [Path::new("schema/foo.capnp"),
//!                    Path::new("schema/bar.capnp")]);
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

#![crate_name="capnpc"]
#![crate_type = "lib"]
#![feature(box_syntax, core, io, os, path, std_misc)]

extern crate capnp;

pub mod schema_capnp;
pub mod codegen;

pub fn compile(prefix : Path, files : &[Path]) -> ::std::old_io::IoResult<()> {
    let out_dir = Path::new(::std::os::getenv("OUT_DIR").unwrap());
    let cwd = try!(::std::os::getcwd());
    try!(::std::os::change_dir(&out_dir));

    let mut command = ::std::old_io::Command::new("capnp");
    command
        .arg("compile")
        .arg("-o/bin/cat")
        .arg(format!("--src-prefix={}", cwd.join(prefix).display()));

    for file in files.iter() {
        command.arg(format!("{}", cwd.join(file).display()));
    }

    command.stdout(::std::old_io::process::CreatePipe(false, true));
    command.stderr(::std::old_io::process::InheritFd(2));

    let mut p =  try!(command.spawn());
    let mut child_stdout = p.stdout.take().unwrap();
    try!(::codegen::main(&mut child_stdout));
    try!(p.wait());
    return Ok(());
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use std::str;
use std::str::FromStr;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

pub type ERes<T> = Result<T, String>;

impl Sexp {
    pub fn list(&self) -> ERes<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            ref x => Err(format!("not a list: {}", x))
        }
    }

    pub fn string(&self) -> ERes<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            ref x => Err(format!("not a string: {}", x))
        }
    }

    pub fn f(&self) -> ERes<f64> {
        let s = try!(self.string());
        match f64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err(format!("Error parsing float"))
        }
    }

    pub fn i(&self) -> ERes<i64> {
        let s = try!(self.string());
        match i64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err(format!("Error parsing int"))
        }
    }

    pub fn list_name(&self) -> ERes<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list doesn't start with {}, but with {}", s, st))
        };
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                if s.contains("(") || s.contains(" ") {
                    write!(f,"\"{}\"", s)
                } else {
                    write!(f,"{}", s)
                }
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                for (i, x) in v.iter().enumerate() {
                    let s = if i == 0 { "" } else { " " };
                    try!(write!(f, "{}{}", s, x));
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}

pub fn display_string(s:&String) -> String {
    if s.contains("(") || s.contains(" ") || s.len() == 0 {
        format!("\"{}\"", s)
    } else {
        s.clone()
    }
}

pub fn parse_str(sexp: &str) -> Result<Sexp, String> {
    if sexp.len() == 0 {
        return Ok(Sexp::Empty)
    }
    match parse_sexp(&sexp.as_bytes()[..]) {
        nom::IResult::Done(_, c) => Ok(c),
        nom::IResult::Error(err) => {
            match err {
                nom::Err::Position(kind,p) =>
                    Err(format!("parse error: {:?} |{}|", kind, str::from_utf8(p).unwrap())),
                _ => Err(format!("parse error"))
            }
        },
        nom::IResult::Incomplete(x) => Err(format!("incomplete: {:?}", x)),
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("{:?}", x))
    });
    parse_str(&s[..])
}

named!(parse_qstring<String>,
       map_res!(
           map_res!(
               delimited!(char!('\"'), is_not!("\""), char!('\"')),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_bare_string<String>,
       map_res!(
           map_res!(
               is_not!(b")( \r\n"),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_string<String>,
       alt!(parse_qstring | parse_bare_string)
       );

named!(parse_list<Vec<Sexp> >,
       chain!(
           char!('(') ~
           v: many0!(parse_sexp) ~
           char!(')'),
           || v)
       );

named!(parse_sexp<Sexp>,
       chain!(
           opt!(nom::multispace) ~
               s: alt!(map!(parse_list,Sexp::List) | map!(parse_string,Sexp::String)) ~
               opt!(nom::multispace)
               ,|| s)
       );

// internal tests
#[test]
fn test_qstring1() {
    assert_eq!(parse_string(&b"\"hello world\""[..]), nom::IResult::Done(&b""[..], String::from("hello world")));
}

#[test]
#[should_panic(expected="assertion failed: `(left == right)` (left: `Incomplete(Size(1))`, right: `Done([], \"hello\")`)")]
fn test_qstring2() {
    assert_eq!(parse_string(&b"\"hello"[..]), nom::IResult::Done(&b""[..], String::from("hello")));
}

#[test]
fn test_string1() {
    assert_eq!(parse_string(&b"hello world"[..]), nom::IResult::Done(&b" world"[..], String::from("hello")));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[allow(dead_code)]
    fn check_parse_res(s: &str, o:&str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(o, t)
    }
    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t)
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_empty_qstring() { check_parse("(hello \"\")") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring_a() { check_parse_res("\"hello\"", "hello") }

    #[test]
    fn test_qstring_a2() { check_parse("\"hello world\"") }

    #[test]
    fn test_qstring_a3() { check_parse("\"hello(world)\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse_res("(\"world\")", "(world)") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    fn test_number_string() { check_parse("567A_WZ") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(2)\"")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"parse error: Alt |)|\"")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(1)\"")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }
}

<commit_msg>remark about formatting hints<commit_after>#[macro_use]
extern crate nom;

use std::str;
use std::str::FromStr;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;

// TODO: store formatting hints in Sexp
#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

pub type ERes<T> = Result<T, String>;

impl Sexp {
    pub fn list(&self) -> ERes<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            ref x => Err(format!("not a list: {}", x))
        }
    }

    pub fn string(&self) -> ERes<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            ref x => Err(format!("not a string: {}", x))
        }
    }

    pub fn f(&self) -> ERes<f64> {
        let s = try!(self.string());
        match f64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err(format!("Error parsing float"))
        }
    }

    pub fn i(&self) -> ERes<i64> {
        let s = try!(self.string());
        match i64::from_str(&s) {
            Ok(f) => Ok(f),
            _ => Err(format!("Error parsing int"))
        }
    }

    pub fn list_name(&self) -> ERes<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> ERes<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return Err(format!("list doesn't start with {}, but with {}", s, st))
        };
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                if s.contains("(") || s.contains(" ") {
                    write!(f,"\"{}\"", s)
                } else {
                    write!(f,"{}", s)
                }
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                for (i, x) in v.iter().enumerate() {
                    let s = if i == 0 { "" } else { " " };
                    try!(write!(f, "{}{}", s, x));
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}

pub fn display_string(s:&String) -> String {
    if s.contains("(") || s.contains(" ") || s.len() == 0 {
        format!("\"{}\"", s)
    } else {
        s.clone()
    }
}

pub fn parse_str(sexp: &str) -> Result<Sexp, String> {
    if sexp.len() == 0 {
        return Ok(Sexp::Empty)
    }
    match parse_sexp(&sexp.as_bytes()[..]) {
        nom::IResult::Done(_, c) => Ok(c),
        nom::IResult::Error(err) => {
            match err {
                nom::Err::Position(kind,p) =>
                    Err(format!("parse error: {:?} |{}|", kind, str::from_utf8(p).unwrap())),
                _ => Err(format!("parse error"))
            }
        },
        nom::IResult::Incomplete(x) => Err(format!("incomplete: {:?}", x)),
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("{:?}", x))
    });
    parse_str(&s[..])
}

named!(parse_qstring<String>,
       map_res!(
           map_res!(
               delimited!(char!('\"'), is_not!("\""), char!('\"')),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_bare_string<String>,
       map_res!(
           map_res!(
               is_not!(b")( \r\n"),
               str::from_utf8),
           FromStr::from_str)
       );

named!(parse_string<String>,
       alt!(parse_qstring | parse_bare_string)
       );

named!(parse_list<Vec<Sexp> >,
       chain!(
           char!('(') ~
           v: many0!(parse_sexp) ~
           char!(')'),
           || v)
       );

named!(parse_sexp<Sexp>,
       chain!(
           opt!(nom::multispace) ~
               s: alt!(map!(parse_list,Sexp::List) | map!(parse_string,Sexp::String)) ~
               opt!(nom::multispace)
               ,|| s)
       );

// internal tests
#[test]
fn test_qstring1() {
    assert_eq!(parse_string(&b"\"hello world\""[..]), nom::IResult::Done(&b""[..], String::from("hello world")));
}

#[test]
#[should_panic(expected="assertion failed: `(left == right)` (left: `Incomplete(Size(1))`, right: `Done([], \"hello\")`)")]
fn test_qstring2() {
    assert_eq!(parse_string(&b"\"hello"[..]), nom::IResult::Done(&b""[..], String::from("hello")));
}

#[test]
fn test_string1() {
    assert_eq!(parse_string(&b"hello world"[..]), nom::IResult::Done(&b" world"[..], String::from("hello")));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[allow(dead_code)]
    fn check_parse_res(s: &str, o:&str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(o, t)
    }
    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t)
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_empty_qstring() { check_parse("(hello \"\")") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring_a() { check_parse_res("\"hello\"", "hello") }

    #[test]
    fn test_qstring_a2() { check_parse("\"hello world\"") }

    #[test]
    fn test_qstring_a3() { check_parse("\"hello(world)\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse_res("(\"world\")", "(world)") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    fn test_number_string() { check_parse("567A_WZ") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(2)\"")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"parse error: Alt |)|\"")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="called `Result::unwrap()` on an `Err` value: \"incomplete: Size(1)\"")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

pub mod parser;
<commit_msg>Add AST module to the main lib<commit_after>#[macro_use]
extern crate nom;

pub mod parser;
pub mod ast;
<|endoftext|>


<commit_before>//! An ergonomic API for reading and writing ZIP files.
//!
//! The current implementation is based on [PKWARE's APPNOTE.TXT v6.3.9](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
// TODO: Decide on the crate's bias: Do we prioritise permissiveness/correctness/speed/ergonomics?

#![warn(missing_docs)]

pub use crate::compression::CompressionMethod;
pub use crate::read::ZipArchive;
pub use crate::types::DateTime;
pub use crate::write::ZipWriter;

mod compression;
mod cp437;
mod crc32;
pub mod read;
pub mod result;
mod spec;
mod types;
pub mod write;
mod zipcrypto;
<commit_msg>docs: add reference to GitHub issue<commit_after>//! An ergonomic API for reading and writing ZIP files.
//!
//! The current implementation is based on [PKWARE's APPNOTE.TXT v6.3.9](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
// TODO(#184): Decide on the crate's bias: Do we prioritise permissiveness/correctness/speed/ergonomics?

#![warn(missing_docs)]

pub use crate::compression::CompressionMethod;
pub use crate::read::ZipArchive;
pub use crate::types::DateTime;
pub use crate::write::ZipWriter;

mod compression;
mod cp437;
mod crc32;
pub mod read;
pub mod result;
mod spec;
mod types;
pub mod write;
mod zipcrypto;
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

pub mod ts {

    use std;
    use std::fmt;
    use std::collections::HashMap;
    use std::str::from_utf8;

    use nom;
    use nom::*;

    #[derive(Debug)]
    pub enum Format {
        RI,
        DB,
        MA,
    }

    impl fmt::Display for Format {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Format::RI => "RI",
                &Format::DB => "DB",
                &Format::MA => "MA",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Format {
        fn default() -> Format {
            Format::MA
        }
    }

    #[derive(Debug)]
    pub enum FreqUnits {
        Hz,
        KHz,
        MHz,
        GHz,
    }

    impl fmt::Display for FreqUnits {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &FreqUnits::Hz => "Hz",
                &FreqUnits::KHz => "KHz",
                &FreqUnits::MHz => "MHz",
                &FreqUnits::GHz => "GHz",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for FreqUnits {
        fn default() -> FreqUnits {
            FreqUnits::GHz
        }
    }

    #[derive(Debug,)]
    pub enum Domain {
        Scattering,
        Transfer,
        Admittance,
        Impedance,
        HybridH,
        HybridG,
    }

    impl fmt::Display for Domain {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Domain::Scattering => "S",
                &Domain::Transfer => "T",
                &Domain::Admittance => "Y",
                &Domain::Impedance => "Z",
                &Domain::HybridH => "H",
                &Domain::HybridG => "G",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Domain {
        fn default() -> Domain {
            Domain::Scattering
        }
    }

    #[derive(Debug)]
    pub struct OptionLine {
        pub freq_units: FreqUnits,
        pub domain: Domain,
        pub format: Format,
        pub system_impedance: Option<f64>,
    }

    impl Default for OptionLine {
        fn default() -> OptionLine {
            OptionLine {
                freq_units: Default::default(),
                domain: Default::default(),
                format: Default::default(),
                system_impedance: Default::default()
            }
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Debug)]
    pub enum TwoPortOrder {
        TPO_12_21,
        TPO_21_12,
    }

    #[derive(Debug)]
    pub struct NoisePoint {
        pub frequency: f64,
        pub minimum_noise_db: f64,
        pub source_refl_coeff_magnitude: f64,
        pub source_refl_coeff_phase: f64,
        pub effective_noise_resistance: f64,
    }

    #[derive(Debug)]
    pub struct Touchstone2 {
        pub num_ports: i32,
        pub option_line: OptionLine,
        pub two_port_order: Option<TwoPortOrder>,
        pub reference_impedance: Option<Vec<f64>>,
        pub information: HashMap<String, String>,
        pub network_data: Vec<Box<[(f64, f64)]>>,
        pub noise_data: Option<Box<[NoisePoint]>>,
    }

    fn parse<T: std::str::FromStr>(bytes: &[u8]) -> Option<T> {
        from_utf8(bytes).ok().and_then(|s| s.trim().parse().ok())
    }

    named!(
        comment_line<()>,
        value!((), delimited!(char!('['), take_until!("\n"), char!('\n')))
    );

    named!(comment_block<()>, value!((), many0!(comment_line)));

    named!(
        version<(i32, i32)>,
        do_parse!(
            tag_no_case!("[version]") >> many0!(space) >>
            ver: map_opt!(
                separated_pair!(digit, char!('.'), digit),
                |(major, minor)| parse(major).and_then(
                    |mjr| parse(minor).map(|mnr| (mjr, mnr)))
            ) >>
            (ver)
        )
    );

    fn not_whitespace(c: u8) -> bool {
        !(
            c == ' '  as u8 ||
            c == '\t' as u8 ||
            c == '\n' as u8 ||
            c == '\r' as u8
        )
    }

    named!(
        float<f64>,
        map_opt!(take_while!(not_whitespace), parse)
    );

    named!(
        option_line<OptionLine>,
        do_parse!(
            char!('#') >>
            many1!(space) >>
            freq_units: opt!(
                alt!(
                    value!(FreqUnits::Hz, tag_no_case!("hz")) |
                    value!(FreqUnits::KHz, tag_no_case!("khz")) |
                    value!(FreqUnits::MHz, tag_no_case!("mhz")) |
                    value!(FreqUnits::GHz, tag_no_case!("ghz"))
                )
            ) >>
            domain: opt!(
                alt!(
                    value!(Domain::Scattering, tag_no_case!("s")) |
                    value!(Domain::Transfer, tag_no_case!("t")) |
                    value!(Domain::Admittance, tag_no_case!("y")) |
                    value!(Domain::Impedance, tag_no_case!("z")) |
                    value!(Domain::HybridH, tag_no_case!("h")) |
                    value!(Domain::HybridG, tag_no_case!("g"))
                )
            ) >>
            format: opt!(
                alt!(
                    value!(Format::DB, tag_no_case!("db")) |
                    value!(Format::MA, tag_no_case!("ma")) |
                    value!(Format::RI, tag_no_case!("ri"))
                )
            ) >>
            system_impedance: opt!(float) >>
            (OptionLine {
                freq_units: freq_units.unwrap_or(Default::default()),
                domain: domain.unwrap_or(Default::default()),
                format: format.unwrap_or(Default::default()),
                system_impedance: system_impedance
            })
        )
    );

    named!(
        number_of_ports<i32>,
        do_parse!(
            tag_no_case!("[number of ports]") >> many0!(space) >>
            num_ports: map_opt!(take_until!("\n"), parse) >>
            (num_ports)
        )
    );

    named!(
        two_port_order<TwoPortOrder>,
        do_parse!(
            tag_no_case!("[two-port order]") >> many0!(space) >>
            tpo: alt!(
                value!(TwoPortOrder::TPO_12_21, tag!("12_21")) |
                value!(TwoPortOrder::TPO_21_12, tag!("21_12"))
            ) >>
            (tpo)
        )
    );

    named!(
        number_of_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        number_of_noise_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of noise frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        reference<Vec<f64>>,
        do_parse!(
            tag_no_case!("[reference]") >> many0!(multispace) >>
            refs: many1!(float) >>
            (refs)
        )
    );

    named!(
        kwarg<(String, String)>,
        do_parse!(
            char!('[') >>
            kw: map_res!(
                take_until!("]"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            char!(']') >>
            many0!(space) >>
            arg: map_res!(
                take_until_and_consume!("\n"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            (kw, "".to_string())
        )
    );

    named!(
        information<HashMap<String, String>>,
        do_parse!(
            tag_no_case!("[begin information") >> many0!(space) >> many1!(newline) >>
            vals: map!(
                many0!(kwarg),
                |v: Vec<(String, String)>| v.iter().cloned().collect()
            ) >>
            tag_no_case!("[end information]") >>
            (vals)
        )
    );

    macro_rules! comments {
        ($i:expr, $($args:tt)*) => ({sep!($i, comment_line, $($args)*)})
    }

    #[derive(Debug)]
    enum Metadata {
        TwoPortOrder(TwoPortOrder),
        NumberOfFrequencies(i32),
        NumberOfNoiseFrequencies(i32),
        Reference(Vec<f64>),
        Information(HashMap<String, String>),
    }

    named!(
        metadata<Vec<Metadata>>,
        many0!(alt!(
            map!(two_port_order, Metadata::TwoPortOrder) |
            map!(number_of_frequencies, Metadata::NumberOfFrequencies) |
            map!(number_of_noise_frequencies, Metadata::NumberOfNoiseFrequencies) |
            map!(reference, Metadata::Reference) |
            map!(information, Metadata::Information)
        ))
    );

    named!(
        header<((i32, i32), OptionLine, i32, Vec<Metadata>)>,
        comments!(do_parse!(
            ver: version >>
            opt_line: option_line >>
            num_ports: number_of_ports >>
            metadata: metadata >>
            (ver, opt_line, num_ports, metadata)
        ))
    );

    mod test {
        use super::*;
        use nom;

        #[test]
        fn test_header() {
            assert_eq!(
                header(b"[version] 2.0\n#\n[number of ports] 1"),
                nom::IResult::Done(&b""[..], ((2i32, 0i32), Default::default(), 1, vec![]))
            );
        }
    }
}
<commit_msg>Almost there with the header info... need to get the begin/end information block working and comments allowed anywhere<commit_after>#[macro_use]
extern crate nom;

#[allow(dead_code)]
pub mod ts {

    use std;
    use std::fmt;
    use std::collections::HashMap;
    use std::str::from_utf8;

    use nom::*;

    #[derive(Debug, PartialEq)]
    pub enum Format {
        RI,
        DB,
        MA,
    }

    impl fmt::Display for Format {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Format::RI => "RI",
                &Format::DB => "DB",
                &Format::MA => "MA",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Format {
        fn default() -> Format {
            Format::MA
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum FreqUnits {
        Hz,
        KHz,
        MHz,
        GHz,
    }

    impl fmt::Display for FreqUnits {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &FreqUnits::Hz => "Hz",
                &FreqUnits::KHz => "KHz",
                &FreqUnits::MHz => "MHz",
                &FreqUnits::GHz => "GHz",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for FreqUnits {
        fn default() -> FreqUnits {
            FreqUnits::GHz
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum Domain {
        Scattering,
        Transfer,
        Admittance,
        Impedance,
        HybridH,
        HybridG,
    }

    impl fmt::Display for Domain {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Domain::Scattering => "S",
                &Domain::Transfer => "T",
                &Domain::Admittance => "Y",
                &Domain::Impedance => "Z",
                &Domain::HybridH => "H",
                &Domain::HybridG => "G",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Domain {
        fn default() -> Domain {
            Domain::Scattering
        }
    }

    #[derive(Debug, PartialEq)]
    pub struct OptionLine {
        pub freq_units: FreqUnits,
        pub domain: Domain,
        pub format: Format,
        pub system_impedance: Option<f64>,
    }

    impl Default for OptionLine {
        fn default() -> OptionLine {
            OptionLine {
                freq_units: Default::default(),
                domain: Default::default(),
                format: Default::default(),
                system_impedance: Default::default()
            }
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Debug, PartialEq)]
    pub enum TwoPortOrder {
        TPO_12_21,
        TPO_21_12,
    }

    #[derive(Debug, PartialEq)]
    pub struct NoisePoint {
        pub frequency: f64,
        pub minimum_noise_db: f64,
        pub source_refl_coeff_magnitude: f64,
        pub source_refl_coeff_phase: f64,
        pub effective_noise_resistance: f64,
    }

    #[derive(Debug)]
    pub struct Touchstone2 {
        pub num_ports: i32,
        pub option_line: OptionLine,
        pub two_port_order: Option<TwoPortOrder>,
        pub reference_impedance: Option<Vec<f64>>,
        pub information: HashMap<String, String>,
        pub network_data: Vec<Box<[(f64, f64)]>>,
        pub noise_data: Option<Box<[NoisePoint]>>,
    }

    fn parse<T: std::str::FromStr>(bytes: &[u8]) -> Option<T> {
        from_utf8(bytes).ok().and_then(|s| s.trim().parse().ok())
    }

    named!(
        int<i32>,
        map_res!(
            map_res!(
                ws!(digit),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    fn is_valid_float_char(c: u8) -> bool {
        (c >= 48u8 && c <= 57) || // is_digit
        (c == 43u8 || c == 45u8 || c == 46u8) || // + or - or .
        (c == 69u8 || c == 101u8) // E or e
    }

    named!(
        float<f64>,
        map_res!(
            map_res!(
                take_while!(is_valid_float_char),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    named!(
        comment_line<()>,
        value!((), delimited!(char!('!'), take_until!("\n"), char!('\n')))
    );

    named!(
        version<(i32, i32)>,
        do_parse!(
            tag_no_case!("[version]") >> many0!(space) >>
            ver: separated_pair!(int, char!('.'), int) >>
            (ver)
        )
    );

    fn not_whitespace(c: u8) -> bool {
        !(
            c == ' '  as u8 ||
            c == '\t' as u8 ||
            c == '\n' as u8 ||
            c == '\r' as u8
        )
    }

    named!(
        option_line<OptionLine>,
        do_parse!(
            char!('#') >>
            units: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(FreqUnits::Hz,  tag_no_case!("hz"))  |
                    value!(FreqUnits::KHz, tag_no_case!("khz")) |
                    value!(FreqUnits::MHz, tag_no_case!("mhz")) |
                    value!(FreqUnits::GHz, tag_no_case!("ghz"))
                ) >>
                (v)
            ))) >>
            domain: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Domain::Scattering, tag_no_case!("s")) |
                    value!(Domain::Transfer,   tag_no_case!("t")) |
                    value!(Domain::Admittance, tag_no_case!("y")) |
                    value!(Domain::Impedance,  tag_no_case!("z")) |
                    value!(Domain::HybridH,    tag_no_case!("h")) |
                    value!(Domain::HybridG,    tag_no_case!("g"))
                ) >>
                (v)
            ))) >>
            format: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Format::DB, tag_no_case!("db")) |
                    value!(Format::MA, tag_no_case!("ma")) |
                    value!(Format::RI, tag_no_case!("ri"))
                ) >>
                (v)
            ))) >>
            system_impedance: opt!(complete!(do_parse!(
                many1!(space) >>
                tag!("R") >>
                many1!(space) >>
                v: float >>
                (v)
            ))) >>
            (OptionLine {
                freq_units: units.unwrap_or(Default::default()),
                domain: domain.unwrap_or(Default::default()),
                format: format.unwrap_or(Default::default()),
                system_impedance: system_impedance
            })
        )
    );

    named!(
        number_of_ports<i32>,
        do_parse!(
            tag_no_case!("[number of ports]") >> many0!(space) >>
            num_ports: int >>
            (num_ports)
        )
    );

    named!(
        two_port_order<TwoPortOrder>,
        do_parse!(
            tag_no_case!("[two-port order]") >> many0!(space) >>
            tpo: alt!(
                value!(TwoPortOrder::TPO_12_21, tag!("12_21")) |
                value!(TwoPortOrder::TPO_21_12, tag!("21_12"))
            ) >>
            (tpo)
        )
    );

    named!(
        number_of_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        number_of_noise_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of noise frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        reference<Vec<f64>>,
        do_parse!(
            tag_no_case!("[reference]") >> many0!(multispace) >>
            refs: many1!(ws!(float)) >>
            (refs)
        )
    );

    named!(
        kwarg<(String, String)>,
        do_parse!(
            char!('[') >>
            kw: map_res!(
                take_until!("]"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            char!(']') >>
            many0!(space) >>
            arg: map_res!(
                take_until!("\n"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            (kw, "".to_string())
        )
    );

    named!(
        information<HashMap<String, String>>,
        ws!(delimited!(
            tag_no_case!("[begin information]"),
            map!(
                many0!(ws!(kwarg)),
                |v: Vec<(String, String)>| v.iter().cloned().collect()
            ),
            tag_no_case!("[end information]")
        ))
    );

    macro_rules! comments {
        ($i:expr, $($args:tt)*) => ({sep!($i, comment_line, $($args)*)})
    }

    #[allow(dead_code)]
    #[derive(Debug, PartialEq)]
    enum Metadata {
        TwoPortOrder(TwoPortOrder),
        NumberOfFrequencies(i32),
        NumberOfNoiseFrequencies(i32),
        Reference(Vec<f64>),
        Information(HashMap<String, String>),
    }

    named!(
        metadata<Vec<Metadata>>,
        many0!(ws!(alt!(
            map!(two_port_order, Metadata::TwoPortOrder) |
            map!(number_of_frequencies, Metadata::NumberOfFrequencies) |
            map!(number_of_noise_frequencies, Metadata::NumberOfNoiseFrequencies) |
            map!(reference, Metadata::Reference)
        )))
    );

    named!(
        header<((i32, i32), OptionLine, i32, Vec<Metadata>)>,
        ws!(do_parse!(
            ver: version >>
            opt_line: option_line >>
            num_ports: number_of_ports >>
            metadata: metadata >>
            (ver, opt_line, num_ports, metadata)
        ))
    );

    #[allow(unused_imports)]
    mod test {
        use super::*;
        use nom;

        fn done<'a, T>(val: T) -> nom::IResult<&'a [u8], T> {
            nom::IResult::Done(&b""[..], val)
        }

        #[test]
        fn test_comment_line() {
            assert_eq!(comment_line(b"!test\n"), done(()));
        }

        #[test]
        fn test_version() {
            assert_eq!(version(b"[version] 2.0"), done((2, 0)));
        }

        #[test]
        fn test_option_line() {
            assert_eq!(option_line(b"#"), done(Default::default()))
        }

        #[test]
        fn test_number_of_ports() {
            assert_eq!(number_of_ports(b"[number of ports] 1"), done(1));
        }

        #[test]
        fn test_header() {
            assert_eq!(
                header(b"\
                    [version] 2.0\n\
                    # Hz DB R 25\n\
                    [number of ports] 4\n\
                    [number of frequencies] 20\n\
                    [two-port order] 12_21\n\
                    [Number of noise frequenCIES] 1\n\
                    [Reference]\n\
                    25.0 50 5.0e1 100\n\
                "),
                done((
                    (2, 0),
                    OptionLine {
                        freq_units: FreqUnits::Hz,
                        domain: Domain::Scattering,
                        format: Format::DB,
                        system_impedance: Some(25.0),
                    },
                    4,
                    vec![
                        Metadata::NumberOfFrequencies(20),
                        Metadata::TwoPortOrder(TwoPortOrder::TPO_12_21),
                        Metadata::NumberOfNoiseFrequencies(1),
                        Metadata::Reference(vec![25.0, 50.0, 50.0, 100.0])
                    ]
                ))
            );
        }
    }
}
<|endoftext|>


<commit_before>//! Replaces the deprecated functionality of std::os::num_cpus.
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of CPUs of the current machine.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current machine.
/// If not possible on the particular architecture returns same as get() which
/// is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(
    any(
        target_os = "linux",
        target_os = "nacl",
        target_os = "macos",
        target_os = "ios",
        target_os = "android",
        target_os = "solaris",
        target_os = "fuchsia",
    )
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}
#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
    assert!(get_physical() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
    assert!(get_physical() < 236_451);
}
<commit_msg>Count processors via affinity mask on linux<commit_after>//! Replaces the deprecated functionality of std::os::num_cpus.
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of CPUs of the current machine.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current machine.
/// If not possible on the particular architecture returns same as get() which
/// is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(feature = "nightly")]
#[feature(asm, naked_functions)]
#[naked]
#[inline(always)]
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn popcnt(bits: u64) -> u64 {
    let ret: u64;
    unsafe {
        asm!("popcntq %rdi, %rax"
             : "={rax}"(ret)
             : "{rdi}"(bits)
        );
    };
    ret
}

#[cfg(not(feature = "nightly"))]
#[inline(always)]
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn popcnt(n: u64) -> u64 {
    let mut c: u64 = (n & 0x5555555555555555) + ((n >> 1) & 0x5555555555555555);
    c = (c & 0x3333333333333333) + ((c >> 2) & 0x3333333333333333);
    c = (c & 0x0f0f0f0f0f0f0f0f) + ((c >> 4) & 0x0f0f0f0f0f0f0f0f);
    c = (c & 0x00ff00ff00ff00ff) + ((c >> 8) & 0x00ff00ff00ff00ff);
    c = (c & 0x0000ffff0000ffff) + ((c >> 16) & 0x0000ffff0000ffff);
    c = (c & 0x00000000ffffffff) + ((c >> 32) & 0x00000000ffffffff);
    c
}

#[inline(always)]
#[cfg(all(target_os = "linux", not(target_arch = "x86_64")))]
fn popcnt(n: u64) -> u64 {
    let mut c: u64 = (n & 0x5555555555555555) + ((n >> 1) & 0x5555555555555555);
    c = (c & 0x3333333333333333) + ((c >> 2) & 0x3333333333333333);
    c = (c & 0x0f0f0f0f0f0f0f0f) + ((c >> 4) & 0x0f0f0f0f0f0f0f0f);
    c = (c & 0x00ff00ff00ff00ff) + ((c >> 8) & 0x00ff00ff00ff00ff);
    c = (c & 0x0000ffff0000ffff) + ((c >> 16) & 0x0000ffff0000ffff);
    c = (c & 0x00000000ffffffff) + ((c >> 32) & 0x00000000ffffffff);
    c
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let ptr = unsafe { std::mem::transmute::<&libc::cpu_set_t, &u64>(&set) };
        popcnt(*ptr) as usize
    } else {
        unsafe {
            libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}
#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
    assert!(get_physical() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
    assert!(get_physical() < 236_451);
}
<|endoftext|>