<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::{cmp, io};

use consts::*;
use util::io_err;

use log;

fn trace_hex(data: &[u8]) {
    if log_enabled!(log::Level::Trace) {
        let parts: Vec<String> = data.iter().map(|byte| format!("{:02x}", byte)).collect();
        trace!("USB send: {}", parts.join(""));
    }
}

// Trait for representing U2F HID Devices. Requires getters/setters for the
// channel ID, created during device initialization.
pub trait U2FDevice {
    fn get_cid(&self) -> &[u8; 4];
    fn set_cid(&mut self, cid: [u8; 4]);
}

// Init structure for U2F Communications. Tells the receiver what channel
// communication is happening on, what command is running, and how much data to
// expect to receive over all.
//
// Spec at https://fidoalliance.org/specs/fido-u2f-v1.
// 0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.html#message--and-packet-structure
pub struct U2FHIDInit {}

impl U2FHIDInit {
    pub fn read<T>(dev: &mut T) -> io::Result<Vec<u8>>
    where
        T: U2FDevice + io::Read,
    {
        let mut frame = [0u8; HID_RPT_SIZE];
        let count = dev.read(&mut frame)?;

        if count != HID_RPT_SIZE {
            return Err(io_err("invalid init packet"));
        }

        if dev.get_cid() != &frame[..4] {
            return Err(io_err("invalid channel id"));
        }

        let cap = (frame[5] as usize) << 8 | (frame[6] as usize);
        let mut data = Vec::with_capacity(cap);

        let len = cmp::min(cap, INIT_DATA_SIZE);
        data.extend_from_slice(&frame[7..7 + len]);

        Ok(data)
    }

    pub fn write<T>(dev: &mut T, cmd: u8, data: &[u8]) -> io::Result<usize>
    where
        T: U2FDevice + io::Write,
    {
        if data.len() > 0xffff {
            return Err(io_err("payload length > 2^16"));
        }

        let mut frame = [0; HID_RPT_SIZE + 1];
        frame[1..5].copy_from_slice(dev.get_cid());
        frame[5] = cmd;
        frame[6] = (data.len() >> 8) as u8;
        frame[7] = data.len() as u8;

        let count = cmp::min(data.len(), INIT_DATA_SIZE);
        frame[8..8 + count].copy_from_slice(&data[..count]);
        trace_hex(&frame);

        if dev.write(&frame)? != frame.len() {
            return Err(io_err("device write failed"));
        }

        Ok(count)
    }
}

// Continuation structure for U2F Communications. After an Init structure is
// sent, continuation structures are used to transmit all extra data that
// wouldn't fit in the initial packet. The sequence number increases with every
// packet, until all data is received.
//
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
// html#message--and-packet-structure
pub struct U2FHIDCont {}

impl U2FHIDCont {
    pub fn read<T>(dev: &mut T, seq: u8, max: usize) -> io::Result<Vec<u8>>
    where
        T: U2FDevice + io::Read,
    {
        let mut frame = [0u8; HID_RPT_SIZE];
        let count = dev.read(&mut frame)?;

        if count != HID_RPT_SIZE {
            return Err(io_err("invalid cont packet"));
        }

        if dev.get_cid() != &frame[..4] {
            return Err(io_err("invalid channel id"));
        }

        if seq != frame[4] {
            return Err(io_err("invalid sequence number"));
        }

        let max = cmp::min(max, CONT_DATA_SIZE);
        Ok(frame[5..5 + max].to_vec())
    }

    pub fn write<T>(dev: &mut T, seq: u8, data: &[u8]) -> io::Result<usize>
    where
        T: U2FDevice + io::Write,
    {
        let mut frame = [0; HID_RPT_SIZE + 1];
        frame[1..5].copy_from_slice(dev.get_cid());
        frame[5] = seq;

        let count = cmp::min(data.len(), CONT_DATA_SIZE);
        frame[6..6 + count].copy_from_slice(&data[..count]);
        trace_hex(&frame);

        if dev.write(&frame)? != frame.len() {
            return Err(io_err("device write failed"));
        }

        Ok(count)
    }
}

// Reply sent after initialization command. Contains information about U2F USB
// Key versioning, as well as the communication channel to be used for all
// further requests.
//
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
// html#u2fhid_init
pub struct U2FHIDInitResp {}

impl U2FHIDInitResp {
    pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<[u8; 4]> {
        assert_eq!(nonce.len(), INIT_NONCE_SIZE);

        if data.len() != INIT_NONCE_SIZE + 9 {
            return Err(io_err("invalid init response"));
        }

        if nonce != &data[..INIT_NONCE_SIZE] {
            return Err(io_err("invalid nonce"));
        }

        let mut cid = [0u8; 4];
        cid.copy_from_slice(&data[INIT_NONCE_SIZE..INIT_NONCE_SIZE + 4]);
        Ok(cid)
    }
}

// https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
// https://fidoalliance.org/specs/fido-u2f-v1.
// 0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#u2f-message-framing
pub struct U2FAPDUHeader {}

impl U2FAPDUHeader {
    pub fn serialize(ins: u8, p1: u8, data: &[u8]) -> io::Result<Vec<u8>> {
        if data.len() > 0xffff {
            return Err(io_err("payload length > 2^16"));
        }

        // Size of header + data + 2 zero bytes for maximum return size.
        let mut bytes = vec![0u8; U2FAPDUHEADER_SIZE + data.len() + 2];
        bytes[1] = ins;
        bytes[2] = p1;
        // p2 is always 0, at least, for our requirements.
        // lc[0] should always be 0.
        bytes[5] = (data.len() >> 8) as u8;
        bytes[6] = data.len() as u8;
        bytes[7..7 + data.len()].copy_from_slice(data);

        Ok(bytes)
    }
}
<commit_msg>Ignore communication on unknown channels (in HID transport protocol)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::{cmp, io};

use consts::*;
use util::io_err;

use log;

fn trace_hex(data: &[u8]) {
    if log_enabled!(log::Level::Trace) {
        let parts: Vec<String> = data.iter().map(|byte| format!("{:02x}", byte)).collect();
        trace!("USB send: {}", parts.join(""));
    }
}

// Trait for representing U2F HID Devices. Requires getters/setters for the
// channel ID, created during device initialization.
pub trait U2FDevice {
    fn get_cid(&self) -> &[u8; 4];
    fn set_cid(&mut self, cid: [u8; 4]);
}

// Init structure for U2F Communications. Tells the receiver what channel
// communication is happening on, what command is running, and how much data to
// expect to receive over all.
//
// Spec at https://fidoalliance.org/specs/fido-u2f-v1.
// 0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.html#message--and-packet-structure
pub struct U2FHIDInit {}

impl U2FHIDInit {
    pub fn read<T>(dev: &mut T) -> io::Result<Vec<u8>>
    where
        T: U2FDevice + io::Read,
    {
        let mut frame = [0u8; HID_RPT_SIZE];
        let mut count = dev.read(&mut frame)?;

        while dev.get_cid() != &frame[..4] {
            count = dev.read(&mut frame)?;
        }

        if count != HID_RPT_SIZE {
            return Err(io_err("invalid init packet"));
        }

        let cap = (frame[5] as usize) << 8 | (frame[6] as usize);
        let mut data = Vec::with_capacity(cap);

        let len = cmp::min(cap, INIT_DATA_SIZE);
        data.extend_from_slice(&frame[7..7 + len]);

        Ok(data)
    }

    pub fn write<T>(dev: &mut T, cmd: u8, data: &[u8]) -> io::Result<usize>
    where
        T: U2FDevice + io::Write,
    {
        if data.len() > 0xffff {
            return Err(io_err("payload length > 2^16"));
        }

        let mut frame = [0; HID_RPT_SIZE + 1];
        frame[1..5].copy_from_slice(dev.get_cid());
        frame[5] = cmd;
        frame[6] = (data.len() >> 8) as u8;
        frame[7] = data.len() as u8;

        let count = cmp::min(data.len(), INIT_DATA_SIZE);
        frame[8..8 + count].copy_from_slice(&data[..count]);
        trace_hex(&frame);

        if dev.write(&frame)? != frame.len() {
            return Err(io_err("device write failed"));
        }

        Ok(count)
    }
}

// Continuation structure for U2F Communications. After an Init structure is
// sent, continuation structures are used to transmit all extra data that
// wouldn't fit in the initial packet. The sequence number increases with every
// packet, until all data is received.
//
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
// html#message--and-packet-structure
pub struct U2FHIDCont {}

impl U2FHIDCont {
    pub fn read<T>(dev: &mut T, seq: u8, max: usize) -> io::Result<Vec<u8>>
    where
        T: U2FDevice + io::Read,
    {
        let mut frame = [0u8; HID_RPT_SIZE];
        let mut count = dev.read(&mut frame)?;

        while dev.get_cid() != &frame[..4] {
            count = dev.read(&mut frame)?;
        }

        if count != HID_RPT_SIZE {
            return Err(io_err("invalid cont packet"));
        }

        if seq != frame[4] {
            return Err(io_err("invalid sequence number"));
        }

        let max = cmp::min(max, CONT_DATA_SIZE);
        Ok(frame[5..5 + max].to_vec())
    }

    pub fn write<T>(dev: &mut T, seq: u8, data: &[u8]) -> io::Result<usize>
    where
        T: U2FDevice + io::Write,
    {
        let mut frame = [0; HID_RPT_SIZE + 1];
        frame[1..5].copy_from_slice(dev.get_cid());
        frame[5] = seq;

        let count = cmp::min(data.len(), CONT_DATA_SIZE);
        frame[6..6 + count].copy_from_slice(&data[..count]);
        trace_hex(&frame);

        if dev.write(&frame)? != frame.len() {
            return Err(io_err("device write failed"));
        }

        Ok(count)
    }
}

// Reply sent after initialization command. Contains information about U2F USB
// Key versioning, as well as the communication channel to be used for all
// further requests.
//
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
// html#u2fhid_init
pub struct U2FHIDInitResp {}

impl U2FHIDInitResp {
    pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<[u8; 4]> {
        assert_eq!(nonce.len(), INIT_NONCE_SIZE);

        if data.len() != INIT_NONCE_SIZE + 9 {
            return Err(io_err("invalid init response"));
        }

        if nonce != &data[..INIT_NONCE_SIZE] {
            return Err(io_err("invalid nonce"));
        }

        let mut cid = [0u8; 4];
        cid.copy_from_slice(&data[INIT_NONCE_SIZE..INIT_NONCE_SIZE + 4]);
        Ok(cid)
    }
}

// https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
// https://fidoalliance.org/specs/fido-u2f-v1.
// 0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#u2f-message-framing
pub struct U2FAPDUHeader {}

impl U2FAPDUHeader {
    pub fn serialize(ins: u8, p1: u8, data: &[u8]) -> io::Result<Vec<u8>> {
        if data.len() > 0xffff {
            return Err(io_err("payload length > 2^16"));
        }

        // Size of header + data + 2 zero bytes for maximum return size.
        let mut bytes = vec![0u8; U2FAPDUHEADER_SIZE + data.len() + 2];
        bytes[1] = ins;
        bytes[2] = p1;
        // p2 is always 0, at least, for our requirements.
        // lc[0] should always be 0.
        bytes[5] = (data.len() >> 8) as u8;
        bytes[6] = data.len() as u8;
        bytes[7..7 + data.len()].copy_from_slice(data);

        Ok(bytes)
    }
}
<|endoftext|>


<commit_before>use super::Config;
use super::EditMode;
use super::KeyPress;
use super::RawReader;
use super::Result;

//#[derive(Clone)]
pub enum Cmd {
    Abort, // Miscellaneous Command
    AcceptLine, // Command For History
    BackwardChar, // Command For Moving
    BackwardDeleteChar, // Command For Text
    BackwardKillWord, // Command For Killing
    BackwardWord, // Command For Moving
    BeginningOfHistory, // Command For History
    BeginningOfLine, // Command For Moving
    CapitalizeWord, // Command For Text
    CharacterSearch(bool), // Miscellaneous Command (TODO Move right to the next occurance of c)
    CharacterSearchBackward(bool), /* Miscellaneous Command (TODO Move left to the previous occurance of c) */
    ClearScreen, // Command For Moving
    Complete, // Command For Completion
    DeleteChar, // Command For Text
    DowncaseWord, // Command For Text
    EndOfFile, // Command For Text
    EndOfHistory, // Command For History
    EndOfLine, // Command For Moving
    ForwardChar, // Command For Moving
    ForwardSearchHistory, // Command For History
    ForwardWord, // Command For Moving
    KillLine, // Command For Killing
    KillWholeLine, // Command For Killing (TODO Delete current line)
    KillWord, // Command For Killing
    NextHistory, // Command For History
    Noop,
    PreviousHistory, // Command For History
    QuotedInsert, // Command For Text
    Replace, // TODO DeleteChar + SelfInsert
    ReverseSearchHistory, // Command For History
    SelfInsert, // Command For Text
    TransposeChars, // Command For Text
    TransposeWords, // Command For Text
    Unknown,
    UnixLikeDiscard, // Command For Killing
    UnixWordRubout, // Command For Killing
    UpcaseWord, // Command For Text
    Yank, // Command For Killing
    YankPop, // Command For Killing
}

// TODO numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
pub struct EditState {
    mode: EditMode,
    // TODO Validate Vi Command, Insert, Visual mode
    insert: bool, // vi only ?
}

impl EditState {
    pub fn new(config: &Config) -> EditState {
        EditState {
            mode: config.edit_mode(),
            insert: true,
        }
    }

    pub fn next_cmd<R: RawReader>(&mut self,
                                  rdr: &mut R,
                                  config: &Config)
                                  -> Result<(KeyPress, Cmd)> {
        match self.mode {
            EditMode::Emacs => self.emacs(rdr, config),
            EditMode::Vi if self.insert => self.vi_insert(rdr, config),
            EditMode::Vi => self.vi_command(rdr, config),
        }
    }

    fn emacs<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Esc => Cmd::Abort, // TODO Validate
            KeyPress::Ctrl('A') => Cmd::BeginningOfLine,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Ctrl('B') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            // KeyPress::Ctrl('D') if s.line.is_empty() => Cmd::EndOfFile,
            KeyPress::Ctrl('D') => Cmd::DeleteChar,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('E') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('F') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Meta('\x08') => Cmd::BackwardKillWord,
            KeyPress::Meta('\x7f') => Cmd::BackwardKillWord,
            // KeyPress::Meta('-') => { // digit-argument
            // }
            // KeyPress::Meta('0'...'9') => { // digit-argument
            // }
            KeyPress::Meta('<') => Cmd::BeginningOfHistory,
            KeyPress::Meta('>') => Cmd::EndOfHistory,
            KeyPress::Meta('B') => Cmd::BackwardWord,
            KeyPress::Meta('C') => Cmd::CapitalizeWord,
            KeyPress::Meta('D') => Cmd::KillWord,
            KeyPress::Meta('F') => Cmd::ForwardWord,
            KeyPress::Meta('L') => Cmd::DowncaseWord,
            KeyPress::Meta('T') => Cmd::TransposeWords,
            KeyPress::Meta('U') => Cmd::UpcaseWord,
            KeyPress::Meta('Y') => Cmd::YankPop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_command<R: RawReader>(&mut self,
                                rdr: &mut R,
                                config: &Config)
                                -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char('$') => Cmd::EndOfLine,
            // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket
            KeyPress::Char('0') => Cmd::BeginningOfLine, // vi-zero: Vi move to the beginning of line.
            // KeyPress::Char('1'...'9') => Cmd::???, // vi-arg-digit
            KeyPress::Char('^') => Cmd::BeginningOfLine, // TODO Move to the first non-blank character of line.
            KeyPress::Char('a') => {
                // vi-append-mode: Vi enter insert mode after the cursor.
                self.insert = true;
                Cmd::ForwardChar
            }
            KeyPress::Char('A') => {
                // vi-append-eol: Vi enter insert mode at end of line.
                self.insert = true;
                Cmd::EndOfLine
            }
            KeyPress::Char('b') => Cmd::BackwardWord,
            // TODO KeyPress::Char('B') => Cmd::???, Move one non-blank word left.
            KeyPress::Char('c') => {
                self.insert = true;
                let mvt = try!(rdr.next_key(config.keyseq_timeout()));
                match mvt {
                    KeyPress::Char('$') => Cmd::KillLine, // vi-change-to-eol: Vi change to end of line.
                    KeyPress::Char('0') => Cmd::UnixLikeDiscard,
                    KeyPress::Char('c') => Cmd::KillWholeLine,
                    // TODO KeyPress::Char('f') => ???,
                    // TODO KeyPress::Char('F') => ???,
                    KeyPress::Char('h') => Cmd::BackwardDeleteChar,
                    KeyPress::Char('l') => Cmd::DeleteChar,
                    KeyPress::Char(' ') => Cmd::DeleteChar,
                    // TODO KeyPress::Char('t') => ???,
                    // TODO KeyPress::Char('T') => ???,
                    KeyPress::Char('w') => Cmd::KillWord,
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('C') => {
                self.insert = true;
                Cmd::KillLine
            }
            KeyPress::Char('d') => {
                let mvt = try!(rdr.next_key(config.keyseq_timeout()));
                match mvt {
                    KeyPress::Char('$') => Cmd::KillLine,
                    KeyPress::Char('0') => Cmd::UnixLikeDiscard, // vi-kill-line-prev: Vi cut from beginning of line to cursor.
                    KeyPress::Char('d') => Cmd::KillWholeLine,
                    // TODO KeyPress::Char('f') => ???,
                    // TODO KeyPress::Char('F') => ???,
                    KeyPress::Char('h') => Cmd::BackwardDeleteChar, // vi-delete-prev-char: Vi move to previous character (backspace).
                    KeyPress::Char('l') => Cmd::DeleteChar,
                    KeyPress::Char(' ') => Cmd::DeleteChar,
                    // TODO KeyPress::Char('t') => ???,
                    // TODO KeyPress::Char('T') => ???,
                    KeyPress::Char('w') => Cmd::KillWord,
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('D') => Cmd::KillLine,
            // TODO KeyPress::Char('e') => Cmd::???, vi-to-end-word: Vi move to the end of the current word. Move to the end of the current word.
            // TODO KeyPress::Char('E') => Cmd::???, vi-end-word: Vi move to the end of the current space delimited word. Move to the end of the current non-blank word.
            KeyPress::Char('i') => {
                // vi-insert: Vi enter insert mode.
                self.insert = true;
                Cmd::Noop
            }
            KeyPress::Char('I') => {
                // vi-insert-at-bol: Vi enter insert mode at the beginning of line.
                self.insert = true;
                Cmd::BeginningOfLine
            }
            KeyPress::Char('f') => {
                // vi-next-char: Vi move to the character specified next.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearch(false))),
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('F') => {
                // vi-prev-char: Vi move to the character specified previous.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearchBackward(false))),
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n
            KeyPress::Char('p') => Cmd::Yank, // vi-paste-next: Vi paste previous deletion to the right of the cursor.
            KeyPress::Char('P') => Cmd::Yank, // vi-paste-prev: Vi paste previous deletion to the left of the cursor. TODO Insert the yanked text before the cursor.
            KeyPress::Char('r') => {
                // vi-replace-char: Vi replace character under the cursor with the next character typed.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::Replace)),
                    KeyPress::Esc => Cmd::Noop,
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('R') => Cmd::???, vi-replace-mode: Vi enter replace mode. Replaces characters under the cursor. (overwrite-mode)
            KeyPress::Char('s') => {
                // vi-substitute-char: Vi replace character under the cursor and enter insert mode.
                self.insert = true;
                Cmd::DeleteChar
            }
            KeyPress::Char('S') => {
                // vi-substitute-line: Vi substitute entire line.
                self.insert = true;
                Cmd::KillWholeLine
            }
            KeyPress::Char('t') => {
                // vi-to-next-char: Vi move up to the character specified next.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearchBackward(true))),
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('T') => {
                // vi-to-prev-char: Vi move up to the character specified previous.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearch(true))),
                    _ => Cmd::Unknown,
                }
            }
            // KeyPress::Char('U') => Cmd::???, // revert-line
            KeyPress::Char('w') => Cmd::ForwardWord, // vi-next-word: Vi move to the next word.
            // TODO KeyPress::Char('W') => Cmd::???, // vi-next-space-word: Vi move to the next space delimited word. Move one non-blank word right.
            KeyPress::Char('x') => Cmd::DeleteChar, // vi-delete: TODO move backward if eol
            KeyPress::Char('X') => Cmd::BackwardDeleteChar,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Char('h') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardChar,
            KeyPress::Char('l') => Cmd::ForwardChar,
            KeyPress::Char(' ') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Char('+') => Cmd::NextHistory,
            KeyPress::Char('j') => Cmd::NextHistory,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Char('-') => Cmd::PreviousHistory,
            KeyPress::Char('k') => Cmd::PreviousHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => Cmd::Noop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile, // vi-eof-maybe
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => {
                // vi-movement-mode/vi-command-mode: Vi enter command mode (use alternative key bindings).
                self.insert = false;
                Cmd::Noop
            }
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }
}
<commit_msg>Second draft for vi mode support (#94)<commit_after>use super::Config;
use super::EditMode;
use super::KeyPress;
use super::RawReader;
use super::Result;

//#[derive(Clone)]
pub enum Cmd {
    Abort, // Miscellaneous Command
    AcceptLine, // Command For History
    BackwardChar, // Command For Moving
    BackwardDeleteChar, // Command For Text
    BackwardKillWord(Word), // Command For Killing
    BackwardWord(Word), // Command For Moving
    BeginningOfHistory, // Command For History
    BeginningOfLine, // Command For Moving
    CapitalizeWord, // Command For Text
    ClearScreen, // Command For Moving
    Complete, // Command For Completion
    DeleteChar, // Command For Text
    DowncaseWord, // Command For Text
    EndOfFile, // Command For Text
    EndOfHistory, // Command For History
    EndOfLine, // Command For Moving
    ForwardChar, // Command For Moving
    ForwardSearchHistory, // Command For History
    ForwardWord(Word), // Command For Moving
    KillLine, // Command For Killing
    KillWholeLine, // Command For Killing (TODO Delete current line)
    KillWord(Word), // Command For Killing
    NextHistory, // Command For History
    Noop, // TODO
    PreviousHistory, // Command For History
    QuotedInsert, // Command For Text
    Replace, // TODO DeleteChar + SelfInsert
    ReverseSearchHistory, // Command For History
    SelfInsert, // Command For Text
    TransposeChars, // Command For Text
    TransposeWords, // Command For Text
    Unknown,
    UnixLikeDiscard, // Command For Killing
    // UnixWordRubout, // = KillWord(Word::BigWord) Command For Killing
    UpcaseWord, // Command For Text
    ViCharSearch(CharSearch), // TODO
    ViEndWord(Word), // TODO
    ViKillTo(CharSearch), // TODO
    Yank, // Command For Killing
    YankPop, // Command For Killing
}

pub enum Word {
    // non-blanks characters
    BigWord,
    // alphanumeric characters
    Word,
    // alphanumeric (and '_') characters
    ViWord,
}

pub enum CharSearch {
    Forward(char),
    // until
    ForwardBefore(char),
    Backward(char),
    // until
    BackwardAfter(char),
}

// TODO numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
pub struct EditState {
    mode: EditMode,
    // Vi Command/Alternate, Insert/Input mode
    insert: bool, // vi only ?
}

impl EditState {
    pub fn new(config: &Config) -> EditState {
        EditState {
            mode: config.edit_mode(),
            insert: true,
        }
    }

    pub fn next_cmd<R: RawReader>(&mut self,
                                  rdr: &mut R,
                                  config: &Config)
                                  -> Result<(KeyPress, Cmd)> {
        match self.mode {
            EditMode::Emacs => self.emacs(rdr, config),
            EditMode::Vi if self.insert => self.vi_insert(rdr, config),
            EditMode::Vi => self.vi_command(rdr, config),
        }
    }

    fn emacs<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Esc => Cmd::Abort, // TODO Validate
            KeyPress::Ctrl('A') => Cmd::BeginningOfLine,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Ctrl('B') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            // KeyPress::Ctrl('D') if s.line.is_empty() => Cmd::EndOfFile,
            KeyPress::Ctrl('D') => Cmd::DeleteChar,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('E') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('F') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Meta('\x08') => Cmd::BackwardKillWord(Word::Word),
            KeyPress::Meta('\x7f') => Cmd::BackwardKillWord(Word::Word),
            // KeyPress::Meta('-') => { // digit-argument
            // }
            // KeyPress::Meta('0'...'9') => { // digit-argument
            // }
            KeyPress::Meta('<') => Cmd::BeginningOfHistory,
            KeyPress::Meta('>') => Cmd::EndOfHistory,
            KeyPress::Meta('B') => Cmd::BackwardWord(Word::Word),
            KeyPress::Meta('C') => Cmd::CapitalizeWord,
            KeyPress::Meta('D') => Cmd::KillWord(Word::Word),
            KeyPress::Meta('F') => Cmd::ForwardWord(Word::Word),
            KeyPress::Meta('L') => Cmd::DowncaseWord,
            KeyPress::Meta('T') => Cmd::TransposeWords,
            KeyPress::Meta('U') => Cmd::UpcaseWord,
            KeyPress::Meta('Y') => Cmd::YankPop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_command<R: RawReader>(&mut self,
                                rdr: &mut R,
                                config: &Config)
                                -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char('$') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket
            KeyPress::Char('0') => Cmd::BeginningOfLine, // vi-zero: Vi move to the beginning of line.
            KeyPress::Home => Cmd::BeginningOfLine,
            // KeyPress::Char('1'...'9') => Cmd::???, // vi-arg-digit
            KeyPress::Char('^') => Cmd::BeginningOfLine, // vi-first-print TODO Move to the first non-blank character of line.
            KeyPress::Char('a') => {
                // vi-append-mode: Vi enter insert mode after the cursor.
                self.insert = true;
                Cmd::ForwardChar
            }
            KeyPress::Char('A') => {
                // vi-append-eol: Vi enter insert mode at end of line.
                self.insert = true;
                Cmd::EndOfLine
            }
            KeyPress::Char('b') => Cmd::BackwardWord(Word::ViWord), // vi-prev-word
            KeyPress::Char('B') => Cmd::BackwardWord(Word::BigWord),
            KeyPress::Char('c') => {
                self.insert = true;
                try!(self.vi_delete_motion(rdr, config, key))
            }
            KeyPress::Char('C') => {
                self.insert = true;
                Cmd::KillLine
            }
            KeyPress::Char('d') => try!(self.vi_delete_motion(rdr, config, key)),
            KeyPress::Char('D') => Cmd::KillLine,
            KeyPress::Char('e') => Cmd::ViEndWord(Word::ViWord),
            KeyPress::Char('E') => Cmd::ViEndWord(Word::BigWord),
            KeyPress::Char('i') => {
                // vi-insertion-mode
                self.insert = true;
                Cmd::Noop
            }
            KeyPress::Char('I') => {
                // vi-insert-beg
                self.insert = true;
                Cmd::BeginningOfLine
            }
            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
                // vi-char-search
                let cs = try!(self.vi_char_search(rdr, config, c));
                match cs {
                    Some(cs) => Cmd::ViCharSearch(cs),
                    None => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n
            KeyPress::Char('p') => Cmd::Yank, // vi-put
            KeyPress::Char('P') => Cmd::Yank, // vi-put TODO Insert the yanked text before the cursor.
            KeyPress::Char('r') => {
                // vi-replace-char: Vi replace character under the cursor with the next character typed.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::Replace)),
                    KeyPress::Esc => Cmd::Noop,
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('R') => Cmd::???, vi-replace-mode: Vi enter replace mode. Replaces characters under the cursor. (overwrite-mode)
            KeyPress::Char('s') => {
                // vi-substitute-char: Vi replace character under the cursor and enter insert mode.
                self.insert = true;
                Cmd::DeleteChar
            }
            KeyPress::Char('S') => {
                // vi-substitute-line: Vi substitute entire line.
                self.insert = true;
                Cmd::KillWholeLine
            }
            // KeyPress::Char('U') => Cmd::???, // revert-line
            KeyPress::Char('w') => Cmd::ForwardWord(Word::ViWord), // vi-next-word
            KeyPress::Char('W') => Cmd::ForwardWord(Word::BigWord), // vi-next-word
            KeyPress::Char('x') => Cmd::DeleteChar, // vi-delete: TODO move backward if eol
            KeyPress::Char('X') => Cmd::BackwardDeleteChar, // vi-rubout
            // KeyPress::Char('y') => Cmd::???, // vi-yank-to
            // KeyPress::Char('Y') => Cmd::???, // vi-yank-to
            KeyPress::Char('h') => Cmd::BackwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardChar,
            KeyPress::Backspace => Cmd::BackwardChar, // TODO Validate
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Char('l') => Cmd::ForwardChar,
            KeyPress::Char(' ') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Char('+') => Cmd::NextHistory,
            KeyPress::Char('j') => Cmd::NextHistory,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Char('-') => Cmd::PreviousHistory,
            KeyPress::Char('k') => Cmd::PreviousHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => Cmd::Noop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile, // vi-eof-maybe
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => {
                // vi-movement-mode/vi-command-mode: Vi enter command mode (use alternative key bindings).
                self.insert = false;
                Cmd::Noop
            }
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_delete_motion<R: RawReader>(&mut self,
                                      rdr: &mut R,
                                      config: &Config,
                                      key: KeyPress)
                                      -> Result<Cmd> {
        let mvt = try!(rdr.next_key(config.keyseq_timeout()));
        Ok(match mvt {
            KeyPress::Char('$') => Cmd::KillLine, // vi-change-to-eol: Vi change to end of line.
            KeyPress::Char('0') => Cmd::UnixLikeDiscard, // vi-kill-line-prev: Vi cut from beginning of line to cursor.
            KeyPress::Char('b') => Cmd::BackwardKillWord(Word::ViWord),
            KeyPress::Char('B') => Cmd::BackwardKillWord(Word::BigWord),
            x if x == key => Cmd::KillWholeLine,
            KeyPress::Char('e') => Cmd::KillWord(Word::ViWord),
            KeyPress::Char('E') => Cmd::KillWord(Word::BigWord),
            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
                let cs = try!(self.vi_char_search(rdr, config, c));
                match cs {
                    Some(cs) => Cmd::ViKillTo(cs),
                    None => Cmd::Unknown,
                }
            }
            KeyPress::Char('h') => Cmd::BackwardDeleteChar, // vi-delete-prev-char: Vi move to previous character (backspace).
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Char('l') => Cmd::DeleteChar,
            KeyPress::Char(' ') => Cmd::DeleteChar,
            KeyPress::Char('w') => Cmd::KillWord(Word::ViWord),
            KeyPress::Char('W') => Cmd::KillWord(Word::BigWord),
            _ => Cmd::Unknown,
        })
    }

    fn vi_char_search<R: RawReader>(&mut self,
                                    rdr: &mut R,
                                    config: &Config,
                                    cmd: char)
                                    -> Result<Option<CharSearch>> {
        let ch = try!(rdr.next_key(config.keyseq_timeout()));
        Ok(match ch {
            KeyPress::Char(ch) => {
                Some(match cmd {
                    'f' => CharSearch::Forward(ch),
                    't' => CharSearch::ForwardBefore(ch),
                    'F' => CharSearch::Backward(ch),
                    'T' => CharSearch::BackwardAfter(ch),
                    _ => unreachable!(),
                })
            }
            _ => None,
        })
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Joost Yervante Damad <joost@productize.be>

use std::fmt;

// from parent
use ERes;
use err;
use read_file;
use footprint;

extern crate rustysexp;
use self::rustysexp::Sexp;

pub struct Layout {
    version:i64,
    elements:Vec<Element>,
}

impl Layout {
    fn new() -> Layout {
        Layout {
            version:0,
            elements:vec![],
        }
    }

}

enum Element {
    Other(Sexp),
    Module(footprint::Module),
}

impl fmt::Display for Layout {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "(kicad_pcb (version {})", self.version));
        for element in &self.elements[..] {
            try!(writeln!(f, "  {}", element));
            try!(writeln!(f, ""));
        }
        writeln!(f, ")")
    }
}

impl fmt::Display for Element {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Element::Other(ref s) => write!(f, "{}", s),
            Element::Module(ref s) => write!(f, "{}", s),
        }
    }
}

fn parse_version(e:&Sexp) -> ERes<i64> {
    let l = try!(e.slice_atom("version"));
    try!(l[0].atom()).i()
}

fn parse_other(e:&Sexp) -> Element {
    let e2 = e.clone();
    Element::Other(e2)
}

fn parse_module(e:&Sexp) -> ERes<Element> {
    let e2 = e.clone();
    let m = try!(footprint::parse_module(e2));
    Ok(Element::Module(m))
}

fn parse(s: &str) -> ERes<Layout> {
    let exp = match rustysexp::parse_str(s) {
        Ok(s) => s,
        Err(x) => return Err(format!("ParseError: {}", x)),
    };
    let l1 = try!(exp.slice_atom("kicad_pcb"));
    let mut layout = Layout::new();

    for ref e in l1 {
        match &try!(e.list_name())[..] {
            "version" => layout.version = try!(parse_version(e)),
            "module" => layout.elements.push(try!(parse_module(e))),
            _ => layout.elements.push(parse_other(e)),
        }
    }
    Ok(layout)
}

pub fn parse_str(s: &str) -> ERes<Layout> {
    parse(s)
}

pub fn parse_file(name: &str) -> ERes<Layout> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("io error: {}", x))
    });
    parse(&s[..])
}
<commit_msg>pathbuf<commit_after>// (c) 2016 Joost Yervante Damad <joost@productize.be>

use std::fmt;
use std::path::PathBuf;

// from parent
use ERes;
use err;
use read_file;
use footprint;

extern crate rustysexp;
use self::rustysexp::Sexp;

pub struct Layout {
    version:i64,
    elements:Vec<Element>,
}

impl Layout {
    fn new() -> Layout {
        Layout {
            version:0,
            elements:vec![],
        }
    }

}

enum Element {
    Other(Sexp),
    Module(footprint::Module),
}

impl fmt::Display for Layout {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "(kicad_pcb (version {})", self.version));
        for element in &self.elements[..] {
            try!(writeln!(f, "  {}", element));
            try!(writeln!(f, ""));
        }
        writeln!(f, ")")
    }
}

impl fmt::Display for Element {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Element::Other(ref s) => write!(f, "{}", s),
            Element::Module(ref s) => write!(f, "{}", s),
        }
    }
}

fn parse_version(e:&Sexp) -> ERes<i64> {
    let l = try!(e.slice_atom("version"));
    try!(l[0].atom()).i()
}

fn parse_other(e:&Sexp) -> Element {
    let e2 = e.clone();
    Element::Other(e2)
}

fn parse_module(e:&Sexp) -> ERes<Element> {
    let e2 = e.clone();
    let m = try!(footprint::parse_module(e2));
    Ok(Element::Module(m))
}

fn parse(s: &str) -> ERes<Layout> {
    let exp = match rustysexp::parse_str(s) {
        Ok(s) => s,
        Err(x) => return Err(format!("ParseError: {}", x)),
    };
    let l1 = try!(exp.slice_atom("kicad_pcb"));
    let mut layout = Layout::new();

    for ref e in l1 {
        match &try!(e.list_name())[..] {
            "version" => layout.version = try!(parse_version(e)),
            "module" => layout.elements.push(try!(parse_module(e))),
            _ => layout.elements.push(parse_other(e)),
        }
    }
    Ok(layout)
}

pub fn parse_str(s: &str) -> ERes<Layout> {
    parse(s)
}

pub fn parse_file(filename: &PathBuf) -> ERes<Layout> {
    let name = filename.to_str().unwrap();
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => Err(format!("io error: {}", x))
    });
    parse(&s[..])
}
<|endoftext|>


<commit_before>use std::f32::consts::PI;

use num::complex::Complex32;
use num::traits::Zero;
use quad_osc::QuadOsc;
use rand::distributions::normal::Normal;
use rand::distributions::IndependentSample;
use rand;

use consts::SAMPLES_PER_FRAME;
use descramble::VoiceDecisions;
use enhance::EnhancedSpectrals;
use params::BaseParams;
use window;

// Result of equation 121
const SCALING_COEF: f32 = 146.6432708443356;

const DFT_SIZE: usize = 256;
const IDFT_SIZE: usize = 256;

const DFT_HALF: usize = DFT_SIZE / 2;
const IDFT_HALF: usize = IDFT_SIZE / 2;

fn edges(l: usize, params: &BaseParams) -> (usize, usize) {
    let common = DFT_SIZE as f32 / (2.0 * PI) * params.fundamental;

    (
        (common * (l as f32 - 0.5)).ceil() as usize,
        (common * (l as f32 + 0.5)).ceil() as usize,
    )
}

/// Constructs unvoiced DFT/IDFT.
///
/// Constructs a frequency-domain representation of an "unvoiced" white noise signal,
/// bandpassed only for the unvoiced frequencies in the current frame. This spectrum can
/// then be used with an inverse Fourier transform to produce a white noise signal
/// containing only unvoiced frequency content.
pub struct UnvoicedDFT([Complex32; DFT_HALF]);

impl UnvoicedDFT {
    pub fn new(params: &BaseParams, voice: &VoiceDecisions, enhanced: &EnhancedSpectrals)
        -> UnvoicedDFT
    {
        let mut dft = [Complex32::default(); DFT_HALF];

        let mut rng = rand::weak_rng();
        let gaus = Normal::new(0.0, (DFT_HALF as f64).sqrt());

        for (l, &amplitude) in enhanced.iter().enumerate() {
            let l = l + 1;

            if voice.is_voiced(l) {
                continue;
            }

            let (lower, upper) = edges(l, params);

            for m in lower..upper {
                dft[m] = Complex32::new(gaus.ind_sample(&mut rng) as f32,
                                        gaus.ind_sample(&mut rng) as f32);
            }

            let energy = (lower..upper)
                .map(|m| dft[m].norm_sqr())
                .fold(0.0, |s, x| s + x);
            let power = energy / (upper - lower) as f32;
            let scale = SCALING_COEF * amplitude / power.sqrt();

            for m in lower..upper {
                dft[m] = scale * dft[m];
            }
        }

        UnvoicedDFT(dft)
    }

    pub fn idft(&self, n: isize) -> f32 {
        if n < -(IDFT_HALF as isize) || n >= IDFT_HALF as isize {
            return 0.0;
        }

        let mut osc = QuadOsc::new(0.0, 2.0 / IDFT_SIZE as f32 * PI * n as f32);

        2.0 / (DFT_SIZE as f32 * IDFT_SIZE as f32).sqrt() * self.0.iter().map(|x| {
            let (sin, cos) = osc.next();
            x.re * cos - x.im * sin
        }).fold(0.0, |s, x| s + x)
    }
}

impl Default for UnvoicedDFT {
    fn default() -> UnvoicedDFT {
        UnvoicedDFT([Complex32::zero(); DFT_HALF])
    }
}

pub struct Unvoiced<'a, 'b> {
    cur: &'a UnvoicedDFT,
    prev: &'b UnvoicedDFT,
    window: window::Window,
}

impl<'a, 'b> Unvoiced<'a, 'b> {
    pub fn new(cur: &'a UnvoicedDFT, prev: &'b UnvoicedDFT) -> Unvoiced<'a, 'b> {
        Unvoiced {
            cur: cur,
            prev: prev,
            window: window::synthesis(),
        }
    }

    pub fn get(&self, n: usize) -> f32 {
        let n = n as isize;

        let numer = self.window.get(n) * self.prev.idft(n) +
            self.window.get(n - SAMPLES_PER_FRAME as isize) * self.cur.idft(n - SAMPLES_PER_FRAME as isize);
        let denom = self.window.get(n).powi(2) +
            self.window.get(n - SAMPLES_PER_FRAME as isize).powi(2);

        numer / denom
    }
}

#[cfg(test)]
mod test {
    use super::edges;
    use descramble::Bootstrap;
    use params::BaseParams;

    #[test]
    fn test_edges() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());

        assert!((p.fundamental - 0.17575344).abs() < 0.000001);

        let (lower, upper) = edges(1, &p);
        assert_eq!(lower, 4);
        assert_eq!(upper, 11);

        let (lower, upper) = edges(2, &p);
        assert_eq!(lower, 11);
        assert_eq!(upper, 18);

        let (lower, upper) = edges(5, &p);
        assert_eq!(lower, 33);
        assert_eq!(upper, 40);

        let (lower, upper) = edges(8, &p);
        assert_eq!(lower, 54);
        assert_eq!(upper, 61);

        let (lower, upper) = edges(16, &p);
        assert_eq!(lower, 111);
        assert_eq!(upper, 119);
    }
}
<commit_msg>Shorten line<commit_after>use std::f32::consts::PI;

use num::complex::Complex32;
use num::traits::Zero;
use quad_osc::QuadOsc;
use rand::distributions::normal::Normal;
use rand::distributions::IndependentSample;
use rand;

use consts::SAMPLES_PER_FRAME;
use descramble::VoiceDecisions;
use enhance::EnhancedSpectrals;
use params::BaseParams;
use window;

// Result of equation 121
const SCALING_COEF: f32 = 146.6432708443356;

const DFT_SIZE: usize = 256;
const IDFT_SIZE: usize = 256;

const DFT_HALF: usize = DFT_SIZE / 2;
const IDFT_HALF: usize = IDFT_SIZE / 2;

fn edges(l: usize, params: &BaseParams) -> (usize, usize) {
    let common = DFT_SIZE as f32 / (2.0 * PI) * params.fundamental;

    (
        (common * (l as f32 - 0.5)).ceil() as usize,
        (common * (l as f32 + 0.5)).ceil() as usize,
    )
}

/// Constructs unvoiced DFT/IDFT.
///
/// Constructs a frequency-domain representation of an "unvoiced" white noise signal,
/// bandpassed only for the unvoiced frequencies in the current frame. This spectrum can
/// then be used with an inverse Fourier transform to produce a white noise signal
/// containing only unvoiced frequency content.
pub struct UnvoicedDFT([Complex32; DFT_HALF]);

impl UnvoicedDFT {
    pub fn new(params: &BaseParams, voice: &VoiceDecisions, enhanced: &EnhancedSpectrals)
        -> UnvoicedDFT
    {
        let mut dft = [Complex32::default(); DFT_HALF];

        let mut rng = rand::weak_rng();
        let gaus = Normal::new(0.0, (DFT_HALF as f64).sqrt());

        for (l, &amplitude) in enhanced.iter().enumerate() {
            let l = l + 1;

            if voice.is_voiced(l) {
                continue;
            }

            let (lower, upper) = edges(l, params);

            for m in lower..upper {
                dft[m] = Complex32::new(gaus.ind_sample(&mut rng) as f32,
                                        gaus.ind_sample(&mut rng) as f32);
            }

            let energy = (lower..upper)
                .map(|m| dft[m].norm_sqr())
                .fold(0.0, |s, x| s + x);
            let power = energy / (upper - lower) as f32;
            let scale = SCALING_COEF * amplitude / power.sqrt();

            for m in lower..upper {
                dft[m] = scale * dft[m];
            }
        }

        UnvoicedDFT(dft)
    }

    pub fn idft(&self, n: isize) -> f32 {
        if n < -(IDFT_HALF as isize) || n >= IDFT_HALF as isize {
            return 0.0;
        }

        let mut osc = QuadOsc::new(0.0, 2.0 / IDFT_SIZE as f32 * PI * n as f32);

        2.0 / (DFT_SIZE as f32 * IDFT_SIZE as f32).sqrt() * self.0.iter().map(|x| {
            let (sin, cos) = osc.next();
            x.re * cos - x.im * sin
        }).fold(0.0, |s, x| s + x)
    }
}

impl Default for UnvoicedDFT {
    fn default() -> UnvoicedDFT {
        UnvoicedDFT([Complex32::zero(); DFT_HALF])
    }
}

pub struct Unvoiced<'a, 'b> {
    cur: &'a UnvoicedDFT,
    prev: &'b UnvoicedDFT,
    window: window::Window,
}

impl<'a, 'b> Unvoiced<'a, 'b> {
    pub fn new(cur: &'a UnvoicedDFT, prev: &'b UnvoicedDFT) -> Unvoiced<'a, 'b> {
        Unvoiced {
            cur: cur,
            prev: prev,
            window: window::synthesis(),
        }
    }

    pub fn get(&self, n: usize) -> f32 {
        debug_assert!(n < SAMPLES_PER_FRAME);

        let n = n as isize;

        let numer = self.window.get(n) * self.prev.idft(n) +
            self.window.get(n - SAMPLES_PER_FRAME as isize) *
                self.cur.idft(n - SAMPLES_PER_FRAME as isize);
        let denom = self.window.get(n).powi(2) +
            self.window.get(n - SAMPLES_PER_FRAME as isize).powi(2);

        numer / denom
    }
}

#[cfg(test)]
mod test {
    use super::edges;
    use descramble::Bootstrap;
    use params::BaseParams;

    #[test]
    fn test_edges() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());

        assert!((p.fundamental - 0.17575344).abs() < 0.000001);

        let (lower, upper) = edges(1, &p);
        assert_eq!(lower, 4);
        assert_eq!(upper, 11);

        let (lower, upper) = edges(2, &p);
        assert_eq!(lower, 11);
        assert_eq!(upper, 18);

        let (lower, upper) = edges(5, &p);
        assert_eq!(lower, 33);
        assert_eq!(upper, 40);

        let (lower, upper) = edges(8, &p);
        assert_eq!(lower, 54);
        assert_eq!(upper, 61);

        let (lower, upper) = edges(16, &p);
        assert_eq!(lower, 111);
        assert_eq!(upper, 119);
    }
}
<|endoftext|>


<commit_before>use std::io::{MemReader, IoError};
use std::io::process::{ProcessOutput, Command};
use std::collections::HashMap;
use std::fmt::Show;
use std::str;

use serialize::{json, Encodable};
use url;

use conduit::{Request, Response, Handler};
use db::RequestTransaction;

pub use self::errors::{CargoError, CargoResult, internal, human, internal_error};
pub use self::errors::{ChainError, BoxError};
pub use self::result::{Require, Wrap};
pub use self::lazy_cell::LazyCell;
pub use self::io::LimitErrorReader;
pub use self::hasher::{HashingReader};

pub mod errors;
pub mod result;
mod lazy_cell;
mod io;
mod hasher;

pub trait RequestUtils {
    fn redirect(self, url: String) -> Response;

    fn json<'a, T: Encodable<json::Encoder<'a>, IoError>>(self, t: &T) -> Response;
    fn query(self) -> HashMap<String, String>;
    fn wants_json(self) -> bool;
    fn pagination(self, default: uint, max: uint) -> CargoResult<(i64, i64)>;
}

pub fn json_response<'a, T>(t: &T) -> Response
                            where T: Encodable<json::Encoder<'a>, IoError> {
    let s = json::encode(t);
    let json = fixup(from_str(s.as_slice()).unwrap());
    let mut headers = HashMap::new();
    headers.insert("Content-Type".to_string(),
                   vec!["application/json; charset=utf-8".to_string()]);
    return Response {
        status: (200, "OK"),
        headers: headers,
        body: box MemReader::new(json.to_string().into_bytes()),
    };

    fn fixup(json: json::Json) -> json::Json {
        match json {
            json::Object(object) => {
                json::Object(object.into_iter().map(|(k, v)| {
                    let k = if k.as_slice() == "krate" {
                        "crate".to_string()
                    } else {
                        k
                    };
                    (k, fixup(v))
                }).collect())
            }
            json::List(list) => {
                json::List(list.into_iter().map(fixup).collect())
            }
            j => j,
        }
    }
}

impl<'a> RequestUtils for &'a Request + 'a {
    fn json<'a, T: Encodable<json::Encoder<'a>, IoError>>(self, t: &T) -> Response {
        json_response(t)
    }

    fn query(self) -> HashMap<String, String> {
        url::form_urlencoded::parse_str(self.query_string().unwrap_or(""))
            .into_iter().collect()
    }

    fn redirect(self, url: String) -> Response {
        let mut headers = HashMap::new();
        headers.insert("Location".to_string(), vec![url.to_string()]);
        Response {
            status: (302, "Found"),
            headers: headers,
            body: box MemReader::new(Vec::new()),
        }
    }

    fn wants_json(self) -> bool {
        let content = self.headers().find("Accept").unwrap_or(Vec::new());
        content.iter().any(|s| s.contains("json"))
    }

    fn pagination(self, default: uint, max: uint) -> CargoResult<(i64, i64)> {
        let query = self.query();
        let page = query.find_equiv(&"page").map(|s| s.as_slice())
                        .and_then(from_str::<uint>).unwrap_or(1);
        let limit = query.find_equiv(&"per_page").map(|s| s.as_slice())
                         .and_then(from_str::<uint>).unwrap_or(default);
        if limit > max {
            return Err(human(format!("cannot request more than {} items", max)))
        }
        Ok((((page - 1) * limit) as i64, limit as i64))
    }
}

pub struct C(pub fn(&mut Request) -> CargoResult<Response>);

impl Handler for C {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show + 'static>> {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => { req.commit(); Ok(resp) }
            Err(e) => {
                match e.response() {
                    Some(response) => Ok(response),
                    None => Err(box e as Box<Show>),
                }
            }
        }
    }
}

pub fn exec(cmd: &Command) -> CargoResult<ProcessOutput> {
    let output = try!(cmd.output().chain_error(|| {
        internal(format!("failed to run command `{}`", cmd))
    }));
    if !output.status.success() {
        let mut desc = String::new();
        if output.output.len() != 0 {
            desc.push_str("--- stdout\n");
            desc.push_str(str::from_utf8(output.output.as_slice()).unwrap());
        }
        if output.error.len() != 0 {
            desc.push_str("--- stderr\n");
            desc.push_str(str::from_utf8(output.error.as_slice()).unwrap());
        }
        Err(internal_error(format!("failed to run command `{}`", cmd), desc))
    } else {
        Ok(output)
    }
}
<commit_msg>Add a sub-router handler<commit_after>use std::collections::HashMap;
use std::fmt::Show;
use std::io::process::{ProcessOutput, Command};
use std::io::{MemReader, IoError};
use std::str;
use std::sync::Arc;

use serialize::{json, Encodable};
use url;

use conduit::{Request, Response, Handler};
use conduit_router::{RouteBuilder, RequestParams};
use db::RequestTransaction;

pub use self::errors::{CargoError, CargoResult, internal, human, internal_error};
pub use self::errors::{ChainError, BoxError};
pub use self::result::{Require, Wrap};
pub use self::lazy_cell::LazyCell;
pub use self::io::LimitErrorReader;
pub use self::hasher::{HashingReader};
pub use self::request_proxy::RequestProxy;
pub use self::head::Head;

pub mod errors;
pub mod result;
mod hasher;
mod head;
mod io;
mod lazy_cell;
mod request_proxy;

pub trait RequestUtils {
    fn redirect(self, url: String) -> Response;

    fn json<'a, T: Encodable<json::Encoder<'a>, IoError>>(self, t: &T) -> Response;
    fn query(self) -> HashMap<String, String>;
    fn wants_json(self) -> bool;
    fn pagination(self, default: uint, max: uint) -> CargoResult<(i64, i64)>;
}

pub fn json_response<'a, T>(t: &T) -> Response
                            where T: Encodable<json::Encoder<'a>, IoError> {
    let s = json::encode(t);
    let json = fixup(from_str(s.as_slice()).unwrap());
    let mut headers = HashMap::new();
    headers.insert("Content-Type".to_string(),
                   vec!["application/json; charset=utf-8".to_string()]);
    return Response {
        status: (200, "OK"),
        headers: headers,
        body: box MemReader::new(json.to_string().into_bytes()),
    };

    fn fixup(json: json::Json) -> json::Json {
        match json {
            json::Object(object) => {
                json::Object(object.into_iter().map(|(k, v)| {
                    let k = if k.as_slice() == "krate" {
                        "crate".to_string()
                    } else {
                        k
                    };
                    (k, fixup(v))
                }).collect())
            }
            json::List(list) => {
                json::List(list.into_iter().map(fixup).collect())
            }
            j => j,
        }
    }
}

impl<'a> RequestUtils for &'a Request + 'a {
    fn json<'a, T: Encodable<json::Encoder<'a>, IoError>>(self, t: &T) -> Response {
        json_response(t)
    }

    fn query(self) -> HashMap<String, String> {
        url::form_urlencoded::parse_str(self.query_string().unwrap_or(""))
            .into_iter().collect()
    }

    fn redirect(self, url: String) -> Response {
        let mut headers = HashMap::new();
        headers.insert("Location".to_string(), vec![url.to_string()]);
        Response {
            status: (302, "Found"),
            headers: headers,
            body: box MemReader::new(Vec::new()),
        }
    }

    fn wants_json(self) -> bool {
        let content = self.headers().find("Accept").unwrap_or(Vec::new());
        content.iter().any(|s| s.contains("json"))
    }

    fn pagination(self, default: uint, max: uint) -> CargoResult<(i64, i64)> {
        let query = self.query();
        let page = query.find_equiv(&"page").map(|s| s.as_slice())
                        .and_then(from_str::<uint>).unwrap_or(1);
        let limit = query.find_equiv(&"per_page").map(|s| s.as_slice())
                         .and_then(from_str::<uint>).unwrap_or(default);
        if limit > max {
            return Err(human(format!("cannot request more than {} items", max)))
        }
        Ok((((page - 1) * limit) as i64, limit as i64))
    }
}

pub struct C(pub fn(&mut Request) -> CargoResult<Response>);

impl Handler for C {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show + 'static>> {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => { req.commit(); Ok(resp) }
            Err(e) => {
                match e.response() {
                    Some(response) => Ok(response),
                    None => Err(box e as Box<Show>),
                }
            }
        }
    }
}

pub struct R(pub Arc<RouteBuilder>);

impl Handler for R {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show + 'static>> {
        let path = req.params()["path"].to_string();
        let R(ref sub_router) = *self;
        sub_router.call(&mut RequestProxy {
            other: req,
            path: Some(path.as_slice()),
            method: None,
        })
    }
}

pub fn exec(cmd: &Command) -> CargoResult<ProcessOutput> {
    let output = try!(cmd.output().chain_error(|| {
        internal(format!("failed to run command `{}`", cmd))
    }));
    if !output.status.success() {
        let mut desc = String::new();
        if output.output.len() != 0 {
            desc.push_str("--- stdout\n");
            desc.push_str(str::from_utf8(output.output.as_slice()).unwrap());
        }
        if output.error.len() != 0 {
            desc.push_str("--- stderr\n");
            desc.push_str(str::from_utf8(output.error.as_slice()).unwrap());
        }
        Err(internal_error(format!("failed to run command `{}`", cmd), desc))
    } else {
        Ok(output)
    }
}
<|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.

#[cfg(not(windows))]
pub mod posix_perm;
pub mod sys;
#[cfg(windows)]
pub mod win_perm;

use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::result;
use std::str::FromStr;

use serde;

pub fn deserialize_using_from_str<'de, T, E, D>(d: D) -> result::Result<T, D::Error>
where
    T: FromStr<Err = E>,
    E: error::Error,
    D: serde::Deserializer<'de>,
{
    struct FromStringable<T, E>(PhantomData<T>, PhantomData<E>);

    impl<'de, T, E> serde::de::Visitor<'de> for FromStringable<T, E>
    where
        T: FromStr<Err = E>,
        E: error::Error,
    {
        type Value = T;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a string")
        }

        fn visit_str<R>(self, value: &str) -> result::Result<T, R>
        where
            R: serde::de::Error,
        {
            match FromStr::from_str(value) {
                Ok(t) => Ok(t),
                Err(err) => Err(R::custom(format!(
                    "string cannot be parsed: \"{}\" ({})",
                    value,
                    err.description()
                ))),
            }
        }
    }

    d.deserialize_any(FromStringable(PhantomData, PhantomData))
}

pub fn serialize_using_to_string<T, S>(t: &T, s: S) -> result::Result<S::Ok, S::Error>
where
    T: ToString,
    S: serde::Serializer,
{
    s.serialize_str(&t.to_string())
}

/// Provide a way to convert numeric types safely to i64
trait ToI64 {
    fn to_i64(self) -> i64;
}

impl ToI64 for usize {
    fn to_i64(self) -> i64 {
        if mem::size_of::<usize>() >= mem::size_of::<i64>() && self > i64::max_value() as usize {
            if cfg!(debug_assertions) {
                panic!("Tried to convert an out-of-range usize ({}) to i64", self);
            } else {
                error!(
                    "Tried to convert an out-of-range usize ({}) to i64; using i64::max_value()",
                    self
                );
                i64::max_value()
            }
        } else {
            self as i64
        }
    }
}

impl ToI64 for u64 {
    fn to_i64(self) -> i64 {
        if self > i64::max_value() as u64 {
            if cfg!(debug_assertions) {
                panic!("Tried to convert an out-of-range u64 ({}) to i64", self);
            } else {
                error!(
                    "Tried to convert an out-of-range u64 ({}) to i64; using i64::max_value()",
                    self
                );
                i64::max_value()
            }
        } else {
            self as i64
        }
    }
}

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

    #[test]
    fn conversion_of_usize_to_i64() {
        let just_right: usize = 42;
        let zero: usize = 0;

        assert_eq!(just_right.to_i64(), 42);
        assert_eq!(zero.to_i64(), 0);
    }

    #[test]
    #[should_panic]
    #[cfg(debug_assertions)]
    fn conversion_of_too_big_usize_panics_in_debug_mode() {
        let too_big = usize::max_value();
        too_big.to_i64();
    }

    #[test]
    #[cfg(not(debug_assertions))]
    fn conversion_of_too_big_usize_caps_in_release_mode() {
        let too_big = usize::max_value();
        assert_eq!(too_big.to_i64(), i64::max_value());
    }

    #[test]
    fn conversion_of_u64_to_i64() {
        let just_right: u64 = 42;
        let zero: u64 = 0;

        assert_eq!(just_right.to_i64(), 42);
        assert_eq!(zero.to_i64(), 0);
    }

    #[test]
    #[should_panic]
    #[cfg(debug_assertions)]
    fn conversion_of_too_big_u64_panics_in_debug_mode() {
        let too_big = u64::max_value();
        too_big.to_i64();
    }

    #[test]
    #[cfg(not(debug_assertions))]
    fn conversion_of_too_big_u64_caps_in_release_mode() {
        let too_big = u64::max_value();
        assert_eq!(too_big.to_i64(), i64::max_value());
    }
}
<commit_msg>ToI64 has to be public if we actually want to use it elsewhere<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.

#[cfg(not(windows))]
pub mod posix_perm;
pub mod sys;
#[cfg(windows)]
pub mod win_perm;

use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::result;
use std::str::FromStr;

use serde;

pub fn deserialize_using_from_str<'de, T, E, D>(d: D) -> result::Result<T, D::Error>
where
    T: FromStr<Err = E>,
    E: error::Error,
    D: serde::Deserializer<'de>,
{
    struct FromStringable<T, E>(PhantomData<T>, PhantomData<E>);

    impl<'de, T, E> serde::de::Visitor<'de> for FromStringable<T, E>
    where
        T: FromStr<Err = E>,
        E: error::Error,
    {
        type Value = T;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a string")
        }

        fn visit_str<R>(self, value: &str) -> result::Result<T, R>
        where
            R: serde::de::Error,
        {
            match FromStr::from_str(value) {
                Ok(t) => Ok(t),
                Err(err) => Err(R::custom(format!(
                    "string cannot be parsed: \"{}\" ({})",
                    value,
                    err.description()
                ))),
            }
        }
    }

    d.deserialize_any(FromStringable(PhantomData, PhantomData))
}

pub fn serialize_using_to_string<T, S>(t: &T, s: S) -> result::Result<S::Ok, S::Error>
where
    T: ToString,
    S: serde::Serializer,
{
    s.serialize_str(&t.to_string())
}

/// Provide a way to convert numeric types safely to i64
pub trait ToI64 {
    fn to_i64(self) -> i64;
}

impl ToI64 for usize {
    fn to_i64(self) -> i64 {
        if mem::size_of::<usize>() >= mem::size_of::<i64>() && self > i64::max_value() as usize {
            if cfg!(debug_assertions) {
                panic!("Tried to convert an out-of-range usize ({}) to i64", self);
            } else {
                error!(
                    "Tried to convert an out-of-range usize ({}) to i64; using i64::max_value()",
                    self
                );
                i64::max_value()
            }
        } else {
            self as i64
        }
    }
}

impl ToI64 for u64 {
    fn to_i64(self) -> i64 {
        if self > i64::max_value() as u64 {
            if cfg!(debug_assertions) {
                panic!("Tried to convert an out-of-range u64 ({}) to i64", self);
            } else {
                error!(
                    "Tried to convert an out-of-range u64 ({}) to i64; using i64::max_value()",
                    self
                );
                i64::max_value()
            }
        } else {
            self as i64
        }
    }
}

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

    #[test]
    fn conversion_of_usize_to_i64() {
        let just_right: usize = 42;
        let zero: usize = 0;

        assert_eq!(just_right.to_i64(), 42);
        assert_eq!(zero.to_i64(), 0);
    }

    #[test]
    #[should_panic]
    #[cfg(debug_assertions)]
    fn conversion_of_too_big_usize_panics_in_debug_mode() {
        let too_big = usize::max_value();
        too_big.to_i64();
    }

    #[test]
    #[cfg(not(debug_assertions))]
    fn conversion_of_too_big_usize_caps_in_release_mode() {
        let too_big = usize::max_value();
        assert_eq!(too_big.to_i64(), i64::max_value());
    }

    #[test]
    fn conversion_of_u64_to_i64() {
        let just_right: u64 = 42;
        let zero: u64 = 0;

        assert_eq!(just_right.to_i64(), 42);
        assert_eq!(zero.to_i64(), 0);
    }

    #[test]
    #[should_panic]
    #[cfg(debug_assertions)]
    fn conversion_of_too_big_u64_panics_in_debug_mode() {
        let too_big = u64::max_value();
        too_big.to_i64();
    }

    #[test]
    #[cfg(not(debug_assertions))]
    fn conversion_of_too_big_u64_caps_in_release_mode() {
        let too_big = u64::max_value();
        assert_eq!(too_big.to_i64(), i64::max_value());
    }
}
<|endoftext|>


<commit_before>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::error::Error;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io::{ErrorKind, Write};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;

extern crate chrono;
use self::chrono::*;

use log;
use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata, SetLoggerError};

//An ugly hack i know...
static mut stopped: bool = false;

//TODO: stop logger on panic
////////////////////////////////////////////////////////////
//                         Logger                         //
////////////////////////////////////////////////////////////

pub struct Logger{
    max_level: LogLevelFilter,
    log_tx:    Arc<Mutex<Sender<(String, LogLevel)>>>,
}

impl Logger {

    pub fn new(max_level: &LogLevelFilter) -> (Logger, Receiver<(String, LogLevel)>) {
        let (tx, rx) = channel::<(String, LogLevel)>();
        let logger = Logger {
            max_level: max_level.clone(),
            log_tx:    Arc::new(Mutex::new(tx)),
        };

        (logger, rx)
    }

    pub fn init(logger: Logger, max_level: LogLevelFilter) -> Result<(), SetLoggerError> {
        //Register backend
        log::set_logger(|max_log_level| {
            max_log_level.set(max_level);
            Box::new(logger)
        })
    }

    pub fn process_logs(rx: Receiver<(String, LogLevel)>, log_dir: PathBuf, log_size: u64) {
        thread::spawn(move || {

            //Open log files
            let mut msg_log_file_name = get_next_logfile_path(&log_dir, "log-msg");
            let mut err_log_file_name = get_next_logfile_path(&log_dir, "log-err");
            let mut msg_log_file      = open_log_file(&msg_log_file_name);
            let mut err_log_file      = open_log_file(&err_log_file_name);

            loop {

                let (log, log_level) = rx.recv().unwrap();

                if log_level == LogLevel::Error {
                    if log == String::from("**INTERNAL** STOP!!!") {break;}
                    err_log_file.write(log.as_bytes());
                } else {
                    msg_log_file.write(log.as_bytes());
                }

                //rotate if needed
                if needs_rotate(&msg_log_file_name, log_size) {
                    msg_log_file_name = get_next_logfile_path(&log_dir, "log-msg");
                    msg_log_file.write(format!("***INFO: Log has been rotated, further messages are in {}", msg_log_file_name).as_bytes());
                    msg_log_file      = open_log_file(&msg_log_file_name);
                }
                if needs_rotate(&err_log_file_name, log_size) {
                    err_log_file_name = get_next_logfile_path(&log_dir, "log-err");
                    err_log_file.write(format!("***INFO: Log has been rotated, further messages are in {}", msg_log_file_name).as_bytes());
                    err_log_file      = open_log_file(&err_log_file_name);
                }
            }
        });
    }
}

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.max_level
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let log_tx        = self.log_tx.clone();
            let time_and_date = Local::now().format("%v %H:%M:%S:%f").to_string();
            let internal      = format!("{}\n", record.args());
            let log_message   = format!("{}: {} {}\n", time_and_date, get_padded_loglevel_string(record.level().clone()), record.args());
            if (internal == String::from("**INTERNAL** STOP!!!") && record.level().clone() == LogLevel::Error) || unsafe{!stopped}{
                unsafe{stopped = true;}
                log_tx.lock().unwrap().send((log_message, record.level()));
            } else {
                log_tx.lock().unwrap().send((log_message, record.level()));
            }
        }
    }
}

////////////////////////////////////////////////////////////
//                       Functions                        //
////////////////////////////////////////////////////////////

//Utils
pub fn get_padded_loglevel_string(log_level: LogLevel) -> String{
    match log_level {
        LogLevel::Error => return String::from("Error:"),
        LogLevel::Warn  => return String::from("Warn: "),
        LogLevel::Info  => return String::from("Info: "),
        LogLevel::Debug => return String::from("Debug:"),
        LogLevel::Trace => return String::from("Trace:")
    }
}

pub fn get_next_logfile_path(log_dir: &PathBuf, variant: &str) -> String {

    let mut counter = 0;

    loop {

        let     filename = format!("{}_{}_{}.log", variant, Local::now().format("%v").to_string(), counter);
        let mut path     = PathBuf::from(log_dir);
        path.push(filename.clone());
        match OpenOptions::new().write(true).create(false).open(path.clone()) {
            Ok(_)    => (), //File exists, lets keep looking for another one.
            Err(err) => {
                match err.kind() {
                    ErrorKind::NotFound         => {return filename},
                    ErrorKind::PermissionDenied => {panic!("Cannot open {} to write logs into: Permission Denied.", path.display());}
                    _                           => {panic!("Failed to open log file: {}", err);}
                }
            }
        }

        counter += 1;
    }
}

pub fn open_log_file(file_name: &String ) -> File {
    match OpenOptions::new().write(true).create(true).open(file_name) {
        Ok(file) => return file,
        Err(err) => {panic!("Failed to crate log file: {}", err.description());}
    };
}

pub fn needs_rotate(file_name: &String, log_size: u64) -> bool{

    //get metadata
    let metadata = match fs::metadata(file_name) {
        Ok(metadata) => metadata,
        Err(err)     => {
            error!("**INTERNAL** STOP!!!");
            panic!("Failed to acquire metadata of the current log file: {}", err.description());
        }
    };

    if metadata.len()/1000000 > log_size {
        return true;
    }

    false
}
<commit_msg>Fixed log file location not respecting CLI option<commit_after>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::error::Error;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io::{ErrorKind, Write};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;

extern crate chrono;
use self::chrono::*;

use log;
use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata, SetLoggerError};

//An ugly hack i know...
static mut stopped: bool = false;

//TODO: stop logger on panic
////////////////////////////////////////////////////////////
//                         Logger                         //
////////////////////////////////////////////////////////////

pub struct Logger{
    max_level: LogLevelFilter,
    log_tx:    Arc<Mutex<Sender<(String, LogLevel)>>>,
}

impl Logger {

    pub fn new(max_level: &LogLevelFilter) -> (Logger, Receiver<(String, LogLevel)>) {
        let (tx, rx) = channel::<(String, LogLevel)>();
        let logger = Logger {
            max_level: max_level.clone(),
            log_tx:    Arc::new(Mutex::new(tx)),
        };

        (logger, rx)
    }

    pub fn init(logger: Logger, max_level: LogLevelFilter) -> Result<(), SetLoggerError> {
        //Register backend
        log::set_logger(|max_log_level| {
            max_log_level.set(max_level);
            Box::new(logger)
        })
    }

    pub fn process_logs(rx: Receiver<(String, LogLevel)>, log_dir: PathBuf, log_size: u64) {
        thread::spawn(move || {

            //Open log files
            let mut msg_log_file_name = get_next_logfile_path(&log_dir, "log-msg");
            let mut err_log_file_name = get_next_logfile_path(&log_dir, "log-err");
            let mut msg_log_file      = open_log_file(&msg_log_file_name);
            let mut err_log_file      = open_log_file(&err_log_file_name);

            loop {

                let (log, log_level) = rx.recv().unwrap();

                if log_level == LogLevel::Error {
                    if log == String::from("**INTERNAL** STOP!!!") {break;}
                    err_log_file.write(log.as_bytes());
                } else {
                    msg_log_file.write(log.as_bytes());
                }

                //rotate if needed
                if needs_rotate(&msg_log_file_name, log_size) {
                    msg_log_file_name = get_next_logfile_path(&log_dir, "log-msg");
                    msg_log_file.write(format!("***INFO: Log has been rotated, further messages are in {}", msg_log_file_name).as_bytes());
                    msg_log_file      = open_log_file(&msg_log_file_name);
                }
                if needs_rotate(&err_log_file_name, log_size) {
                    err_log_file_name = get_next_logfile_path(&log_dir, "log-err");
                    err_log_file.write(format!("***INFO: Log has been rotated, further messages are in {}", msg_log_file_name).as_bytes());
                    err_log_file      = open_log_file(&err_log_file_name);
                }
            }
        });
    }
}

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.max_level
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let log_tx        = self.log_tx.clone();
            let time_and_date = Local::now().format("%v %H:%M:%S:%f").to_string();
            let internal      = format!("{}\n", record.args());
            let log_message   = format!("{}: {} {}\n", time_and_date, get_padded_loglevel_string(record.level().clone()), record.args());
            if (internal == String::from("**INTERNAL** STOP!!!") && record.level().clone() == LogLevel::Error) || unsafe{!stopped}{
                unsafe{stopped = true;}
                log_tx.lock().unwrap().send((log_message, record.level()));
            } else {
                log_tx.lock().unwrap().send((log_message, record.level()));
            }
        }
    }
}

////////////////////////////////////////////////////////////
//                       Functions                        //
////////////////////////////////////////////////////////////

//Utils
pub fn get_padded_loglevel_string(log_level: LogLevel) -> String{
    match log_level {
        LogLevel::Error => return String::from("Error:"),
        LogLevel::Warn  => return String::from("Warn: "),
        LogLevel::Info  => return String::from("Info: "),
        LogLevel::Debug => return String::from("Debug:"),
        LogLevel::Trace => return String::from("Trace:")
    }
}

pub fn get_next_logfile_path(log_dir: &PathBuf, variant: &str) -> String {

    let mut counter = 0;

    loop {

        let     filename = format!("{}_{}_{}.log", variant, Local::now().format("%v").to_string(), counter);
        let mut path     = PathBuf::from(log_dir);
        path.push(filename.clone());
        match OpenOptions::new().write(true).create(false).open(path.clone()) {
            Ok(_)    => (), //File exists, lets keep looking for another one.
            Err(err) => {
                match err.kind() {
                    ErrorKind::NotFound         => {
                        match path.to_str() {
                            Some(file) => return String::from(file),
                            None       => {
                                panic!("Failed to get file name from PathBuf (invalid UTF-8)");
                            }
                        }
                    },
                    ErrorKind::PermissionDenied => {panic!("Cannot open {} to write logs into: Permission Denied.", path.display());}
                    _                           => {panic!("Failed to open log file: {}", err);}
                }
            }
        }

        counter += 1;
    }
}

pub fn open_log_file(file_name: &String ) -> File {
    match OpenOptions::new().write(true).create(true).open(file_name) {
        Ok(file) => return file,
        Err(err) => {panic!("Failed to crate log file: {}", err.description());}
    };
}

pub fn needs_rotate(file_name: &String, log_size: u64) -> bool{

    //get metadata
    let metadata = match fs::metadata(file_name) {
        Ok(metadata) => metadata,
        Err(err)     => {
            error!("**INTERNAL** STOP!!!");
            panic!("Failed to acquire metadata of the current log file: {}", err.description());
        }
    };

    if metadata.len()/1000000 > log_size {
        return true;
    }

    false
}
<|endoftext|>


<commit_before>use autograd::Function;
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use ::*;

pub type VarList<T> = Vec<Variable<T>>;

pub enum SavedVarKind {
    FloatVariable(SavedVariable<f32>),
    LongVariable(SavedVariable<i64>),
}
pub enum VarKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl Clone for VarKind {
    fn clone(&self) -> Self {
        panic!("match and clone!")
    }
}

pub struct VariableImpl<T> {
    data: Tensor<T>,
    // AKA Creator
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
    // version_counter etc ...
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(data: Tensor<T>) -> Self {
        VariableImpl {
            data: data,
            grad_fn: None,
            grad: None,
            // XXX
            volatile: false,
            requires_grad: false,
        }
    }
}

pub struct Variable<T> {
    value: RcMut<VariableImpl<T>>,
}

impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable { value: self.value.clone() }
    }
}

pub struct SavedVariable<T> {
    data: Box<Tensor<T>>,
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
	// version_counter etc ...
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

pub struct VariableArgs {}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable { value: RcMutNew(VariableImpl::new(data)) }
    }
    pub fn new_volatile(data: Tensor<T>) -> Self {
        Variable { value: RcMutNew(VariableImpl::new(data)) }
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        let mut v = self.value.borrow_mut();
        callback(&mut v.data);
    }
    // XXX FIXME
    pub fn data(&mut self) -> Tensor<T> {
        self.value.borrow_mut().data.clone()
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        self.clone()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward(&mut self, args: &BackwardArgs) {}
    // Detach from graph
    pub fn detach_(&mut self) {}
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        self.clone()
    }
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        let v = VariableImpl {
            data: Tensor::new(),
            grad_fn: None,
            grad: None,
            // XXX
            volatile: false,
            requires_grad: false,
        };
        Variable { value: RcMutNew(v) }
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        panic!("implement")
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<commit_msg>fixup Variable implementation a little bit<commit_after>use autograd::Function;
use tensor::Tensor;
use std::ops::{AddAssign, Index};
use ::*;

pub type VarList<T> = Vec<Variable<T>>;

pub enum SavedVarKind {
    FloatVariable(SavedVariable<f32>),
    LongVariable(SavedVariable<i64>),
}
pub enum VarKind {
    FloatVariable(Variable<f32>),
    LongVariable(Variable<i64>),
}

impl Clone for VarKind {
    fn clone(&self) -> Self {
        panic!("match and clone!")
    }
}

pub struct VariableImpl<T> {
    data: Tensor<T>,
    // AKA Creator
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
    // version_counter etc ...
    volatile: bool,
    requires_grad: bool,
}

impl<T> VariableImpl<T> {
    fn new(data: Tensor<T>) -> Self {
        VariableImpl {
            data: data,
            grad_fn: None,
            grad: None,
            // XXX
            volatile: false,
            requires_grad: false,
        }
    }
    fn new_args(data: Tensor<T>, args: VariableArgs) -> Self {

        let grad_fn = match args.creator {
            Some(creator) => Some(RcMutNew(creator)),
            _ => None,
        };

        VariableImpl {
            data: data,
            grad_fn: grad_fn,
            grad: None,
            volatile: args.volatile,
            requires_grad: args.requires_grad,
        }
    }
}

pub struct Variable<T> {
    value: RcMut<VariableImpl<T>>,
}

impl<T> Clone for Variable<T> {
    fn clone(&self) -> Self {
        Variable { value: self.value.clone() }
    }
}

pub struct SavedVariable<T> {
    data: Box<Tensor<T>>,
    grad_fn: OptRcMut<Function>,
    grad: OptRcMut<Variable<T>>,
	// version_counter etc ...
}

#[derive(Default, Clone)]
pub struct BackwardArgs {}

pub struct VariableArgs {
    pub creator: Option<Function>,
    pub volatile: bool,
    pub requires_grad: bool,
}

impl Default for VariableArgs {
    fn default() -> Self {
        VariableArgs {
            creator: None,
            volatile: false,
            requires_grad: true,
        }
    }
}

impl<T> Variable<T> {
    pub fn new(data: Tensor<T>) -> Self {
        Variable::new_args(data, VariableArgs::default())
    }
    pub fn new_args(data: Tensor<T>, args: VariableArgs) -> Self {

        Variable { value: RcMutNew(VariableImpl::new(data)) }
    }
    pub fn apply(&mut self, callback: fn(&mut Tensor<T>)) {
        let mut v = self.value.borrow_mut();
        callback(&mut v.data);
    }
    // XXX FIXME
    pub fn data(&mut self) -> RefMut<Tensor<T>> {
        RefMut::map(self.value.borrow_mut(), |v| &mut v.data)
    }
    pub fn view(&self, dims: &[i32]) -> Self {
        self.clone()
    }
    // Computes the gradient of current variable w.r.t. graph leaves
    pub fn backward(&mut self, args: &BackwardArgs) {}
    // Detach from graph
    pub fn detach_(&mut self) {}
    // return a new variable detached from graph
    pub fn detach(&self) -> Variable<T> {
        self.clone()
    }
}

impl<T> Default for Variable<T> {
    fn default() -> Self {
        let v = VariableImpl {
            data: Tensor::new(),
            grad_fn: None,
            grad: None,
            // XXX
            volatile: false,
            requires_grad: false,
        };
        Variable { value: RcMutNew(v) }
    }
}

impl<T: Copy> Index<isize> for Variable<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        panic!("implement")
    }
}

impl AddAssign<Variable<f32>> for f32 {
    fn add_assign(&mut self, rhs: Variable<f32>) {
        *self = *self + rhs[0]
    }
}
<|endoftext|>


<commit_before>// Used to specify checks that shouldn't fail (but might in unsafe)
macro_rules! dbg_gl_error {
    ($($pat:pat => $msg:expr),*) => {
        if cfg!(debug_assertions) {
            let err = $crate::Context::get_error();
            match err {
                $(Some($pat) => {
                    panic!("OpenGL error {:?} - {}", err, $msg)
                }),*
                None => { }
            }
        }
    }
}

// Used to specify checks that should *never* be able to fail (even in unsafe!)
macro_rules! dbg_gl_sanity_check {
    ($($pat:pat => $msg:expr),*) => {
        dbg_gl_error! { $($pat => concat!("Sanity check failed: ", $msg)),* }
    }
}

macro_rules! gl_enum {
    (
        pub gl_enum $name:ident {
            $($variant:ident as $const_name:ident = $value:expr),+
        }
    ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub enum $name {
            $($variant = $value as isize),+
        }
        $(pub const $const_name: $name = $name::$variant;)+

        #[allow(dead_code)]
        impl $name {
            pub fn from_gl(gl_enum: $crate::gl::types::GLenum)
                -> Result<Self, ()>
            {
                match gl_enum {
                    $(x if x == $value => { Ok($name::$variant) },)+
                    _ => { Err(()) }
                }
            }

            pub fn gl_enum(&self) -> $crate::gl::types::GLenum {
                *self as $crate::gl::types::GLenum
            }
        }
    }
}
<commit_msg>Derive `Hash` for all `gl_enum!`-based enums<commit_after>// Used to specify checks that shouldn't fail (but might in unsafe)
macro_rules! dbg_gl_error {
    ($($pat:pat => $msg:expr),*) => {
        if cfg!(debug_assertions) {
            let err = $crate::Context::get_error();
            match err {
                $(Some($pat) => {
                    panic!("OpenGL error {:?} - {}", err, $msg)
                }),*
                None => { }
            }
        }
    }
}

// Used to specify checks that should *never* be able to fail (even in unsafe!)
macro_rules! dbg_gl_sanity_check {
    ($($pat:pat => $msg:expr),*) => {
        dbg_gl_error! { $($pat => concat!("Sanity check failed: ", $msg)),* }
    }
}

macro_rules! gl_enum {
    (
        pub gl_enum $name:ident {
            $($variant:ident as $const_name:ident = $value:expr),+
        }
    ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub enum $name {
            $($variant = $value as isize),+
        }
        $(pub const $const_name: $name = $name::$variant;)+

        #[allow(dead_code)]
        impl $name {
            pub fn from_gl(gl_enum: $crate::gl::types::GLenum)
                -> Result<Self, ()>
            {
                match gl_enum {
                    $(x if x == $value => { Ok($name::$variant) },)+
                    _ => { Err(()) }
                }
            }

            pub fn gl_enum(&self) -> $crate::gl::types::GLenum {
                *self as $crate::gl::types::GLenum
            }
        }
    }
}
<|endoftext|>