<commit_before>#[test]
fn it_works() {
}
<commit_msg>first?<commit_after>/*
    CMQ - Controller Message Query
*/

#[test]
fn it_works() {
}

#![allow(unused_features)]
#![cfg_attr(test, plugin(quickcheck_macros))]
#![deny(missing_docs, warnings)]
#![feature(collections)]
#![feature(core)]
#![feature(io)]
#![feature(libc)]
#![feature(old_io)]
#![feature(old_path)]
#![feature(path)]
#![feature(plugin)]
#![feature(std_misc)]

//! A library for serial port communication

/// Based on the termios API
extern crate termios;

#[cfg(test)]
extern crate quickcheck;
use std::old_io::{File, FileAccess, FileMode, IoResult};
use std::os::unix::AsRawFd;
pub use termios::BaudRate;
use termios::prelude::*;

#[cfg(test)]
mod socat;

#[cfg(test)]
mod test;

/// For how long to block `read()` calls
#[derive(Copy, PartialEq)]
pub struct BlockingMode {

    /// The device will block until *at least* `bytes` are received
    pub bytes: u8,

    /// The device will block for at least `deciseconds` after each `read()` call
    pub deciseconds: u8,
}

/// A serial device
pub struct SerialPort(File);

/// Serial Port Object
impl SerialPort {

    /// Opens a serial `device` in "raw" mode
    pub fn open(device: &Path, access: FileAccess) -> IoResult<SerialPort> {
        let file = try!(File::open_mode(device, FileMode::Open, access));

        let mut termios = try!(Termios::fetch(file.as_raw_fd()));
        termios.make_raw();

        let sp = SerialPort(file);

        try!(sp.update(termios));

        Ok(sp)
    }

    /// Returns the input and output baud rates
    pub fn baud_rate(&self) -> IoResult<(BaudRate, BaudRate)> {
        self.fetch().map(|termios| {
            (termios.ispeed(), termios.ospeed())
        })
    }

    /// Returns the blocking mode used by the device
    pub fn blocking_mode(&self) -> IoResult<BlockingMode> {
        self.fetch().map(|termios| {
            BlockingMode {
                bytes: termios.cc[control::Char::VMIN],
                deciseconds: termios.cc[control::Char::VTIME],
            }
        })
    }

    /// Returns the number of data bits used per character
    pub fn data_bits(&self) -> IoResult<DataBits> {
        self.fetch().map(|termios| {
            match termios.get::<control::CSIZE>() {
                control::CSIZE::CS5 => DataBits::Five,
                control::CSIZE::CS6 => DataBits::Six,
                control::CSIZE::CS7 => DataBits::Seven,
                control::CSIZE::CS8 => DataBits::Eight,
            }
        })
    }

    /// Returns the flow control used by the device
    pub fn flow_control(&self) -> IoResult<FlowControl> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CRTSCTS) {
                FlowControl::Hardware
            } else if termios.contains(input::Flag::IXANY) &&
                termios.contains(input::Flag::IXOFF) &&
                termios.contains(input::Flag::IXON)
            {
                FlowControl::Software
            } else {
                FlowControl::None
            }
        })
    }

    /// Returns the bit parity used by the device
    pub fn parity(&self) -> IoResult<Parity> {
        self.fetch().map(|termios| {
            match (
                termios.contains(control::Flag::PARENB),
                termios.contains(control::Flag::PARODD),
            ) {
                (true, true) => Parity::Odd,
                (true, false) => Parity::Even,
                (false, _) => Parity::None,
            }
        })
    }

    /// Changes the baud rate of the input/output or both directions
    pub fn set_baud_rate(&mut self, direction: Direction, rate: BaudRate) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match direction {
                Direction::Both => termios.set_speed(rate),
                Direction::Input => termios.set_ispeed(rate),
                Direction::Output => termios.set_ospeed(rate),
            }

            self.update(termios)
        })
    }

    /// Changes the blocking mode used by the device
    pub fn set_blocking_mode(&mut self, mode: BlockingMode) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.cc[control::Char::VMIN] = mode.bytes;
            termios.cc[control::Char::VTIME] = mode.deciseconds;
            self.update(termios)
        })
    }

    /// Changes the number of data bits per character
    pub fn set_data_bits(&mut self, bits: DataBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.set(match bits {
                DataBits::Five => control::CSIZE::CS5,
                DataBits::Six => control::CSIZE::CS6,
                DataBits::Seven => control::CSIZE::CS7,
                DataBits::Eight => control::CSIZE::CS8,
            });
            self.update(termios)
        })
    }

    /// Changes the flow control used by the device
    pub fn set_flow_control(&mut self, flow: FlowControl) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match flow {
                FlowControl::Hardware => {
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                    termios.set(control::Flag::CRTSCTS);
                },
                FlowControl::None => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                },
                FlowControl::Software => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.set(input::Flag::IXANY);
                    termios.set(input::Flag::IXOFF);
                    termios.set(input::Flag::IXON);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the bit parity used by the device
    pub fn set_parity(&mut self, parity: Parity) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match parity {
                Parity::Even => {
                    termios.clear(control::Flag::PARODD);
                    termios.set(control::Flag::PARENB);
                },
                Parity::None => termios.clear(control::Flag::PARENB),
                Parity::Odd => {
                    termios.set(control::Flag::PARENB);
                    termios.set(control::Flag::PARODD);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the number of stop bits per character
    pub fn set_stop_bits(&mut self, bits: StopBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match bits {
                StopBits::One => termios.clear(control::Flag::CSTOPB),
                StopBits::Two => termios.set(control::Flag::CSTOPB),
            }

            self.update(termios)
        })
    }

    /// Returns the number of stop bits per character
    pub fn stop_bits(&self) -> IoResult<StopBits> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CSTOPB) {
                StopBits::Two
            } else {
                StopBits::One
            }
        })
    }

    /// Fetches the current state of the termios structure
    fn fetch(&self) -> IoResult<Termios> {
        Termios::fetch(self.0.as_raw_fd())
    }

    /// Updates the underlying termios structure
    fn update(&self, termios: Termios) -> IoResult<()> {
        termios.update(self.0.as_raw_fd(), When::Now)
    }
}

/// Reader
impl Reader for SerialPort {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        self.0.read(buf)
    }
}

/// Writer
impl Writer for SerialPort {
    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
        self.0.write_all(buf)
    }
}

/// Number of data bits
#[allow(missing_docs)]
#[derive(Copy, Debug, PartialEq)]
pub enum DataBits {
    Five,
    Six,
    Seven,
    Eight,
}

#[allow(missing_docs)]
#[derive(Copy)]
pub enum Direction {
    Both,
    Input,
    Output,
}

/// Flow control
#[allow(missing_docs)]
#[derive(Copy, Debug, PartialEq)]
pub enum FlowControl {
    Hardware,
    None,
    Software,
}

/// Parity checking
#[allow(missing_docs)]
#[derive(Copy, Debug, PartialEq)]
pub enum Parity {
    Even,
    None,
    Odd,
}

/// Number of stop bits
#[allow(missing_docs)]
#[derive(Copy, Debug, PartialEq)]
pub enum StopBits {
    One,
    Two,
}
<|endoftext|>


<commit_before>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
//! FFI bindings to user32.
#![no_std]
#![experimental]
extern crate winapi;
use winapi::*;
extern "system" {
    pub fn AdjustWindowRectEx(
        lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD,
    ) -> BOOL;
    pub fn BeginPaint(hwnd: HWND, lpPaint: LPPAINTSTRUCT) -> HDC;
    pub fn ChangeDisplaySettingsW(lpDevMode: *mut DEVMODEW, dwFlags: DWORD) -> LONG;
    pub fn ChangeDisplaySettingsExW(
        lpszDeviceName: LPCWSTR, lpDevMode: *mut DEVMODEW, hwnd: HWND, dwFlags: DWORD,
        lParam: LPVOID,
    ) -> LONG;
    pub fn CreateWindowExW(
        dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, x: c_int,
        y: c_int, nWidth: c_int, nHeight: c_int, hWndParent: HWND, hMenu: HMENU,
        hInstance: HINSTANCE, lpParam: LPVOID,
    ) -> HWND;
    pub fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
    pub fn DestroyWindow(hWnd: HWND) -> BOOL;
    pub fn DispatchMessageW(lpmsg: *const MSG) -> LRESULT;
    pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
    pub fn EnumDisplayDevicesW(
        lpDevice: LPCWSTR, iDevNum: DWORD, lpDisplayDevice: PDISPLAY_DEVICEW, dwFlags: DWORD,
    ) -> BOOL;
    pub fn EnumDisplaySettingsExW(
        lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: *mut DEVMODEW, dwFlags: DWORD,
    ) -> BOOL;
    pub fn FillRect(hDC: HDC, lprc: *const RECT, hbr: HBRUSH) -> c_int;
    pub fn FindWindowA (lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND;
    pub fn GetClientRect(hWnd: HWND, lpRect: LPRECT) -> BOOL;
    pub fn GetDC(hWnd: HWND) -> HDC;
    pub fn GetMessageW(lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL;
    pub fn GetWindowPlacement(hWnd: HWND, lpwndpl: *mut WINDOWPLACEMENT) -> BOOL;
    pub fn GetWindowRect(hWnd: HWND, lpRect: LPRECT) -> BOOL;
    pub fn MessageBoxA(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int;
    pub fn MessageBoxW(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int;
    pub fn MessageBoxExA(
        hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD,
    ) -> c_int;
    pub fn MessageBoxExW(
        hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD,
    ) -> c_int;
    pub fn PeekMessageW(
        lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT,
    ) -> BOOL;
    pub fn PostMessageW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL;
    pub fn PostQuitMessage(nExitCode: c_int);
    pub fn RegisterClassExW(lpWndClass: *const WNDCLASSEXW) -> ATOM;
    pub fn SetForegroundWindow(hWnd: HWND) -> BOOL;
    pub fn SetWindowPos(
        hWnd: HWND, hWndInsertAfter: HWND, X: c_int, Y: c_int, cx: c_int, cy: c_int, uFlags: UINT,
    ) -> BOOL;
    pub fn SetWindowTextW(hWnd: HWND, lpString: LPCWSTR) -> BOOL;
    pub fn ShowWindow(hWnd: HWND, nCmdShow: c_int) -> BOOL;
    pub fn TranslateMessage(lpmsg: *const MSG) -> BOOL;
    pub fn UpdateWindow(hWnd: HWND) -> BOOL;
    pub fn WaitMessage() -> BOOL;
}
<commit_msg>Add SendInput<commit_after>// Copyright © 2015, Peter Atashian
// Licensed under the MIT License <LICENSE.md>
//! FFI bindings to user32.
#![no_std]
#![experimental]
extern crate winapi;
use winapi::*;
extern "system" {
    pub fn AdjustWindowRectEx(
        lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD,
    ) -> BOOL;
    pub fn BeginPaint(hwnd: HWND, lpPaint: LPPAINTSTRUCT) -> HDC;
    pub fn ChangeDisplaySettingsW(lpDevMode: *mut DEVMODEW, dwFlags: DWORD) -> LONG;
    pub fn ChangeDisplaySettingsExW(
        lpszDeviceName: LPCWSTR, lpDevMode: *mut DEVMODEW, hwnd: HWND, dwFlags: DWORD,
        lParam: LPVOID,
    ) -> LONG;
    pub fn CreateWindowExW(
        dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, x: c_int,
        y: c_int, nWidth: c_int, nHeight: c_int, hWndParent: HWND, hMenu: HMENU,
        hInstance: HINSTANCE, lpParam: LPVOID,
    ) -> HWND;
    pub fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
    pub fn DestroyWindow(hWnd: HWND) -> BOOL;
    pub fn DispatchMessageW(lpmsg: *const MSG) -> LRESULT;
    pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
    pub fn EnumDisplayDevicesW(
        lpDevice: LPCWSTR, iDevNum: DWORD, lpDisplayDevice: PDISPLAY_DEVICEW, dwFlags: DWORD,
    ) -> BOOL;
    pub fn EnumDisplaySettingsExW(
        lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: *mut DEVMODEW, dwFlags: DWORD,
    ) -> BOOL;
    pub fn FillRect(hDC: HDC, lprc: *const RECT, hbr: HBRUSH) -> c_int;
    pub fn FindWindowA (lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND;
    pub fn GetClientRect(hWnd: HWND, lpRect: LPRECT) -> BOOL;
    pub fn GetDC(hWnd: HWND) -> HDC;
    pub fn GetMessageW(lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL;
    pub fn GetWindowPlacement(hWnd: HWND, lpwndpl: *mut WINDOWPLACEMENT) -> BOOL;
    pub fn GetWindowRect(hWnd: HWND, lpRect: LPRECT) -> BOOL;
    pub fn MessageBoxA(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int;
    pub fn MessageBoxW(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int;
    pub fn MessageBoxExA(
        hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD,
    ) -> c_int;
    pub fn MessageBoxExW(
        hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD,
    ) -> c_int;
    pub fn PeekMessageW(
        lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT,
    ) -> BOOL;
    pub fn PostMessageW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL;
    pub fn PostQuitMessage(nExitCode: c_int);
    pub fn RegisterClassExW(lpWndClass: *const WNDCLASSEXW) -> ATOM;
    pub fn SendInput(nInputs: UINT, pInputs: LPINPUT, cbSize: c_int) -> UINT;
    pub fn SetForegroundWindow(hWnd: HWND) -> BOOL;
    pub fn SetWindowPos(
        hWnd: HWND, hWndInsertAfter: HWND, X: c_int, Y: c_int, cx: c_int, cy: c_int, uFlags: UINT,
    ) -> BOOL;
    pub fn SetWindowTextW(hWnd: HWND, lpString: LPCWSTR) -> BOOL;
    pub fn ShowWindow(hWnd: HWND, nCmdShow: c_int) -> BOOL;
    pub fn TranslateMessage(lpmsg: *const MSG) -> BOOL;
    pub fn UpdateWindow(hWnd: HWND) -> BOOL;
    pub fn WaitMessage() -> BOOL;
}
<|endoftext|>


<commit_before>//! A library for reading [SRT Subtitles][1].
//!
//! # Examples
//!
//! ## Reading from string
//!
//! ```
//! use srtparse::parse;
//! let subtitles = parse("1\n00:00:01,100 --> 00:00:02,120\nHello!").unwrap();
//! println!("{:?}", subtitles);
//! ```
//!
//! ## Reading from file
//!
//! ```
//! use srtparse::read_from_file;
//! let subtitles = read_from_file("./data/underworld.srt").unwrap();
//! println!("{:?}", subtitles[0]);
//! ```
//!
//! [1]: https://matroska.org/technical/specs/subtitles/srt.html
#![warn(missing_docs)]
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::Path;
use std::result::Result as StdResult;
use std::time::Duration;

const UTF8_BOM: &str = "\u{feff}";

#[derive(Debug)]
enum State {
    Pos,
    Time,
    Text,
}

/// A subtitle item
#[derive(Debug)]
pub struct Subtitle {
    /// A number indicating which subtitle it is in the sequence
    pub pos: usize,
    /// The time that the subtitle should appear
    pub start_time: Duration,
    /// The time that the subtitle should disappear
    pub end_time: Duration,
    /// The subtitle itself
    pub text: String,
}

/// Read subtitles from a string
pub fn parse(source: &str) -> Result<Vec<Subtitle>> {
    let source = source.trim_start_matches(UTF8_BOM).trim().lines();

    let mut result = Vec::new();

    let mut state = State::Pos;

    let mut pos: Option<usize> = None;
    let mut start_time: Option<Duration> = None;
    let mut end_time: Option<Duration> = None;
    let mut text: Option<String> = None;

    macro_rules! push_subtitle {
        ($pos:ident) => {
            result.push(Subtitle {
                pos: $pos,
                start_time: match start_time {
                    Some(val) => val,
                    None => return Err(Error::MissingStartTime),
                },
                end_time: match end_time {
                    Some(val) => val,
                    None => return Err(Error::MissingEndTime),
                },
                text: match text {
                    Some(val) => val,
                    None => return Err(Error::MissingText),
                },
            });
        };
    }

    for line in source {
        match state {
            State::Pos => {
                if let Some(ps) = pos {
                    push_subtitle!(ps);
                    start_time = None;
                    end_time = None;
                    text = None;
                }
                pos = Some(line.parse::<usize>().map_err(|_| Error::BadPosition)?);
                state = State::Time;
            }
            State::Time => {
                let mut parts = line.split("-->");
                start_time = match parts.next() {
                    Some(v) => Some(duration_from_str(v)?),
                    None => None,
                };
                end_time = match parts.next() {
                    Some(v) => Some(duration_from_str(v)?),
                    None => None,
                };
                if parts.next().is_some() {
                    return Err(Error::BadTime);
                }
                state = State::Text;
            }
            State::Text => {
                if line.is_empty() {
                    state = State::Pos;
                } else if let Some(ref mut txt) = text {
                    txt.push('\n');
                    txt.push_str(line);
                } else {
                    text = Some(line.to_string());
                }
            }
        }
    }
    if let Some(ps) = pos {
        push_subtitle!(ps);
    }

    Ok(result)
}

/// Read subtitles from a file
pub fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<Subtitle>> {
    let mut file = File::open(path).map_err(Error::OpenFile)?;
    let mut buf = String::new();
    file.read_to_string(&mut buf).map_err(Error::ReadFile)?;
    parse(&buf)
}

/// Alias for std result
pub type Result<T> = StdResult<T, Error>;

/// Describes all errors that may occur
pub enum Error {
    /// An error when parsing subtitle position
    BadPosition,
    /// An error when parsing subtitle time
    BadTime,
    /// Unsupported subtitle time format
    BadTimeFormat,
    /// Subtitle start time is missing
    MissingStartTime,
    /// Subtitle end time is missing
    MissingEndTime,
    /// Subtitle text is missing
    MissingText,
    /// Unable to open a file
    OpenFile(IoError),
    /// Unable to read data from a file
    ReadFile(IoError),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::BadPosition => "Invalid subtitle position",
            Error::BadTime => "Invalid subtitle time",
            Error::BadTimeFormat => "Invalid subtitle time format",
            Error::MissingStartTime => "Subtitle start time is missing",
            Error::MissingEndTime => "Subtitle end time is missing",
            Error::MissingText => "Subtitle text is missing",
            Error::OpenFile(ref err) => err.description(),
            Error::ReadFile(ref err) => err.description(),
        }
    }

    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match *self {
            Error::OpenFile(ref err) => Some(err),
            Error::ReadFile(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
        write!(out, "{}", self.description())
    }
}

impl fmt::Display for Error {
    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
        write!(out, "{}", self.description())
    }
}

macro_rules! parse_time_part {
    ($part:expr) => {{
        match $part {
            Some(val) => val.trim().parse::<u64>().map_err(|_| Error::BadTime)?,
            None => {
                return Err(Error::BadTimeFormat);
            }
        }
    }};
}

fn duration_from_str(time: &str) -> Result<Duration> {
    let mut time = time.split(',');
    let (hours, mut minutes, mut seconds) = match time.next() {
        Some(val) => {
            let mut parts = val.split(':');
            let result = (
                parse_time_part!(parts.next()),
                parse_time_part!(parts.next()),
                parse_time_part!(parts.next()),
            );
            if parts.next().is_some() {
                return Err(Error::BadTimeFormat);
            }
            result
        }
        None => {
            return Err(Error::BadTimeFormat);
        }
    };
    let mut milliseconds = parse_time_part!(time.next());
    if time.next().is_some() {
        return Err(Error::BadTimeFormat);
    }
    minutes += hours * 60;
    seconds += minutes * 60;
    milliseconds += seconds * 1000;
    Ok(Duration::from_millis(milliseconds))
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use {parse, read_from_file, Result, UTF8_BOM};

    #[test]
    fn it_works() {
        let source_without_bom = "1
00:00:58,392 --> 00:01:02,563
The war had all but ground to a halt
in the blink of an eye.

2
00:01:04,565 --> 00:01:08,986
Lucian, the most feared and ruthless
leader ever to rule the Lycan clan...

3
00:01:09,070 --> 00:01:11,656
...had finally been killed.

652
01:53:02,325 --> 01:53:06,162
Soon, Marcus will take the throne.
";
        let source_with_bom = format!("{}{}", UTF8_BOM, source_without_bom);

        fn assert_it_works(data: &str) -> Result<()> {
            let result = parse(&data)?;
            assert_eq!(result.len(), 4);
            assert_eq!(result[0].pos, 1);
            assert_eq!(result[0].start_time, Duration::from_millis(58392));
            assert_eq!(result[0].end_time, Duration::from_millis(62563));
            assert_eq!(
                result[0].text,
                "The war had all but ground to a halt\nin the blink of an eye."
            );
            assert_eq!(result[1].pos, 2);
            assert_eq!(result[1].start_time, Duration::from_millis(64565));
            assert_eq!(result[1].end_time, Duration::from_millis(68986));
            assert_eq!(
                result[1].text,
                "Lucian, the most feared and ruthless\nleader ever to rule the Lycan clan..."
            );
            assert_eq!(result[2].pos, 3);
            assert_eq!(result[2].start_time, Duration::from_millis(69070));
            assert_eq!(result[2].end_time, Duration::from_millis(71656));
            assert_eq!(result[2].text, "...had finally been killed.");
            assert_eq!(result[3].pos, 652);
            assert_eq!(result[3].start_time, Duration::from_millis(6_782_325));
            assert_eq!(result[3].end_time, Duration::from_millis(6_786_162));
            assert_eq!(result[3].text, "Soon, Marcus will take the throne.");
            Ok(())
        }

        assert_it_works(&source_without_bom).expect("Failed to parse UTF-8 source without BOM");
        assert_it_works(&source_with_bom).expect("Failed to parse UTF-8 source with BOM");
        let empty = parse("").unwrap();
        assert_eq!(empty.len(), 0);
    }

    #[test]
    #[should_panic(expected = "Invalid subtitle position")]
    fn it_fails_with_bad_position() {
        parse("bad position").unwrap();
    }

    #[test]
    #[should_panic(expected = "Invalid subtitle time")]
    fn it_fails_with_bad_start_time() {
        parse("1\nbad time").unwrap();
    }

    #[test]
    #[should_panic(expected = "Invalid subtitle time")]
    fn it_fails_with_bad_end_time() {
        parse("1\n00:00:58,392 --> bad end time").unwrap();
    }

    #[test]
    #[should_panic(expected = "Invalid subtitle time format")]
    fn it_fails_with_bad_time_format() {
        parse("1\n00:00:00:00").unwrap();
    }

    #[test]
    #[should_panic(expected = "Invalid subtitle time")]
    fn it_fails_with_extra_time() {
        parse("1\n00:00:58,392 --> 00:01:02,563 -> 00:01:02,563").unwrap();
    }

    #[test]
    #[should_panic(expected = "Subtitle start time is missing")]
    fn it_fails_with_missing_start_time() {
        parse("1").unwrap();
    }

    #[test]
    #[should_panic(expected = "Subtitle end time is missing")]
    fn it_fails_with_missing_end_time() {
        parse("1\n00:00:58,392").unwrap();
    }

    #[test]
    #[should_panic(expected = "Subtitle text is missing")]
    fn it_fails_with_missing_text() {
        parse("1\n00:00:58,392 --> 00:01:02,563").unwrap();
    }

    #[test]
    #[should_panic(expected = "entity not found")]
    fn read_from_file_failed() {
        read_from_file("/file/does/not/exist").unwrap();
    }

    #[test]
    fn read_from_file_success() {
        let result = read_from_file("./data/underworld.srt").unwrap();
        assert_eq!(result.len(), 706);

        let first = result.first().unwrap();
        assert_eq!(first.pos, 1);
        assert_eq!(first.start_time, Duration::from_millis(58392));
        assert_eq!(first.end_time, Duration::from_millis(61478));
        assert_eq!(first.text, "Война закончилась в мгновение ока.");

        let last = result.last().unwrap();
        assert_eq!(last.pos, 706);
        assert_eq!(last.start_time, Duration::from_millis(6_801_628));
        assert_eq!(last.end_time, Duration::from_millis(6_804_381));
        assert_eq!(last.text, "... будет объявлена охота.");
    }
}
<commit_msg>Do not use deprecated Error::description<commit_after>//! A library for reading [SRT Subtitles][1].
//!
//! # Examples
//!
//! ## Reading from string
//!
//! ```
//! use srtparse::parse;
//! let subtitles = parse("1\n00:00:01,100 --> 00:00:02,120\nHello!").unwrap();
//! println!("{:?}", subtitles);
//! ```
//!
//! ## Reading from file
//!
//! ```
//! use srtparse::read_from_file;
//! let subtitles = read_from_file("./data/underworld.srt").unwrap();
//! println!("{:?}", subtitles[0]);
//! ```
//!
//! [1]: https://matroska.org/technical/specs/subtitles/srt.html
#![warn(missing_docs)]
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::Path;
use std::result::Result as StdResult;
use std::time::Duration;

const UTF8_BOM: &str = "\u{feff}";

#[derive(Debug)]
enum State {
    Pos,
    Time,
    Text,
}

/// A subtitle item
#[derive(Debug)]
pub struct Subtitle {
    /// A number indicating which subtitle it is in the sequence
    pub pos: usize,
    /// The time that the subtitle should appear
    pub start_time: Duration,
    /// The time that the subtitle should disappear
    pub end_time: Duration,
    /// The subtitle itself
    pub text: String,
}

/// Read subtitles from a string
pub fn parse(source: &str) -> Result<Vec<Subtitle>> {
    let source = source.trim_start_matches(UTF8_BOM).trim().lines();

    let mut result = Vec::new();

    let mut state = State::Pos;

    let mut pos: Option<usize> = None;
    let mut start_time: Option<Duration> = None;
    let mut end_time: Option<Duration> = None;
    let mut text: Option<String> = None;

    macro_rules! push_subtitle {
        ($pos:ident) => {
            result.push(Subtitle {
                pos: $pos,
                start_time: match start_time {
                    Some(val) => val,
                    None => return Err(Error::MissingStartTime),
                },
                end_time: match end_time {
                    Some(val) => val,
                    None => return Err(Error::MissingEndTime),
                },
                text: match text {
                    Some(val) => val,
                    None => return Err(Error::MissingText),
                },
            });
        };
    }

    for line in source {
        match state {
            State::Pos => {
                if let Some(ps) = pos {
                    push_subtitle!(ps);
                    start_time = None;
                    end_time = None;
                    text = None;
                }
                pos = Some(line.parse::<usize>().map_err(|_| Error::BadPosition)?);
                state = State::Time;
            }
            State::Time => {
                let mut parts = line.split("-->");
                start_time = match parts.next() {
                    Some(v) => Some(duration_from_str(v)?),
                    None => None,
                };
                end_time = match parts.next() {
                    Some(v) => Some(duration_from_str(v)?),
                    None => None,
                };
                if parts.next().is_some() {
                    return Err(Error::BadTime);
                }
                state = State::Text;
            }
            State::Text => {
                if line.is_empty() {
                    state = State::Pos;
                } else if let Some(ref mut txt) = text {
                    txt.push('\n');
                    txt.push_str(line);
                } else {
                    text = Some(line.to_string());
                }
            }
        }
    }
    if let Some(ps) = pos {
        push_subtitle!(ps);
    }

    Ok(result)
}

/// Read subtitles from a file
pub fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<Subtitle>> {
    let mut file = File::open(path).map_err(Error::OpenFile)?;
    let mut buf = String::new();
    file.read_to_string(&mut buf).map_err(Error::ReadFile)?;
    parse(&buf)
}

/// Alias for std result
pub type Result<T> = StdResult<T, Error>;

/// Describes all errors that may occur
#[derive(Debug)]
pub enum Error {
    /// An error when parsing subtitle position
    BadPosition,
    /// An error when parsing subtitle time
    BadTime,
    /// Unsupported subtitle time format
    BadTimeFormat,
    /// Subtitle start time is missing
    MissingStartTime,
    /// Subtitle end time is missing
    MissingEndTime,
    /// Subtitle text is missing
    MissingText,
    /// Unable to open a file
    OpenFile(IoError),
    /// Unable to read data from a file
    ReadFile(IoError),
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match *self {
            Error::OpenFile(ref err) => Some(err),
            Error::ReadFile(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
        write!(
            out,
            "{}",
            match self {
                Error::BadPosition => "Invalid subtitle position",
                Error::BadTime => "Invalid subtitle time",
                Error::BadTimeFormat => "Invalid subtitle time format",
                Error::MissingStartTime => "Subtitle start time is missing",
                Error::MissingEndTime => "Subtitle end time is missing",
                Error::MissingText => "Subtitle text is missing",
                Error::OpenFile(ref err) => err.description(),
                Error::ReadFile(ref err) => err.description(),
            }
        )
    }
}

macro_rules! parse_time_part {
    ($part:expr) => {{
        match $part {
            Some(val) => val.trim().parse::<u64>().map_err(|_| Error::BadTime)?,
            None => {
                return Err(Error::BadTimeFormat);
            }
        }
    }};
}

fn duration_from_str(time: &str) -> Result<Duration> {
    let mut time = time.split(',');
    let (hours, mut minutes, mut seconds) = match time.next() {
        Some(val) => {
            let mut parts = val.split(':');
            let result = (
                parse_time_part!(parts.next()),
                parse_time_part!(parts.next()),
                parse_time_part!(parts.next()),
            );
            if parts.next().is_some() {
                return Err(Error::BadTimeFormat);
            }
            result
        }
        None => {
            return Err(Error::BadTimeFormat);
        }
    };
    let mut milliseconds = parse_time_part!(time.next());
    if time.next().is_some() {
        return Err(Error::BadTimeFormat);
    }
    minutes += hours * 60;
    seconds += minutes * 60;
    milliseconds += seconds * 1000;
    Ok(Duration::from_millis(milliseconds))
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use {parse, read_from_file, Result, UTF8_BOM};

    #[test]
    fn it_works() {
        let source_without_bom = "1
00:00:58,392 --> 00:01:02,563
The war had all but ground to a halt
in the blink of an eye.

2
00:01:04,565 --> 00:01:08,986
Lucian, the most feared and ruthless
leader ever to rule the Lycan clan...

3
00:01:09,070 --> 00:01:11,656
...had finally been killed.

652
01:53:02,325 --> 01:53:06,162
Soon, Marcus will take the throne.
";
        let source_with_bom = format!("{}{}", UTF8_BOM, source_without_bom);

        fn assert_it_works(data: &str) -> Result<()> {
            let result = parse(&data)?;
            assert_eq!(result.len(), 4);
            assert_eq!(result[0].pos, 1);
            assert_eq!(result[0].start_time, Duration::from_millis(58392));
            assert_eq!(result[0].end_time, Duration::from_millis(62563));
            assert_eq!(
                result[0].text,
                "The war had all but ground to a halt\nin the blink of an eye."
            );
            assert_eq!(result[1].pos, 2);
            assert_eq!(result[1].start_time, Duration::from_millis(64565));
            assert_eq!(result[1].end_time, Duration::from_millis(68986));
            assert_eq!(
                result[1].text,
                "Lucian, the most feared and ruthless\nleader ever to rule the Lycan clan..."
            );
            assert_eq!(result[2].pos, 3);
            assert_eq!(result[2].start_time, Duration::from_millis(69070));
            assert_eq!(result[2].end_time, Duration::from_millis(71656));
            assert_eq!(result[2].text, "...had finally been killed.");
            assert_eq!(result[3].pos, 652);
            assert_eq!(result[3].start_time, Duration::from_millis(6_782_325));
            assert_eq!(result[3].end_time, Duration::from_millis(6_786_162));
            assert_eq!(result[3].text, "Soon, Marcus will take the throne.");
            Ok(())
        }

        assert_it_works(&source_without_bom).expect("Failed to parse UTF-8 source without BOM");
        assert_it_works(&source_with_bom).expect("Failed to parse UTF-8 source with BOM");
        let empty = parse("").unwrap();
        assert_eq!(empty.len(), 0);
    }

    #[test]
    fn it_fails_with_bad_position() {
        let err = parse("bad position").unwrap_err().to_string();
        assert_eq!(err, "Invalid subtitle position");
    }

    #[test]
    fn it_fails_with_bad_start_time() {
        let err = parse("1\nbad time").unwrap_err().to_string();
        assert_eq!(err, "Invalid subtitle time");
    }

    #[test]
    fn it_fails_with_bad_end_time() {
        let err = parse("1\n00:00:58,392 --> bad end time")
            .unwrap_err()
            .to_string();
        assert_eq!(err, "Invalid subtitle time");
    }

    #[test]
    fn it_fails_with_bad_time_format() {
        let err = parse("1\n00:00:00:00").unwrap_err().to_string();
        assert_eq!(err, "Invalid subtitle time format");
    }

    #[test]
    fn it_fails_with_extra_time() {
        let err = parse("1\n00:00:58,392 --> 00:01:02,563 -> 00:01:02,563")
            .unwrap_err()
            .to_string();
        assert_eq!(err, "Invalid subtitle time");
    }

    #[test]
    fn it_fails_with_missing_start_time() {
        let err = parse("1").unwrap_err().to_string();
        assert_eq!(err, "Subtitle start time is missing");
    }

    #[test]
    fn it_fails_with_missing_end_time() {
        let err = parse("1\n00:00:58,392").unwrap_err().to_string();
        assert_eq!(err, "Subtitle end time is missing");
    }

    #[test]
    fn it_fails_with_missing_text() {
        let err = parse("1\n00:00:58,392 --> 00:01:02,563")
            .unwrap_err()
            .to_string();
        assert_eq!(err, "Subtitle text is missing");
    }

    #[test]
    fn read_from_file_failed() {
        let err = read_from_file("/file/does/not/exist")
            .unwrap_err()
            .to_string();
        assert_eq!(err, "entity not found");
    }

    #[test]
    fn read_from_file_success() {
        let result = read_from_file("./data/underworld.srt").unwrap();
        assert_eq!(result.len(), 706);

        let first = result.first().unwrap();
        assert_eq!(first.pos, 1);
        assert_eq!(first.start_time, Duration::from_millis(58392));
        assert_eq!(first.end_time, Duration::from_millis(61478));
        assert_eq!(first.text, "Война закончилась в мгновение ока.");

        let last = result.last().unwrap();
        assert_eq!(last.pos, 706);
        assert_eq!(last.start_time, Duration::from_millis(6_801_628));
        assert_eq!(last.end_time, Duration::from_millis(6_804_381));
        assert_eq!(last.text, "... будет объявлена охота.");
    }
}
<|endoftext|>


<commit_before>extern crate json_schema;
extern crate serde_json;

#[macro_use]
extern crate quote;

use std::borrow::Cow;
use std::error::Error;

use json_schema::{Schema, Type};

use quote::{Tokens, ToTokens};

struct Ident<S>(S);

impl<S: AsRef<str>> ToTokens for Ident<S> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        tokens.append(self.0.as_ref())
    }
}

fn field(s: &str) -> Ident<Cow<str>> {
    Ident(if ["type", "struct", "enum"].iter().any(|&keyword| keyword == s) {
        format!("{}_", s).into()
    } else {
        s.into()
    })
}

fn merge(result: &mut Schema, r: &Schema) {
    use std::collections::hash_map::Entry;

    for (k, v) in &r.properties {
        match result.properties.entry(k.clone()) {
            Entry::Vacant(entry) => {
                entry.insert(v.clone());
            }
            Entry::Occupied(mut entry) => merge(entry.get_mut(), v),
        }
    }
}

struct Expander<'r> {
    root: &'r Schema,
}

impl<'r> Expander<'r> {
    fn type_ref(&self, s: &str) -> String {
        s.split('/').last().expect("Component").into()
    }

    fn schema(&self, s: &'r Schema) -> &'r Schema {
        if let Some(ref ref_) = s.ref_ {
            self.schema_ref(ref_)
        } else {
            s
        }
    }

    fn schema_ref(&self, s: &str) -> &'r Schema {
        s.split('/').fold(self.root, |schema, comp| {
            if comp == "#" {
                self.root
            } else if comp == "definitions" {
                schema
            } else {
                schema.definitions
                    .get(comp)
                    .unwrap_or_else(|| panic!("Expected definition: `{}` {}", s, comp))
            }
        })
    }

    fn expand_type(&mut self, typ: &Schema) -> String {
        if let Some(ref ref_) = typ.ref_ {
            self.type_ref(ref_)
        } else if typ.type_.len() == 1 {
            match typ.type_[0] {
                Type::String => {
                    if !typ.enum_.is_empty() {
                        "serde_json::Value".into()
                    } else {
                        "String".into()
                    }
                }
                Type::Integer => "i64".into(),
                Type::Boolean => "bool".into(),
                Type::Number => "f64".into(),
                Type::Object => "serde_json::Value".into(),
                Type::Array => {
                    let item_schema =
                        typ.items.as_ref().expect("Array type must have items schema");
                    format!("Vec<{}>", self.expand_type(item_schema))
                }
                _ => panic!("Type"),
            }
        } else {
            "serde_json::Value".into()
        }
    }

    fn expand_fields(&mut self, schema: &Schema) -> Vec<Tokens> {
        if let Some(ref ref_) = schema.ref_ {
            let schema = self.schema_ref(ref_);
            self.expand_fields(schema)
        } else if !schema.allOf.is_empty() {
            let first = schema.allOf.first().unwrap().clone();
            let result = schema.allOf
                .iter()
                .skip(1)
                .fold(first, |mut result, def| {
                    merge(&mut result, self.schema(def));
                    result
                });
            self.expand_fields(&result)
        } else {
            schema.properties
                .iter()
                .map(|(key, value)| {
                    let key = field(key);
                    let typ = Ident(self.expand_type(value));
                    quote!( pub #key : #typ )
                })
                .collect()
        }
    }

    pub fn expand_schema(&mut self, schema: &Schema) -> Tokens {
        let mut types = Vec::new();
        for (name, def) in &schema.definitions {
            let fields = self.expand_fields(def);
            let name = Ident(name);
            let tokens = quote! {
                #[derive(Deserialize, Serialize)]
                pub struct #name {
                    #(#fields),*
                }
            };
            types.push(tokens);
        }
        quote! { #(
            #types
            )*
        }
    }
}

pub fn generate(s: &str) -> Result<String, Box<Error>> {
    use std::process::{Command, Stdio};
    use std::io::Write;

    let schema = serde_json::from_str(s).unwrap();
    let mut expander = Expander { root: &schema };
    let output = expander.expand_schema(&schema).to_string();
    let mut child =
        try!(Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn());
    try!(child.stdin.as_mut().expect("stdin").write_all(output.as_bytes()));
    let output = try!(child.wait_with_output());
    Ok(try!(String::from_utf8(output.stdout)))
}

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

    #[test]
    fn attempt() {
        let s = include_str!("../../json-schema/tests/debugserver-schema.json");

        let s = generate(s).unwrap().to_string();
        println!("{}", s);
        assert!(false);
    }
}
<commit_msg>test: Test that the generated code builds<commit_after>extern crate json_schema;
extern crate serde_json;

#[macro_use]
extern crate quote;

use std::borrow::Cow;
use std::error::Error;

use json_schema::{Schema, Type};

use quote::{Tokens, ToTokens};

struct Ident<S>(S);

impl<S: AsRef<str>> ToTokens for Ident<S> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        tokens.append(self.0.as_ref())
    }
}

fn field(s: &str) -> Ident<Cow<str>> {
    Ident(if ["type", "struct", "enum"].iter().any(|&keyword| keyword == s) {
        format!("{}_", s).into()
    } else {
        s.into()
    })
}

fn merge(result: &mut Schema, r: &Schema) {
    use std::collections::hash_map::Entry;

    for (k, v) in &r.properties {
        match result.properties.entry(k.clone()) {
            Entry::Vacant(entry) => {
                entry.insert(v.clone());
            }
            Entry::Occupied(mut entry) => merge(entry.get_mut(), v),
        }
    }
}

struct Expander<'r> {
    root: &'r Schema,
}

impl<'r> Expander<'r> {
    fn type_ref(&self, s: &str) -> String {
        s.split('/').last().expect("Component").into()
    }

    fn schema(&self, s: &'r Schema) -> &'r Schema {
        if let Some(ref ref_) = s.ref_ {
            self.schema_ref(ref_)
        } else {
            s
        }
    }

    fn schema_ref(&self, s: &str) -> &'r Schema {
        s.split('/').fold(self.root, |schema, comp| {
            if comp == "#" {
                self.root
            } else if comp == "definitions" {
                schema
            } else {
                schema.definitions
                    .get(comp)
                    .unwrap_or_else(|| panic!("Expected definition: `{}` {}", s, comp))
            }
        })
    }

    fn expand_type(&mut self, typ: &Schema) -> String {
        if let Some(ref ref_) = typ.ref_ {
            self.type_ref(ref_)
        } else if typ.type_.len() == 1 {
            match typ.type_[0] {
                Type::String => {
                    if !typ.enum_.is_empty() {
                        "serde_json::Value".into()
                    } else {
                        "String".into()
                    }
                }
                Type::Integer => "i64".into(),
                Type::Boolean => "bool".into(),
                Type::Number => "f64".into(),
                Type::Object => "serde_json::Value".into(),
                Type::Array => {
                    let item_schema =
                        typ.items.as_ref().expect("Array type must have items schema");
                    format!("Vec<{}>", self.expand_type(item_schema))
                }
                _ => panic!("Type"),
            }
        } else {
            "serde_json::Value".into()
        }
    }

    fn expand_fields(&mut self, schema: &Schema) -> Vec<Tokens> {
        if let Some(ref ref_) = schema.ref_ {
            let schema = self.schema_ref(ref_);
            self.expand_fields(schema)
        } else if !schema.allOf.is_empty() {
            let first = schema.allOf.first().unwrap().clone();
            let result = schema.allOf
                .iter()
                .skip(1)
                .fold(first, |mut result, def| {
                    merge(&mut result, self.schema(def));
                    result
                });
            self.expand_fields(&result)
        } else {
            schema.properties
                .iter()
                .map(|(key, value)| {
                    let key = field(key);
                    let typ = Ident(self.expand_type(value));
                    quote!( pub #key : #typ )
                })
                .collect()
        }
    }

    pub fn expand_schema(&mut self, schema: &Schema) -> Tokens {
        let mut types = Vec::new();
        for (name, def) in &schema.definitions {
            let fields = self.expand_fields(def);
            let name = Ident(name);
            let tokens = quote! {
                #[derive(Deserialize, Serialize)]
                pub struct #name {
                    #(#fields),*
                }
            };
            types.push(tokens);
        }
        quote! { #(
            #types
            )*
        }
    }
}

pub fn generate(s: &str) -> Result<String, Box<Error>> {
    use std::process::{Command, Stdio};
    use std::io::Write;

    let schema = serde_json::from_str(s).unwrap();
    let mut expander = Expander { root: &schema };
    let output = expander.expand_schema(&schema).to_string();
    let mut child =
        try!(Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn());
    try!(child.stdin.as_mut().expect("stdin").write_all(output.as_bytes()));
    let output = try!(child.wait_with_output());
    Ok(try!(String::from_utf8(output.stdout)))
}

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

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

    #[test]
    fn builds_with_rustc() {
        let s = include_str!("../../json-schema/tests/debugserver-schema.json");

        let s = generate(s).unwrap().to_string();

        let mut filename = PathBuf::from("target/debug");
        filename.push("test.rs");
        println!("{}", filename.display());
        {
            let mut file = File::create(&filename).unwrap();
            let header = r#"
            #![feature(proc_macro)]

            #[macro_use]
            extern crate serde_derive;
            extern crate serde_json;
            "#;
            file.write_all(header.as_bytes()).unwrap();
            file.write_all(s.as_bytes()).unwrap();
        }

        let child = Command::new("rustc")
            .args(&["-L", "target/debug/deps/", "--crate-type=rlib", filename.to_str().unwrap()])
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();

        let output = child.wait_with_output().unwrap();
        let error = String::from_utf8(output.stderr).unwrap();
        assert!(output.status.success(), "{}", error);
    }
}
<|endoftext|>


<commit_before>use std::fmt::{self, Debug, Formatter};

/// A data-structure for storing a sequence of 4-bit values.
///
/// Values are stored in a `Vec<u8>`, with two values per byte.
///
/// Values at even indices are stored in the most-significant half of their byte,
/// while values at odd indices are stored in the least-significant half.
///
/// Imagine a vector of MSB bytes, and you'll be right.
///
/// n = [_ _ | _ _ | _ _]
///
/// # Invariants
/// * If the length is odd, then the second half of the last byte must be 0.
pub struct NibbleVec {
    length: usize,
    data: Vec<u8>
}

impl NibbleVec {
    /// Create an empty nibble vector.
    pub fn new() -> NibbleVec {
        NibbleVec {
            length: 0,
            data: Vec::new()
        }
    }

    /// Create a nibble vector from a vector of bytes.
    ///
    /// Each byte is split into two 4-bit entries (MSB, LSB).
    pub fn from_byte_vec(vec: Vec<u8>) -> NibbleVec {
        let length = 2 * vec.len();
        NibbleVec {
            length: length,
            data: vec
        }
    }

    /// Get the number of elements stored in the vector.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Fetch a single entry from the vector.
    ///
    /// Guaranteed to be a value in the interval [0, 15].
    ///
    /// **Panics** if `idx >= self.len()`.
    pub fn get(&self, idx: usize) -> u8 {
        if idx >= self.length {
            panic!("attempted access beyond vector end. len is {}, index is {}", self.length, idx);
        }
        let vec_idx = idx / 2;
        match idx % 2 {
            // If the index is even, take the first (most significant) half of the stored byte.
            0 => self.data[vec_idx] >> 4,
            // If the index is odd, take the second (least significant) half.
            _ => self.data[vec_idx] & 0x0F
        }
    }

    /// Add a single nibble to the vector.
    ///
    /// Only the 4 least-significant bits of the value are used.
    pub fn push(&mut self, val: u8) {
        if self.length % 2 == 0 {
            self.data.push(val << 4);
        } else {
            let vec_len = self.data.len();

            // Zero the second half of the last byte just to be safe.
            self.data[vec_len - 1] &= 0xF0;

            // Write the new value.
            self.data[vec_len - 1] |= val & 0x0F;
        }
        self.length += 1;
    }

    /// Split the vector into two parts.
    ///
    /// All elements at or following the given index are returned in a new `NibbleVec`,
    /// with exactly `idx` elements remaining in this vector.
    ///
    /// **Panics** if `idx > self.len()`.
    pub fn split(&mut self, idx: usize) -> NibbleVec {
        if idx > self.length {
            panic!("attempted to split past vector end. len is {}, index is {}", self.length, idx);
        } else if idx == self.length {
            NibbleVec::new()
        } else if idx % 2 == 0 {
            self.split_even(idx)
        } else {
            self.split_odd(idx)
        }
    }

    /// Split function for odd *indices*.
    #[inline(always)]
    fn split_odd(&mut self, idx: usize) -> NibbleVec {
        let tail_vec_size = (self.length - idx) / 2;
        let mut tail = NibbleVec::from_byte_vec(Vec::with_capacity(tail_vec_size));

        // Perform an overlap copy, copying the last nibble of the original vector only if
        // the length of the new tail is *odd*.
        let tail_length = self.length - idx;
        let take_last = tail_length % 2 == 1;
        self.overlap_copy(idx / 2, self.data.len(), &mut tail.data, &mut tail.length, take_last);

        // Remove the copied bytes, being careful to skip the idx byte.
        for _ in range(idx / 2 + 1, self.data.len()) {
            self.data.pop();
        }

        // Zero the second half of the index byte so as to maintain the last-nibble invariant.
        self.data[idx / 2] &= 0xF0;

        // Update the length of the first NibbleVec.
        self.length = idx;

        tail
    }

    /// Split function for even *indices*.
    #[inline(always)]
    fn split_even(&mut self, idx: usize) -> NibbleVec {
        // Avoid allocating a temporary vector by copying all the bytes in order, then popping them.
        let tail_vec_size = (self.length - idx) / 2;
        let mut tail = NibbleVec::from_byte_vec(Vec::with_capacity(tail_vec_size));

        // Copy the bytes.
        for i in range(idx / 2, self.data.len()) {
            tail.data.push(self.data[i]);
        }

        // Pop the same bytes.
        for _ in range(0, tail_vec_size) {
            self.data.pop();
        }

        // Update lengths.
        tail.length = self.length - idx;
        self.length = idx;

        tail
    }

    /// Copy data between the second half of self.data[start] and
    /// self.data[end - 1]. The second half of the last entry is included
    /// if include_last is true.
    #[inline(always)]
    fn overlap_copy(&self, start: usize, end: usize, vec: &mut Vec<u8>, length: &mut usize, include_last: bool) {
        // Copy up to the first half of the last byte.
        for i in range(start, end - 1) {
            // The first half is the second half of the old entry.
            let first_half = self.data[i] & 0x0f;

            // The second half is the first half of the next entry.
            let second_half = self.data[i + 1] >> 4;

            vec.push((first_half << 4) | second_half);
            *length += 2;
        }

        if include_last {
            let last = self.data[end - 1] & 0x0f;
            vec.push(last << 4);
            *length += 1;
        }
    }

    /// Append another nibble vector.
    pub fn join(mut self, other: &NibbleVec) -> NibbleVec {
        // If the length is even, we can append directly.
        if self.length % 2 == 0 {
            self.length += other.length;
            self.data.push_all(other.data.as_slice());
            return self;
        }

        // If the other vector is empty, bail out.
        if other.len() == 0 {
            return self;
        }

        // If the length is odd, we have to perform an overlap copy.
        // Copy the first half of the first element, to make the vector an even length.
        self.push(other.get(0));

        // Copy the rest of the vector using an overlap copy.
        let take_last = other.len() % 2 == 0;
        other.overlap_copy(0, other.data.len(), &mut self.data, &mut self.length, take_last);

        self
    }
}

impl PartialEq<[u8]> for NibbleVec {
    fn eq(&self, other: &[u8]) -> bool {
        if other.len() != self.len() {
            return false;
        }

        for (i, x) in other.iter().enumerate() {
            if self.get(i) != *x {
                return false;
            }
        }
        true
    }
}

impl Clone for NibbleVec {
    fn clone(&self) -> NibbleVec {
        NibbleVec {
            length: self.length,
            data: self.data.clone()
        }
    }
}

impl Debug for NibbleVec {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "NibbleVec ["));

        if self.len() > 0 {
            try!(write!(fmt, "{}", self.get(0)));
        }

        for i in range(1, self.len()) {
            try!(write!(fmt, ", {}", self.get(i)));
        }
        write!(fmt, "]")
    }
}

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

    fn v8_7_6_5() -> NibbleVec {
        NibbleVec::from_byte_vec(vec![8 << 4 | 7, 6 << 4 | 5])
    }

    fn v11_10_9() -> NibbleVec {
        let mut result = NibbleVec::from_byte_vec(vec![11 << 4 | 10]);
        result.push(9);
        result
    }

    #[test]
    fn get() {
        let nv = NibbleVec::from_byte_vec(vec![3 << 4 | 7]);
        assert_eq!(nv.get(0), 3u8);
        assert_eq!(nv.get(1), 7u8);
    }

    #[test]
    fn push() {
        let mut nv = NibbleVec::new();
        let data = vec![0, 1, 3, 5, 7, 9, 11, 15];
        for val in data.iter() {
            nv.push(*val);
        }

        for (i, val) in data.iter().enumerate() {
            assert_eq!(nv.get(i), *val);
        }
    }

    fn split_test(  nibble_vec: &NibbleVec,
                    idx: usize,
                    first: Vec<u8>,
                    second: Vec<u8>) {
        let mut init = nibble_vec.clone();
        let tail = init.split(idx);
        assert!(init == first[..]);
        assert!(tail == second[..]);
    }

    #[test]
    fn split_even_length() {
        let even_length = v8_7_6_5();
        split_test(&even_length, 0, vec![], vec![8, 7, 6, 5]);
        split_test(&even_length, 1, vec![8], vec![7, 6, 5]);
        split_test(&even_length, 2, vec![8, 7], vec![6, 5]);
        split_test(&even_length, 4, vec![8, 7, 6, 5], vec![]);
    }

    #[test]
    fn split_odd_length() {
        let odd_length = v11_10_9();
        split_test(&odd_length, 0, vec![], vec![11, 10, 9]);
        split_test(&odd_length, 1, vec![11], vec![10, 9]);
        split_test(&odd_length, 2, vec![11, 10], vec![9]);
        split_test(&odd_length, 3, vec![11, 10, 9], vec![]);
    }

    /// Join vec2 onto vec1 and ensure that the results matches the one expected.
    fn join_test(vec1: &NibbleVec, vec2: &NibbleVec, result: Vec<u8>) {
        let joined = vec1.clone().join(vec2);
        assert!(joined == result[..]);
    }

    #[test]
    fn join_even_length() {
        let v1 = v8_7_6_5();
        let v2 = v11_10_9();
        join_test(&v1, &v2, vec![8, 7, 6, 5, 11, 10, 9]);
        join_test(&v1, &v1, vec![8, 7, 6, 5, 8, 7, 6, 5]);
        join_test(&v1, &NibbleVec::new(), vec![8, 7, 6, 5]);
        join_test(&NibbleVec::new(), &v1, vec![8, 7, 6, 5]);
    }

    #[test]
    fn join_odd_length() {
        let v1 = v8_7_6_5();
        let v2 = v11_10_9();
        join_test(&v2, &v1, vec![11, 10, 9, 8, 7, 6, 5]);
        join_test(&v2, &v2, vec![11, 10, 9, 11, 10, 9]);
        join_test(&v2, &NibbleVec::new(), vec![11, 10, 9]);
    }

    /// Ensure that the last nibble is zeroed before reuse.
    #[test]
    fn memory_reuse() {
        let mut vec = NibbleVec::new();
        vec.push(10);
        vec.push(1);

        // Pushing.
        vec.split(1);
        vec.push(2);
        assert_eq!(vec.get(1), 2);

        // Joining.
        vec.split(1);
        vec = vec.join(&NibbleVec::from_byte_vec(vec![1 << 4 | 3, 5 << 4]));
        assert_eq!(vec.get(1), 1);
    }
}
<commit_msg>Implement Eq and NibbleVec self comparisons.<commit_after>use std::fmt::{self, Debug, Formatter};

/// A data-structure for storing a sequence of 4-bit values.
///
/// Values are stored in a `Vec<u8>`, with two values per byte.
///
/// Values at even indices are stored in the most-significant half of their byte,
/// while values at odd indices are stored in the least-significant half.
///
/// Imagine a vector of MSB bytes, and you'll be right.
///
/// n = [_ _ | _ _ | _ _]
///
/// # Invariants
/// * If the length is odd, then the second half of the last byte must be 0.
pub struct NibbleVec {
    length: usize,
    data: Vec<u8>
}

impl NibbleVec {
    /// Create an empty nibble vector.
    pub fn new() -> NibbleVec {
        NibbleVec {
            length: 0,
            data: Vec::new()
        }
    }

    /// Create a nibble vector from a vector of bytes.
    ///
    /// Each byte is split into two 4-bit entries (MSB, LSB).
    pub fn from_byte_vec(vec: Vec<u8>) -> NibbleVec {
        let length = 2 * vec.len();
        NibbleVec {
            length: length,
            data: vec
        }
    }

    /// Get the number of elements stored in the vector.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Fetch a single entry from the vector.
    ///
    /// Guaranteed to be a value in the interval [0, 15].
    ///
    /// **Panics** if `idx >= self.len()`.
    pub fn get(&self, idx: usize) -> u8 {
        if idx >= self.length {
            panic!("attempted access beyond vector end. len is {}, index is {}", self.length, idx);
        }
        let vec_idx = idx / 2;
        match idx % 2 {
            // If the index is even, take the first (most significant) half of the stored byte.
            0 => self.data[vec_idx] >> 4,
            // If the index is odd, take the second (least significant) half.
            _ => self.data[vec_idx] & 0x0F
        }
    }

    /// Add a single nibble to the vector.
    ///
    /// Only the 4 least-significant bits of the value are used.
    pub fn push(&mut self, val: u8) {
        if self.length % 2 == 0 {
            self.data.push(val << 4);
        } else {
            let vec_len = self.data.len();

            // Zero the second half of the last byte just to be safe.
            self.data[vec_len - 1] &= 0xF0;

            // Write the new value.
            self.data[vec_len - 1] |= val & 0x0F;
        }
        self.length += 1;
    }

    /// Split the vector into two parts.
    ///
    /// All elements at or following the given index are returned in a new `NibbleVec`,
    /// with exactly `idx` elements remaining in this vector.
    ///
    /// **Panics** if `idx > self.len()`.
    pub fn split(&mut self, idx: usize) -> NibbleVec {
        if idx > self.length {
            panic!("attempted to split past vector end. len is {}, index is {}", self.length, idx);
        } else if idx == self.length {
            NibbleVec::new()
        } else if idx % 2 == 0 {
            self.split_even(idx)
        } else {
            self.split_odd(idx)
        }
    }

    /// Split function for odd *indices*.
    #[inline(always)]
    fn split_odd(&mut self, idx: usize) -> NibbleVec {
        let tail_vec_size = (self.length - idx) / 2;
        let mut tail = NibbleVec::from_byte_vec(Vec::with_capacity(tail_vec_size));

        // Perform an overlap copy, copying the last nibble of the original vector only if
        // the length of the new tail is *odd*.
        let tail_length = self.length - idx;
        let take_last = tail_length % 2 == 1;
        self.overlap_copy(idx / 2, self.data.len(), &mut tail.data, &mut tail.length, take_last);

        // Remove the copied bytes, being careful to skip the idx byte.
        for _ in range(idx / 2 + 1, self.data.len()) {
            self.data.pop();
        }

        // Zero the second half of the index byte so as to maintain the last-nibble invariant.
        self.data[idx / 2] &= 0xF0;

        // Update the length of the first NibbleVec.
        self.length = idx;

        tail
    }

    /// Split function for even *indices*.
    #[inline(always)]
    fn split_even(&mut self, idx: usize) -> NibbleVec {
        // Avoid allocating a temporary vector by copying all the bytes in order, then popping them.
        let tail_vec_size = (self.length - idx) / 2;
        let mut tail = NibbleVec::from_byte_vec(Vec::with_capacity(tail_vec_size));

        // Copy the bytes.
        for i in range(idx / 2, self.data.len()) {
            tail.data.push(self.data[i]);
        }

        // Pop the same bytes.
        for _ in range(0, tail_vec_size) {
            self.data.pop();
        }

        // Update lengths.
        tail.length = self.length - idx;
        self.length = idx;

        tail
    }

    /// Copy data between the second half of self.data[start] and
    /// self.data[end - 1]. The second half of the last entry is included
    /// if include_last is true.
    #[inline(always)]
    fn overlap_copy(&self, start: usize, end: usize, vec: &mut Vec<u8>, length: &mut usize, include_last: bool) {
        // Copy up to the first half of the last byte.
        for i in range(start, end - 1) {
            // The first half is the second half of the old entry.
            let first_half = self.data[i] & 0x0f;

            // The second half is the first half of the next entry.
            let second_half = self.data[i + 1] >> 4;

            vec.push((first_half << 4) | second_half);
            *length += 2;
        }

        if include_last {
            let last = self.data[end - 1] & 0x0f;
            vec.push(last << 4);
            *length += 1;
        }
    }

    /// Append another nibble vector.
    pub fn join(mut self, other: &NibbleVec) -> NibbleVec {
        // If the length is even, we can append directly.
        if self.length % 2 == 0 {
            self.length += other.length;
            self.data.push_all(other.data.as_slice());
            return self;
        }

        // If the other vector is empty, bail out.
        if other.len() == 0 {
            return self;
        }

        // If the length is odd, we have to perform an overlap copy.
        // Copy the first half of the first element, to make the vector an even length.
        self.push(other.get(0));

        // Copy the rest of the vector using an overlap copy.
        let take_last = other.len() % 2 == 0;
        other.overlap_copy(0, other.data.len(), &mut self.data, &mut self.length, take_last);

        self
    }
}

impl PartialEq<NibbleVec> for NibbleVec {
    fn eq(&self, other: &NibbleVec) -> bool {
        self.length == other.length &&
        self.data == other.data
    }
}

impl Eq for NibbleVec {}

impl PartialEq<[u8]> for NibbleVec {
    fn eq(&self, other: &[u8]) -> bool {
        if other.len() != self.len() {
            return false;
        }

        for (i, x) in other.iter().enumerate() {
            if self.get(i) != *x {
                return false;
            }
        }
        true
    }
}

impl Clone for NibbleVec {
    fn clone(&self) -> NibbleVec {
        NibbleVec {
            length: self.length,
            data: self.data.clone()
        }
    }
}

impl Debug for NibbleVec {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "NibbleVec ["));

        if self.len() > 0 {
            try!(write!(fmt, "{}", self.get(0)));
        }

        for i in range(1, self.len()) {
            try!(write!(fmt, ", {}", self.get(i)));
        }
        write!(fmt, "]")
    }
}

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

    fn v8_7_6_5() -> NibbleVec {
        NibbleVec::from_byte_vec(vec![8 << 4 | 7, 6 << 4 | 5])
    }

    fn v11_10_9() -> NibbleVec {
        let mut result = NibbleVec::from_byte_vec(vec![11 << 4 | 10]);
        result.push(9);
        result
    }

    #[test]
    fn get() {
        let nv = NibbleVec::from_byte_vec(vec![3 << 4 | 7]);
        assert_eq!(nv.get(0), 3u8);
        assert_eq!(nv.get(1), 7u8);
    }

    #[test]
    fn push() {
        let mut nv = NibbleVec::new();
        let data = vec![0, 1, 3, 5, 7, 9, 11, 15];
        for val in data.iter() {
            nv.push(*val);
        }

        for (i, val) in data.iter().enumerate() {
            assert_eq!(nv.get(i), *val);
        }
    }

    fn split_test(  nibble_vec: &NibbleVec,
                    idx: usize,
                    first: Vec<u8>,
                    second: Vec<u8>) {
        let mut init = nibble_vec.clone();
        let tail = init.split(idx);
        assert!(init == first[..]);
        assert!(tail == second[..]);
    }

    #[test]
    fn split_even_length() {
        let even_length = v8_7_6_5();
        split_test(&even_length, 0, vec![], vec![8, 7, 6, 5]);
        split_test(&even_length, 1, vec![8], vec![7, 6, 5]);
        split_test(&even_length, 2, vec![8, 7], vec![6, 5]);
        split_test(&even_length, 4, vec![8, 7, 6, 5], vec![]);
    }

    #[test]
    fn split_odd_length() {
        let odd_length = v11_10_9();
        split_test(&odd_length, 0, vec![], vec![11, 10, 9]);
        split_test(&odd_length, 1, vec![11], vec![10, 9]);
        split_test(&odd_length, 2, vec![11, 10], vec![9]);
        split_test(&odd_length, 3, vec![11, 10, 9], vec![]);
    }

    /// Join vec2 onto vec1 and ensure that the results matches the one expected.
    fn join_test(vec1: &NibbleVec, vec2: &NibbleVec, result: Vec<u8>) {
        let joined = vec1.clone().join(vec2);
        assert!(joined == result[..]);
    }

    #[test]
    fn join_even_length() {
        let v1 = v8_7_6_5();
        let v2 = v11_10_9();
        join_test(&v1, &v2, vec![8, 7, 6, 5, 11, 10, 9]);
        join_test(&v1, &v1, vec![8, 7, 6, 5, 8, 7, 6, 5]);
        join_test(&v1, &NibbleVec::new(), vec![8, 7, 6, 5]);
        join_test(&NibbleVec::new(), &v1, vec![8, 7, 6, 5]);
    }

    #[test]
    fn join_odd_length() {
        let v1 = v8_7_6_5();
        let v2 = v11_10_9();
        join_test(&v2, &v1, vec![11, 10, 9, 8, 7, 6, 5]);
        join_test(&v2, &v2, vec![11, 10, 9, 11, 10, 9]);
        join_test(&v2, &NibbleVec::new(), vec![11, 10, 9]);
    }

    /// Ensure that the last nibble is zeroed before reuse.
    #[test]
    fn memory_reuse() {
        let mut vec = NibbleVec::new();
        vec.push(10);
        vec.push(1);

        // Pushing.
        vec.split(1);
        vec.push(2);
        assert_eq!(vec.get(1), 2);

        // Joining.
        vec.split(1);
        vec = vec.join(&NibbleVec::from_byte_vec(vec![1 << 4 | 3, 5 << 4]));
        assert_eq!(vec.get(1), 1);
    }
}
<|endoftext|>


<commit_before>//! This is a library for controlling a cursor and a screen,
//! on ANSI terminals.
//!
//!
//! ## Example
//!
//!     use ansi_control::*;
//!

use std::cmp::{self, Ordering};

/// A Pos is position of clearing (display|line) from cursor.
pub enum Pos {
    Back,
    Front,
    Both,
}

/// Moves the cursor _i_ (row), _j_ (column) cells. If the cursor
/// is already at the edge of the screen, this has no effect.
pub fn move_cursor(i: i32, j: i32) -> String {
    let code0 = match i.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}B", -i),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}A", i),
    };
    let code1 = match j.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}D", -j),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}C", j),
    };
    format!("{}{}", code0, code1)
}

/// Moves the cursor to beginning of the line _n_ lines down.
/// If _n_ is a negative number, this function moves the cursor
/// _|n|_ lines up.
pub fn move_line(n: i32) -> String {
    match n.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}F", -n),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}E", n),
    }
}

/// Sets the column of the cursor. (n: column)
pub fn set_column(n: u32) -> String {
    format!("\x1B[{}G", cmp::max(1, n))
}

/// Sets the position of the cursor. (i: row, j: column)
pub fn set_position(i: u32, j: u32) -> String {
    format!("\x1B[{};{}H", cmp::max(1, i), cmp::max(1, j))
}

/// Clears part of screen. If pos is Pos::Back, clear from cursor
/// to the end of the screen. If pos is Pos::Front, clear from
/// cursor to beginning of the screen. If pos is Pos::Both, clear
/// entire screen.
pub fn clear_display(pos: Pos) -> String {
    format!("")
}

/// Clears part of line. If pos is Pos::Back, clear from cursor
/// to the end of the line. If pos is Pos::Front, clear from
/// cursor to beginning of the line. If pos is Pos::Both, clear
/// entire line.
pub fn clear_line(pos: Pos) -> String {
    format!("")
}

/// Scroll whole page up by _n_ lines. If _n_ is a negative
/// number, this function scroll whole page down by _|n|_ lines.
pub fn scroll(n: i32) -> String {
    format!("")
}

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

    macro_rules! test {
        ($name: ident; $ctrl: expr => $result: expr) => {
            #[test]
            fn $name() {
                assert_eq!($result, $ctrl)
            }
        };
    }

    test!(move_cursor0; move_cursor(2, -4) => "\x1B[2A\x1B[4D");
    test!(move_cursor1; move_cursor(-2, 0) => "\x1B[2B");
    test!(move_cursor2; move_cursor(0, 6) => "\x1B[6C");
    test!(move_cursor3; move_cursor(0, 0) => "");

    test!(move_line0; move_line(4) => "\x1B[4E");
    test!(move_line1; move_line(-4) => "\x1B[4F");

    test!(set_column0; set_column(3) => "\x1B[3G");
    test!(set_column1; set_column(0) => "\x1B[1G");

    test!(set_position0; set_position(3, 5) => "\x1B[3;5H");
    test!(set_position1; set_position(0, 0) => "\x1B[1;1H");

    test!(clear_display0; clear_display(Pos::Back) => "\x1B[0J");
    test!(clear_display1; clear_display(Pos::Front) => "\x1B[1J");
    test!(clear_display2; clear_display(Pos::Both) => "\x1B[2J");

    test!(clear_line0; clear_line(Pos::Back) => "\x1B[0K");
    test!(clear_line1; clear_line(Pos::Front) => "\x1B[1K");
    test!(clear_line2; clear_line(Pos::Both) => "\x1B[2K");

    test!(scroll0; scroll(7) => "\x1B[7S");
    test!(scroll1; scroll(-5) => "\x1B[5T");
}
<commit_msg>Improve clear_display<commit_after>//! This is a library for controlling a cursor and a screen,
//! on ANSI terminals.
//!
//!
//! ## Example
//!
//!     use ansi_control::*;
//!

use std::cmp::{self, Ordering};

/// A Pos is position of clearing (display|line) from cursor.
pub enum Pos {
    Back,
    Front,
    Both,
}

/// Moves the cursor _i_ (row), _j_ (column) cells. If the cursor
/// is already at the edge of the screen, this has no effect.
pub fn move_cursor(i: i32, j: i32) -> String {
    let code0 = match i.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}B", -i),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}A", i),
    };
    let code1 = match j.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}D", -j),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}C", j),
    };
    format!("{}{}", code0, code1)
}

/// Moves the cursor to beginning of the line _n_ lines down.
/// If _n_ is a negative number, this function moves the cursor
/// _|n|_ lines up.
pub fn move_line(n: i32) -> String {
    match n.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}F", -n),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}E", n),
    }
}

/// Sets the column of the cursor. (n: column)
pub fn set_column(n: u32) -> String {
    format!("\x1B[{}G", cmp::max(1, n))
}

/// Sets the position of the cursor. (i: row, j: column)
pub fn set_position(i: u32, j: u32) -> String {
    format!("\x1B[{};{}H", cmp::max(1, i), cmp::max(1, j))
}

/// Clears part of screen. If pos is Pos::Back, clear from cursor
/// to the end of the screen. If pos is Pos::Front, clear from
/// cursor to beginning of the screen. If pos is Pos::Both, clear
/// entire screen.
pub fn clear_display(pos: Pos) -> String {
    let num = match pos {
        Pos::Back   => 0,
        Pos::Front  => 1,
        Pos::Both   => 2,
    };
    format!("\x1B[{}J", num)
}

/// Clears part of line. If pos is Pos::Back, clear from cursor
/// to the end of the line. If pos is Pos::Front, clear from
/// cursor to beginning of the line. If pos is Pos::Both, clear
/// entire line.
pub fn clear_line(pos: Pos) -> String {
    format!("")
}

/// Scroll whole page up by _n_ lines. If _n_ is a negative
/// number, this function scroll whole page down by _|n|_ lines.
pub fn scroll(n: i32) -> String {
    format!("")
}

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

    macro_rules! test {
        ($name: ident; $ctrl: expr => $result: expr) => {
            #[test]
            fn $name() {
                assert_eq!($result, $ctrl)
            }
        };
    }

    test!(move_cursor0; move_cursor(2, -4) => "\x1B[2A\x1B[4D");
    test!(move_cursor1; move_cursor(-2, 0) => "\x1B[2B");
    test!(move_cursor2; move_cursor(0, 6) => "\x1B[6C");
    test!(move_cursor3; move_cursor(0, 0) => "");

    test!(move_line0; move_line(4) => "\x1B[4E");
    test!(move_line1; move_line(-4) => "\x1B[4F");

    test!(set_column0; set_column(3) => "\x1B[3G");
    test!(set_column1; set_column(0) => "\x1B[1G");

    test!(set_position0; set_position(3, 5) => "\x1B[3;5H");
    test!(set_position1; set_position(0, 0) => "\x1B[1;1H");

    test!(clear_display0; clear_display(Pos::Back) => "\x1B[0J");
    test!(clear_display1; clear_display(Pos::Front) => "\x1B[1J");
    test!(clear_display2; clear_display(Pos::Both) => "\x1B[2J");

    test!(clear_line0; clear_line(Pos::Back) => "\x1B[0K");
    test!(clear_line1; clear_line(Pos::Front) => "\x1B[1K");
    test!(clear_line2; clear_line(Pos::Both) => "\x1B[2K");

    test!(scroll0; scroll(7) => "\x1B[7S");
    test!(scroll1; scroll(-5) => "\x1B[5T");
}
<|endoftext|>


<commit_before>//! Crate ruma_api contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a type that implements `Endpoint`, plus
//! the necessary associated types.
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![feature(try_from)]

use std::{convert::TryInto, io};

use futures::future::FutureFrom;
use http::{self, Method, Request, Response, StatusCode};
use hyper::{self, Body};
use ruma_identifiers;
use serde_json;
use serde_urlencoded;

/// A Matrix API endpoint.
pub trait Endpoint<T = Body, U = Body> {
    /// Data needed to make a request to the endpoint.
    type Request: TryInto<Request<T>, Error = Error> + FutureFrom<Request<T>, Error = Error>;
    /// Data returned from the endpoint.
    type Response: FutureFrom<Response<U>, Error = Error> + TryInto<Response<U>>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// An error when converting an `Endpoint::Request` to a `http::Request` or a `http::Response` to
/// an `Endpoint::Response`.
#[derive(Debug)]
pub enum Error {
    /// An HTTP error.
    Http(http::Error),
    /// An Hyper error.
    Hyper(hyper::Error),
    /// A I/O error.
    Io(io::Error),
    /// A Serde JSON error.
    SerdeJson(serde_json::Error),
    /// A Serde URL decoding error.
    SerdeUrlEncodedDe(serde_urlencoded::de::Error),
    /// A Serde URL encoding error.
    SerdeUrlEncodedSer(serde_urlencoded::ser::Error),
    /// A Ruma Identitifiers error.
    RumaIdentifiers(ruma_identifiers::Error),
    /// An HTTP status code indicating error.
    StatusCode(StatusCode),
    /// Standard hack to prevent exhaustive matching.
    /// This will be replaced by the #[non_exhaustive] feature when available.
    #[doc(hidden)]
    __Nonexhaustive,
}

impl From<http::Error> for Error {
    fn from(error: http::Error) -> Self {
        Error::Http(error)
    }
}

impl From<hyper::Error> for Error {
    fn from(error: hyper::Error) -> Self {
        Error::Hyper(error)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Error::SerdeJson(error)
    }
}

impl From<serde_urlencoded::de::Error> for Error {
    fn from(error: serde_urlencoded::de::Error) -> Self {
        Error::SerdeUrlEncodedDe(error)
    }
}

impl From<serde_urlencoded::ser::Error> for Error {
    fn from(error: serde_urlencoded::ser::Error) -> Self {
        Error::SerdeUrlEncodedSer(error)
    }
}

impl From<ruma_identifiers::Error> for Error {
    fn from(error: ruma_identifiers::Error) -> Self {
        Error::RumaIdentifiers(error)
    }
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,
    /// The HTTP method used by this endpoint.
    pub method: Method,
    /// A unique identifier for this endpoint.
    pub name: &'static str,
    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,
    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,
    /// Whether or not the server requires an authenticated user for this endpoint.
    pub requires_authentication: bool,
}

#[cfg(test)]
mod tests {
    /// PUT /_matrix/client/r0/directory/room/:room_alias
    pub mod create {
        use std::convert::TryFrom;

        use futures::future::{err, ok, FutureFrom, FutureResult};
        use http::{
            header::CONTENT_TYPE, method::Method, Request as HttpRequest, Response as HttpResponse,
        };
        use ruma_identifiers::{RoomAliasId, RoomId};
        use serde::{de::IntoDeserializer, Deserialize, Serialize};
        use serde_json;
        use url::percent_encoding;

        use super::super::{Endpoint as ApiEndpoint, Error, Metadata};

        #[derive(Debug)]
        pub struct Endpoint;

        impl ApiEndpoint<Vec<u8>, Vec<u8>> for Endpoint {
            type Request = Request;
            type Response = Response;

            const METADATA: Metadata = Metadata {
                description: "Add an alias to a room.",
                method: Method::PUT,
                name: "create_alias",
                path: "/_matrix/client/r0/directory/room/:room_alias",
                rate_limited: false,
                requires_authentication: true,
            };
        }

        /// A request to create a new room alias.
        #[derive(Debug)]
        pub struct Request {
            pub room_id: RoomId,         // body
            pub room_alias: RoomAliasId, // path
        }

        #[derive(Debug, Serialize, Deserialize)]
        struct RequestBody {
            room_id: RoomId,
        }

        impl TryFrom<Request> for HttpRequest<Vec<u8>> {
            type Error = Error;

            fn try_from(request: Request) -> Result<HttpRequest<Vec<u8>>, Self::Error> {
                let metadata = Endpoint::METADATA;

                let path = metadata
                    .path
                    .to_string()
                    .replace(":room_alias", &request.room_alias.to_string());

                let request_body = RequestBody {
                    room_id: request.room_id,
                };

                let http_request = HttpRequest::builder()
                    .method(metadata.method)
                    .uri(path)
                    .body(serde_json::to_vec(&request_body).map_err(Error::from)?)?;

                Ok(http_request)
            }
        }

        impl FutureFrom<HttpRequest<Vec<u8>>> for Request {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(request: HttpRequest<Vec<u8>>) -> Self::Future {
                FutureResult::from(Self::try_from(request))
            }
        }

        impl TryFrom<HttpRequest<Vec<u8>>> for Request {
            type Error = Error;

            fn try_from(request: HttpRequest<Vec<u8>>) -> Result<Request, Self::Error> {
                let request_body: RequestBody =
                    ::serde_json::from_slice(request.body().as_slice())?;
                let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();
                Ok(Request {
                    room_id: request_body.room_id,
                    room_alias: {
                        let segment = path_segments.get(5).unwrap().as_bytes();
                        let decoded = percent_encoding::percent_decode(segment).decode_utf8_lossy();
                        RoomAliasId::deserialize(decoded.into_deserializer())
                            .map_err(|e: serde_json::error::Error| e)?
                    },
                })
            }
        }

        /// The response to a request to create a new room alias.
        #[derive(Debug)]
        pub struct Response;

        impl FutureFrom<HttpResponse<Vec<u8>>> for Response {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(
                http_response: HttpResponse<Vec<u8>>,
            ) -> FutureResult<Self, Self::Error> {
                if http_response.status().is_success() {
                    ok(Response)
                } else {
                    err(Error::StatusCode(http_response.status().clone()))
                }
            }
        }

        impl TryFrom<Response> for HttpResponse<Vec<u8>> {
            type Error = Error;

            fn try_from(_response: Response) -> Result<HttpResponse<Vec<u8>>, Self::Error> {
                let response = HttpResponse::builder()
                    .header(CONTENT_TYPE, "application/json")
                    .body("{}".as_bytes().to_vec())
                    .unwrap();
                Ok(response)
            }
        }
    }
}
<commit_msg>Remove try_from feature.<commit_after>//! Crate ruma_api contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a type that implements `Endpoint`, plus
//! the necessary associated types.
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]

use std::{convert::TryInto, io};

use futures::future::FutureFrom;
use http::{self, Method, Request, Response, StatusCode};
use hyper::{self, Body};
use ruma_identifiers;
use serde_json;
use serde_urlencoded;

/// A Matrix API endpoint.
pub trait Endpoint<T = Body, U = Body> {
    /// Data needed to make a request to the endpoint.
    type Request: TryInto<Request<T>, Error = Error> + FutureFrom<Request<T>, Error = Error>;
    /// Data returned from the endpoint.
    type Response: FutureFrom<Response<U>, Error = Error> + TryInto<Response<U>>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// An error when converting an `Endpoint::Request` to a `http::Request` or a `http::Response` to
/// an `Endpoint::Response`.
#[derive(Debug)]
pub enum Error {
    /// An HTTP error.
    Http(http::Error),
    /// An Hyper error.
    Hyper(hyper::Error),
    /// A I/O error.
    Io(io::Error),
    /// A Serde JSON error.
    SerdeJson(serde_json::Error),
    /// A Serde URL decoding error.
    SerdeUrlEncodedDe(serde_urlencoded::de::Error),
    /// A Serde URL encoding error.
    SerdeUrlEncodedSer(serde_urlencoded::ser::Error),
    /// A Ruma Identitifiers error.
    RumaIdentifiers(ruma_identifiers::Error),
    /// An HTTP status code indicating error.
    StatusCode(StatusCode),
    /// Standard hack to prevent exhaustive matching.
    /// This will be replaced by the #[non_exhaustive] feature when available.
    #[doc(hidden)]
    __Nonexhaustive,
}

impl From<http::Error> for Error {
    fn from(error: http::Error) -> Self {
        Error::Http(error)
    }
}

impl From<hyper::Error> for Error {
    fn from(error: hyper::Error) -> Self {
        Error::Hyper(error)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Error::SerdeJson(error)
    }
}

impl From<serde_urlencoded::de::Error> for Error {
    fn from(error: serde_urlencoded::de::Error) -> Self {
        Error::SerdeUrlEncodedDe(error)
    }
}

impl From<serde_urlencoded::ser::Error> for Error {
    fn from(error: serde_urlencoded::ser::Error) -> Self {
        Error::SerdeUrlEncodedSer(error)
    }
}

impl From<ruma_identifiers::Error> for Error {
    fn from(error: ruma_identifiers::Error) -> Self {
        Error::RumaIdentifiers(error)
    }
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,
    /// The HTTP method used by this endpoint.
    pub method: Method,
    /// A unique identifier for this endpoint.
    pub name: &'static str,
    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,
    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,
    /// Whether or not the server requires an authenticated user for this endpoint.
    pub requires_authentication: bool,
}

#[cfg(test)]
mod tests {
    /// PUT /_matrix/client/r0/directory/room/:room_alias
    pub mod create {
        use std::convert::TryFrom;

        use futures::future::{err, ok, FutureFrom, FutureResult};
        use http::{
            header::CONTENT_TYPE, method::Method, Request as HttpRequest, Response as HttpResponse,
        };
        use ruma_identifiers::{RoomAliasId, RoomId};
        use serde::{de::IntoDeserializer, Deserialize, Serialize};
        use serde_json;
        use url::percent_encoding;

        use super::super::{Endpoint as ApiEndpoint, Error, Metadata};

        #[derive(Debug)]
        pub struct Endpoint;

        impl ApiEndpoint<Vec<u8>, Vec<u8>> for Endpoint {
            type Request = Request;
            type Response = Response;

            const METADATA: Metadata = Metadata {
                description: "Add an alias to a room.",
                method: Method::PUT,
                name: "create_alias",
                path: "/_matrix/client/r0/directory/room/:room_alias",
                rate_limited: false,
                requires_authentication: true,
            };
        }

        /// A request to create a new room alias.
        #[derive(Debug)]
        pub struct Request {
            pub room_id: RoomId,         // body
            pub room_alias: RoomAliasId, // path
        }

        #[derive(Debug, Serialize, Deserialize)]
        struct RequestBody {
            room_id: RoomId,
        }

        impl TryFrom<Request> for HttpRequest<Vec<u8>> {
            type Error = Error;

            fn try_from(request: Request) -> Result<HttpRequest<Vec<u8>>, Self::Error> {
                let metadata = Endpoint::METADATA;

                let path = metadata
                    .path
                    .to_string()
                    .replace(":room_alias", &request.room_alias.to_string());

                let request_body = RequestBody {
                    room_id: request.room_id,
                };

                let http_request = HttpRequest::builder()
                    .method(metadata.method)
                    .uri(path)
                    .body(serde_json::to_vec(&request_body).map_err(Error::from)?)?;

                Ok(http_request)
            }
        }

        impl FutureFrom<HttpRequest<Vec<u8>>> for Request {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(request: HttpRequest<Vec<u8>>) -> Self::Future {
                FutureResult::from(Self::try_from(request))
            }
        }

        impl TryFrom<HttpRequest<Vec<u8>>> for Request {
            type Error = Error;

            fn try_from(request: HttpRequest<Vec<u8>>) -> Result<Request, Self::Error> {
                let request_body: RequestBody =
                    ::serde_json::from_slice(request.body().as_slice())?;
                let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();
                Ok(Request {
                    room_id: request_body.room_id,
                    room_alias: {
                        let segment = path_segments.get(5).unwrap().as_bytes();
                        let decoded = percent_encoding::percent_decode(segment).decode_utf8_lossy();
                        RoomAliasId::deserialize(decoded.into_deserializer())
                            .map_err(|e: serde_json::error::Error| e)?
                    },
                })
            }
        }

        /// The response to a request to create a new room alias.
        #[derive(Debug)]
        pub struct Response;

        impl FutureFrom<HttpResponse<Vec<u8>>> for Response {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(
                http_response: HttpResponse<Vec<u8>>,
            ) -> FutureResult<Self, Self::Error> {
                if http_response.status().is_success() {
                    ok(Response)
                } else {
                    err(Error::StatusCode(http_response.status().clone()))
                }
            }
        }

        impl TryFrom<Response> for HttpResponse<Vec<u8>> {
            type Error = Error;

            fn try_from(_response: Response) -> Result<HttpResponse<Vec<u8>>, Self::Error> {
                let response = HttpResponse::builder()
                    .header(CONTENT_TYPE, "application/json")
                    .body("{}".as_bytes().to_vec())
                    .unwrap();
                Ok(response)
            }
        }
    }
}
<|endoftext|>