<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, File, OpenOptions};
use std::io::Result;
use std::path::Path;

#[derive(Eq, PartialEq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp,
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_IO_BLOCKS: &str = "io-blocks";
static OPT_NO_CREATE: &str = "no-create";
static OPT_REFERENCE: &str = "reference";
static OPT_SIZE: &str = "size";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    /*
    let args = args.collect_str();

    let mut opts = getopts::Options::new();*/
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(OPT_IO_BLOCKS)
            .short("o")
            .long(OPT_IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_CREATE)
            .short("c")
            .long(OPT_NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
            .short("r")
            .long(OPT_REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(OPT_SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(OPT_IO_BLOCKS);
        let no_create = matches.is_present(OPT_NO_CREATE);
        let reference = matches.value_of(OPT_REFERENCE).map(String::from);
        let size = matches.value_of(OPT_SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            match truncate(no_create, io_blocks, reference, size, files) {
                Ok(()) => ( /* pass */ ),
                Err(_) => return 1,
            }
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> Result<()> {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            let _ = match File::open(Path::new(&rfilename)) {
                Ok(m) => m,
                Err(f) => crash!(1, "{}", f.to_string()),
            };
            match metadata(rfilename) {
                Ok(meta) => (meta.len(), TruncateMode::Reference),
                Err(f) => crash!(1, "{}", f.to_string()),
            }
        }
        None => parse_size(size.unwrap().as_ref()),
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new()
            .read(true)
            .write(true)
            .create(!no_create)
            .open(path)
        {
            Ok(file) => {
                let fsize = match metadata(filename) {
                    Ok(meta) => meta.len(),
                    Err(f) => {
                        show_warning!("{}", f.to_string());
                        continue;
                    }
                };
                let tsize: u64 = match mode {
                    TruncateMode::Reference => refsize,
                    TruncateMode::Extend => fsize + refsize,
                    TruncateMode::Reduce => fsize - refsize,
                    TruncateMode::AtMost => {
                        if fsize > refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::AtLeast => {
                        if fsize < refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::RoundDown => fsize - fsize % refsize,
                    TruncateMode::RoundUp => fsize + fsize % refsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
    Ok(())
}

fn parse_size(size: &str) -> (u64, TruncateMode) {
    let mode = match size.chars().next().unwrap() {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '*' => TruncateMode::RoundUp,
        _ => TruncateMode::Reference, /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice = if mode == TruncateMode::Reference {
            size
        } else {
            &size[1..]
        };
        if slice.chars().last().unwrap().is_alphabetic() {
            slice = &slice[..slice.len() - 1];
            if !slice.is_empty() && slice.chars().last().unwrap().is_alphabetic() {
                slice = &slice[..slice.len() - 1];
            }
        }
        slice
    }
    .to_owned();
    let mut number: u64 = match bytes.parse() {
        Ok(num) => num,
        Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e),
    };
    if size.chars().last().unwrap().is_alphabetic() {
        number *= match size.chars().last().unwrap().to_ascii_uppercase() {
            'B' => match size
                .chars()
                .nth(size.len() - 2)
                .unwrap()
                .to_ascii_uppercase()
            {
                'K' => 1000u64,
                'M' => 1000u64.pow(2),
                'G' => 1000u64.pow(3),
                'T' => 1000u64.pow(4),
                'P' => 1000u64.pow(5),
                'E' => 1000u64.pow(6),
                'Z' => 1000u64.pow(7),
                'Y' => 1000u64.pow(8),
                letter => crash!(1, "'{}B' is not a valid suffix.", letter),
            },
            'K' => 1024u64,
            'M' => 1024u64.pow(2),
            'G' => 1024u64.pow(3),
            'T' => 1024u64.pow(4),
            'P' => 1024u64.pow(5),
            'E' => 1024u64.pow(6),
            'Z' => 1024u64.pow(7),
            'Y' => 1024u64.pow(8),
            letter => crash!(1, "'{}' is not a valid suffix.", letter),
        };
    }
    (number, mode)
}
<commit_msg>trivial(truncate): remove old comments<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, File, OpenOptions};
use std::io::Result;
use std::path::Path;

#[derive(Eq, PartialEq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp,
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_IO_BLOCKS: &str = "io-blocks";
static OPT_NO_CREATE: &str = "no-create";
static OPT_REFERENCE: &str = "reference";
static OPT_SIZE: &str = "size";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(OPT_IO_BLOCKS)
            .short("o")
            .long(OPT_IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_CREATE)
            .short("c")
            .long(OPT_NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
            .short("r")
            .long(OPT_REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(OPT_SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(OPT_IO_BLOCKS);
        let no_create = matches.is_present(OPT_NO_CREATE);
        let reference = matches.value_of(OPT_REFERENCE).map(String::from);
        let size = matches.value_of(OPT_SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            match truncate(no_create, io_blocks, reference, size, files) {
                Ok(()) => ( /* pass */ ),
                Err(_) => return 1,
            }
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> Result<()> {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            let _ = match File::open(Path::new(&rfilename)) {
                Ok(m) => m,
                Err(f) => crash!(1, "{}", f.to_string()),
            };
            match metadata(rfilename) {
                Ok(meta) => (meta.len(), TruncateMode::Reference),
                Err(f) => crash!(1, "{}", f.to_string()),
            }
        }
        None => parse_size(size.unwrap().as_ref()),
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new()
            .read(true)
            .write(true)
            .create(!no_create)
            .open(path)
        {
            Ok(file) => {
                let fsize = match metadata(filename) {
                    Ok(meta) => meta.len(),
                    Err(f) => {
                        show_warning!("{}", f.to_string());
                        continue;
                    }
                };
                let tsize: u64 = match mode {
                    TruncateMode::Reference => refsize,
                    TruncateMode::Extend => fsize + refsize,
                    TruncateMode::Reduce => fsize - refsize,
                    TruncateMode::AtMost => {
                        if fsize > refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::AtLeast => {
                        if fsize < refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::RoundDown => fsize - fsize % refsize,
                    TruncateMode::RoundUp => fsize + fsize % refsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
    Ok(())
}

fn parse_size(size: &str) -> (u64, TruncateMode) {
    let mode = match size.chars().next().unwrap() {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '*' => TruncateMode::RoundUp,
        _ => TruncateMode::Reference, /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice = if mode == TruncateMode::Reference {
            size
        } else {
            &size[1..]
        };
        if slice.chars().last().unwrap().is_alphabetic() {
            slice = &slice[..slice.len() - 1];
            if !slice.is_empty() && slice.chars().last().unwrap().is_alphabetic() {
                slice = &slice[..slice.len() - 1];
            }
        }
        slice
    }
    .to_owned();
    let mut number: u64 = match bytes.parse() {
        Ok(num) => num,
        Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e),
    };
    if size.chars().last().unwrap().is_alphabetic() {
        number *= match size.chars().last().unwrap().to_ascii_uppercase() {
            'B' => match size
                .chars()
                .nth(size.len() - 2)
                .unwrap()
                .to_ascii_uppercase()
            {
                'K' => 1000u64,
                'M' => 1000u64.pow(2),
                'G' => 1000u64.pow(3),
                'T' => 1000u64.pow(4),
                'P' => 1000u64.pow(5),
                'E' => 1000u64.pow(6),
                'Z' => 1000u64.pow(7),
                'Y' => 1000u64.pow(8),
                letter => crash!(1, "'{}B' is not a valid suffix.", letter),
            },
            'K' => 1024u64,
            'M' => 1024u64.pow(2),
            'G' => 1024u64.pow(3),
            'T' => 1024u64.pow(4),
            'P' => 1024u64.pow(5),
            'E' => 1024u64.pow(6),
            'Z' => 1024u64.pow(7),
            'Y' => 1024u64.pow(8),
            letter => crash!(1, "'{}' is not a valid suffix.", letter),
        };
    }
    (number, mode)
}
<|endoftext|>


<commit_before>use std::num;

pub struct Point<T> {
    pub x: T,
    pub y: T
}

impl<T> Point<T> {
    pub fn new(x: T, y: T) -> Point<T> {
        return Point{x: x, y: y};
    }
}

impl<T:Clone + Add<T,T>> Add<Point<T>, Point<T>> for Point<T> {
    fn add(&self, other: &Point<T>) -> Point<T> {
        return Point::new(self.x + other.x, self.y + other.y);
    }
}

impl<T:Clone + Sub<T,T>> Sub<Point<T>, Point<T>> for Point<T> {
    fn sub(&self, other: &Point<T>) -> Point<T> {
        return Point::new(self.x - other.x, self.y - other.y);
    }
}

impl<T:Clone + Mul<T,T>> Point<T> {
    pub fn scale(&self, factor: T) -> Point<T> {
        return Point::new(self.x * factor, self.y * factor);
    }
}

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

    #[test]
    fn test_add() {
        let p1 = Point::<int>::new(2, 3);
        let p2 = Point::<int>::new(1, 5);
        let p3 = p1 + p2;
        assert_eq!((p3.x, p3.y), (3, 8));
    }

    #[test]
    fn test_sub() {
        let p1 = Point::<int>::new(2, 3);
        let p2 = Point::<int>::new(1, 5);
        let p3 = p1 - p2;
        assert_eq!((p3.x, p3.y), (1, -2));
    }

    #[test]
    fn test_scale() {
        let p = Point::<int>::new(2, 3).scale(2);
        assert_eq!((p.x, p.y), (4, 6));
    }
}
<commit_msg>remove unused import<commit_after>pub struct Point<T> {
    pub x: T,
    pub y: T
}

impl<T> Point<T> {
    pub fn new(x: T, y: T) -> Point<T> {
        return Point{x: x, y: y};
    }
}

impl<T:Clone + Add<T,T>> Add<Point<T>, Point<T>> for Point<T> {
    fn add(&self, other: &Point<T>) -> Point<T> {
        return Point::new(self.x + other.x, self.y + other.y);
    }
}

impl<T:Clone + Sub<T,T>> Sub<Point<T>, Point<T>> for Point<T> {
    fn sub(&self, other: &Point<T>) -> Point<T> {
        return Point::new(self.x - other.x, self.y - other.y);
    }
}

impl<T:Clone + Mul<T,T>> Point<T> {
    pub fn scale(&self, factor: T) -> Point<T> {
        return Point::new(self.x * factor, self.y * factor);
    }
}

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

    #[test]
    fn test_add() {
        let p1 = Point::<int>::new(2, 3);
        let p2 = Point::<int>::new(1, 5);
        let p3 = p1 + p2;
        assert_eq!((p3.x, p3.y), (3, 8));
    }

    #[test]
    fn test_sub() {
        let p1 = Point::<int>::new(2, 3);
        let p2 = Point::<int>::new(1, 5);
        let p3 = p1 - p2;
        assert_eq!((p3.x, p3.y), (1, -2));
    }

    #[test]
    fn test_scale() {
        let p = Point::<int>::new(2, 3).scale(2);
        assert_eq!((p.x, p.y), (4, 6));
    }
}
<|endoftext|>


<commit_before>use crate::err::{self, Result};
use smallvec::{smallvec, SmallVec};
use snafu::ensure;
use std::convert::TryFrom;
use std::result;
use termion::event::Key;
use tui::style::{Color, Style};
use tui::widgets::Text;
use unicode_width::UnicodeWidthChar;

/// A prompt to enter commands in that provides suggestions.
pub struct CommandPrompt<'a> {
    buffer: String,
    hint_text: Option<&'a str>,
    width: usize,
}

impl<'a> CommandPrompt<'a> {
    /// Create a new `CommandPrompt`.
    pub fn new() -> Self {
        CommandPrompt {
            buffer: String::new(),
            hint_text: None,
            width: 0,
        }
    }

    /// Process a key for the `CommandPrompt`.
    ///
    /// Returns a `PromptResult` representing the state of the prompt based off of `key`.
    /// Example return values are given below:
    ///
    /// | `key`             | `PromptResult`             |
    /// | ----------------- | -------------------------- |
    /// | `Key::Char('a')`  | `PromptResult::NotDone`    |
    /// | `Key::Esc`        | `PromptResult::Done`       |
    /// | `Key::Char('\n')` | `PromptResult::Command(_)` |
    pub fn process_key(&mut self, key: Key) -> Result<PromptResult> {
        match key {
            Key::Char('\n') => {
                let command = Command::try_from(self.buffer.as_ref())?;
                self.buffer.clear();
                self.width = 0;
                return Ok(PromptResult::Command(command));
            }
            Key::Char('\t') => {
                if let Some(hint_text) = self.hint_text {
                    self.buffer.push_str(hint_text);
                    self.buffer.push(' ');
                    // Our hint text should always be ASCII, so we can skip getting the unicode length in this case
                    self.width += hint_text.len() + 1;
                    self.hint_text = None;
                }
            }
            Key::Char(ch) => {
                self.buffer.push(ch);
                self.width += UnicodeWidthChar::width(ch).unwrap_or(0);

                if let Some(matching_cmd) = Command::best_matching_name(&self.buffer) {
                    if self.buffer.len() <= matching_cmd.len() {
                        let visible_slice = &matching_cmd[self.buffer.len()..];
                        self.hint_text = Some(visible_slice);
                    }
                } else {
                    self.hint_text = None;
                }
            }
            Key::Backspace => {
                if let Some(popped) = self.buffer.pop() {
                    self.width -= UnicodeWidthChar::width(popped).unwrap_or(0);
                }

                self.hint_text = None;
            }
            Key::Esc => {
                self.buffer.clear();
                self.width = 0;
                return Ok(PromptResult::Done);
            }
            _ => (),
        }

        Ok(PromptResult::NotDone)
    }

    #[inline(always)]
    pub fn width(&self) -> usize {
        self.width
    }

    /// The items of the `CommandPrompt` in a form ready for drawing.
    pub fn draw_items<'b>(&'b self) -> SmallVec<[Text<'b>; 2]> {
        let mut text = smallvec![Text::raw(&self.buffer)];

        if let Some(hint_text) = self.hint_text {
            text.push(Text::styled(
                hint_text,
                Style::default().fg(Color::DarkGray),
            ));
        }

        text
    }
}

/// The result of processing a key in a `CommandPrompt`.
pub enum PromptResult {
    /// A successfully parsed command.
    Command(Command),
    /// Input is considered completed without a command.
    Done,
    /// More input is needed to reach a `Command(_)` or `Done` state.
    NotDone,
}

macro_rules! impl_command_matching {
    ($enum_name:ident, $num_cmds:expr, $($field:pat => { name: $name:expr, min_args: $min_args:expr, fn: $parse_fn:expr, },)+) => {
        impl $enum_name {
            const CMD_NAMES: [&'static str; $num_cmds] = [
                $($name,)+
            ];
        }

        impl TryFrom<&str> for $enum_name {
            type Error = err::Error;

            fn try_from(value: &str) -> result::Result<Self, Self::Error> {
                let fragments = value.split_whitespace().collect::<SmallVec<[&str; 3]>>();

                ensure!(!fragments.is_empty(), err::NoCommandSpecified);

                let name = fragments[0].to_ascii_lowercase();
                let args = if fragments.len() > 1 {
                    &fragments[1..]
                } else {
                    &[]
                };

                match name.as_ref() {
                    $($name => {
                        #[allow(unused_comparisons)]
                        let has_min_args = args.len() >= $min_args;

                        ensure!(
                            has_min_args,
                            err::NotEnoughArguments {
                                has: args.len(),
                                need: $min_args as usize,
                            }
                        );

                        $parse_fn(args)
                    },)+
                    _ => Err(err::Error::CommandNotFound {
                        command: value.into(),
                    }),
                }
            }
        }
    };
}

/// A parsed command with its arguments.
#[derive(Debug, Clone)]
pub enum Command {
    /// Syncronize the selected season to the remote service.
    SyncFromRemote,
    /// Syncronize the selected season from the remote service.
    SyncToRemote,
    /// Set the watch status of the selected season.
    Status(anime::remote::Status),
    /// Increment / decrement the watched episodes of the selected season.
    Progress(ProgressDirection),
    /// Rate the selected season.
    Score(String),
    /// Specify the video player arguments for the selected season.
    PlayerArgs(Vec<String>),
    /// Set the user's login token for a remote service.
    LoginToken(String),
    /// Remove the selected series from the program.
    Delete,
}

impl_command_matching!(Command, 8,
    SyncFromRemote => {
        name: "syncfromremote",
        min_args: 0,
        fn: |_| Ok(Command::SyncFromRemote),
    },
    SyncToRemote => {
        name: "synctoremote",
        min_args: 0,
        fn: |_| Ok(Command::SyncToRemote),
    },
    Status(_) => {
        name: "status",
        min_args: 1,
        fn: |args: &[&str]| {
            use anime::remote::Status;

            let status = match args[0].to_ascii_lowercase().as_ref() {
                "w" | "watching" => Status::Watching,
                "c" | "completed" => Status::Completed,
                "h" | "hold" => Status::OnHold,
                "d" | "drop" => Status::Dropped,
                "p" | "plan" => Status::PlanToWatch,
                "r" | "rewatch" => Status::Rewatching,
                _ => {
                    return Err(err::Error::UnknownCmdPromptArg {
                        value: args[0].into(),
                    })
                }
            };

            Ok(Command::Status(status))
        },
    },
    Progress(_) => {
        name: "progress",
        min_args: 1,
        fn: |args: &[&str]| {
            let dir = ProgressDirection::try_from(args[0])?;
            Ok(Command::Progress(dir))
        },
    },
    Score(_) => {
        name: "rate",
        min_args: 1,
        fn: |args: &[&str]| {
            let score = args[0].into();
            Ok(Command::Score(score))
        },
    },
    PlayerArgs(_) => {
        name: "args",
        min_args: 0,
        fn: |args: &[&str]| {
            let args = args.iter()
                .map(|frag| frag.to_string())
                .collect();

            Ok(Command::PlayerArgs(args))
        },
    },
    LoginToken(_) => {
        name: "token",
        min_args: 1,
        fn: |args: &[&str]| {
            Ok(Command::LoginToken(args.join(" ")))
        },
    },
    Delete => {
        name: "delete",
        min_args: 0,
        fn: |_| Ok(Command::Delete),
    },
);

impl Command {
    /// Returns the command most similar to `name`.
    ///
    /// `None` will be returned if `name` does not match a command with
    /// at least 70% similarity.
    fn best_matching_name(name: &str) -> Option<&'static str> {
        const MIN_CONFIDENCE: f32 = 0.7;

        detect::closest_str_match(
            &Command::CMD_NAMES,
            name,
            MIN_CONFIDENCE,
            strsim::jaro_winkler,
        )
    }
}

/// Indicates which way to advance the episode count of a season.
#[derive(Debug, Copy, Clone)]
pub enum ProgressDirection {
    /// Increase the episode count.
    Forwards,
    /// Decrease the episode count.
    Backwards,
}

impl TryFrom<&str> for ProgressDirection {
    type Error = err::Error;

    fn try_from(value: &str) -> result::Result<Self, Self::Error> {
        match value {
            "f" | "forward" => Ok(ProgressDirection::Forwards),
            "b" | "backward" => Ok(ProgressDirection::Backwards),
            _ => Err(err::Error::UnknownCmdPromptArg {
                value: value.into(),
            }),
        }
    }
}

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

    #[test]
    fn test_commands() {
        use anime::remote::Status;

        let mut prompt = CommandPrompt::new();

        let mut enter_command = |name: &str| {
            for ch in name.chars() {
                match prompt.process_key(Key::Char(ch)) {
                    Ok(PromptResult::NotDone) => (),
                    Ok(PromptResult::Done) => panic!("expected {} command, got nothing", name),
                    Ok(PromptResult::Command(cmd)) => return cmd,
                    Err(err) => panic!("error processing command: {}", err),
                }
            }

            panic!("unexpected finish when entering \"{}\"", name);
        };

        macro_rules! expected {
            ($got:expr, $wanted:expr) => {
                panic!("got unexpected command: {:?}, wanted  {:?}", $got, $wanted)
            };
        }

        macro_rules! test_command {
            ($name:expr, $should_eq:pat) => {
                match enter_command($name) {
                    $should_eq => (),
                    other => expected!(other, stringify!($should_eq)),
                }
            };
        }

        test_command!("synctoremote\n", Command::SyncToRemote);
        test_command!("syncfromremote\n", Command::SyncFromRemote);
        test_command!("status watching\n", Command::Status(Status::Watching));
        test_command!(
            "progress forward\n",
            Command::Progress(ProgressDirection::Forwards)
        );

        let expected_args: Vec<String> = vec!["arg1".into(), "arg2".into()];

        match enter_command("args arg1 arg2\n") {
            Command::PlayerArgs(args) => {
                if args.len() != expected_args.len() {
                    expected!(
                        Command::PlayerArgs(args),
                        Command::PlayerArgs(expected_args)
                    );
                }

                for (parsed, expected_arg) in args.iter().zip(&expected_args) {
                    if *parsed != *expected_arg {
                        expected!(
                            Command::PlayerArgs(args),
                            Command::PlayerArgs(expected_args)
                        );
                    }
                }
            }
            other => expected!(other, Command::PlayerArgs(expected_args)),
        }

        match enter_command("token inserttokenhere\n") {
            Command::LoginToken(token) if token == "inserttokenhere" => (),
            other => expected!(other, Command::LoginToken(String::from("inserttokenhere"))),
        }
    }
}
<commit_msg>anup: sort commands alphabetically<commit_after>use crate::err::{self, Result};
use smallvec::{smallvec, SmallVec};
use snafu::ensure;
use std::convert::TryFrom;
use std::result;
use termion::event::Key;
use tui::style::{Color, Style};
use tui::widgets::Text;
use unicode_width::UnicodeWidthChar;

/// A prompt to enter commands in that provides suggestions.
pub struct CommandPrompt<'a> {
    buffer: String,
    hint_text: Option<&'a str>,
    width: usize,
}

impl<'a> CommandPrompt<'a> {
    /// Create a new `CommandPrompt`.
    pub fn new() -> Self {
        CommandPrompt {
            buffer: String::new(),
            hint_text: None,
            width: 0,
        }
    }

    /// Process a key for the `CommandPrompt`.
    ///
    /// Returns a `PromptResult` representing the state of the prompt based off of `key`.
    /// Example return values are given below:
    ///
    /// | `key`             | `PromptResult`             |
    /// | ----------------- | -------------------------- |
    /// | `Key::Char('a')`  | `PromptResult::NotDone`    |
    /// | `Key::Esc`        | `PromptResult::Done`       |
    /// | `Key::Char('\n')` | `PromptResult::Command(_)` |
    pub fn process_key(&mut self, key: Key) -> Result<PromptResult> {
        match key {
            Key::Char('\n') => {
                let command = Command::try_from(self.buffer.as_ref())?;
                self.buffer.clear();
                self.width = 0;
                return Ok(PromptResult::Command(command));
            }
            Key::Char('\t') => {
                if let Some(hint_text) = self.hint_text {
                    self.buffer.push_str(hint_text);
                    self.buffer.push(' ');
                    // Our hint text should always be ASCII, so we can skip getting the unicode length in this case
                    self.width += hint_text.len() + 1;
                    self.hint_text = None;
                }
            }
            Key::Char(ch) => {
                self.buffer.push(ch);
                self.width += UnicodeWidthChar::width(ch).unwrap_or(0);

                if let Some(matching_cmd) = Command::best_matching_name(&self.buffer) {
                    if self.buffer.len() <= matching_cmd.len() {
                        let visible_slice = &matching_cmd[self.buffer.len()..];
                        self.hint_text = Some(visible_slice);
                    }
                } else {
                    self.hint_text = None;
                }
            }
            Key::Backspace => {
                if let Some(popped) = self.buffer.pop() {
                    self.width -= UnicodeWidthChar::width(popped).unwrap_or(0);
                }

                self.hint_text = None;
            }
            Key::Esc => {
                self.buffer.clear();
                self.width = 0;
                return Ok(PromptResult::Done);
            }
            _ => (),
        }

        Ok(PromptResult::NotDone)
    }

    #[inline(always)]
    pub fn width(&self) -> usize {
        self.width
    }

    /// The items of the `CommandPrompt` in a form ready for drawing.
    pub fn draw_items<'b>(&'b self) -> SmallVec<[Text<'b>; 2]> {
        let mut text = smallvec![Text::raw(&self.buffer)];

        if let Some(hint_text) = self.hint_text {
            text.push(Text::styled(
                hint_text,
                Style::default().fg(Color::DarkGray),
            ));
        }

        text
    }
}

/// The result of processing a key in a `CommandPrompt`.
pub enum PromptResult {
    /// A successfully parsed command.
    Command(Command),
    /// Input is considered completed without a command.
    Done,
    /// More input is needed to reach a `Command(_)` or `Done` state.
    NotDone,
}

macro_rules! impl_command_matching {
    ($enum_name:ident, $num_cmds:expr, $($field:pat => { name: $name:expr, min_args: $min_args:expr, fn: $parse_fn:expr, },)+) => {
        impl $enum_name {
            const CMD_NAMES: [&'static str; $num_cmds] = [
                $($name,)+
            ];
        }

        impl TryFrom<&str> for $enum_name {
            type Error = err::Error;

            fn try_from(value: &str) -> result::Result<Self, Self::Error> {
                let fragments = value.split_whitespace().collect::<SmallVec<[&str; 3]>>();

                ensure!(!fragments.is_empty(), err::NoCommandSpecified);

                let name = fragments[0].to_ascii_lowercase();
                let args = if fragments.len() > 1 {
                    &fragments[1..]
                } else {
                    &[]
                };

                match name.as_ref() {
                    $($name => {
                        #[allow(unused_comparisons)]
                        let has_min_args = args.len() >= $min_args;

                        ensure!(
                            has_min_args,
                            err::NotEnoughArguments {
                                has: args.len(),
                                need: $min_args as usize,
                            }
                        );

                        $parse_fn(args)
                    },)+
                    _ => Err(err::Error::CommandNotFound {
                        command: value.into(),
                    }),
                }
            }
        }
    };
}

/// A parsed command with its arguments.
#[derive(Debug, Clone)]
pub enum Command {
    /// Remove the selected series from the program.
    Delete,
    /// Set the user's login token for a remote service.
    LoginToken(String),
    /// Specify the video player arguments for the selected season.
    PlayerArgs(Vec<String>),
    /// Increment / decrement the watched episodes of the selected season.
    Progress(ProgressDirection),
    /// Syncronize the selected season to the remote service.
    SyncFromRemote,
    /// Syncronize the selected season from the remote service.
    SyncToRemote,
    /// Rate the selected season.
    Score(String),
    /// Set the watch status of the selected season.
    Status(anime::remote::Status),
}

impl_command_matching!(Command, 8,
    Delete => {
        name: "delete",
        min_args: 0,
        fn: |_| Ok(Command::Delete),
    },
    LoginToken(_) => {
        name: "token",
        min_args: 1,
        fn: |args: &[&str]| {
            Ok(Command::LoginToken(args.join(" ")))
        },
    },
    PlayerArgs(_) => {
        name: "args",
        min_args: 0,
        fn: |args: &[&str]| {
            let args = args.iter()
                .map(|frag| frag.to_string())
                .collect();

            Ok(Command::PlayerArgs(args))
        },
    },
    Progress(_) => {
        name: "progress",
        min_args: 1,
        fn: |args: &[&str]| {
            let dir = ProgressDirection::try_from(args[0])?;
            Ok(Command::Progress(dir))
        },
    },
    SyncFromRemote => {
        name: "syncfromremote",
        min_args: 0,
        fn: |_| Ok(Command::SyncFromRemote),
    },
    SyncToRemote => {
        name: "synctoremote",
        min_args: 0,
        fn: |_| Ok(Command::SyncToRemote),
    },
    Score(_) => {
        name: "rate",
        min_args: 1,
        fn: |args: &[&str]| {
            let score = args[0].into();
            Ok(Command::Score(score))
        },
    },
    Status(_) => {
        name: "status",
        min_args: 1,
        fn: |args: &[&str]| {
            use anime::remote::Status;

            let status = match args[0].to_ascii_lowercase().as_ref() {
                "w" | "watching" => Status::Watching,
                "c" | "completed" => Status::Completed,
                "h" | "hold" => Status::OnHold,
                "d" | "drop" => Status::Dropped,
                "p" | "plan" => Status::PlanToWatch,
                "r" | "rewatch" => Status::Rewatching,
                _ => {
                    return Err(err::Error::UnknownCmdPromptArg {
                        value: args[0].into(),
                    })
                }
            };

            Ok(Command::Status(status))
        },
    },
);

impl Command {
    /// Returns the command most similar to `name`.
    ///
    /// `None` will be returned if `name` does not match a command with
    /// at least 70% similarity.
    fn best_matching_name(name: &str) -> Option<&'static str> {
        const MIN_CONFIDENCE: f32 = 0.7;

        detect::closest_str_match(
            &Command::CMD_NAMES,
            name,
            MIN_CONFIDENCE,
            strsim::jaro_winkler,
        )
    }
}

/// Indicates which way to advance the episode count of a season.
#[derive(Debug, Copy, Clone)]
pub enum ProgressDirection {
    /// Increase the episode count.
    Forwards,
    /// Decrease the episode count.
    Backwards,
}

impl TryFrom<&str> for ProgressDirection {
    type Error = err::Error;

    fn try_from(value: &str) -> result::Result<Self, Self::Error> {
        match value {
            "f" | "forward" => Ok(ProgressDirection::Forwards),
            "b" | "backward" => Ok(ProgressDirection::Backwards),
            _ => Err(err::Error::UnknownCmdPromptArg {
                value: value.into(),
            }),
        }
    }
}

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

    #[test]
    fn test_commands() {
        use anime::remote::Status;

        let mut prompt = CommandPrompt::new();

        let mut enter_command = |name: &str| {
            for ch in name.chars() {
                match prompt.process_key(Key::Char(ch)) {
                    Ok(PromptResult::NotDone) => (),
                    Ok(PromptResult::Done) => panic!("expected {} command, got nothing", name),
                    Ok(PromptResult::Command(cmd)) => return cmd,
                    Err(err) => panic!("error processing command: {}", err),
                }
            }

            panic!("unexpected finish when entering \"{}\"", name);
        };

        macro_rules! expected {
            ($got:expr, $wanted:expr) => {
                panic!("got unexpected command: {:?}, wanted  {:?}", $got, $wanted)
            };
        }

        macro_rules! test_command {
            ($name:expr, $should_eq:pat) => {
                match enter_command($name) {
                    $should_eq => (),
                    other => expected!(other, stringify!($should_eq)),
                }
            };
        }

        test_command!("synctoremote\n", Command::SyncToRemote);
        test_command!("syncfromremote\n", Command::SyncFromRemote);
        test_command!("status watching\n", Command::Status(Status::Watching));
        test_command!(
            "progress forward\n",
            Command::Progress(ProgressDirection::Forwards)
        );

        let expected_args: Vec<String> = vec!["arg1".into(), "arg2".into()];

        match enter_command("args arg1 arg2\n") {
            Command::PlayerArgs(args) => {
                if args.len() != expected_args.len() {
                    expected!(
                        Command::PlayerArgs(args),
                        Command::PlayerArgs(expected_args)
                    );
                }

                for (parsed, expected_arg) in args.iter().zip(&expected_args) {
                    if *parsed != *expected_arg {
                        expected!(
                            Command::PlayerArgs(args),
                            Command::PlayerArgs(expected_args)
                        );
                    }
                }
            }
            other => expected!(other, Command::PlayerArgs(expected_args)),
        }

        match enter_command("token inserttokenhere\n") {
            Command::LoginToken(token) if token == "inserttokenhere" => (),
            other => expected!(other, Command::LoginToken(String::from("inserttokenhere"))),
        }
    }
}
<|endoftext|>


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

//! Interface for the GPIO peripheral.
//!
//! To ensure fast performance, RPPAL interfaces with the GPIO peripheral by
//! directly accessing the registers through either `/dev/gpiomem` or `/dev/mem`.
//! GPIO interrupts are controlled using the `/dev/gpiochipN` (where N=0, 1 and 2)
//! character device.
//!
//! ## Pins
//!
//! Pins are addressed by their BCM numbers, rather than their
//! physical location.
//!
//! By default, pins are reset to their original state when they go out of scope.
//! Use [`InputPin::set_clear_on_drop(false)`], [`OutputPin::set_clear_on_drop(false)`]
//! or [`AltPin::set_clear_on_drop(false)`], respecively, to disable this behavior.
//! Note that `drop` methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ## Single instance
//!
//! Only a single [`Gpio`] instance can exist at any time. Multiple instances could
//! cause race conditions or pin configuration issues when several threads write to
//! the same register simultaneously. While other applications can't be prevented from
//! writing to the GPIO registers at the same time, limiting [`Gpio`] to a single instance
//! will at least make the Rust interface thread-safe.
//!
//! Constructing another instance before the existing one goes out of scope will return
//! an [`Error::InstanceExists`]. You can share a [`Gpio`] instance with other
//! threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
//! a `Mutex<Gpio>`.
//!
//! ## Permission denied
//!
//! In recent releases of Raspbian (December 2017 or later), users that are part of the
//! `gpio` group (like the default `pi` user) can access `/dev/gpiomem` and
//! `/dev/gpiochipN` without needing additional permissions. If you encounter any
//! Permission Denied errors when creating a new [`Gpio`] instance, either the current
//! user isn't a member of the `gpio` group, or your Raspbian distribution isn't
//! up-to-date and doesn't automatically configure permissions for the above-mentioned
//! files. Updating Raspbian to the latest release should fix any permission issues.
//! Alternatively, although not recommended, you can run your application with superuser
//! privileges by using `sudo`.
//!
//! If you're unable to update Raspbian and its packages (namely `raspberrypi-sys-mods`) to
//! the latest available release, or updating hasn't fixed the issue, you might be able to
//! manually update your udev rules to set the appropriate permissions. More information
//! can be found at [raspberrypi/linux#1225] and [raspberrypi/linux#2289].
//!
//! [raspberrypi/linux#1225]: https://github.com/raspberrypi/linux/issues/1225
//! [raspberrypi/linux#2289]: https://github.com/raspberrypi/linux/issues/2289
//! [`Gpio`]: struct.Gpio.html
//! [`InputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`OutputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`AltPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
//!
//! ## Examples
//!
//! Basic example:
//!
//! ```
//! use std::thread::sleep;
//! use std::time::Duration;
//!
//! use rppal::gpio::Gpio;
//!
//! # fn main() -> rppal::gpio::Result<()> {
//! let gpio = Gpio::new()?;
//! let mut pin = gpio.get(23).unwrap().into_output();
//!
//! pin.set_high();
//! sleep(Duration::from_secs(1));
//! pin.set_low();
//! # Ok(())
//! # }
//! ```

use std::fmt;
use std::io;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;

use lazy_static::lazy_static;
use quick_error::quick_error;

mod epoll;
mod interrupt;
mod ioctl;
mod mem;
mod pin;

pub use self::pin::{AltPin, InputPin, OutputPin, Pin};

// Continue to keep track of taken pins when Gpio goes out of scope
lazy_static! {
    static ref PINS_TAKEN: [AtomicBool; pin::MAX] = init_array!(AtomicBool::new(false), pin::MAX);
}

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown SoC.
///
/// It wasn't possible to automatically identify the Raspberry Pi's SoC.
        UnknownSoC { description("unknown SoC") }
/// Permission denied when opening `/dev/gpiomem` and/or `/dev/mem` for read/write access.
///
/// Make sure the user has read and write access to `/dev/gpiomem`.
/// Common causes are either incorrect file permissions on `/dev/gpiomem`, or
/// the user isn't a member of the `gpio` group. If `/dev/gpiomem` is missing, upgrade to a more
/// recent version of Raspbian.
///
/// `/dev/mem` is a fallback when `/dev/gpiomem` can't be accessed. Getting read and write
/// access to `/dev/mem` is typically accomplished by executing the program as a
/// privileged user through `sudo`. A better solution that doesn't require `sudo` would be
/// to upgrade to a version of Raspbian that implements `/dev/gpiomem`.
        PermissionDenied { description("/dev/gpiomem and/or /dev/mem insufficient permissions") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

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

/// Pin modes.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
    Input = 0b000,
    Output = 0b001,
    Alt5 = 0b010, // PWM
    Alt4 = 0b011, // SPI
    Alt0 = 0b100, // PCM
    Alt1 = 0b101, // SMI
    Alt2 = 0b110, // ---
    Alt3 = 0b111, // BSC-SPI
}

impl fmt::Display for Mode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Mode::Input => write!(f, "In"),
            Mode::Output => write!(f, "Out"),
            Mode::Alt0 => write!(f, "Alt0"),
            Mode::Alt1 => write!(f, "Alt1"),
            Mode::Alt2 => write!(f, "Alt2"),
            Mode::Alt3 => write!(f, "Alt3"),
            Mode::Alt4 => write!(f, "Alt4"),
            Mode::Alt5 => write!(f, "Alt5"),
        }
    }
}

/// Pin logic levels.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
    Low = 0,
    High = 1,
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Level::Low => write!(f, "Low"),
            Level::High => write!(f, "High"),
        }
    }
}

/// Built-in pull-up/pull-down resistor states.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PullUpDown {
    Off = 0b00,
    PullDown = 0b01,
    PullUp = 0b10,
}

impl fmt::Display for PullUpDown {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PullUpDown::Off => write!(f, "Off"),
            PullUpDown::PullDown => write!(f, "PullDown"),
            PullUpDown::PullUp => write!(f, "PullUp"),
        }
    }
}

/// Interrupt trigger conditions.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Trigger {
    Disabled = 0,
    RisingEdge = 1,
    FallingEdge = 2,
    Both = 3,
}

impl fmt::Display for Trigger {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Trigger::Disabled => write!(f, "Disabled"),
            Trigger::RisingEdge => write!(f, "RisingEdge"),
            Trigger::FallingEdge => write!(f, "FallingEdge"),
            Trigger::Both => write!(f, "Both"),
        }
    }
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
pub struct Gpio {
    pub(crate) gpio_mem: Arc<mem::GpioMem>,
    cdev: Arc<std::fs::File>,
    sync_interrupts: Arc<Mutex<interrupt::EventLoop>>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    ///
    /// Only a single instance of `Gpio` can exist at any time. Constructing
    /// another instance before the existing one goes out of scope will return
    /// an [`Error::InstanceExists`]. You can share a `Gpio` instance with other
    /// threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
    /// a `Mutex<Gpio>`.
    ///
    /// [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
    pub fn new() -> Result<Gpio> {
        let cdev = ioctl::find_gpiochip()?;
        let cdev_fd = cdev.as_raw_fd();

        let cdev = Arc::new(cdev);
        let event_loop = Arc::new(Mutex::new(interrupt::EventLoop::new(cdev_fd, pin::MAX)?));
        let gpio_mem = Arc::new(mem::GpioMem::open()?);

        let gpio = Gpio {
            gpio_mem,
            cdev,
            sync_interrupts: event_loop,
        };

        Ok(gpio)
    }

    /// Returns a [`Pin`] for the specified GPIO pin number.
    ///
    /// Retrieving a GPIO pin using `get` grants exclusive access to the GPIO
    /// pin through an owned [`Pin`]. If the selected pin number is already
    /// in use, `get` returns `None`. After a [`Pin`] goes out of scope, it can be retrieved
    /// again using `get`.
    ///
    /// [`Pin`]: struct.Pin.html
    pub fn get(&self, pin: u8) -> Option<pin::Pin> {
        if pin as usize >= pin::MAX {
            return None;
        }

        // Returns true if the pin is currently taken, otherwise atomically sets
        // it to true here
        if PINS_TAKEN[pin as usize].compare_and_swap(false, true, Ordering::SeqCst) {
            // Pin is currently taken
            None
        } else {
            // Return an owned Pin
            let pin_instance = pin::Pin::new(
                pin,
                self.sync_interrupts.clone(),
                self.gpio_mem.clone(),
                self.cdev.clone(),
            );

            Some(pin_instance)
        }
    }

    /// Blocks until an interrupt is triggered on any of the specified pins, or until a timeout occurs.
    ///
    /// This only works for pins that have been configured for synchronous interrupts using
    /// [`InputPin::set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to [`InputPin::poll_interrupt`] or `poll_interrupts`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call to `poll_interrupts` will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// When an interrupt event is triggered, `poll_interrupts` returns
    /// `Ok((&`[`InputPin`]`, `[`Level`]`))` containing the corresponding pin and logic level. If multiple events trigger
    /// at the same time, only the first one is returned. The remaining events are cached and will be returned
    /// the next time [`InputPin::poll_interrupt`] or `poll_interrupts` is called.
    ///
    /// [`InputPin::set_interrupt`]: struct.InputPin#method.set_interrupt
    /// [`InputPin::poll_interrupt`]: struct.InputPin#method.poll_interrupt
    /// [`InputPin`]: struct.InputPin
    /// [`Level`]: struct.Level
    pub fn poll_interrupts<'a>(
        &self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        (*self.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}

impl fmt::Debug for Gpio {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Gpio")
            .field("gpio_mem", &*self.gpio_mem)
            .field("sync_interrupts", &format_args!("{{ .. }}"))
            .finish()
    }
}
<commit_msg>Share state between multiple Gpio instances<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the GPIO peripheral.
//!
//! To ensure fast performance, RPPAL interfaces with the GPIO peripheral by
//! directly accessing the registers through either `/dev/gpiomem` or `/dev/mem`.
//! GPIO interrupts are controlled using the `/dev/gpiochipN` (where N=0, 1 and 2)
//! character device.
//!
//! ## Pins
//!
//! Pins are addressed by their BCM numbers, rather than their
//! physical location.
//!
//! By default, pins are reset to their original state when they go out of scope.
//! Use [`InputPin::set_clear_on_drop(false)`], [`OutputPin::set_clear_on_drop(false)`]
//! or [`AltPin::set_clear_on_drop(false)`], respecively, to disable this behavior.
//! Note that `drop` methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ## Single instance
//!
//! Only a single [`Gpio`] instance can exist at any time. Multiple instances could
//! cause race conditions or pin configuration issues when several threads write to
//! the same register simultaneously. While other applications can't be prevented from
//! writing to the GPIO registers at the same time, limiting [`Gpio`] to a single instance
//! will at least make the Rust interface thread-safe.
//!
//! Constructing another instance before the existing one goes out of scope will return
//! an [`Error::InstanceExists`]. You can share a [`Gpio`] instance with other
//! threads using channels, cloning an `Arc<Mutex<Gpio>>` or globally sharing
//! a `Mutex<Gpio>`.
//!
//! ## Permission denied
//!
//! In recent releases of Raspbian (December 2017 or later), users that are part of the
//! `gpio` group (like the default `pi` user) can access `/dev/gpiomem` and
//! `/dev/gpiochipN` without needing additional permissions. If you encounter any
//! Permission Denied errors when creating a new [`Gpio`] instance, either the current
//! user isn't a member of the `gpio` group, or your Raspbian distribution isn't
//! up-to-date and doesn't automatically configure permissions for the above-mentioned
//! files. Updating Raspbian to the latest release should fix any permission issues.
//! Alternatively, although not recommended, you can run your application with superuser
//! privileges by using `sudo`.
//!
//! If you're unable to update Raspbian and its packages (namely `raspberrypi-sys-mods`) to
//! the latest available release, or updating hasn't fixed the issue, you might be able to
//! manually update your udev rules to set the appropriate permissions. More information
//! can be found at [raspberrypi/linux#1225] and [raspberrypi/linux#2289].
//!
//! [raspberrypi/linux#1225]: https://github.com/raspberrypi/linux/issues/1225
//! [raspberrypi/linux#2289]: https://github.com/raspberrypi/linux/issues/2289
//! [`Gpio`]: struct.Gpio.html
//! [`InputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`OutputPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`AltPin::set_clear_on_drop(false)`]: struct.InputPin.html#method.set_clear_on_drop
//! [`Error::InstanceExists`]: enum.Error.html#variant.InstanceExists
//!
//! ## Examples
//!
//! Basic example:
//!
//! ```
//! use std::thread::sleep;
//! use std::time::Duration;
//!
//! use rppal::gpio::Gpio;
//!
//! # fn main() -> rppal::gpio::Result<()> {
//! let gpio = Gpio::new()?;
//! let mut pin = gpio.get(23).unwrap().into_output();
//!
//! pin.set_high();
//! sleep(Duration::from_secs(1));
//! pin.set_low();
//! # Ok(())
//! # }
//! ```

use std::fmt;
use std::io;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;

use lazy_static::lazy_static;
use quick_error::quick_error;

mod epoll;
mod interrupt;
mod ioctl;
mod mem;
mod pin;

pub use self::pin::{AltPin, InputPin, OutputPin, Pin};

quick_error! {
/// Errors that can occur when accessing the GPIO peripheral.
    #[derive(Debug)]
    pub enum Error {
/// Unknown SoC.
///
/// It wasn't possible to automatically identify the Raspberry Pi's SoC.
        UnknownSoC { description("unknown SoC") }
/// Permission denied when opening `/dev/gpiomem` and/or `/dev/mem` for read/write access.
///
/// Make sure the user has read and write access to `/dev/gpiomem`.
/// Common causes are either incorrect file permissions on `/dev/gpiomem`, or
/// the user isn't a member of the `gpio` group. If `/dev/gpiomem` is missing, upgrade to a more
/// recent version of Raspbian.
///
/// `/dev/mem` is a fallback when `/dev/gpiomem` can't be accessed. Getting read and write
/// access to `/dev/mem` is typically accomplished by executing the program as a
/// privileged user through `sudo`. A better solution that doesn't require `sudo` would be
/// to upgrade to a version of Raspbian that implements `/dev/gpiomem`.
        PermissionDenied { description("/dev/gpiomem and/or /dev/mem insufficient permissions") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

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

/// Pin modes.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
    Input = 0b000,
    Output = 0b001,
    Alt5 = 0b010, // PWM
    Alt4 = 0b011, // SPI
    Alt0 = 0b100, // PCM
    Alt1 = 0b101, // SMI
    Alt2 = 0b110, // ---
    Alt3 = 0b111, // BSC-SPI
}

impl fmt::Display for Mode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Mode::Input => write!(f, "In"),
            Mode::Output => write!(f, "Out"),
            Mode::Alt0 => write!(f, "Alt0"),
            Mode::Alt1 => write!(f, "Alt1"),
            Mode::Alt2 => write!(f, "Alt2"),
            Mode::Alt3 => write!(f, "Alt3"),
            Mode::Alt4 => write!(f, "Alt4"),
            Mode::Alt5 => write!(f, "Alt5"),
        }
    }
}

/// Pin logic levels.
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Level {
    Low = 0,
    High = 1,
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Level::Low => write!(f, "Low"),
            Level::High => write!(f, "High"),
        }
    }
}

/// Built-in pull-up/pull-down resistor states.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PullUpDown {
    Off = 0b00,
    PullDown = 0b01,
    PullUp = 0b10,
}

impl fmt::Display for PullUpDown {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PullUpDown::Off => write!(f, "Off"),
            PullUpDown::PullDown => write!(f, "PullDown"),
            PullUpDown::PullUp => write!(f, "PullUp"),
        }
    }
}

/// Interrupt trigger conditions.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Trigger {
    Disabled = 0,
    RisingEdge = 1,
    FallingEdge = 2,
    Both = 3,
}

impl fmt::Display for Trigger {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Trigger::Disabled => write!(f, "Disabled"),
            Trigger::RisingEdge => write!(f, "RisingEdge"),
            Trigger::FallingEdge => write!(f, "FallingEdge"),
            Trigger::Both => write!(f, "Both"),
        }
    }
}

// Continue to keep track of taken pins when Gpio goes out of scope
lazy_static! {
    static ref PINS_TAKEN: [AtomicBool; pin::MAX] = init_array!(AtomicBool::new(false), pin::MAX);
}

// Share state between multiple Gpio instances
lazy_static! {
    static ref GPIO_STATE: Mutex<Option<Gpio>> = Mutex::new(None);
}

/// Provides access to the Raspberry Pi's GPIO peripheral.
#[derive(Clone)]
pub struct Gpio {
    gpio_mem: Arc<mem::GpioMem>,
    cdev: Arc<std::fs::File>,
    sync_interrupts: Arc<Mutex<interrupt::EventLoop>>,
}

impl Gpio {
    /// Constructs a new `Gpio`.
    pub fn new() -> Result<Gpio> {
        let mut state = GPIO_STATE.lock().unwrap();

        // Create a clone if a Gpio instance already exists, otherwise
        // initialize it here so we can return any relevant errors.
        if let Some(ref state) = *state {
            Ok(state.clone())
        } else {
            let gpio_mem = Arc::new(mem::GpioMem::open()?);
            let cdev = Arc::new(ioctl::find_gpiochip()?);
            let event_loop = Arc::new(Mutex::new(interrupt::EventLoop::new(
                cdev.as_raw_fd(),
                pin::MAX,
            )?));

            let gpio = Gpio {
                gpio_mem,
                cdev,
                sync_interrupts: event_loop,
            };

            *state = Some(gpio.clone());

            Ok(gpio)
        }
    }

    /// Returns a [`Pin`] for the specified GPIO pin number.
    ///
    /// Retrieving a GPIO pin using `get` grants exclusive access to the GPIO
    /// pin through an owned [`Pin`]. If the selected pin number is already
    /// in use, `get` returns `None`. After a [`Pin`] goes out of scope, it can be retrieved
    /// again using `get`.
    ///
    /// [`Pin`]: struct.Pin.html
    pub fn get(&self, pin: u8) -> Option<pin::Pin> {
        if pin as usize >= pin::MAX {
            return None;
        }

        // Returns true if the pin is currently taken, otherwise atomically sets
        // it to true here
        if PINS_TAKEN[pin as usize].compare_and_swap(false, true, Ordering::SeqCst) {
            // Pin is currently taken
            None
        } else {
            // Return an owned Pin
            let pin_instance = pin::Pin::new(
                pin,
                self.sync_interrupts.clone(),
                self.gpio_mem.clone(),
                self.cdev.clone(),
            );

            Some(pin_instance)
        }
    }

    /// Blocks until an interrupt is triggered on any of the specified pins, or until a timeout occurs.
    ///
    /// This only works for pins that have been configured for synchronous interrupts using
    /// [`InputPin::set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to [`InputPin::poll_interrupt`] or `poll_interrupts`.
    /// If `reset` is set to `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call to `poll_interrupts` will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// When an interrupt event is triggered, `poll_interrupts` returns
    /// `Ok((&`[`InputPin`]`, `[`Level`]`))` containing the corresponding pin and logic level. If multiple events trigger
    /// at the same time, only the first one is returned. The remaining events are cached and will be returned
    /// the next time [`InputPin::poll_interrupt`] or `poll_interrupts` is called.
    ///
    /// [`InputPin::set_interrupt`]: struct.InputPin#method.set_interrupt
    /// [`InputPin::poll_interrupt`]: struct.InputPin#method.poll_interrupt
    /// [`InputPin`]: struct.InputPin
    /// [`Level`]: struct.Level
    pub fn poll_interrupts<'a>(
        &self,
        pins: &[&'a InputPin],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<(&'a InputPin, Level)>> {
        (*self.sync_interrupts.lock().unwrap()).poll(pins, reset, timeout)
    }
}

impl fmt::Debug for Gpio {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Gpio")
            .field("gpio_mem", &*self.gpio_mem)
            .field("sync_interrupts", &format_args!("{{ .. }}"))
            .finish()
    }
}
<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use types::{gchar,gpointer};

use libc;
use libc::{c_char,size_t};
use std::mem::transmute;
use std::slice;
use std::str;
use std::string;

pub struct Utf8 {
    data: *mut gchar,
}

impl Utf8 {
    pub unsafe fn new(data: *mut gchar) -> Utf8 {
        Utf8 { data: data }
    }

    pub fn into_string(self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const gchar as *const u8)
        }
    }
}

impl Drop for Utf8 {
    fn drop(&mut self) {
        unsafe {
            ffi::g_free(self.data as gpointer);
        }
    }
}

impl Clone for Utf8 {
    fn clone(&self) -> Utf8 {
        unsafe {
            Utf8::new(ffi::g_strdup(self.data as *const gchar))
        }
    }
}

impl Str for Utf8 {
    fn as_slice<'a>(&'a self) -> &'a str {
        unsafe {
            let len = libc::strlen(self.data as *const c_char);
            slice::raw::buf_as_slice(
                self.data as *const u8,
                len as uint,
                |bytes| {
                    let s = str::raw::from_utf8(bytes);
                    transmute(s)
                })
        }
    }
}

impl StrAllocating for Utf8 {
    fn into_string(self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const gchar as *const u8)
        }
    }
}

impl PartialEq for Utf8 {
    fn eq(&self, other: &Utf8) -> bool {
        unsafe {
            libc::strcmp(self.data as *const gchar,
                         other.data as *const gchar) == 0
        }
    }

    fn ne(&self, other: &Utf8) -> bool {
        unsafe {
            libc::strcmp(self.data as *const c_char,
                         other.data as *const c_char) != 0
        }
    }
}

impl Eq for Utf8 { }

impl<S: Str> Equiv<S> for Utf8 {
    fn equiv(&self, other: &S) -> bool {
        let os = other.as_slice();
        let len = os.len();
        unsafe {
            libc::strncmp(self.data as *const c_char,
                          os.as_ptr() as *const c_char,
                          len as size_t) == 0
            && *self.data.offset(len as int) == 0
        }
    }
}
<commit_msg>Implemented ToCStr for gstr::Utf8<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use types::{gchar,gpointer};

use libc;
use libc::{c_char,size_t};
use std::c_str::{CString,ToCStr};
use std::mem::transmute;
use std::slice;
use std::str;
use std::string;

// Huh, no strdup in the libc crate?
extern {
    fn strdup(p: *const c_char) -> *mut c_char;
}

pub struct Utf8 {
    data: *mut gchar,
}

impl Utf8 {
    pub unsafe fn new(data: *mut gchar) -> Utf8 {
        Utf8 { data: data }
    }

    pub fn into_string(self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const gchar as *const u8)
        }
    }
}

impl Drop for Utf8 {
    fn drop(&mut self) {
        unsafe {
            ffi::g_free(self.data as gpointer);
        }
    }
}

impl Clone for Utf8 {
    fn clone(&self) -> Utf8 {
        unsafe {
            Utf8::new(ffi::g_strdup(self.data as *const gchar))
        }
    }
}

impl Str for Utf8 {
    fn as_slice<'a>(&'a self) -> &'a str {
        unsafe {
            let len = libc::strlen(self.data as *const c_char);
            slice::raw::buf_as_slice(
                self.data as *const u8,
                len as uint,
                |bytes| {
                    let s = str::raw::from_utf8(bytes);
                    transmute(s)
                })
        }
    }
}

impl StrAllocating for Utf8 {
    fn into_string(self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const gchar as *const u8)
        }
    }
}

impl ToCStr for Utf8 {

    fn to_c_str(&self) -> CString {
        // It's all right, Glib does not grok interior nulls
        unsafe { self.to_c_str_unchecked() }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        let copy = strdup(self.data as *const c_char);
        CString::new(copy as *const c_char, true)
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }
}

impl PartialEq for Utf8 {
    fn eq(&self, other: &Utf8) -> bool {
        unsafe {
            libc::strcmp(self.data as *const gchar,
                         other.data as *const gchar) == 0
        }
    }

    fn ne(&self, other: &Utf8) -> bool {
        unsafe {
            libc::strcmp(self.data as *const c_char,
                         other.data as *const c_char) != 0
        }
    }
}

impl Eq for Utf8 { }

impl<S: Str> Equiv<S> for Utf8 {
    fn equiv(&self, other: &S) -> bool {
        let os = other.as_slice();
        let len = os.len();
        unsafe {
            libc::strncmp(self.data as *const c_char,
                          os.as_ptr() as *const c_char,
                          len as size_t) == 0
            && *self.data.offset(len as int) == 0
        }
    }
}
<|endoftext|>


<commit_before>use cards::value;
use cards::value::Value;
use cards::card::Card;
use std::fmt;
use shoe::shoe::DirectShoe;

pub const INDEX_TO_SCORE: [uint, ..13] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10];

pub fn score_for_value(v: &Value) -> uint {
    return INDEX_TO_SCORE[v.index()];
}

pub struct BJHand {
    score: uint,
    ace_count: uint,
    splits_done: uint,
    splits_to_solve: uint,
    num_cards: uint,
    double_count: uint,
    // The first two cards are very important for blackjack
    cards: Vec<Card>,
}

impl fmt::Show for BJHand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.cards)
    }
}

impl BJHand {
    pub fn cards(&self) -> &Vec<Card> {
        &self.cards
    }

    pub fn score(&self) -> uint {
        if self.is_soft() {
            return self.score + 10;
        } else {
            return self.score;
        }
    }

    pub fn is_soft(&self) -> bool {
        return self.ace_count > 0 && self.score + 10 <= 21;
    }

    pub fn double_count(&self) -> uint {
        self.double_count
    }

    pub fn add_double_count(&mut self) {
        self.double_count += 1
    }

    pub fn subtract_double_count(&mut self) {
        assert!(self.double_count >= 1);
        self.double_count -= 1;
    }

    pub fn len(&self) -> uint {
        assert_eq!(self.num_cards, self.cards.len());
        return self.num_cards;
    }

    pub fn split_number(&self) -> uint {
        return self.splits_done + self.splits_to_solve;
    }

    pub fn add_card(&mut self, card: Card) {
        self.score += score_for_value(card.value());
        self.num_cards += 1;
        if card.value().index() == value::ACE.index() {
            self.ace_count += 1;
        }
        self.cards.push(card);
    }

    pub fn remove_card(&mut self, card: Card) {
        assert!(self.score >= score_for_value(card.value()));
        assert!(self.num_cards >= 1);
        self.score -= score_for_value(card.value());
        self.num_cards -= 1;
        if card.value().index() == value::ACE.index() {
            assert!(self.ace_count >= 1);
            self.ace_count -= 1;
        }
        for i in range(0, self.cards.len()) {
            let c = self.cards[i];
            if c.suit().index() == card.suit().index() && c.value().index() == card.value().index() {
                self.cards.swap_remove(i);
                return;
            }
        }
        panic!("Could not find the card in the hand");
    }

    pub fn new() -> BJHand {
        return BJHand{
            score: 0,
            ace_count: 0,
            splits_done: 0,
            splits_to_solve: 0,
            num_cards: 0,
            double_count: 0,
            cards: Vec::new(),
        }
    }
    pub fn new_with_cards(cards: &Vec<Card>) -> BJHand {
        let mut h = BJHand::new();
        for &c in cards.iter() {
            h.add_card(c);
        }
        return h;
    }
    pub fn new_from_deck(deck: &mut DirectShoe, values: &Vec<Value>) -> Option<BJHand> {
        let mut h = BJHand::new();
        for &v in values.iter() {
            match deck.remove(&v) {
                Some(c) => h.add_card(c),
                None => return None
            }
        }
        return Some(h);
    }
}

#[test]
fn test_hand() {
    use cards::suit;
    let mut h = BJHand::new();
    assert_eq!(0, h.score());
    assert_eq!(0, h.len());
    assert_eq!(false, h.is_soft());
    h.add_card(Card::new(value::TEN, suit::SPADE));
    assert_eq!(1, h.len());
    assert_eq!(10, h.score());
    assert_eq!(false, h.is_soft());
    h.add_card(Card::new(value::ACE, suit::SPADE));
    assert_eq!(2, h.len());
    assert_eq!(21, h.score());
    assert_eq!(true, h.is_soft());
    h.add_card(Card::new(value::TWO, suit::SPADE));
    assert_eq!(3, h.len());
    assert_eq!(13, h.score());
    assert_eq!(false, h.is_soft());
    assert_eq!(3, h.len());
    h.add_card(Card::new(value::KING, suit::SPADE));
    assert_eq!(4, h.len());
    assert_eq!(23, h.score());
    assert_eq!(false, h.is_soft());
    assert_eq!(4, h.len());
}
<commit_msg>Add start of split support<commit_after>use cards::value;
use cards::value::Value;
use cards::card::Card;
use std::fmt;
use shoe::shoe::DirectShoe;

pub const INDEX_TO_SCORE: [uint, ..13] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10];

pub fn score_for_value(v: &Value) -> uint {
    return INDEX_TO_SCORE[v.index()];
}

pub struct BJHand {
    score: uint,
    ace_count: uint,
    splits_done: uint,
    splits_to_solve: uint,
    num_cards: uint,
    double_count: uint,
    // The first two cards are very important for blackjack
    cards: Vec<Card>,
}

impl fmt::Show for BJHand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.cards)
    }
}

impl BJHand {
    pub fn cards(&self) -> &Vec<Card> {
        &self.cards
    }

    pub fn score(&self) -> uint {
        if self.is_soft() {
            return self.score + 10;
        } else {
            return self.score;
        }
    }

    pub fn is_soft(&self) -> bool {
        return self.ace_count > 0 && self.score + 10 <= 21;
    }

    pub fn split(&self) -> BJHand {
        assert_eq!(2, self.cards.len());
        assert_eq!(self.cards[0], self.cards[1]);
        return BJHand::new_split_hand(
            self.cards[0],
            self.splits_done + 1,
            self.splits_to_solve + 1);
    }

    pub fn double_count(&self) -> uint {
        self.double_count
    }

    pub fn add_double_count(&mut self) {
        self.double_count += 1
    }

    pub fn subtract_double_count(&mut self) {
        assert!(self.double_count >= 1);
        self.double_count -= 1;
    }

    pub fn len(&self) -> uint {
        assert_eq!(self.num_cards, self.cards.len());
        return self.num_cards;
    }

    pub fn split_number(&self) -> uint {
        return self.splits_done + self.splits_to_solve;
    }

    pub fn add_card(&mut self, card: Card) -> &mut BJHand {
        self.score += score_for_value(card.value());
        self.num_cards += 1;
        if card.value().index() == value::ACE.index() {
            self.ace_count += 1;
        }
        self.cards.push(card);
        self
    }

    pub fn remove_card(&mut self, card: Card) {
        assert!(self.score >= score_for_value(card.value()));
        assert!(self.num_cards >= 1);
        self.score -= score_for_value(card.value());
        self.num_cards -= 1;
        if card.value().index() == value::ACE.index() {
            assert!(self.ace_count >= 1);
            self.ace_count -= 1;
        }
        for i in range(0, self.cards.len()) {
            let c = self.cards[i];
            if c.suit().index() == card.suit().index() && c.value().index() == card.value().index() {
                self.cards.swap_remove(i);
                return;
            }
        }
        panic!("Could not find the card in the hand");
    }

    pub fn new_split_hand(card: Card, splits_done: uint, splits_to_solve: uint) -> BJHand {
        let mut h = BJHand {
            score: 0,
            ace_count: 0,
            splits_done: splits_done,
            splits_to_solve: splits_to_solve,
            num_cards: 0,
            double_count: 0,
            cards: Vec::new(),
        };
        h.add_card(card);
        h
    }

    pub fn new() -> BJHand {
        return BJHand{
            score: 0,
            ace_count: 0,
            splits_done: 0,
            splits_to_solve: 0,
            num_cards: 0,
            double_count: 0,
            cards: Vec::new(),
        }
    }

    pub fn new_with_cards(cards: &Vec<Card>) -> BJHand {
        let mut h = BJHand::new();
        for &c in cards.iter() {
            h.add_card(c);
        }
        return h;
    }

    pub fn new_from_deck(deck: &mut DirectShoe, values: &Vec<Value>) -> Option<BJHand> {
        let mut h = BJHand::new();
        for &v in values.iter() {
            match deck.remove(&v) {
                Some(c) => {
                    h.add_card(c);
                }
                None => return None
            }
        }
        return Some(h);
    }
}

#[test]
fn test_hand() {
    use cards::suit;
    let mut h = BJHand::new();
    assert_eq!(0, h.score());
    assert_eq!(0, h.len());
    assert_eq!(false, h.is_soft());
    h.add_card(Card::new(value::TEN, suit::SPADE));
    assert_eq!(1, h.len());
    assert_eq!(10, h.score());
    assert_eq!(false, h.is_soft());
    h.add_card(Card::new(value::ACE, suit::SPADE));
    assert_eq!(2, h.len());
    assert_eq!(21, h.score());
    assert_eq!(true, h.is_soft());
    h.add_card(Card::new(value::TWO, suit::SPADE));
    assert_eq!(3, h.len());
    assert_eq!(13, h.score());
    assert_eq!(false, h.is_soft());
    assert_eq!(3, h.len());
    h.add_card(Card::new(value::KING, suit::SPADE));
    assert_eq!(4, h.len());
    assert_eq!(23, h.score());
    assert_eq!(false, h.is_soft());
    assert_eq!(4, h.len());
}
<|endoftext|>