<commit_before>extern crate c_ares_sys;
extern crate libc;

use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::ptr;
use std::slice;
use std::str;

use error::AresError ;
use utils::ares_error;

/// The result of a successful SOA lookup.
#[derive(Debug)]
#[allow(raw_pointer_derive)]
pub struct SOAResult {
    soa_reply: *mut c_ares_sys::Struct_ares_soa_reply,
    phantom: PhantomData<c_ares_sys::Struct_ares_soa_reply>,
}

impl SOAResult {
    /// Obtain an `SOAResult` from the response to a CNAME lookup.
    pub fn parse_from(data: &[u8]) -> Result<SOAResult, AresError> {
        let mut soa_reply: *mut c_ares_sys::Struct_ares_soa_reply =
            ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_soa_reply(
                data.as_ptr(),
                data.len() as libc::c_int,
                &mut soa_reply)
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let result = SOAResult::new(soa_reply);
            Ok(result)
        }
    }

    fn new(soa_reply: *mut c_ares_sys::Struct_ares_soa_reply) -> SOAResult {
        SOAResult {
            soa_reply: soa_reply,
            phantom: PhantomData,
        }
    }

    /// Returns the name server from this `SOAResult`.
    pub fn name_server(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr((*self.soa_reply).nsname);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Returns the hostmaster from this `SOAResult`.
    pub fn hostmaster(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr((*self.soa_reply).hostmaster);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Returns the serial number from this `SOAResult`.
    pub fn serial(&self) -> u32 {
        unsafe { (*self.soa_reply).serial }
    }

    /// Returns the refresh time from this `SOAResult`.
    pub fn refresh(&self) -> u32 {
        unsafe { (*self.soa_reply).refresh }
    }

    /// Returns the retry time from this `SOAResult`.
    pub fn retry(&self) -> u32 {
        unsafe { (*self.soa_reply).retry }
    }

    /// Returns the expire time from this `SOAResult`.
    pub fn expire(&self) -> u32 {
        unsafe { (*self.soa_reply).expire }
    }

    /// Returns the minimum time-to-live from this `SOAResult`.
    pub fn min_ttl(&self) -> u32 {
        unsafe { (*self.soa_reply).minttl }
    }
}

impl fmt::Display for SOAResult {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "Name server: {}, ", self.name_server()));
        try!(write!(fmt, "Hostmaster: {}, ", self.hostmaster()));
        try!(write!(fmt, "Serial: {}, ", self.serial()));
        try!(write!(fmt, "Refresh: {}, ", self.refresh()));
        try!(write!(fmt, "Retry: {}, ", self.retry()));
        try!(write!(fmt, "Expire: {}, ", self.expire()));
        try!(write!(fmt, "Minimum time-to-live: {}", self.min_ttl()));
        Ok(())
    }
}

impl Drop for SOAResult {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_free_data(self.soa_reply as *mut libc::c_void);
        }
    }
}

unsafe impl Send for SOAResult { }
unsafe impl Sync for SOAResult { }

pub unsafe extern "C" fn query_soa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<SOAResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let data = slice::from_raw_parts(abuf, alen as usize);
        SOAResult::parse_from(data)
    };
    let handler = Box::from_raw(arg as *mut F);
    handler(result);
}
<commit_msg>Docs fix<commit_after>extern crate c_ares_sys;
extern crate libc;

use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::ptr;
use std::slice;
use std::str;

use error::AresError ;
use utils::ares_error;

/// The result of a successful SOA lookup.
#[derive(Debug)]
#[allow(raw_pointer_derive)]
pub struct SOAResult {
    soa_reply: *mut c_ares_sys::Struct_ares_soa_reply,
    phantom: PhantomData<c_ares_sys::Struct_ares_soa_reply>,
}

impl SOAResult {
    /// Obtain an `SOAResult` from the response to an SOA lookup.
    pub fn parse_from(data: &[u8]) -> Result<SOAResult, AresError> {
        let mut soa_reply: *mut c_ares_sys::Struct_ares_soa_reply =
            ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_soa_reply(
                data.as_ptr(),
                data.len() as libc::c_int,
                &mut soa_reply)
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let result = SOAResult::new(soa_reply);
            Ok(result)
        }
    }

    fn new(soa_reply: *mut c_ares_sys::Struct_ares_soa_reply) -> SOAResult {
        SOAResult {
            soa_reply: soa_reply,
            phantom: PhantomData,
        }
    }

    /// Returns the name server from this `SOAResult`.
    pub fn name_server(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr((*self.soa_reply).nsname);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Returns the hostmaster from this `SOAResult`.
    pub fn hostmaster(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr((*self.soa_reply).hostmaster);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Returns the serial number from this `SOAResult`.
    pub fn serial(&self) -> u32 {
        unsafe { (*self.soa_reply).serial }
    }

    /// Returns the refresh time from this `SOAResult`.
    pub fn refresh(&self) -> u32 {
        unsafe { (*self.soa_reply).refresh }
    }

    /// Returns the retry time from this `SOAResult`.
    pub fn retry(&self) -> u32 {
        unsafe { (*self.soa_reply).retry }
    }

    /// Returns the expire time from this `SOAResult`.
    pub fn expire(&self) -> u32 {
        unsafe { (*self.soa_reply).expire }
    }

    /// Returns the minimum time-to-live from this `SOAResult`.
    pub fn min_ttl(&self) -> u32 {
        unsafe { (*self.soa_reply).minttl }
    }
}

impl fmt::Display for SOAResult {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "Name server: {}, ", self.name_server()));
        try!(write!(fmt, "Hostmaster: {}, ", self.hostmaster()));
        try!(write!(fmt, "Serial: {}, ", self.serial()));
        try!(write!(fmt, "Refresh: {}, ", self.refresh()));
        try!(write!(fmt, "Retry: {}, ", self.retry()));
        try!(write!(fmt, "Expire: {}, ", self.expire()));
        try!(write!(fmt, "Minimum time-to-live: {}", self.min_ttl()));
        Ok(())
    }
}

impl Drop for SOAResult {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_free_data(self.soa_reply as *mut libc::c_void);
        }
    }
}

unsafe impl Send for SOAResult { }
unsafe impl Sync for SOAResult { }

pub unsafe extern "C" fn query_soa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<SOAResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let data = slice::from_raw_parts(abuf, alen as usize);
        SOAResult::parse_from(data)
    };
    let handler = Box::from_raw(arg as *mut F);
    handler(result);
}
<|endoftext|>


<commit_before>use std::char;
use std::io::{Read, Result, Error, ErrorKind, Seek, SeekFrom, BufReader};
use std::path::Path;
use std::fs::File;
use super::binary_reader::{ReadAll, BinaryRead, BinaryReader};

macro_rules! fail {
    ($expr:expr) => (return Err(Error::new(ErrorKind::Other, $expr)))
}

pub const RAM_LEN: usize = 0x10000;
pub const REG_LEN: usize = 128;
pub const IPL_ROM_LEN: usize = 64;

const HEADER_LEN: usize = 33;
const HEADER_BYTES: &'static [u8; HEADER_LEN] =
    b"SNES-SPC700 Sound File Data v0.30";

pub struct Spc {
    pub version_minor: u8,
    pub pc: u16,
    pub a: u8,
    pub x: u8,
    pub y: u8,
    pub psw: u8,
    pub sp: u8,
    pub id666_tag: Option<Id666Tag>,
    pub ram: [u8; RAM_LEN],
    pub regs: [u8; REG_LEN],
    pub ipl_rom: [u8; IPL_ROM_LEN]
}

impl Spc {
    pub fn load<P: AsRef<Path>>(path: P) -> Result<Spc> {
        let file = try!(File::open(path));
        Spc::from_reader(BufReader::new(file))
    }
    pub fn from_reader<R: Read + Seek>(reader: R) -> Result<Spc> {
        let mut r = BinaryReader::new(reader);

        let mut header = [0; HEADER_LEN];
        try!(r.read_all(&mut header));
        if header.iter().zip(HEADER_BYTES.iter()).any(|(x, y)| x != y) {
            fail!("Invalid header string");
        }

        if try!(r.read_le_u16()) != 0x1a1a {
            fail!("Invalid padding bytes");
        }

        let has_id666_tag = match try!(r.read_u8()) {
            0x1a => true,
            0x1b => false,
            _ => fail!("Unable to determine if file contains ID666 tag")
        };

        let version_minor = try!(r.read_u8());

        let pc = try!(r.read_le_u16());
        let a = try!(r.read_u8());
        let x = try!(r.read_u8());
        let y = try!(r.read_u8());
        let psw = try!(r.read_u8());
        let sp = try!(r.read_u8());

        let id666_tag = match has_id666_tag {
            true => {
                try!(r.seek(SeekFrom::Start(0x2e)));
                match Id666Tag::load(&mut r) {
                    Ok(x) => Some(x),
                    Err(e) => fail!(format!("Invalid ID666 tag: {}", e))
                }
            },
            false => None
        };

        try!(r.seek(SeekFrom::Start(0x100)));
        let mut ram = [0; RAM_LEN];
        try!(r.read_all(&mut ram));
        let mut regs = [0; REG_LEN];
        try!(r.read_all(&mut regs));
        try!(r.seek(SeekFrom::Start(0x101c0)));
        let mut ipl_rom = [0; IPL_ROM_LEN];
        try!(r.read_all(&mut ipl_rom));

        Ok(Spc {
            version_minor: version_minor,
            pc: pc,
            a: a,
            x: x,
            y: y,
            psw: psw,
            sp: sp,
            id666_tag: id666_tag,
            ram: ram,
            regs: regs,
            ipl_rom: ipl_rom
        })
    }
}

pub struct Id666Tag {
    pub song_title: String,
    pub game_title: String,
    pub dumper_name: String,
    pub comments: String,
    pub date_dumped: String,
    pub seconds_to_play_before_fading_out: i32,
    pub fade_out_length: i32,
    pub artist_name: String,
    pub default_channel_disables: u8,
    pub dumping_emulator: Emulator
}

pub enum Emulator {
    Unknown,
    ZSnes,
    Snes9x
}

impl Id666Tag {
    fn load<R: BinaryRead + Seek>(r: &mut R) -> Result<Id666Tag> {
        let song_title = try!(Id666Tag::read_string(r, 32));
        let game_title = try!(Id666Tag::read_string(r, 32));
        let dumper_name = try!(Id666Tag::read_string(r, 16));
        let comments = try!(Id666Tag::read_string(r, 32));

        // So, apparently, there's really no reliable way to detect whether or not
        //  an id666 tag is in text or binary format. I tried using the date field,
        //  but that's actually invalid in a lot of files anyways. I've read that
        //  the dumping emu can give clues (znes seems to dump binary files and
        //  snes9x seems to dump text), but these don't cover cases where the
        //  dumping emu is "unknown", so that sucks too. I've even seen some source
        //  where people try to differentiate based on the value of the psw register
        //  (lol). Ultimately, the most sensible solution I was able to dig up that
        //  seems to work on all of the .spc's I've tried is to just check if there
        //  appears to be textual data where the length and/or date fields should be.
        //  Still pretty icky, but it works pretty well.
        try!(r.seek(SeekFrom::Start(0x9e)));
        let is_text_format = match try!(Id666Tag::is_text_region(r, 11)) {
            true => {
                try!(r.seek(SeekFrom::Start(0xa9)));
                try!(Id666Tag::is_text_region(r, 3))
            },
            _ => false
        };

        try!(r.seek(SeekFrom::Start(0x9e)));

        let (date_dumped, seconds_to_play_before_fading_out, fade_out_length) =
            if is_text_format {
                let date_dumped = try!(Id666Tag::read_string(r, 11));
                let seconds_to_play_before_fading_out = try!(Id666Tag::read_number(r, 3));
                let fade_out_length = try!(Id666Tag::read_number(r, 5));

                (date_dumped, seconds_to_play_before_fading_out, fade_out_length)
            } else {
                // TODO: Find SPC's to test this with
                unimplemented!();

                /*let year = try!(r.read_le_u16());
                let month = try!(r.read_u8());
                let day = try!(r.read_u8());
                let date_dumped = format!("{}/{}/{}", month, day, year);

                try!(r.seek(SeekFrom::Start(0xa9)));
                let seconds_to_play_before_fading_out = try!(r.read_le_u16());
                try!(r.read_u8());
                let fade_out_length = try!(r.read_le_i32());

                (date_dumped, seconds_to_play_before_fading_out, fade_out_length)*/
            };

        let artist_name = try!(Id666Tag::read_string(r, 32));

        let default_channel_disables = try!(r.read_u8());

        let dumping_emulator = match Id666Tag::read_digit(r) {
            Ok(1) => Emulator::ZSnes,
            Ok(2) => Emulator::Snes9x,
            _ => Emulator::Unknown
        };

        Ok(Id666Tag {
            song_title: song_title,
            game_title: game_title,
            dumper_name: dumper_name,
            comments: comments,
            date_dumped: date_dumped,
            seconds_to_play_before_fading_out: seconds_to_play_before_fading_out,
            fade_out_length: fade_out_length,
            artist_name: artist_name,
            default_channel_disables: default_channel_disables,
            dumping_emulator: dumping_emulator
        })
    }

    fn read_string<R: BinaryRead>(r: &mut R, max_len: i32) -> Result<String> {
        // TODO: Reimplement as iterator or something similar
        let mut ret = "".to_string();
        let mut has_ended = false;
        for _ in 0..max_len {
            let b = try!(r.read_u8());
            if !has_ended {
                match char::from_u32(b as u32) {
                    Some(c) if b != 0 => ret.push(c),
                    _ => has_ended = true
                }
            }
        }
        Ok(ret)
    }

    fn is_text_region<R: BinaryRead>(r: &mut R, len: i32) -> Result<bool> {
        // TODO: This code is probably shit
        for _ in 0..len {
            let b = try!(r.read_u8());
            if b != 0 {
                if let Some(c) = char::from_u32(b as u32) {
                    if !c.is_digit(10) && c != '/' {
                        return Ok(false);
                    }
                }
            }
        }
        Ok(true)
    }

    fn read_digit<R: BinaryRead>(r: &mut R) -> Result<i32> {
        let d = try!(r.read_u8());
        Id666Tag::digit(d)
    }

    fn digit(d: u8) -> Result<i32> {
        match char::from_u32(d as u32) {
            Some(c) if c.is_digit(10) => Ok(c.to_digit(10).unwrap() as i32),
            _ => fail!("Expected numeric value")
        }
    }

    fn read_number<R: BinaryRead>(r: &mut R, max_len: i32) -> Result<i32> {
        let mut ret = 0;
        for _ in 0..max_len {
        let d = try!(r.read_u8());
            if d == 0 {
                break;
            }
            ret *= 10;
            ret += try!(Id666Tag::digit(d));
        }
        Ok(ret)
    }
}
<commit_msg>Add a blank like<commit_after>use std::char;
use std::io::{Read, Result, Error, ErrorKind, Seek, SeekFrom, BufReader};
use std::path::Path;
use std::fs::File;
use super::binary_reader::{ReadAll, BinaryRead, BinaryReader};

macro_rules! fail {
    ($expr:expr) => (return Err(Error::new(ErrorKind::Other, $expr)))
}

pub const RAM_LEN: usize = 0x10000;
pub const REG_LEN: usize = 128;
pub const IPL_ROM_LEN: usize = 64;

const HEADER_LEN: usize = 33;
const HEADER_BYTES: &'static [u8; HEADER_LEN] =
    b"SNES-SPC700 Sound File Data v0.30";

pub struct Spc {
    pub version_minor: u8,
    pub pc: u16,
    pub a: u8,
    pub x: u8,
    pub y: u8,
    pub psw: u8,
    pub sp: u8,
    pub id666_tag: Option<Id666Tag>,
    pub ram: [u8; RAM_LEN],
    pub regs: [u8; REG_LEN],
    pub ipl_rom: [u8; IPL_ROM_LEN]
}

impl Spc {
    pub fn load<P: AsRef<Path>>(path: P) -> Result<Spc> {
        let file = try!(File::open(path));
        Spc::from_reader(BufReader::new(file))
    }

    pub fn from_reader<R: Read + Seek>(reader: R) -> Result<Spc> {
        let mut r = BinaryReader::new(reader);

        let mut header = [0; HEADER_LEN];
        try!(r.read_all(&mut header));
        if header.iter().zip(HEADER_BYTES.iter()).any(|(x, y)| x != y) {
            fail!("Invalid header string");
        }

        if try!(r.read_le_u16()) != 0x1a1a {
            fail!("Invalid padding bytes");
        }

        let has_id666_tag = match try!(r.read_u8()) {
            0x1a => true,
            0x1b => false,
            _ => fail!("Unable to determine if file contains ID666 tag")
        };

        let version_minor = try!(r.read_u8());

        let pc = try!(r.read_le_u16());
        let a = try!(r.read_u8());
        let x = try!(r.read_u8());
        let y = try!(r.read_u8());
        let psw = try!(r.read_u8());
        let sp = try!(r.read_u8());

        let id666_tag = match has_id666_tag {
            true => {
                try!(r.seek(SeekFrom::Start(0x2e)));
                match Id666Tag::load(&mut r) {
                    Ok(x) => Some(x),
                    Err(e) => fail!(format!("Invalid ID666 tag: {}", e))
                }
            },
            false => None
        };

        try!(r.seek(SeekFrom::Start(0x100)));
        let mut ram = [0; RAM_LEN];
        try!(r.read_all(&mut ram));
        let mut regs = [0; REG_LEN];
        try!(r.read_all(&mut regs));
        try!(r.seek(SeekFrom::Start(0x101c0)));
        let mut ipl_rom = [0; IPL_ROM_LEN];
        try!(r.read_all(&mut ipl_rom));

        Ok(Spc {
            version_minor: version_minor,
            pc: pc,
            a: a,
            x: x,
            y: y,
            psw: psw,
            sp: sp,
            id666_tag: id666_tag,
            ram: ram,
            regs: regs,
            ipl_rom: ipl_rom
        })
    }
}

pub struct Id666Tag {
    pub song_title: String,
    pub game_title: String,
    pub dumper_name: String,
    pub comments: String,
    pub date_dumped: String,
    pub seconds_to_play_before_fading_out: i32,
    pub fade_out_length: i32,
    pub artist_name: String,
    pub default_channel_disables: u8,
    pub dumping_emulator: Emulator
}

pub enum Emulator {
    Unknown,
    ZSnes,
    Snes9x
}

impl Id666Tag {
    fn load<R: BinaryRead + Seek>(r: &mut R) -> Result<Id666Tag> {
        let song_title = try!(Id666Tag::read_string(r, 32));
        let game_title = try!(Id666Tag::read_string(r, 32));
        let dumper_name = try!(Id666Tag::read_string(r, 16));
        let comments = try!(Id666Tag::read_string(r, 32));

        // So, apparently, there's really no reliable way to detect whether or not
        //  an id666 tag is in text or binary format. I tried using the date field,
        //  but that's actually invalid in a lot of files anyways. I've read that
        //  the dumping emu can give clues (znes seems to dump binary files and
        //  snes9x seems to dump text), but these don't cover cases where the
        //  dumping emu is "unknown", so that sucks too. I've even seen some source
        //  where people try to differentiate based on the value of the psw register
        //  (lol). Ultimately, the most sensible solution I was able to dig up that
        //  seems to work on all of the .spc's I've tried is to just check if there
        //  appears to be textual data where the length and/or date fields should be.
        //  Still pretty icky, but it works pretty well.
        try!(r.seek(SeekFrom::Start(0x9e)));
        let is_text_format = match try!(Id666Tag::is_text_region(r, 11)) {
            true => {
                try!(r.seek(SeekFrom::Start(0xa9)));
                try!(Id666Tag::is_text_region(r, 3))
            },
            _ => false
        };

        try!(r.seek(SeekFrom::Start(0x9e)));

        let (date_dumped, seconds_to_play_before_fading_out, fade_out_length) =
            if is_text_format {
                let date_dumped = try!(Id666Tag::read_string(r, 11));
                let seconds_to_play_before_fading_out = try!(Id666Tag::read_number(r, 3));
                let fade_out_length = try!(Id666Tag::read_number(r, 5));

                (date_dumped, seconds_to_play_before_fading_out, fade_out_length)
            } else {
                // TODO: Find SPC's to test this with
                unimplemented!();

                /*let year = try!(r.read_le_u16());
                let month = try!(r.read_u8());
                let day = try!(r.read_u8());
                let date_dumped = format!("{}/{}/{}", month, day, year);

                try!(r.seek(SeekFrom::Start(0xa9)));
                let seconds_to_play_before_fading_out = try!(r.read_le_u16());
                try!(r.read_u8());
                let fade_out_length = try!(r.read_le_i32());

                (date_dumped, seconds_to_play_before_fading_out, fade_out_length)*/
            };

        let artist_name = try!(Id666Tag::read_string(r, 32));

        let default_channel_disables = try!(r.read_u8());

        let dumping_emulator = match Id666Tag::read_digit(r) {
            Ok(1) => Emulator::ZSnes,
            Ok(2) => Emulator::Snes9x,
            _ => Emulator::Unknown
        };

        Ok(Id666Tag {
            song_title: song_title,
            game_title: game_title,
            dumper_name: dumper_name,
            comments: comments,
            date_dumped: date_dumped,
            seconds_to_play_before_fading_out: seconds_to_play_before_fading_out,
            fade_out_length: fade_out_length,
            artist_name: artist_name,
            default_channel_disables: default_channel_disables,
            dumping_emulator: dumping_emulator
        })
    }

    fn read_string<R: BinaryRead>(r: &mut R, max_len: i32) -> Result<String> {
        // TODO: Reimplement as iterator or something similar
        let mut ret = "".to_string();
        let mut has_ended = false;
        for _ in 0..max_len {
            let b = try!(r.read_u8());
            if !has_ended {
                match char::from_u32(b as u32) {
                    Some(c) if b != 0 => ret.push(c),
                    _ => has_ended = true
                }
            }
        }
        Ok(ret)
    }

    fn is_text_region<R: BinaryRead>(r: &mut R, len: i32) -> Result<bool> {
        // TODO: This code is probably shit
        for _ in 0..len {
            let b = try!(r.read_u8());
            if b != 0 {
                if let Some(c) = char::from_u32(b as u32) {
                    if !c.is_digit(10) && c != '/' {
                        return Ok(false);
                    }
                }
            }
        }
        Ok(true)
    }

    fn read_digit<R: BinaryRead>(r: &mut R) -> Result<i32> {
        let d = try!(r.read_u8());
        Id666Tag::digit(d)
    }

    fn digit(d: u8) -> Result<i32> {
        match char::from_u32(d as u32) {
            Some(c) if c.is_digit(10) => Ok(c.to_digit(10).unwrap() as i32),
            _ => fail!("Expected numeric value")
        }
    }

    fn read_number<R: BinaryRead>(r: &mut R, max_len: i32) -> Result<i32> {
        let mut ret = 0;
        for _ in 0..max_len {
        let d = try!(r.read_u8());
            if d == 0 {
                break;
            }
            ret *= 10;
            ret += try!(Id666Tag::digit(d));
        }
        Ok(ret)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// 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.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! ## SPI buses
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`.
//! Replace `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave
//! Select pins. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs`
//! or `1cs` if you only require 2 or 1 Slave Select pins. The associated pins
//! are listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration.
//! Some of their functionality can be moved to different pins. Read
//! `/boot/overlays/README` for more information.
//!
//! ## Buffer size limits
//!
//! By default, the Linux SPI driver can handle up to 4096 bytes in a single
//! transfer. You can increase this limit to a maximum of 65536 bytes by adding
//! `spidev.bufsiz=65536` to the single line of parameters in `/boot/cmdline.txt`.
//! Remember to reboot the Raspberry Pi afterwards. The current value of bufsiz
//! can be checked with `cat /sys/module/spidev/parameters/bufsiz`.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// System call error.
        Sys(errno: Errno) { display("System call error: {}", errno) }
/// Invalid path.
        InvalidPath {}
/// Invalid Utf8.
        InvalidUtf8 {}
/// Unsupported operation.
        UnsupportedOperation {}
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Sys(errno),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;
    const SPI_IOC_TYPE_MODE32: u8 = 5;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. 0 = default.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits. 0 = default.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next, and keep SS active after the final segment.
        cs_change: u8,
        // Used for dual/quad SPI.
        tx_nbits: u8,
        // Used for dual/quad SPI.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    // TODO: Doublecheck cs_change behavior after the final transfer segment. Some SPI drivers may have
    // interpreted the documentation incorrectly.

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; TransferSegment);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE32);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should pay attention to
/// the SPI bus. Chip Enable is more commonly known as Slave Select or Chip
/// Select.
///
/// The number of available Chip Enable pins for the selected SPI bus depends
/// on your `/boot/config.txt` configuration. More information can be found
/// [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode configures the
/// clock polarity (CPOL) and clock phase (CPHA) as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're
/// communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will
/// transfer the least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses
/// and Chip Enable pins enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its
    /// associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver
    /// will automatically select the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to
    /// most-significant bit first or least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the maximum number of bytes read depends on the length of `buffer`.
    ///
    /// During the read, the MOSI line is kept in a state that results in a
    /// zero value byte shifted out for every byte `read` receives on the MISO
    /// line.
    ///
    /// Chip Enable is set to Low (active) at the start of the read, and High
    /// (inactive) when the read completes.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        Ok(self.spidev.read(buffer)?)
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    ///
    /// Any data received on the MISO line from the slave is ignored.
    ///
    /// Chip Enable is set to Low (active) at the start of the write, and
    /// High (inactive) when the write completes.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        Ok(self.spidev.write(buffer)?)
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device
    /// on the MOSI line while simultaneously shifting in bits it receives on
    /// the MISO line. `transfer` stores the incoming data in `read_buffer`,
    /// and sends the outgoing data contained in `write_buffer`.
    ///
    /// Because data is sent and received simultaneously, `transfer` only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// Chip Enable is set to Low (active) at the start of the transfer, and
    /// High (inactive) when the transfer completes.
    ///
    /// Returns how many bytes were transferred.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
        let segment = ioctl::TransferSegment::new(Some(read_buffer), Some(write_buffer));

        unsafe {
            ioctl::spi_transfer(self.spidev.as_raw_fd(), &[segment])?;
        }

        Ok(segment.len() as usize)
    }
}
<commit_msg>Convert raw OS errorcodes to io::Errors<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// 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.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! ## SPI buses
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`.
//! Replace `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave
//! Select pins. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs`
//! or `1cs` if you only require 2 or 1 Slave Select pins. The associated pins
//! are listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration.
//! Some of their functionality can be moved to different pins. Read
//! `/boot/overlays/README` for more information.
//!
//! ## Buffer size limits
//!
//! By default, the Linux SPI driver can handle up to 4096 bytes in a single
//! transfer. You can increase this limit to a maximum of 65536 bytes by adding
//! `spidev.bufsiz=65536` to the single line of parameters in `/boot/cmdline.txt`.
//! Remember to reboot the Raspberry Pi afterwards. The current value of bufsiz
//! can be checked with `cat /sys/module/spidev/parameters/bufsiz`.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Invalid path.
        InvalidPath { description("invalid path") }
/// Invalid Utf8.
        InvalidUtf8 { description("invalid UTF8") }
/// Unsupported operation.
        UnsupportedOperation { description("unsupported operation") }
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Io(io::Error::from_raw_os_error(errno as i32)),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;
    const SPI_IOC_TYPE_MODE32: u8 = 5;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. Default = 0.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits. Default = 0.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next, and keep SS active after the final segment.
        cs_change: u8,
        // Number of bits used for writing (dual/quad SPI). Default = 0.
        tx_nbits: u8,
        // Number of bits used for reading (dual/quad SPI). Default = 0.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    // TODO: Doublecheck cs_change behavior after the final transfer segment. Some SPI drivers may have
    // interpreted the documentation incorrectly.

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; TransferSegment);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE32);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should pay attention to
/// the SPI bus. Chip Enable is more commonly known as Slave Select or Chip
/// Select.
///
/// The number of available Chip Enable pins for the selected SPI bus depends
/// on your `/boot/config.txt` configuration. More information can be found
/// [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode configures the
/// clock polarity (CPOL) and clock phase (CPHA) as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're
/// communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will
/// transfer the least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses
/// and Chip Enable pins enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its
    /// associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver
    /// will automatically select the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to
    /// most-significant bit first or least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the maximum number of bytes read depends on the length of `buffer`.
    ///
    /// During the read, the MOSI line is kept in a state that results in a
    /// zero value byte shifted out for every byte `read` receives on the MISO
    /// line.
    ///
    /// Chip Enable is set to Low (active) at the start of the read, and High
    /// (inactive) when the read completes.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        Ok(self.spidev.read(buffer)?)
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    ///
    /// Any data received on the MISO line from the slave is ignored.
    ///
    /// Chip Enable is set to Low (active) at the start of the write, and
    /// High (inactive) when the write completes.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        Ok(self.spidev.write(buffer)?)
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device
    /// on the MOSI line while simultaneously shifting in bits it receives on
    /// the MISO line. `transfer` stores the incoming data in `read_buffer`,
    /// and sends the outgoing data contained in `write_buffer`.
    ///
    /// Because data is sent and received simultaneously, `transfer` only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// Chip Enable is set to Low (active) at the start of the transfer, and
    /// High (inactive) when the transfer completes.
    ///
    /// Returns how many bytes were transferred.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
        let segment = ioctl::TransferSegment::new(Some(read_buffer), Some(write_buffer));

        unsafe {
            ioctl::spi_transfer(self.spidev.as_raw_fd(), &[segment])?;
        }

        Ok(segment.len() as usize)
    }
}
<|endoftext|>


<commit_before>//! Parsers and helper functions operating on strings, especially useful when writing parsers for
//! text-based formats.

use ::{
    Err,
    ErrorKind,
    IResult,
    Needed,

};

/// Parses the input for the supplied tag, and returns it on success.
pub fn tag_str<'a>(input: &'a str, tag: &str) -> IResult<&'a str, &'a str> {
    if input.starts_with(tag) {
        IResult::Done(&input[tag.len()..], &input[0..tag.len()])
    } else {
        if input.len() < tag.len() {
            IResult::Incomplete(Needed::Size(input.len()))
        } else {
            IResult::Error(Err::Position(ErrorKind::Tag, input))
        }
    }
}

#[cfg(test)]
mod test {
    use ::IResult;
    use super::tag_str;

    #[test]
    fn tag_str_succeed() {
        const INPUT: &'static str = "Hello World!";
        const TAG: &'static str = "Hello";

        match tag_str(INPUT, TAG) {
            IResult::Done(extra, output) => {
                assert!(extra == " World!", "Parser `tag_str` consumed leftover input.");
                assert!(output == TAG,
                    "Parser `tag_str` doesn't return the tag it matched on success. \
                     Expected `{}`, got `{}`.", TAG, output);
            },
            other => panic!("Parser `tag_str` didn't succeed when it should have. \
                             Got `{:?}`.", other),
        };
    }

    #[test]
    fn tag_str_incomplete() {
        const INPUT: &'static str = "Hello";
        const TAG: &'static str = "Hello World!";

        match tag_str(INPUT, TAG) {
            IResult::Incomplete(_) => (),
            other => {
                panic!("Parser `tag_str` didn't require more input when it should have. \
                        Got `{:?}`.", other);
            }
        };
    }

    #[test]
    fn tag_str_error() {
        const INPUT: &'static str = "Hello World!";
        const TAG: &'static str = "Random String";

        match tag_str(INPUT, TAG) {
            IResult::Error(_) => (),
            other => {
                panic!("Parser `tag_str` didn't fail when it should have. Got `{:?}`.`", other);
            },
        };
    }
}
<commit_msg>Fixed unit test.<commit_after>//! Parsers and helper functions operating on strings, especially useful when writing parsers for
//! text-based formats.

use ::{
    Err,
    ErrorKind,
    IResult,
    Needed,

};

/// Parses the input for the supplied tag, and returns it on success.
// TODO: Examples once we have figured out a way to do str-based parsing in Nom.
pub fn tag_str<'a>(input: &'a str, tag: &str) -> IResult<&'a str, &'a str> {
    if input.len() < tag.len() {
        IResult::Incomplete(Needed::Size(input.len()))
    } else if input.starts_with(tag) {
        IResult::Done(&input[tag.len()..], &input[0..tag.len()])
    } else {
        IResult::Error(Err::Position(ErrorKind::Tag, input))
    }
}

#[cfg(test)]
mod test {
    use ::IResult;
    use super::tag_str;

    #[test]
    fn tag_str_succeed() {
        const INPUT: &'static str = "Hello World!";
        const TAG: &'static str = "Hello";

        match tag_str(INPUT, TAG) {
            IResult::Done(extra, output) => {
                assert!(extra == " World!", "Parser `tag_str` consumed leftover input.");
                assert!(output == TAG,
                    "Parser `tag_str` doesn't return the tag it matched on success. \
                     Expected `{}`, got `{}`.", TAG, output);
            },
            other => panic!("Parser `tag_str` didn't succeed when it should have. \
                             Got `{:?}`.", other),
        };
    }

    #[test]
    fn tag_str_incomplete() {
        const INPUT: &'static str = "Hello";
        const TAG: &'static str = "Hello World!";

        match tag_str(INPUT, TAG) {
            IResult::Incomplete(_) => (),
            other => {
                panic!("Parser `tag_str` didn't require more input when it should have. \
                        Got `{:?}`.", other);
            }
        };
    }

    #[test]
    fn tag_str_error() {
        const INPUT: &'static str = "Hello World!";
        const TAG: &'static str = "Random"; // TAG must be closer than INPUT.

        match tag_str(INPUT, TAG) {
            IResult::Error(_) => (),
            other => {
                panic!("Parser `tag_str` didn't fail when it should have. Got `{:?}`.`", other);
            },
        };
    }
}
<|endoftext|>


<commit_before>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, ContentType, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.ip().to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(ContentType::form_url_encoded());
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/org/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.ip().to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}&scope=read:org",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<commit_msg>Fix org thing<commit_after>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, ContentType, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.ip().to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(ContentType::form_url_encoded());
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/orgs/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.ip().to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}&scope=read:org",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<|endoftext|>


<commit_before>use board;
use fen;

pub fn looping(cboard : &mut board::chessboard) {
    use std::io;

    let stdin = io::stdin();
    let mut input = &mut String::new();

    loop {
        input.clear();
        stdin.read_line(input).unwrap();

        if input == "" || input == "\n" {
            continue
        }
        else if input == "uci\n" {
            startup_info();
        }
        else if input == "isready\n" {
            println!("readyok");
        }
        else if input.split_whitespace().any(|x| x == "ucinewgame") {
            new_game(cboard);
        }
        else if input.split_whitespace().any(|x| x == "position") {
            parse_position(input, cboard);
        }
        else if input.split_whitespace().any(|x| x == "go") {
            parse_search(input, cboard);
        }
        else if input.split_whitespace().any(|x| x == "quit") {
            break
        }
    }
}

fn startup_info() {
    println!("id name Cicada");
    println!("id author Kayali");
    println!("uciok");
}

fn new_game(cboard : &mut board::chessboard) {
    fen::parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", cboard);
    // TODO clear transposition table!
}

fn parse_position(input : &str, cboard : &mut board::chessboard) {
    let v: Vec<&str> = input.split_whitespace().collect();

    if v[1] == "startpos" {
        fen::parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", cboard);
        parse_moves(&v, 2, cboard);
    } else if v[1] == "fen" {
        let fen_code = v[2..8].join(" ");
        fen::parse(&fen_code, cboard);
        parse_moves(&v, 8, cboard);
    }

    board::print(cboard);
}

fn parse_moves(input : &[&str], input_index : usize, cboard : &mut board::chessboard) {
    use moves;
    use movement;

    for move_str in &input[input_index + 1..] {
        let move_bytes = move_str.as_bytes();
        let move_ = moves::from_AN(move_bytes, cboard);
        let success = movement::make(&move_, cboard);
        assert!(success);
    }
}

fn parse_search(input : &str, cboard : &mut board::chessboard) {
    use think;

    let v: Vec<&str> = input.split_whitespace().collect();
    let mut time = 0;
    let mut depth = 0;

    let time_mark = if cboard.side == board::white { "wtime" } else { "btime" };
    if let Some(i) = v.iter().position(|s| *s == time_mark) {
        time = v[i + 1].parse::<i64>().unwrap();
    }

    if let Some(i) = v.iter().position(|s| *s == "depth") {
        depth = v[i + 1].parse::<u8>().unwrap();
    }

    println!("time: {}, depth: {}", time, depth);
    if time != 0 || depth != 0 {
        think::start(cboard, depth, time);
    }
}
<commit_msg>only parse moves when position command line has at least three segments<commit_after>use board;
use fen;

pub fn looping(cboard : &mut board::chessboard) {
    use std::io;

    let stdin = io::stdin();
    let mut input = &mut String::new();

    loop {
        input.clear();
        stdin.read_line(input).unwrap();

        if input == "" || input == "\n" {
            continue
        }
        else if input == "uci\n" {
            startup_info();
        }
        else if input == "isready\n" {
            println!("readyok");
        }
        else if input.split_whitespace().any(|x| x == "ucinewgame") {
            new_game(cboard);
        }
        else if input.split_whitespace().any(|x| x == "position") {
            parse_position(input, cboard);
        }
        else if input.split_whitespace().any(|x| x == "go") {
            parse_search(input, cboard);
        }
        else if input.split_whitespace().any(|x| x == "quit") {
            break
        }
    }
}

fn startup_info() {
    println!("id name Cicada");
    println!("id author Kayali");
    println!("uciok");
}

fn new_game(cboard : &mut board::chessboard) {
    fen::parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", cboard);
    // TODO clear transposition table!
}

fn parse_position(input : &str, cboard : &mut board::chessboard) {
    let v: Vec<&str> = input.split_whitespace().collect();

    if v[1] == "startpos" {
        fen::parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", cboard);
        if v.len() > 2 {
            parse_moves(&v, 2, cboard);
        }
    } else if v[1] == "fen" {
        let fen_code = v[2..8].join(" ");
        fen::parse(&fen_code, cboard);
        parse_moves(&v, 8, cboard);
    }

    board::print(cboard);
}

fn parse_moves(input : &[&str], input_index : usize, cboard : &mut board::chessboard) {
    use moves;
    use movement;

    for move_str in &input[input_index + 1..] {
        let move_bytes = move_str.as_bytes();
        let move_ = moves::from_AN(move_bytes, cboard);
        let success = movement::make(&move_, cboard);
        assert!(success);
    }
}

fn parse_search(input : &str, cboard : &mut board::chessboard) {
    use think;

    let v: Vec<&str> = input.split_whitespace().collect();
    let mut time = 0;
    let mut depth = 0;

    let time_mark = if cboard.side == board::white { "wtime" } else { "btime" };
    if let Some(i) = v.iter().position(|s| *s == time_mark) {
        time = v[i + 1].parse::<i64>().unwrap();
    }

    if let Some(i) = v.iter().position(|s| *s == "depth") {
        depth = v[i + 1].parse::<u8>().unwrap();
    }

    println!("time: {}, depth: {}", time, depth);
    if time != 0 || depth != 0 {
        think::start(cboard, depth, time);
    }
}
<|endoftext|>


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

use env as henv;

/// Default Depot URL
pub const DEFAULT_DEPOT_URL: &'static str = "https://willem.habitat.sh/v1/depot";

/// Default Depot channel
pub const DEFAULT_DEPOT_CHANNEL: &'static str = "unstable";

/// Default Depot publishing
pub const DEFAULT_DEPOT_PUBLISH: &'static str = "false";

/// Default Depot URL environment variable
pub const DEPOT_URL_ENVVAR: &'static str = "HAB_DEPOT_URL";

/// Default Depot Channel environment variable
pub const DEPOT_CHANNEL_ENVVAR: &'static str = "HAB_DEPOT_CHANNEL";

/// Default Depot Builder publishing environment variable
pub const DEPOT_PUBLISH_ENVVAR: &'static str = "HAB_DEPOT_PUBLISH";

pub fn default_depot_url() -> String {
    match henv::var(DEPOT_URL_ENVVAR) {
        Ok(val) => val,
        Err(_) => DEFAULT_DEPOT_URL.to_string(),
    }
}

pub fn default_depot_channel() -> String {
    match henv::var(DEPOT_CHANNEL_ENVVAR) {
        Ok(val) => val,
        Err(_) => DEFAULT_DEPOT_CHANNEL.to_string(),
    }
}

pub fn default_depot_publish() -> String {
    match henv::var(DEPOT_PUBLISH_ENVVAR) {
        Ok(val) => val,
        Err(_) => DEFAULT_DEPOT_PUBLISH.to_string(),
    }
}
<commit_msg>Automatically determine default publish settings for builder-worker<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use env;

/// Default Depot URL
pub const DEFAULT_DEPOT_URL: &'static str = "https://willem.habitat.sh/v1/depot";

/// Default Depot URL environment variable
pub const DEPOT_URL_ENVVAR: &'static str = "HAB_DEPOT_URL";

pub fn default_depot_url() -> String {
    match env::var(DEPOT_URL_ENVVAR) {
        Ok(val) => val,
        Err(_) => DEFAULT_DEPOT_URL.to_string(),
    }
}
<|endoftext|>


<commit_before>/// MPI-based method for summing many arrays

// Argument parsing
// logging
#[macro_use] extern crate log;
#[macro_use] extern crate clap;
extern crate ndarray;
extern crate env_logger;
// lastly, this library
extern crate cabarrus;
use cabarrus::errors::*;

pub fn main() {
    // Main can't return a Result, and the ? operator needs the enclosing function to return Result
    inner_main().expect("Could not recover. Exiting.");
}
pub fn inner_main() -> Result<()> {
    env_logger::init().unwrap();
    let args = app_from_crate!()
        .arg_from_usage("<size> 'how many workers there are'")
        .arg_from_usage("<rank> 'which worker am I, starting from 0'")
        .arg_from_usage("<addends>.. 'files containing matrices to add'")
        .arg_from_usage("<output> 'file in which to store the resulting matrix'")
        .get_matches();
    let outname = args.value_of("output").unwrap();
    let mut mats : Vec<&str> = args.values_of("addends").unwrap().collect();
    mats.sort();

    let size = value_t!(args, "size", usize).unwrap_or_else(|e| e.exit());
    let rank = value_t!(args, "rank", usize).unwrap_or_else(|e| e.exit());
    let chunksize = (mats.len() + size - 1) / size;
    mats = mats.into_iter().skip(rank*chunksize).take(chunksize).collect();
    info!("{} files, {} workers (I'm #{}), chunks of {} (I get {}), output goes to {}",
        mats.len(), size, rank, chunksize, mats.len(), outname);

    if ! mats.is_empty() {
        {
            let matname = mats[0];
            let ref matfile = cabarrus::numpy::open_matrix_mmap(matname)
                .expect("Failed to open first matrix");
            let ref mat = cabarrus::numpy::read_matrix_mmap(matfile)
                .unwrap();
            cabarrus::numpy::write_matrix(outname, mat)
                .expect("Failed to create accumulator matrix file");
        };
        let ref accumfile = cabarrus::numpy::open_matrix_mmap(outname)
            .expect("Failed to reopen accumulator matrix");
        let mut accum = cabarrus::numpy::read_matrix_mmap(accumfile)?;

        for matname in mats {
            let ref matfile = cabarrus::numpy::open_matrix_mmap(matname)
                .expect(&format!("Failed to open matrix {}", matname));
            let ref mat = cabarrus::numpy::read_matrix_mmap(matfile)?;

            info!("Reading {} ({} GB) ..", matname, mat.len() >> 27);
            accum += mat;
        }
    } else {
            info!("No matrices processed so nothing saved.");
        }
    Ok(())
}
<commit_msg>More debugging<commit_after>/// MPI-based method for summing many arrays

// Argument parsing
// logging
#[macro_use] extern crate log;
#[macro_use] extern crate clap;
extern crate ndarray;
extern crate env_logger;
// lastly, this library
extern crate cabarrus;
use cabarrus::errors::*;

pub fn main() {
    // Main can't return a Result, and the ? operator needs the enclosing function to return Result
    inner_main().expect("Could not recover. Exiting.");
}
pub fn inner_main() -> Result<()> {
    env_logger::init().unwrap();
    let args = app_from_crate!()
        .arg_from_usage("<size> 'how many workers there are'")
        .arg_from_usage("<rank> 'which worker am I, starting from 0'")
        .arg_from_usage("<addends>.. 'files containing matrices to add'")
        .arg_from_usage("<output> 'file in which to store the resulting matrix'")
        .get_matches();
    let outname = args.value_of("output").unwrap();
    let mut mats : Vec<&str> = args.values_of("addends").unwrap().collect();
    mats.sort();

    let size = value_t!(args, "size", usize).unwrap_or_else(|e| e.exit());
    let rank = value_t!(args, "rank", usize).unwrap_or_else(|e| e.exit());
    let chunksize = (mats.len() + size - 1) / size;
    mats = mats.into_iter().skip(rank*chunksize).take(chunksize).collect();
    info!("{} files, {} workers (I'm #{}), chunks of {} (I get {}), output goes to {}",
        mats.len(), size, rank, chunksize, mats.len(), outname);

    if ! mats.is_empty() {
        {
            let matname = mats[0];
            let ref matfile = cabarrus::numpy::open_matrix_mmap(matname)
                .expect(&format!("Failed to open first matrix, {}", matname));
            let ref mat = cabarrus::numpy::read_matrix_mmap(matfile)
                .unwrap();
            cabarrus::numpy::write_matrix(outname, mat)
                .expect("Failed to create accumulator matrix file");
        };
        let ref accumfile = cabarrus::numpy::open_matrix_mmap(outname)
            .expect("Failed to reopen accumulator matrix");
        let mut accum = cabarrus::numpy::read_matrix_mmap(accumfile)?;

        for matname in mats {
            let ref matfile = cabarrus::numpy::open_matrix_mmap(matname)
                .expect(&format!("Failed to open matrix {}", matname));
            let ref mat = cabarrus::numpy::read_matrix_mmap(matfile)?;

            info!("Reading {} ({} GB) ..", matname, mat.len() >> 27);
            accum += mat;
        }
    } else {
            info!("No matrices processed so nothing saved.");
        }
    Ok(())
}
<|endoftext|>