<commit_before>use libc::c_char;
use std::error;
use std::ffi::{CString, CStr, NulError};
use std::fmt;
use std::path::Path;
use rwops::RWops;

use GameControllerSubsystem;
use get_error;
use joystick;
use common::{validate_int, IntegerOrSdlError};

use sys::controller as ll;
use sys::event::{SDL_QUERY, SDL_ENABLE};

#[derive(Debug)]
pub enum AddMappingError {
    InvalidMapping(NulError),
    InvalidFilePath(String),
    SdlError(String),
}

impl fmt::Display for AddMappingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(ref e) => write!(f, "Null error: {}", e),
            InvalidFilePath(ref value) => write!(f, "Invalid file path ({})", value),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

impl error::Error for AddMappingError {
    fn description(&self) -> &str {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(_) => "invalid mapping",
            InvalidFilePath(_) => "invalid file path",
            SdlError(ref e) => e,
        }
    }
}

impl GameControllerSubsystem {
    /// Retreive the total number of attached joysticks *and* controllers identified by SDL.
    pub fn num_joysticks(&self) -> Result<u32, String> {
        let result = unsafe { ::sys::joystick::SDL_NumJoysticks() };

        if result >= 0 {
            Ok(result as u32)
        } else {
            Err(get_error())
        }
    }

    /// Return true if the joystick at index `id` is a game controller.
    #[inline]
    pub fn is_game_controller(&self, id: u32) -> bool {
        match validate_int(id, "id") {
            Ok(id) => unsafe { ll::SDL_IsGameController(id) != 0 },
            Err(_) => false
        }
    }

    /// Attempt to open the controller number `id` and return
    /// it. Controller IDs are the same as joystick IDs and the
    /// maximum number can be retreived using the `SDL_NumJoysticks`
    /// function.
    pub fn open(&self, id: u32) -> Result<GameController, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let id = try!(validate_int(id, "id"));
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };

        if controller.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(GameController {
                subsystem: self.clone(),
                raw: controller
            })
        }
    }

    /// Return the name of the controller at the given index.
    pub fn name_for_index(&self, index: u32) -> Result<String, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let index = try!(validate_int(index, "index"));
        let c_str = unsafe { ll::SDL_GameControllerNameForIndex(index) };

        if c_str.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(unsafe {
                CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
            })
        }
    }

    /// If state is `true` controller events are processed, otherwise
    /// they're ignored.
    pub fn set_event_state(&self, state: bool) {
        unsafe { ll::SDL_GameControllerEventState(state as i32) };
    }

    /// Return `true` if controller events are processed.
    pub fn event_state(&self) -> bool {
        unsafe { ll::SDL_GameControllerEventState(SDL_QUERY as i32)
                 == SDL_ENABLE as i32 }
    }

    /// Add a new mapping from a mapping string
    pub fn add_mapping(&self, mapping: &str)
            -> Result<MappingStatus, AddMappingError> {
        use self::AddMappingError::*;
        let mapping = match CString::new(mapping) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMapping(err)),
        };

        let result = unsafe { ll::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };

        match result {
            1 => Ok(MappingStatus::Added),
            0 => Ok(MappingStatus::Updated),
            _ => Err(SdlError(get_error())),
        }
    }

    /// Load mappings from a file
    pub fn load_mappings<P: AsRef<Path>>(&self, path: P)
            -> Result<i32, AddMappingError> {
        use self::AddMappingError::*;

        let file = match RWops::from_file(path, "r") {
            Ok(f) => f,
            Err(s) => return Err(InvalidFilePath(s))
        };

        let result = unsafe { ll::SDL_GameControllerAddMappingsFromRW(file.raw(), 0) };

        match result {
            -1 => Err(SdlError(get_error())),
            _ => Ok(result)
        }
    }

    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> Result<String, String> {
        let c_str = unsafe { ll::SDL_GameControllerMappingForGUID(guid.raw()) };

        c_str_to_string_or_err(c_str)
    }

    #[inline]
    /// Force controller update when not using the event loop
    pub fn update(&self) {
        unsafe { ll::SDL_GameControllerUpdate() };
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Axis {
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

impl Axis {
    /// Return the Axis from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(axis: &str) -> Option<Axis> {
        let id = match CString::new(axis) {
            Ok(axis) => unsafe { ll::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_AXIS_INVALID
        };

        Axis::from_ll(id)
    }

    /// Return a string for a given axis in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let axis = self as ll::SDL_GameControllerAxis;

        let string = unsafe { ll::SDL_GameControllerGetStringForAxis(axis) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerAxis) -> Option<Axis> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_AXIS_INVALID      => return None,
            ll::SDL_CONTROLLER_AXIS_LEFTX        => Axis::LeftX,
            ll::SDL_CONTROLLER_AXIS_LEFTY        => Axis::LeftY,
            ll::SDL_CONTROLLER_AXIS_RIGHTX       => Axis::RightX,
            ll::SDL_CONTROLLER_AXIS_RIGHTY       => Axis::RightY,
            ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => Axis::TriggerLeft,
            ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
            _ => panic!("unhandled controller axis")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerAxis {
        match *self {
            Axis::LeftX => ll::SDL_CONTROLLER_AXIS_LEFTX,
            Axis::LeftY => ll::SDL_CONTROLLER_AXIS_LEFTY,
            Axis::RightX => ll::SDL_CONTROLLER_AXIS_RIGHTX,
            Axis::RightY => ll::SDL_CONTROLLER_AXIS_RIGHTY,
            Axis::TriggerLeft => ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
            Axis::TriggerRight => ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Button {
    A             = ll::SDL_CONTROLLER_BUTTON_A,
    B             = ll::SDL_CONTROLLER_BUTTON_B,
    X             = ll::SDL_CONTROLLER_BUTTON_X,
    Y             = ll::SDL_CONTROLLER_BUTTON_Y,
    Back          = ll::SDL_CONTROLLER_BUTTON_BACK,
    Guide         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    Start         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStick     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStick    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulder  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulder = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUp        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDown      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeft      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRight     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

impl Button {
    /// Return the Button from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(button: &str) -> Option<Button> {
        let id = match CString::new(button) {
            Ok(button) => unsafe { ll::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_BUTTON_INVALID
        };

        Button::from_ll(id)
    }

    /// Return a string for a given button in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let button = self as ll::SDL_GameControllerButton;

        let string = unsafe { ll::SDL_GameControllerGetStringForButton(button) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerButton) -> Option<Button> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_BUTTON_INVALID       => return None,
            ll::SDL_CONTROLLER_BUTTON_A             => Button::A,
            ll::SDL_CONTROLLER_BUTTON_B             => Button::B,
            ll::SDL_CONTROLLER_BUTTON_X             => Button::X,
            ll::SDL_CONTROLLER_BUTTON_Y             => Button::Y,
            ll::SDL_CONTROLLER_BUTTON_BACK          => Button::Back,
            ll::SDL_CONTROLLER_BUTTON_GUIDE         => Button::Guide,
            ll::SDL_CONTROLLER_BUTTON_START         => Button::Start,
            ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => Button::LeftStick,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => Button::RightStick,
            ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => Button::LeftShoulder,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => Button::RightShoulder,
            ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => Button::DPadUp,
            ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => Button::DPadDown,
            ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => Button::DPadLeft,
            ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => Button::DPadRight,
            _ => panic!("unhandled controller button")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerButton {
        match *self {
            Button::A             => ll::SDL_CONTROLLER_BUTTON_A,
            Button::B             => ll::SDL_CONTROLLER_BUTTON_B,
            Button::X             => ll::SDL_CONTROLLER_BUTTON_X,
            Button::Y             => ll::SDL_CONTROLLER_BUTTON_Y,
            Button::Back          => ll::SDL_CONTROLLER_BUTTON_BACK,
            Button::Guide         => ll::SDL_CONTROLLER_BUTTON_GUIDE,
            Button::Start         => ll::SDL_CONTROLLER_BUTTON_START,
            Button::LeftStick     => ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
            Button::RightStick    => ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
            Button::LeftShoulder  => ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
            Button::RightShoulder => ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
            Button::DPadUp        => ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
            Button::DPadDown      => ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
            Button::DPadLeft      => ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
            Button::DPadRight     => ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
        }
    }
}

/// Possible return values for `add_mapping`
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MappingStatus {
    Added   = 1,
    Updated = 0,
}

/// Wrapper around the SDL_GameController object
pub struct GameController {
    subsystem: GameControllerSubsystem,
    raw: *mut ll::SDL_GameController
}

impl GameController {
    #[inline]
    pub fn subsystem(&self) -> &GameControllerSubsystem { &self.subsystem }

    /// Return the name of the controller or an empty string if no
    /// name is found.
    pub fn name(&self) -> String {
        let name = unsafe { ll::SDL_GameControllerName(self.raw) };

        c_str_to_string(name)
    }

    /// Return a String describing the controller's button and axis
    /// mappings
    pub fn mapping(&self) -> String {
        let mapping = unsafe { ll::SDL_GameControllerMapping(self.raw) };

        c_str_to_string(mapping)
    }

    /// Return true if the controller has been opened and currently
    /// connected.
    pub fn attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }

    /// Return the joystick id of this controller
    pub fn instance_id(&self) -> i32 {
        let result = unsafe {
          let joystick = ll::SDL_GameControllerGetJoystick(self.raw);
          ::sys::joystick::SDL_JoystickInstanceID(joystick)
        };

        if result < 0 {
            // Should only fail if the joystick is NULL.
            panic!(get_error())
        } else {
            result
        }
    }

    /// Get the position of the given `axis`
    pub fn axis(&self, axis: Axis) -> i16 {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let axis = axis as ll::SDL_GameControllerAxis;

        unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) }
    }

    /// Returns `true` if `button` is pressed.
    pub fn button(&self, button: Button) -> bool {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let button = button as ll::SDL_GameControllerButton;

        unsafe { ll::SDL_GameControllerGetButton(self.raw, button) != 0 }
    }
}

impl Drop for GameController {
    fn drop(&mut self) {
        unsafe { ll::SDL_GameControllerClose(self.raw) }
    }
}

/// Convert C string `c_str` to a String. Return an empty string if
/// c_str is NULL.
fn c_str_to_string(c_str: *const c_char) -> String {
    if c_str.is_null() {
        String::new()
    } else {
        unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        }
    }
}

/// Convert C string `c_str` to a String. Return an SDL error if
/// `c_str` is NULL.
fn c_str_to_string_or_err(c_str: *const c_char) -> Result<String, String> {
    if c_str.is_null() {
        Err(get_error())
    } else {
        Ok(unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        })
    }
}
<commit_msg>Clarify GameController method documentation<commit_after>use libc::c_char;
use std::error;
use std::ffi::{CString, CStr, NulError};
use std::fmt;
use std::path::Path;
use rwops::RWops;

use GameControllerSubsystem;
use get_error;
use joystick;
use common::{validate_int, IntegerOrSdlError};

use sys::controller as ll;
use sys::event::{SDL_QUERY, SDL_ENABLE};

#[derive(Debug)]
pub enum AddMappingError {
    InvalidMapping(NulError),
    InvalidFilePath(String),
    SdlError(String),
}

impl fmt::Display for AddMappingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(ref e) => write!(f, "Null error: {}", e),
            InvalidFilePath(ref value) => write!(f, "Invalid file path ({})", value),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

impl error::Error for AddMappingError {
    fn description(&self) -> &str {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(_) => "invalid mapping",
            InvalidFilePath(_) => "invalid file path",
            SdlError(ref e) => e,
        }
    }
}

impl GameControllerSubsystem {
    /// Retreive the total number of attached joysticks *and* controllers identified by SDL.
    pub fn num_joysticks(&self) -> Result<u32, String> {
        let result = unsafe { ::sys::joystick::SDL_NumJoysticks() };

        if result >= 0 {
            Ok(result as u32)
        } else {
            Err(get_error())
        }
    }

    /// Return true if the joystick at index `joystick_index` is a game controller.
    #[inline]
    pub fn is_game_controller(&self, joystick_index: u32) -> bool {
        match validate_int(joystick_index, "joystick_index") {
            Ok(joystick_index) => unsafe { ll::SDL_IsGameController(joystick_index) != 0 },
            Err(_) => false
        }
    }

    /// Attempt to open the controller ad index `joystick_index` and return it.
    /// Controller IDs are the same as joystick IDs and the maximum number can
    /// be retreived using the `SDL_NumJoysticks` function.
    pub fn open(&self, joystick_index: u32) -> Result<GameController, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let joystick_index = try!(validate_int(joystick_index, "joystick_index"));
        let controller = unsafe { ll::SDL_GameControllerOpen(joystick_index) };

        if controller.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(GameController {
                subsystem: self.clone(),
                raw: controller
            })
        }
    }

    /// Return the name of the controller at index `joystick_index`.
    pub fn name_for_index(&self, joystick_index: u32) -> Result<String, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let joystick_index = try!(validate_int(joystick_index, "joystick_index"));
        let c_str = unsafe { ll::SDL_GameControllerNameForIndex(joystick_index) };

        if c_str.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(unsafe {
                CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
            })
        }
    }

    /// If state is `true` controller events are processed, otherwise
    /// they're ignored.
    pub fn set_event_state(&self, state: bool) {
        unsafe { ll::SDL_GameControllerEventState(state as i32) };
    }

    /// Return `true` if controller events are processed.
    pub fn event_state(&self) -> bool {
        unsafe { ll::SDL_GameControllerEventState(SDL_QUERY as i32)
                 == SDL_ENABLE as i32 }
    }

    /// Add a new mapping from a mapping string
    pub fn add_mapping(&self, mapping: &str)
            -> Result<MappingStatus, AddMappingError> {
        use self::AddMappingError::*;
        let mapping = match CString::new(mapping) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMapping(err)),
        };

        let result = unsafe { ll::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };

        match result {
            1 => Ok(MappingStatus::Added),
            0 => Ok(MappingStatus::Updated),
            _ => Err(SdlError(get_error())),
        }
    }

    /// Load mappings from a file
    pub fn load_mappings<P: AsRef<Path>>(&self, path: P)
            -> Result<i32, AddMappingError> {
        use self::AddMappingError::*;

        let file = match RWops::from_file(path, "r") {
            Ok(f) => f,
            Err(s) => return Err(InvalidFilePath(s))
        };

        let result = unsafe { ll::SDL_GameControllerAddMappingsFromRW(file.raw(), 0) };

        match result {
            -1 => Err(SdlError(get_error())),
            _ => Ok(result)
        }
    }

    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> Result<String, String> {
        let c_str = unsafe { ll::SDL_GameControllerMappingForGUID(guid.raw()) };

        c_str_to_string_or_err(c_str)
    }

    #[inline]
    /// Force controller update when not using the event loop
    pub fn update(&self) {
        unsafe { ll::SDL_GameControllerUpdate() };
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Axis {
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

impl Axis {
    /// Return the Axis from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(axis: &str) -> Option<Axis> {
        let id = match CString::new(axis) {
            Ok(axis) => unsafe { ll::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_AXIS_INVALID
        };

        Axis::from_ll(id)
    }

    /// Return a string for a given axis in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let axis = self as ll::SDL_GameControllerAxis;

        let string = unsafe { ll::SDL_GameControllerGetStringForAxis(axis) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerAxis) -> Option<Axis> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_AXIS_INVALID      => return None,
            ll::SDL_CONTROLLER_AXIS_LEFTX        => Axis::LeftX,
            ll::SDL_CONTROLLER_AXIS_LEFTY        => Axis::LeftY,
            ll::SDL_CONTROLLER_AXIS_RIGHTX       => Axis::RightX,
            ll::SDL_CONTROLLER_AXIS_RIGHTY       => Axis::RightY,
            ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => Axis::TriggerLeft,
            ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
            _ => panic!("unhandled controller axis")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerAxis {
        match *self {
            Axis::LeftX => ll::SDL_CONTROLLER_AXIS_LEFTX,
            Axis::LeftY => ll::SDL_CONTROLLER_AXIS_LEFTY,
            Axis::RightX => ll::SDL_CONTROLLER_AXIS_RIGHTX,
            Axis::RightY => ll::SDL_CONTROLLER_AXIS_RIGHTY,
            Axis::TriggerLeft => ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
            Axis::TriggerRight => ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Button {
    A             = ll::SDL_CONTROLLER_BUTTON_A,
    B             = ll::SDL_CONTROLLER_BUTTON_B,
    X             = ll::SDL_CONTROLLER_BUTTON_X,
    Y             = ll::SDL_CONTROLLER_BUTTON_Y,
    Back          = ll::SDL_CONTROLLER_BUTTON_BACK,
    Guide         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    Start         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStick     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStick    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulder  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulder = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUp        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDown      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeft      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRight     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

impl Button {
    /// Return the Button from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(button: &str) -> Option<Button> {
        let id = match CString::new(button) {
            Ok(button) => unsafe { ll::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_BUTTON_INVALID
        };

        Button::from_ll(id)
    }

    /// Return a string for a given button in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let button = self as ll::SDL_GameControllerButton;

        let string = unsafe { ll::SDL_GameControllerGetStringForButton(button) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerButton) -> Option<Button> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_BUTTON_INVALID       => return None,
            ll::SDL_CONTROLLER_BUTTON_A             => Button::A,
            ll::SDL_CONTROLLER_BUTTON_B             => Button::B,
            ll::SDL_CONTROLLER_BUTTON_X             => Button::X,
            ll::SDL_CONTROLLER_BUTTON_Y             => Button::Y,
            ll::SDL_CONTROLLER_BUTTON_BACK          => Button::Back,
            ll::SDL_CONTROLLER_BUTTON_GUIDE         => Button::Guide,
            ll::SDL_CONTROLLER_BUTTON_START         => Button::Start,
            ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => Button::LeftStick,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => Button::RightStick,
            ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => Button::LeftShoulder,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => Button::RightShoulder,
            ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => Button::DPadUp,
            ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => Button::DPadDown,
            ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => Button::DPadLeft,
            ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => Button::DPadRight,
            _ => panic!("unhandled controller button")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerButton {
        match *self {
            Button::A             => ll::SDL_CONTROLLER_BUTTON_A,
            Button::B             => ll::SDL_CONTROLLER_BUTTON_B,
            Button::X             => ll::SDL_CONTROLLER_BUTTON_X,
            Button::Y             => ll::SDL_CONTROLLER_BUTTON_Y,
            Button::Back          => ll::SDL_CONTROLLER_BUTTON_BACK,
            Button::Guide         => ll::SDL_CONTROLLER_BUTTON_GUIDE,
            Button::Start         => ll::SDL_CONTROLLER_BUTTON_START,
            Button::LeftStick     => ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
            Button::RightStick    => ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
            Button::LeftShoulder  => ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
            Button::RightShoulder => ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
            Button::DPadUp        => ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
            Button::DPadDown      => ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
            Button::DPadLeft      => ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
            Button::DPadRight     => ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
        }
    }
}

/// Possible return values for `add_mapping`
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MappingStatus {
    Added   = 1,
    Updated = 0,
}

/// Wrapper around the SDL_GameController object
pub struct GameController {
    subsystem: GameControllerSubsystem,
    raw: *mut ll::SDL_GameController
}

impl GameController {
    #[inline]
    pub fn subsystem(&self) -> &GameControllerSubsystem { &self.subsystem }

    /// Return the name of the controller or an empty string if no
    /// name is found.
    pub fn name(&self) -> String {
        let name = unsafe { ll::SDL_GameControllerName(self.raw) };

        c_str_to_string(name)
    }

    /// Return a String describing the controller's button and axis
    /// mappings
    pub fn mapping(&self) -> String {
        let mapping = unsafe { ll::SDL_GameControllerMapping(self.raw) };

        c_str_to_string(mapping)
    }

    /// Return true if the controller has been opened and currently
    /// connected.
    pub fn attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }

    /// Return the joystick instance id of this controller
    pub fn instance_id(&self) -> i32 {
        let result = unsafe {
          let joystick = ll::SDL_GameControllerGetJoystick(self.raw);
          ::sys::joystick::SDL_JoystickInstanceID(joystick)
        };

        if result < 0 {
            // Should only fail if the joystick is NULL.
            panic!(get_error())
        } else {
            result
        }
    }

    /// Get the position of the given `axis`
    pub fn axis(&self, axis: Axis) -> i16 {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let axis = axis as ll::SDL_GameControllerAxis;

        unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) }
    }

    /// Returns `true` if `button` is pressed.
    pub fn button(&self, button: Button) -> bool {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let button = button as ll::SDL_GameControllerButton;

        unsafe { ll::SDL_GameControllerGetButton(self.raw, button) != 0 }
    }
}

impl Drop for GameController {
    fn drop(&mut self) {
        unsafe { ll::SDL_GameControllerClose(self.raw) }
    }
}

/// Convert C string `c_str` to a String. Return an empty string if
/// c_str is NULL.
fn c_str_to_string(c_str: *const c_char) -> String {
    if c_str.is_null() {
        String::new()
    } else {
        unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        }
    }
}

/// Convert C string `c_str` to a String. Return an SDL error if
/// `c_str` is NULL.
fn c_str_to_string_or_err(c_str: *const c_char) -> Result<String, String> {
    if c_str.is_null() {
        Err(get_error())
    } else {
        Ok(unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        })
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashSet};
use std::ascii::AsciiExt;
use std::cmp::Ordering;

use syntax::{ast, visit};
use syntax::parse::token;
use syntax::codemap::{Span, BytePos};
use syntax::attr::AttrMetaMethods;
use syntax::ast::NodeId;

use rustc::middle::privacy::ExportedItems;

use words;
use stem;

#[derive(Copy)]
pub struct Position {
    pub span: Span,
    pub id: NodeId,
}

impl Position {
    fn new(sp: Span, id: NodeId) -> Position {
        Position { span: sp, id: id, }
    }
}

impl PartialEq for Position {
    fn eq(&self, other: &Position) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl Eq for Position {}
impl PartialOrd for Position {
    fn partial_cmp(&self, other: &Position) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Position {
    fn cmp(&self, other: &Position) -> Ordering {
        let Span { lo: BytePos(slo), hi: BytePos(shi), .. } = self.span;
        let Span { lo: BytePos(olo), hi: BytePos(ohi), .. } = other.span;
        // order by span, and then by ID.
        (slo, shi, self.id).cmp(&(olo, ohi, other.id))
    }
}

/// Keeps track of the reference dictionary and the misspelled words
/// through a traversal of the whole ast.
pub struct SpellingVisitor<'a> {
    /// The reference dictionary.
    words: &'a HashSet<String>,

    /// The truly exported items.
    exported: &'a ExportedItems,

    /// The misspelled words
    pub misspellings: BTreeMap<Position, Vec<String>>,

    /// Whether the traversal should only check documentation, not
    /// idents; gets controlled internally, e.g. for `extern` blocks.
    doc_only: bool
}

impl<'a> SpellingVisitor<'a> {
    /// ast::Create a new Spelling Visitor.
    pub fn new<'b>(words: &'b HashSet<String>,
                   exported: &'b ExportedItems) -> SpellingVisitor<'b> {
        SpellingVisitor {
            words: words,
            exported: exported,
            misspellings: BTreeMap::new(),
            doc_only: false
        }
    }

    /// Checks if the given string is a correct "word", without
    /// splitting it at all. Any word that isn't entirely alphabetic
    /// is automatically considered a proper word.
    fn raw_word_is_correct(&mut self, w: &str) -> bool {
        self.words.contains(w) ||
            (w.chars().all(|c| c.is_alphabetic()) && {
                let lower = w.to_ascii_lowercase();
                self.words.contains(&lower) ||
                self.stemmed_word_is_correct(&lower)
            })
    }

    fn stemmed_word_is_correct(&self, w: &str) -> bool {
        stem::get(w).ok().map_or(false, |s| self.words.contains(&s))
    }

    /// Check a word for correctness, including splitting `foo_bar`
    /// and `FooBar` into `foo` & `bar` and `Foo` & `Bar`
    /// respectively. This inserts any incorrect word(s) into the
    /// misspelling map.
    fn check_subwords(&mut self, w: &str, pos: Position) {
        for w in words::subwords(w) {
            if !self.raw_word_is_correct(w) {
                let w = w.to_string();
                self.misspellings.entry(pos).get().unwrap_or_else(|v| v.insert(vec![])).push(w);
            }
        }
    }

    /// Check a single ident for misspellings; possibly separating it
    /// into subwords.
    fn check_ident(&mut self, ident: ast::Ident, pos: Position) {
        if self.doc_only { return }

        // spooky action at a distance; extracts the string
        // representation from TLS.
        let word = token::get_ident(ident);
        // secret rust internals, e.g. __std_macros
        if word.starts_with("__") { return }

        // the ident itself is correct, so shortcircuit to avoid doing
        // any of the submatching done below.
        if self.raw_word_is_correct(&word) {
            return
        }

        self.check_subwords(&word, pos);
    }

    /// Check the #[doc="..."] (and the commment forms) attributes for
    /// spelling.
    fn check_doc_attrs(&mut self, attrs: &[ast::Attribute], id: NodeId) {
        for attr in attrs.iter() {
            if attr.check_name("doc") {
                match attr.value_str() {
                    Some(s) => self.check_subwords(&s, Position::new(attr.span, id)),
                    None => {}
                }
            }
        }
    }

    /// Spell-check a whole krate.
    pub fn check_crate(&mut self, krate: &ast::Crate) {
        self.check_doc_attrs(&krate.attrs, ast::CRATE_NODE_ID);
        visit::walk_crate(self, krate)
    }
}

// visits anything that could be visible to the outside world,
// e.g. documentation, pub fns, pub mods etc and checks their
// spelling.
impl<'a, 'v> visit::Visitor<'v> for SpellingVisitor<'a> {
    fn visit_foreign_item(&mut self, foreign_item: &ast::ForeignItem) {
        if self.exported.contains(&foreign_item.id) {
            // don't check the ident; there's nothing the user can do to
            // control the name.
            self.check_doc_attrs(&foreign_item.attrs, foreign_item.id);
        }
    }

    fn visit_item(&mut self, item: &ast::Item) {
        let is_impl = match item.node {
            ast::ItemImpl(..) => true,
            _ => false
        };
        let is_exported = self.exported.contains(&item.id);

        // checking names in impl headers is pointless: they're declared elsewhere.
        if is_exported && !is_impl {
            self.check_ident(item.ident, Position::new(item.span, item.id));
        }
        if is_exported {
            self.check_doc_attrs(&item.attrs, item.id);
        }

        match item.node {
            // no visitor method for enum variants so have to do it by
            // hand. This is probably (subtly or otherwise) incorrect
            // wrt to visibility.
            ast::ItemEnum(ref ed, _) => {
                for var in ed.variants.iter() {
                    if self.exported.contains(&var.node.id) {
                        self.check_ident(var.node.name, Position::new(var.span, var.node.id));
                        self.check_doc_attrs(&var.node.attrs, var.node.id);
                    }
                }
            }
            ast::ItemMod(..) | ast::ItemForeignMod(..) | ast::ItemStruct(..) => {
                visit::walk_item(self, item)
            }
            // impl Type { ... }
            ast::ItemImpl(_, _, _, ref trait_, _, ref items) => {
                let is_trait = trait_.is_some();
                for item in items.iter() {
                    self.check_doc_attrs(&item.attrs, item.id);
                    if !is_trait {
                        // name comes from the trait
                        self.check_ident(item.ident, Position::new(item.span, item.id));
                    }
                }
            }
            ast::ItemTrait(..) if item.vis == ast::Public => {
                visit::walk_item(self, item)
            }
            _ => {}
        }
    }

    fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
        self.check_doc_attrs(&trait_item.attrs, trait_item.id);
        self.check_ident(trait_item.ident, Position::new(trait_item.span, trait_item.id));
    }

    fn visit_struct_def(&mut self,
                        struct_definition: &ast::StructDef,
                        _identifier: ast::Ident,
                        _generics: &ast::Generics,
                        _node_id: ast::NodeId) {
        visit::walk_struct_def(self,
                               struct_definition)
    }
    fn visit_struct_field(&mut self, struct_field: &ast::StructField) {
        match struct_field.node.kind {
            ast::NamedField(ident, vis) => {
                match vis {
                    ast::Public => {
                        self.check_ident(ident,
                                         Position::new(struct_field.span, struct_field.node.id));
                        self.check_doc_attrs(&struct_field.node.attrs,
                                             struct_field.node.id);
                    }
                    ast::Inherited => {}
                }
            }
            ast::UnnamedField(_) => {}
        }

        // no need to recur; nothing below this level to check.
    }

    /// we're only interested in top-level things, so we can just
    /// ignore these entirely.
    fn visit_local(&mut self, _local: &ast::Local) {}
    fn visit_block(&mut self, _block: &ast::Block) {}
    fn visit_stmt(&mut self, _statement: &ast::Stmt) {}
    fn visit_arm(&mut self, _arm: &ast::Arm) {}
    fn visit_pat(&mut self, _pattern: &ast::Pat) {}
    fn visit_decl(&mut self, _declaration: &ast::Decl) {}
    fn visit_expr(&mut self, _expression: &ast::Expr) {}
    fn visit_expr_post(&mut self, _expression: &ast::Expr) {}
    fn visit_ty(&mut self, _typ: &ast::Ty) {}
    fn visit_generics(&mut self, _generics: &ast::Generics) {}
    fn visit_fn(&mut self,
                _function_kind: visit::FnKind,
                _function_declaration: &ast::FnDecl,
                _block: &ast::Block,
                _span: Span,
                _node_id: ast::NodeId) {}
}
<commit_msg>Remove unnecessary field.<commit_after>use std::collections::{BTreeMap, HashSet};
use std::ascii::AsciiExt;
use std::cmp::Ordering;

use syntax::{ast, visit};
use syntax::parse::token;
use syntax::codemap::{Span, BytePos};
use syntax::attr::AttrMetaMethods;
use syntax::ast::NodeId;

use rustc::middle::privacy::ExportedItems;

use words;
use stem;

#[derive(Copy)]
pub struct Position {
    pub span: Span,
    pub id: NodeId,
}

impl Position {
    fn new(sp: Span, id: NodeId) -> Position {
        Position { span: sp, id: id, }
    }
}

impl PartialEq for Position {
    fn eq(&self, other: &Position) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl Eq for Position {}
impl PartialOrd for Position {
    fn partial_cmp(&self, other: &Position) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Position {
    fn cmp(&self, other: &Position) -> Ordering {
        let Span { lo: BytePos(slo), hi: BytePos(shi), .. } = self.span;
        let Span { lo: BytePos(olo), hi: BytePos(ohi), .. } = other.span;
        // order by span, and then by ID.
        (slo, shi, self.id).cmp(&(olo, ohi, other.id))
    }
}

/// Keeps track of the reference dictionary and the misspelled words
/// through a traversal of the whole ast.
pub struct SpellingVisitor<'a> {
    /// The reference dictionary.
    words: &'a HashSet<String>,

    /// The truly exported items.
    exported: &'a ExportedItems,

    /// The misspelled words
    pub misspellings: BTreeMap<Position, Vec<String>>,
}

impl<'a> SpellingVisitor<'a> {
    /// ast::Create a new Spelling Visitor.
    pub fn new<'b>(words: &'b HashSet<String>,
                   exported: &'b ExportedItems) -> SpellingVisitor<'b> {
        SpellingVisitor {
            words: words,
            exported: exported,
            misspellings: BTreeMap::new(),
        }
    }

    /// Checks if the given string is a correct "word", without
    /// splitting it at all. Any word that isn't entirely alphabetic
    /// is automatically considered a proper word.
    fn raw_word_is_correct(&mut self, w: &str) -> bool {
        self.words.contains(w) ||
            (w.chars().all(|c| c.is_alphabetic()) && {
                let lower = w.to_ascii_lowercase();
                self.words.contains(&lower) ||
                self.stemmed_word_is_correct(&lower)
            })
    }

    fn stemmed_word_is_correct(&self, w: &str) -> bool {
        stem::get(w).ok().map_or(false, |s| self.words.contains(&s))
    }

    /// Check a word for correctness, including splitting `foo_bar`
    /// and `FooBar` into `foo` & `bar` and `Foo` & `Bar`
    /// respectively. This inserts any incorrect word(s) into the
    /// misspelling map.
    fn check_subwords(&mut self, w: &str, pos: Position) {
        for w in words::subwords(w) {
            if !self.raw_word_is_correct(w) {
                let w = w.to_string();
                self.misspellings.entry(pos).get().unwrap_or_else(|v| v.insert(vec![])).push(w);
            }
        }
    }

    /// Check a single ident for misspellings; possibly separating it
    /// into subwords.
    fn check_ident(&mut self, ident: ast::Ident, pos: Position) {
        // spooky action at a distance; extracts the string
        // representation from TLS.
        let word = token::get_ident(ident);
        // secret rust internals, e.g. __std_macros
        if word.starts_with("__") { return }

        // the ident itself is correct, so shortcircuit to avoid doing
        // any of the submatching done below.
        if self.raw_word_is_correct(&word) {
            return
        }

        self.check_subwords(&word, pos);
    }

    /// Check the #[doc="..."] (and the commment forms) attributes for
    /// spelling.
    fn check_doc_attrs(&mut self, attrs: &[ast::Attribute], id: NodeId) {
        for attr in attrs.iter() {
            if attr.check_name("doc") {
                match attr.value_str() {
                    Some(s) => self.check_subwords(&s, Position::new(attr.span, id)),
                    None => {}
                }
            }
        }
    }

    /// Spell-check a whole krate.
    pub fn check_crate(&mut self, krate: &ast::Crate) {
        self.check_doc_attrs(&krate.attrs, ast::CRATE_NODE_ID);
        visit::walk_crate(self, krate)
    }
}

// visits anything that could be visible to the outside world,
// e.g. documentation, pub fns, pub mods etc and checks their
// spelling.
impl<'a, 'v> visit::Visitor<'v> for SpellingVisitor<'a> {
    fn visit_foreign_item(&mut self, foreign_item: &ast::ForeignItem) {
        if self.exported.contains(&foreign_item.id) {
            // don't check the ident; there's nothing the user can do to
            // control the name.
            self.check_doc_attrs(&foreign_item.attrs, foreign_item.id);
        }
    }

    fn visit_item(&mut self, item: &ast::Item) {
        let is_impl = match item.node {
            ast::ItemImpl(..) => true,
            _ => false
        };
        let is_exported = self.exported.contains(&item.id);

        // checking names in impl headers is pointless: they're declared elsewhere.
        if is_exported && !is_impl {
            self.check_ident(item.ident, Position::new(item.span, item.id));
        }
        if is_exported {
            self.check_doc_attrs(&item.attrs, item.id);
        }

        match item.node {
            // no visitor method for enum variants so have to do it by
            // hand. This is probably (subtly or otherwise) incorrect
            // wrt to visibility.
            ast::ItemEnum(ref ed, _) => {
                for var in ed.variants.iter() {
                    if self.exported.contains(&var.node.id) {
                        self.check_ident(var.node.name, Position::new(var.span, var.node.id));
                        self.check_doc_attrs(&var.node.attrs, var.node.id);
                    }
                }
            }
            ast::ItemMod(..) | ast::ItemForeignMod(..) | ast::ItemStruct(..) => {
                visit::walk_item(self, item)
            }
            // impl Type { ... }
            ast::ItemImpl(_, _, _, ref trait_, _, ref items) => {
                let is_trait = trait_.is_some();
                for item in items.iter() {
                    self.check_doc_attrs(&item.attrs, item.id);
                    if !is_trait {
                        // name comes from the trait
                        self.check_ident(item.ident, Position::new(item.span, item.id));
                    }
                }
            }
            ast::ItemTrait(..) if item.vis == ast::Public => {
                visit::walk_item(self, item)
            }
            _ => {}
        }
    }

    fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
        self.check_doc_attrs(&trait_item.attrs, trait_item.id);
        self.check_ident(trait_item.ident, Position::new(trait_item.span, trait_item.id));
    }

    fn visit_struct_def(&mut self,
                        struct_definition: &ast::StructDef,
                        _identifier: ast::Ident,
                        _generics: &ast::Generics,
                        _node_id: ast::NodeId) {
        visit::walk_struct_def(self,
                               struct_definition)
    }
    fn visit_struct_field(&mut self, struct_field: &ast::StructField) {
        match struct_field.node.kind {
            ast::NamedField(ident, vis) => {
                match vis {
                    ast::Public => {
                        self.check_ident(ident,
                                         Position::new(struct_field.span, struct_field.node.id));
                        self.check_doc_attrs(&struct_field.node.attrs,
                                             struct_field.node.id);
                    }
                    ast::Inherited => {}
                }
            }
            ast::UnnamedField(_) => {}
        }

        // no need to recur; nothing below this level to check.
    }

    /// we're only interested in top-level things, so we can just
    /// ignore these entirely.
    fn visit_local(&mut self, _local: &ast::Local) {}
    fn visit_block(&mut self, _block: &ast::Block) {}
    fn visit_stmt(&mut self, _statement: &ast::Stmt) {}
    fn visit_arm(&mut self, _arm: &ast::Arm) {}
    fn visit_pat(&mut self, _pattern: &ast::Pat) {}
    fn visit_decl(&mut self, _declaration: &ast::Decl) {}
    fn visit_expr(&mut self, _expression: &ast::Expr) {}
    fn visit_expr_post(&mut self, _expression: &ast::Expr) {}
    fn visit_ty(&mut self, _typ: &ast::Ty) {}
    fn visit_generics(&mut self, _generics: &ast::Generics) {}
    fn visit_fn(&mut self,
                _function_kind: visit::FnKind,
                _function_declaration: &ast::FnDecl,
                _block: &ast::Block,
                _span: Span,
                _node_id: ast::NodeId) {}
}
<|endoftext|>


<commit_before>use assert_cmd::prelude::*;
use copy_dir::copy_dir;
use predicates::prelude::*;
use predicates::str::{contains, similar};
use std::error::Error;
use std::fs;
use std::io;
use std::path::PathBuf;
use std::process::Command;
use tempfile::{tempdir, TempDir};

pub struct TempRepo {
    dir: TempDir,
}

impl TempRepo {
    pub fn new() -> io::Result<TempRepo> {
        let dir = tempdir()?;
        copy_dir("tests/repo", dir.path().join("repo"))?;
        Ok(TempRepo { dir })
    }

    pub fn path(&self) -> PathBuf {
        self.dir.path().join("repo")
    }

    pub fn path_string(&self) -> String {
        self.path().to_string_lossy().to_string()
    }
}

const DIRECTORY: &str = "foo";
const EXISTING_SECRET: &str = "foo/@bar";
const NON_EXISTING_SECRET: &str = "foo/@new";
const INVALID_SECRET_PATH_DEEP: &str = "foo/@bar/@baz";
const NON_EXISTING_SECRET_DEEP: &str = "foo/bar/baz/@new";

// get

#[test]
fn get_no_repo() -> Result<(), Box<Error>> {
    let dir = tempdir()?;
    fs::write(dir.path().join("foo"), "".as_bytes())?;

    Command::cargo_bin("sala")?
        .current_dir(&dir)
        .args(&["get", "foo"])
        .assert()
        .failure()
        .stderr(similar("Run `sala init' first\n"));

    Ok(())
}

#[test]
fn get_wrong_passphrase() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["get", EXISTING_SECRET])
        .with_stdin()
        .buffer("this is wrong\n")
        .output()?
        .assert()
        .failure()
        .stderr(similar(
            "\
Enter the master passphrase:
Error: Unable to unlock the encryption key
",
        ));

    Ok(())
}

#[test]
fn get_not_found() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["get", NON_EXISTING_SECRET])
        .assert()
        .failure()
        .stderr(similar(format!(
            "\
Error: File does not exist or invalid: {}
",
            NON_EXISTING_SECRET
        )));

    Ok(())
}

#[test]
fn get_directory() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["get", DIRECTORY])
        .assert()
        .failure()
        .stderr(similar(format!(
            "\
Error: File does not exist or invalid: {}
",
            DIRECTORY
        )));

    Ok(())
}

#[test]
fn get_success() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["get", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .success()
        .stderr(similar("Enter the master passphrase: "))
        .stdout(similar(
            "
foo/@bar: baz

",
        ));

    Ok(())
}

#[test]
fn get_raw() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["-r", "get", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .success()
        .stderr(similar("Enter the master passphrase: "))
        .stdout(similar("baz\n"));

    Ok(())
}

#[test]
fn implicit_get() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .arg(EXISTING_SECRET)
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .success()
        .stderr(similar("Enter the master passphrase: "))
        .stdout(similar(
            "
foo/@bar: baz

",
        ));

    Ok(())
}

#[test]
fn get_in_dir_does_not_exist() -> Result<(), Box<Error>> {
    let dir = tempdir()?.path().join("foo");
    Command::cargo_bin("sala")?
        .args(&["-C", &dir.to_string_lossy(), "get", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .failure()
        .stderr(contains("Error: Cannot change to directory: "));

    Ok(())
}

#[test]
fn get_in_dir_success() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .args(&["-C", &repo.path_string(), "get", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .success()
        .stderr(similar("Enter the master passphrase: "))
        .stdout(similar(
            "
foo/@bar: baz

",
        ));

    Ok(())
}

#[test]
fn get_saladir_env() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .env("SALADIR", repo.path_string())
        .args(&["get", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\n")
        .output()?
        .assert()
        .success()
        .stderr(similar("Enter the master passphrase: "))
        .stdout(similar(
            "
foo/@bar: baz

",
        ));

    Ok(())
}

// set

#[test]
fn set_no_repo() -> Result<(), Box<Error>> {
    let dir = tempdir()?;

    Command::cargo_bin("sala")?
        .current_dir(&dir)
        .args(&["set", "foobar"])
        .assert()
        .failure()
        .stderr(similar("Run `sala init' first").trim());

    Ok(())
}

#[test]
fn set_wrong_passphrase() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", NON_EXISTING_SECRET])
        .with_stdin()
        .buffer("this is wrong\n")
        .output()?
        .assert()
        .failure()
        .stderr(similar(
            "\
Enter the master passphrase:
Error: Unable to unlock the encryption key
",
        ));

    Ok(())
}

#[test]
fn set_target_is_directory() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", DIRECTORY])
        .assert()
        .failure()
        .stderr(similar(
            "\
Error: Target is a directory: foo
",
        ));

    Ok(())
}

#[test]
fn set_secrets_dont_match() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", NON_EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\nfoo\nother\n")
        .output()?
        .assert()
        .failure()
        .stderr(similar(
            "\
Enter the master passphrase: Type a new secret for foo/@new: Confirm:
Inputs did not match.
",
        ));

    assert_eq!(repo.path().join(NON_EXISTING_SECRET).exists(), false);
    Ok(())
}

#[test]
fn set_new_success() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", NON_EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\nfoo\nfoo\n")
        .output()?
        .assert()
        .success()
        .stderr(similar(
            "Enter the master passphrase: Type a new secret for foo/@new: Confirm: ",
        ));

    let secret_path = repo.path().join(NON_EXISTING_SECRET);
    assert_eq!(secret_path.is_file(), true);
    assert_eq!(secret_path.metadata()?.len() > 0, true);
    Ok(())
}

#[test]
fn set_new_cannot_create_parent_dirs() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", INVALID_SECRET_PATH_DEEP])
        .with_stdin()
        .buffer("qwerty\nfoo\nfoo\n")
        .output()?
        .assert()
        .failure()
        .stderr(similar("Error: Cannot create directory: foo/@bar\n"));

    Ok(())
}

#[test]
fn set_new_creates_parent_dirs() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", NON_EXISTING_SECRET_DEEP])
        .with_stdin()
        .buffer("qwerty\nfoo\nfoo\n")
        .output()?
        .assert()
        .success()
        .stderr(similar(
            "Enter the master passphrase: Type a new secret for foo/bar/baz/@new: Confirm: ",
        ));

    let secret_path = repo.path().join(NON_EXISTING_SECRET_DEEP);
    assert_eq!(secret_path.is_file(), true);
    assert_eq!(secret_path.metadata()?.len() > 0, true);
    Ok(())
}

#[test]
fn set_replace_existing_success() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .args(&["set", EXISTING_SECRET])
        .with_stdin()
        .buffer("qwerty\nquux\nquux\n")
        .output()?
        .assert()
        .success()
        .stderr(similar(
            "Enter the master passphrase: Type a new secret for foo/@bar: Confirm: ",
        ));

    let secret_path = repo.path().join(EXISTING_SECRET);
    assert_eq!(secret_path.is_file(), true);
    assert_eq!(secret_path.metadata()?.len() > 0, true);
    Ok(())
}

#[test]
fn implicit_set() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .arg(NON_EXISTING_SECRET)
        .with_stdin()
        .buffer("qwerty\nfoo\nfoo\n")
        .output()?
        .assert()
        .success()
        .stderr(similar(
            "Enter the master passphrase: Type a new secret for foo/@new: Confirm: ",
        ));

    let secret_path = repo.path().join(NON_EXISTING_SECRET);
    assert_eq!(secret_path.is_file(), true);
    assert_eq!(secret_path.metadata()?.len() > 0, true);
    Ok(())
}

// init

#[test]
fn init_already_initialized() -> Result<(), Box<Error>> {
    let repo = TempRepo::new()?;
    Command::cargo_bin("sala")?
        .current_dir(repo.path())
        .arg("init")
        .output()?
        .assert()
        .failure()
        .stderr(similar("Error: The master key already exists\n"));

    Ok(())
}

#[test]
fn init_passphrases_dont_match() -> Result<(), Box<Error>> {
    let dir = tempdir()?;
    Command::cargo_bin("sala")?
        .current_dir(dir.path())
        .arg("init")
        .with_stdin()
        .buffer("foobar\nquux\n")
        .output()?
        .assert()
        .failure()
        .stderr(similar(
            "\
Enter a master passphrase: Confirm:
Inputs did not match.
",
        ));

    assert_eq!(dir.path().join(".sala/key").exists(), false);
    Ok(())
}

#[test]
fn init_success() -> Result<(), Box<Error>> {
    let dir = tempdir()?;
    Command::cargo_bin("sala")?
        .current_dir(dir.path())
        .arg("init")
        .with_stdin()
        .buffer("foobar\nfoobar\n")
        .output()?
        .assert()
        .success()
        .stdout(similar(
            "\
Please pick a master passphrase. It is used to encrypt a very long
random key, which in turn is used to encrypt all the private data in
this directory.

Make sure you remember the master passphrase and that it's strong
enough for your privacy needs.

Generating a master key (512 bits)... done
",
        ))
        .stderr(similar("Enter a master passphrase: Confirm: "));

    assert_eq!(dir.path().join(".sala/key").metadata()?.len() > 0, true);
    Ok(())
}

// misc

#[test]
fn no_args() -> Result<(), Box<Error>> {
    let dir = tempdir()?;
    Command::cargo_bin("sala")?
        .current_dir(&dir)
        .assert()
        .failure()
        .stderr(similar("Try `sala --help'\n"));

    Ok(())
}
<commit_msg>Refactor tests to use common setup (Rust)<commit_after>use assert_cmd::prelude::*;
use copy_dir::copy_dir;
use predicates::prelude::*;
use predicates::str::{contains, similar};
use std::error::Error;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::{tempdir, TempDir};

// helpers

pub struct TempRepo {
    dir: TempDir,
}

impl TempRepo {
    pub fn new() -> io::Result<TempRepo> {
        let dir = tempdir()?;
        copy_dir("tests/repo", dir.path().join("repo"))?;
        Ok(TempRepo { dir })
    }

    pub fn path(&self) -> PathBuf {
        self.dir.path().join("repo")
    }

    pub fn path_string(&self) -> String {
        self.path().to_string_lossy().to_string()
    }
}

fn run_test<T>(test_fn: T) -> Result<(), Box<Error>>
where
    T: FnOnce(&mut Command, &Path, &TempRepo) -> Result<(), Box<Error>>,
{
    let empty_dir = tempdir()?;
    let repo = TempRepo::new()?;
    let mut cmd = Command::cargo_bin("sala")?;
    test_fn(&mut cmd, &empty_dir.path(), &repo)
}

const DIRECTORY: &str = "foo";
const EXISTING_SECRET: &str = "foo/@bar";
const NON_EXISTING_SECRET: &str = "foo/@new";
const INVALID_SECRET_PATH_DEEP: &str = "foo/@bar/@baz";
const NON_EXISTING_SECRET_DEEP: &str = "foo/bar/baz/@new";

// get

#[test]
fn get_no_repo() -> Result<(), Box<Error>> {
    run_test(|cmd, dir, _| {
        fs::write(dir.join("foo"), "".as_bytes())?;

        cmd.current_dir(&dir)
            .args(&["get", "foo"])
            .assert()
            .failure()
            .stderr(similar("Run `sala init' first\n"));

        Ok(())
    })
}

#[test]
fn get_wrong_passphrase() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["get", EXISTING_SECRET])
            .with_stdin()
            .buffer("this is wrong\n")
            .output()?
            .assert()
            .failure()
            .stderr(similar(
                "\
Enter the master passphrase:
Error: Unable to unlock the encryption key
",
            ));

        Ok(())
    })
}

#[test]
fn get_not_found() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["get", NON_EXISTING_SECRET])
            .assert()
            .failure()
            .stderr(similar(format!(
                "\
Error: File does not exist or invalid: {}
",
                NON_EXISTING_SECRET
            )));

        Ok(())
    })
}

#[test]
fn get_directory() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["get", DIRECTORY])
            .assert()
            .failure()
            .stderr(similar(format!(
                "\
Error: File does not exist or invalid: {}
",
                DIRECTORY
            )));

        Ok(())
    })
}

#[test]
fn get_success() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["get", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .success()
            .stderr(similar("Enter the master passphrase: "))
            .stdout(similar(
                "
foo/@bar: baz

",
            ));

        Ok(())
    })
}

#[test]
fn get_raw() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["-r", "get", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .success()
            .stderr(similar("Enter the master passphrase: "))
            .stdout(similar("baz\n"));

        Ok(())
    })
}

#[test]
fn implicit_get() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .arg(EXISTING_SECRET)
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .success()
            .stderr(similar("Enter the master passphrase: "))
            .stdout(similar(
                "
foo/@bar: baz

",
            ));

        Ok(())
    })
}

#[test]
fn get_in_dir_does_not_exist() -> Result<(), Box<Error>> {
    run_test(|cmd, tmpdir, _| {
        let dir = tmpdir.join("foo");
        cmd.args(&["-C", &dir.to_string_lossy(), "get", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .failure()
            .stderr(contains("Error: Cannot change to directory: "));

        Ok(())
    })
}

#[test]
fn get_in_dir_success() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.args(&["-C", &repo.path_string(), "get", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .success()
            .stderr(similar("Enter the master passphrase: "))
            .stdout(similar(
                "
foo/@bar: baz

",
            ));

        Ok(())
    })
}

#[test]
fn get_saladir_env() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.env("SALADIR", repo.path_string())
            .args(&["get", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\n")
            .output()?
            .assert()
            .success()
            .stderr(similar("Enter the master passphrase: "))
            .stdout(similar(
                "
foo/@bar: baz

",
            ));

        Ok(())
    })
}

// set

#[test]
fn set_no_repo() -> Result<(), Box<Error>> {
    run_test(|cmd, dir, _| {
        cmd.current_dir(&dir)
            .args(&["set", "foobar"])
            .assert()
            .failure()
            .stderr(similar("Run `sala init' first").trim());

        Ok(())
    })
}

#[test]
fn set_wrong_passphrase() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", NON_EXISTING_SECRET])
            .with_stdin()
            .buffer("this is wrong\n")
            .output()?
            .assert()
            .failure()
            .stderr(similar(
                "\
Enter the master passphrase:
Error: Unable to unlock the encryption key
",
            ));

        Ok(())
    })
}

#[test]
fn set_target_is_directory() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", DIRECTORY])
            .assert()
            .failure()
            .stderr(similar(
                "\
Error: Target is a directory: foo
",
            ));

        Ok(())
    })
}

#[test]
fn set_secrets_dont_match() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", NON_EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\nfoo\nother\n")
            .output()?
            .assert()
            .failure()
            .stderr(similar(
                "\
Enter the master passphrase: Type a new secret for foo/@new: Confirm:
Inputs did not match.
",
            ));

        assert_eq!(repo.path().join(NON_EXISTING_SECRET).exists(), false);
        Ok(())
    })
}

#[test]
fn set_new_success() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", NON_EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\nfoo\nfoo\n")
            .output()?
            .assert()
            .success()
            .stderr(similar(
                "Enter the master passphrase: Type a new secret for foo/@new: Confirm: ",
            ));

        let secret_path = repo.path().join(NON_EXISTING_SECRET);
        assert_eq!(secret_path.is_file(), true);
        assert_eq!(secret_path.metadata()?.len() > 0, true);
        Ok(())
    })
}

#[test]
fn set_new_cannot_create_parent_dirs() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", INVALID_SECRET_PATH_DEEP])
            .with_stdin()
            .buffer("qwerty\nfoo\nfoo\n")
            .output()?
            .assert()
            .failure()
            .stderr(similar("Error: Cannot create directory: foo/@bar\n"));

        Ok(())
    })
}

#[test]
fn set_new_creates_parent_dirs() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", NON_EXISTING_SECRET_DEEP])
            .with_stdin()
            .buffer("qwerty\nfoo\nfoo\n")
            .output()?
            .assert()
            .success()
            .stderr(similar(
                "Enter the master passphrase: Type a new secret for foo/bar/baz/@new: Confirm: ",
            ));

        let secret_path = repo.path().join(NON_EXISTING_SECRET_DEEP);
        assert_eq!(secret_path.is_file(), true);
        assert_eq!(secret_path.metadata()?.len() > 0, true);
        Ok(())
    })
}

#[test]
fn set_replace_existing_success() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .args(&["set", EXISTING_SECRET])
            .with_stdin()
            .buffer("qwerty\nquux\nquux\n")
            .output()?
            .assert()
            .success()
            .stderr(similar(
                "Enter the master passphrase: Type a new secret for foo/@bar: Confirm: ",
            ));

        let secret_path = repo.path().join(EXISTING_SECRET);
        assert_eq!(secret_path.is_file(), true);
        assert_eq!(secret_path.metadata()?.len() > 0, true);
        Ok(())
    })
}

#[test]
fn implicit_set() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .arg(NON_EXISTING_SECRET)
            .with_stdin()
            .buffer("qwerty\nfoo\nfoo\n")
            .output()?
            .assert()
            .success()
            .stderr(similar(
                "Enter the master passphrase: Type a new secret for foo/@new: Confirm: ",
            ));

        let secret_path = repo.path().join(NON_EXISTING_SECRET);
        assert_eq!(secret_path.is_file(), true);
        assert_eq!(secret_path.metadata()?.len() > 0, true);
        Ok(())
    })
}

// init

#[test]
fn init_already_initialized() -> Result<(), Box<Error>> {
    run_test(|cmd, _, repo| {
        cmd.current_dir(repo.path())
            .arg("init")
            .output()?
            .assert()
            .failure()
            .stderr(similar("Error: The master key already exists\n"));

        Ok(())
    })
}

#[test]
fn init_passphrases_dont_match() -> Result<(), Box<Error>> {
    run_test(|cmd, dir, _| {
        cmd.current_dir(dir)
            .arg("init")
            .with_stdin()
            .buffer("foobar\nquux\n")
            .output()?
            .assert()
            .failure()
            .stderr(similar(
                "\
Enter a master passphrase: Confirm:
Inputs did not match.
",
            ));

        assert_eq!(dir.join(".sala/key").exists(), false);
        Ok(())
    })
}

#[test]
fn init_success() -> Result<(), Box<Error>> {
    run_test(|cmd, dir, _| {
        cmd.current_dir(dir)
            .arg("init")
            .with_stdin()
            .buffer("foobar\nfoobar\n")
            .output()?
            .assert()
            .success()
            .stdout(similar(
                "\
Please pick a master passphrase. It is used to encrypt a very long
random key, which in turn is used to encrypt all the private data in
this directory.

Make sure you remember the master passphrase and that it's strong
enough for your privacy needs.

Generating a master key (512 bits)... done
",
            ))
            .stderr(similar("Enter a master passphrase: Confirm: "));

        assert_eq!(dir.join(".sala/key").metadata()?.len() > 0, true);
        Ok(())
    })
}

// misc

#[test]
fn no_args() -> Result<(), Box<Error>> {
    run_test(|cmd, dir, _| {
        cmd.current_dir(dir)
            .assert()
            .failure()
            .stderr(similar("Try `sala --help'\n"));

        Ok(())
    })
}
<|endoftext|>


<commit_before>extern crate firebase;
extern crate url;
extern crate rustc_serialize;

use firebase::*;
use url::Url;

use std::sync::{Arc, Mutex};

#[test]
fn builds_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    assert_eq!(f.get_url(), "http://db.rifebass.com/?auth=deadbeaf");
}

#[test]
fn extends_auth_url() {
    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn double_extends_url() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_slashes() {
    let f = Firebase::authed("http://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());

    let f = Firebase::authed("http://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/").ok().unwrap();
    let f = f.at("/SpacePilot3000/").ok().unwrap();
    let url_now = "http://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_json_suffix() {
    let f = Firebase::new("http://db.rifebass.com").ok().unwrap();
    let f = f.at("0.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("8.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("7.json").ok().unwrap().at("2.json").ok().unwrap()
             .at("5.json").ok().unwrap().at("3.json").ok().unwrap();
    let url_now = "http://db.rifebass.com//0/1/1/8/9/9/9/8/8/1/9/9/9/1/1/9/7/2/5/3.json";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn test_ops() {
    let f = Firebase::new("http://db.fe/").ok().expect("url err");
    let f = f.at("lol").ok().expect("extend err");
    let req = f.end_at(13).limit_to_first(4).equal_to(8).shallow(false);
    let correct = Url::parse("http://db.fe//lol.json?limitToFirst=4&endAt=13&equalTo=8&shallow=false").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_auth_ops() {
    let f = Firebase::authed("db.fe/", "key").ok().expect("url err").at("lol").ok().unwrap();
    let req = f.order_by("pts").limit_to_last(5).start_at(8);

    let correct = Url::parse("https://db.fe/lol.json?auth=key&orderBy=pts&limitToLast=5&startAt=8").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_async_get() {
    let fb = Firebase::new("mybd.firebase.com").ok().unwrap();
    let db_ref = fb.at("Profiles/a9sdc8asd99acc/profile_img").ok().unwrap();

    let finished = Arc::new(Mutex::new(false));

    let marker = finished.clone();
    let thread = db_ref.get_async(move |_| {
        let mut finished = marker.lock().unwrap();
        *finished = true;
    });

    assert!(!*finished.lock().unwrap());
    thread.join().ok();
}

#[test]
fn test_ops_ctor() {
    let fb = Firebase::new("db.fb.com").ok().unwrap();
    let query = fb.ops(&FbOps {
        order_by:       Some("Hello World"),
        limit_to_first: Some(5),
        end_at:         Some(7),
        equal_to:       Some(3),
        shallow:        Some(true),
        format:         Some(true),
        .. FbOps::default()
    });

    let corr = Url::parse("https://db.fb.com/?limitToFirst=5&orderBy=Hello+World&equalTo=3&format=export&shallow=true&endAt=7").ok().unwrap();
    let this = Url::parse(&query.get_url()).ok().unwrap();
    assert_queries(&corr, &this);
}

#[test]
fn test_resp_json() {
    let response = Response {
        code: 200,
        body: "{
            \"id\":   \"mongo id\",
            \"data\": \"Hello World!\"
        }".to_string(),
    };

    let record = match response.json().ok().expect("Should've parsed json") {
        Json::Object(o) => o,
        _ => panic!("This shouldv'e been a object!"),
    };

    let data = record.get("data").expect("Should've had a data member");

    match data.clone() {
        Json::String(d) => assert_eq!("Hello World!", d),
        _ => panic!("This shouldv'e been a string!"),
    }
}

#[test]
fn test_resp_struct_easy() {
    let response = Response {
        code: 200,
        body: "{
            \"fizz\": 3,
            \"buzz\": 5
        }".to_string(),
    };

    let bee: FizzBuzz = response.parse().ok().expect("Should parse into FizzBuzz struct");

    assert_eq!(bee.fizz, 3);
    assert_eq!(bee.buzz, 5);
}

fn assert_queries(a: &Url, b: &Url) {
    let param_a = a.query_pairs().expect("Url should have query params.");
    let param_b = b.query_pairs().expect("Url should have query params.");

    assert_eq!(param_b.len(), param_a.len());

    for pair in param_a.iter() {
        assert!(param_b.contains(pair));
    }
}

#[derive(RustcDecodable)]
struct FizzBuzz {
    fizz: u32,
    buzz: u32,
}
<commit_msg>Changed tests to all use HTTPS<commit_after>extern crate firebase;
extern crate url;
extern crate rustc_serialize;

use firebase::*;
use url::Url;

use std::sync::{Arc, Mutex};

#[test]
fn builds_auth_url() {
    let f = Firebase::authed("https://db.rifebass.com/", "deadbeaf").ok().unwrap();
    assert_eq!(f.get_url(), "https://db.rifebass.com/?auth=deadbeaf");
}

#[test]
fn extends_auth_url() {
    let f = Firebase::authed("https://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/SpacePilot3000").ok().unwrap();
    let url_now = "https://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn double_extends_url() {
    let f = Firebase::authed("https://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000").ok().unwrap();
    let url_now = "https://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_slashes() {
    let f = Firebase::authed("https://db.rifebass.com", "deadbeaf").ok().unwrap();
    let f = f.at("futurama.json").ok().unwrap();
    let f = f.at("SpacePilot3000.json").ok().unwrap();
    let url_now = "https://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());

    let f = Firebase::authed("https://db.rifebass.com/", "deadbeaf").ok().unwrap();
    let f = f.at("/futurama/").ok().unwrap();
    let f = f.at("/SpacePilot3000/").ok().unwrap();
    let url_now = "https://db.rifebass.com//futurama/SpacePilot3000.json?auth=deadbeaf";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn handle_json_suffix() {
    let f = Firebase::new("https://db.rifebass.com").ok().unwrap();
    let f = f.at("0.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("8.json").ok().unwrap()
             .at("8.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("9.json").ok().unwrap().at("1.json").ok().unwrap()
             .at("1.json").ok().unwrap().at("9.json").ok().unwrap()
             .at("7.json").ok().unwrap().at("2.json").ok().unwrap()
             .at("5.json").ok().unwrap().at("3.json").ok().unwrap();
    let url_now = "https://db.rifebass.com//0/1/1/8/9/9/9/8/8/1/9/9/9/1/1/9/7/2/5/3.json";
    assert_eq!(url_now, f.get_url());
}

#[test]
fn test_ops() {
    let f = Firebase::new("https://db.fe/").ok().expect("url err");
    let f = f.at("lol").ok().expect("extend err");
    let req = f.end_at(13).limit_to_first(4).equal_to(8).shallow(false);
    let correct = Url::parse("https://db.fe//lol.json?limitToFirst=4&endAt=13&equalTo=8&shallow=false").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_auth_ops() {
    let f = Firebase::authed("https://db.fe/", "key").ok().expect("url err").at("lol").ok().unwrap();
    let req = f.order_by("pts").limit_to_last(5).start_at(8);

    let correct = Url::parse("https://db.fe/lol.json?auth=key&orderBy=pts&limitToLast=5&startAt=8").ok().unwrap();
    let generated = Url::parse(&req.get_url()).ok().unwrap();

    assert_queries(&correct, &generated);
}

#[test]
fn test_async_get() {
    let fb = Firebase::new("https://mybd.firebase.com").ok().unwrap();
    let db_ref = fb.at("Profiles/a9sdc8asd99acc/profile_img").ok().unwrap();

    let finished = Arc::new(Mutex::new(false));

    let marker = finished.clone();
    let thread = db_ref.get_async(move |_| {
        let mut finished = marker.lock().unwrap();
        *finished = true;
    });

    assert!(!*finished.lock().unwrap());
    thread.join().ok();
}

#[test]
fn test_ops_ctor() {
    let fb = Firebase::new("https://db.fb.com").ok().unwrap();
    let query = fb.ops(&FbOps {
        order_by:       Some("Hello World"),
        limit_to_first: Some(5),
        end_at:         Some(7),
        equal_to:       Some(3),
        shallow:        Some(true),
        format:         Some(true),
        .. FbOps::default()
    });

    let corr = Url::parse("https://db.fb.com/?limitToFirst=5&orderBy=Hello+World&equalTo=3&format=export&shallow=true&endAt=7").ok().unwrap();
    let this = Url::parse(&query.get_url()).ok().unwrap();
    assert_queries(&corr, &this);
}

#[test]
fn test_resp_json() {
    let response = Response {
        code: 200,
        body: "{
            \"id\":   \"mongo id\",
            \"data\": \"Hello World!\"
        }".to_string(),
    };

    let record = match response.json().ok().expect("Should've parsed json") {
        Json::Object(o) => o,
        _ => panic!("This shouldv'e been a object!"),
    };

    let data = record.get("data").expect("Should've had a data member");

    match data.clone() {
        Json::String(d) => assert_eq!("Hello World!", d),
        _ => panic!("This shouldv'e been a string!"),
    }
}

#[test]
fn test_resp_struct_easy() {
    let response = Response {
        code: 200,
        body: "{
            \"fizz\": 3,
            \"buzz\": 5
        }".to_string(),
    };

    let bee: FizzBuzz = response.parse().ok().expect("Should parse into FizzBuzz struct");

    assert_eq!(bee.fizz, 3);
    assert_eq!(bee.buzz, 5);
}

fn assert_queries(a: &Url, b: &Url) {
    let param_a = a.query_pairs().expect("Url should have query params.");
    let param_b = b.query_pairs().expect("Url should have query params.");

    assert_eq!(param_b.len(), param_a.len());

    for pair in param_a.iter() {
        assert!(param_b.contains(pair));
    }
}

#[derive(RustcDecodable)]
struct FizzBuzz {
    fizz: u32,
    buzz: u32,
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<'a, HP>(&self, f: File, p: &Parser<HP>) ->
        Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let written = p.write(f.contents());
        if let Ok(string) = written {
            let path = self.build_filepath(&f);
            debug!("Writing file: {}", path);
            Ok(Ok(()))
        } else {
            Err(written.err().unwrap())
        }
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<'a, HP>(&self, f: File, p: &Parser<HP>)
        -> Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let contents = p.write(f.contents());

        if contents.is_err() {
            return Err(contents.err().unwrap());
        }

        let content = contents.unwrap();

        let path = self.build_filepath(&f);
        if let Err(_) = FSFile::open(&path) {
            return Ok(Err(StorageBackendError::new(
                String::from("File::open()"),
                format!("Tried to open '{}'", path),
                String::from("Tried to update contents of this file, though file doesn't exist"),
                None)))
        }

        if let Ok(mut file) = FSFile::create(&path) {
            if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
                return Ok(Err(StorageBackendError::new(
                    String::from("File::write()"),
                    format!("Tried to write '{}'", path),
                    String::from("Tried to write contents of this file, though operation did not succeed"),
                    Some(content))))
            }
        }

        Ok(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub data_dump: Option<String>   // Data dump, if any
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           explan: String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            explanation:    explan,
            data_dump:      data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<commit_msg>Add debug output in storage backend code<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        debug!("Getting files from {}", self.basepath);
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<'a, HP>(&self, f: File, p: &Parser<HP>) ->
        Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let written = p.write(f.contents());
        if let Ok(string) = written {
            let path = self.build_filepath(&f);
            debug!("Writing file: {}", path);
            debug!("    contents: {}", string);
            Ok(Ok(()))
        } else {
            debug!("Error parsing : {:?}", f.contents());
            Err(written.err().unwrap())
        }
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<'a, HP>(&self, f: File, p: &Parser<HP>)
        -> Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let contents = p.write(f.contents());

        if contents.is_err() {
            debug!("Error parsing contents: {:?}", f.contents());
            return Err(contents.err().unwrap());
        }

        let content = contents.unwrap();
        debug!("Success parsing content : {}", content);

        let path = self.build_filepath(&f);
        debug!("Trying to write to file at {}", path);
        if let Err(_) = FSFile::open(&path) {
            debug!("Error opening {}", path);
            return Ok(Err(StorageBackendError::new(
                String::from("File::open()"),
                format!("Tried to open '{}'", path),
                String::from("Tried to update contents of this file, though file doesn't exist"),
                None)))
        }

        if let Ok(mut file) = FSFile::create(&path) {
            if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
                debug!("Error writing to {}", path);
                return Ok(Err(StorageBackendError::new(
                    String::from("File::write()"),
                    format!("Tried to write '{}'", path),
                    String::from("Tried to write contents of this file, though operation did not succeed"),
                    Some(content))))
            }
        }

        debug!("Successfully written to file.");
        Ok(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        debug!("Building filepath for id '{}'", id);
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub data_dump: Option<String>   // Data dump, if any
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           explan: String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            explanation:    explan,
            data_dump:      data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<|endoftext|>