<commit_before>use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use geometry::CGPoint;
use event_source::{CGEventSource,CGEventSourceRef};

use libc;
use std::mem;
use std::ptr;

pub type CGKeyCode = libc::uint16_t;

/// Flags for events
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
bitflags! {
    #[repr(C)]
    pub struct CGEventFlags: u64 {
        const CGEventFlagNull = 0;

        // Device-independent modifier key bits.
        const CGEventFlagAlphaShift = 0x00010000;
        const CGEventFlagShift = 0x00020000;
        const CGEventFlagControl = 0x00040000;
        const CGEventFlagAlternate = 0x00080000;
        const CGEventFlagCommand = 0x00100000;

        // Special key identifiers.
        const CGEventFlagHelp = 0x00400000;
        const CGEventFlagSecondaryFn = 0x00800000;

        // Identifies key events from numeric keypad area on extended keyboards.
        const CGEventFlagNumericPad = 0x00200000;

        // Indicates if mouse/pen movement events are not being coalesced
        const CGEventFlagNonCoalesced = 0x00000100;
    }
}

/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
    Null = 0,

    // Mouse events.
    LeftMouseDown = 1,
    LeftMouseUp = 2,
    RightMouseDown = 3,
    RightMouseUp = 4,
    MouseMoved = 5,
    LeftMouseDragged = 6,
    RightMouseDragged = 7,

    // Keyboard events.
    KeyDown = 10,
    KeyUp = 11,
    FlagsChanged = 12,

    // Specialized control devices.
    ScrollWheel = 22,
    TabletPointer = 23,
    TabletProximity = 24,
    OtherMouseDown = 25,
    OtherMouseUp = 26,
    OtherMouseDragged = 27,

    // Out of band event types. These are delivered to the event tap callback
    // to notify it of unusual conditions that disable the event tap.
    TapDisabledByTimeout = 0xFFFFFFFE,
    TapDisabledByUserInput = 0xFFFFFFFF,
}

// Constants that specify buttons on a one, two, or three-button mouse.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
    Left,
    Right,
    Center,
}

/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
    HID,
    Session,
    AnnotatedSession,
}

// This is an enum due to zero-sized types warnings.
// For more details see https://github.com/rust-lang/rust/issues/27303
pub enum __CGEvent {}

pub type CGEventRef = *const __CGEvent;

pub struct CGEvent {
    obj: CGEventRef,
}

impl Clone for CGEvent {
    #[inline]
    fn clone(&self) -> CGEvent {
        unsafe {
            TCFType::wrap_under_get_rule(self.obj)
        }
    }
}

impl Drop for CGEvent {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.as_CFTypeRef();
            assert!(ptr != ptr::null());
            CFRelease(ptr);
        }
    }
}

impl TCFType<CGEventRef> for CGEvent {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CGEventRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CGEventRef) -> CGEvent {
        let reference: CGEventRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CGEventRef) -> CGEvent {
        CGEvent {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CGEventGetTypeID()
        }
    }
}

impl CGEvent {
    pub fn new(source: CGEventSource) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreate(source.as_concrete_TypeRef());
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_keyboard_event(
        source: CGEventSource,
        keycode: CGKeyCode,
        keydown: bool
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_mouse_event(
        source: CGEventSource,
        mouse_type: CGEventType,
        mouse_cursor_position: CGPoint,
        mouse_button: CGMouseButton
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouse_type,
                mouse_cursor_position, mouse_button);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn post(&self, tap_location: CGEventTapLocation) {
        unsafe {
            CGEventPost(tap_location, self.as_concrete_TypeRef());
        }
    }

    pub fn location(&self) -> CGPoint {
        unsafe {
            CGEventGetLocation(self.as_concrete_TypeRef())
        }
    }

    #[cfg(feature = "elcapitan")]
    pub fn post_to_pid(&self, pid: libc::pid_t) {
        unsafe {
            CGEventPostToPid(pid, self.as_concrete_TypeRef());
        }
    }

    pub fn set_flags(&self, flags: CGEventFlags) {
        unsafe {
            CGEventSetFlags(self.as_concrete_TypeRef(), flags);
        }
    }

    pub fn get_flags(&self) -> CGEventFlags {
        unsafe {
            CGEventGetFlags(self.as_concrete_TypeRef())
        }
    }

    pub fn set_type(&self, event_type: CGEventType) {
        unsafe {
            CGEventSetType(self.as_concrete_TypeRef(), event_type);
        }
    }

    pub fn get_type(&self) -> CGEventType {
        unsafe {
            CGEventGetType(self.as_concrete_TypeRef())
        }
    }

    pub fn set_string_from_utf16_unchecked(&self, buf: &[u16]) {
        let buflen = buf.len() as libc::c_ulong;
        unsafe {
            CGEventKeyboardSetUnicodeString(self.as_concrete_TypeRef(), buflen, buf.as_ptr());
        }
    }

    pub fn set_string(&self, string: &str) {
        let buf: Vec<u16> = string.encode_utf16().collect();
        self.set_string_from_utf16_unchecked(&buf);
    }
}

#[link(name = "ApplicationServices", kind = "framework")]
extern {
    /// Return the type identifier for the opaque type `CGEventRef'.
    fn CGEventGetTypeID() -> CFTypeID;

    /// Return a new event using the event source `source'. If `source' is NULL,
    /// the default source is used.
    fn CGEventCreate(source: CGEventSourceRef) -> CGEventRef;

    /// Return a new keyboard event.
    ///
    /// The event source may be taken from another event, or may be NULL. Based
    /// on the virtual key code values entered, the appropriate key down, key up,
    /// or flags changed events are generated.
    ///
    /// All keystrokes needed to generate a character must be entered, including
    /// SHIFT, CONTROL, OPTION, and COMMAND keys. For example, to produce a 'Z',
    /// the SHIFT key must be down, the 'z' key must go down, and then the SHIFT
    /// and 'z' key must be released:
    fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, keycode: CGKeyCode,
        keydown: bool) -> CGEventRef;

    /// Return a new mouse event.
    ///
    /// The event source may be taken from another event, or may be NULL.
    /// `mouseType' should be one of the mouse event types. `mouseCursorPosition'
    /// should be the position of the mouse cursor in global coordinates.
    /// `mouseButton' should be the button that's changing state; `mouseButton'
    /// is ignored unless `mouseType' is one of `kCGEventOtherMouseDown',
    /// `kCGEventOtherMouseDragged', or `kCGEventOtherMouseUp'.
    ///
    /// The current implemementation of the event system supports a maximum of
    /// thirty-two buttons. Mouse button 0 is the primary button on the mouse.
    /// Mouse button 1 is the secondary mouse button (right). Mouse button 2 is
    /// the center button, and the remaining buttons are in USB device order.
    fn CGEventCreateMouseEvent(source: CGEventSourceRef, mouseType: CGEventType,
        mouseCursorPosition: CGPoint, mouseButton: CGMouseButton) -> CGEventRef;

    /// Post an event into the event stream at a specified location.
    ///
    /// This function posts the specified event immediately before any event taps
    /// instantiated for that location, and the event passes through any such
    /// taps.
    fn CGEventPost(tapLocation: CGEventTapLocation, event: CGEventRef);

    #[cfg(feature = "elcapitan")]
    /// Post an event to a specified process ID
    fn CGEventPostToPid(pid: libc::pid_t, event: CGEventRef);

    /// Set the event flags of an event.
    fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);

    /// Return the event flags of an event.
    fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;

    /// Return the location of an event in global display coordinates.
    /// CGPointZero is returned if event is not a valid CGEventRef.
    fn CGEventGetLocation(event: CGEventRef) -> CGPoint;

    /// Set the event type of an event.
    fn CGEventSetType(event: CGEventRef, eventType: CGEventType);

    /// Return the event type of an event (left mouse down, for example).
    fn CGEventGetType(event: CGEventRef) -> CGEventType;

    /// Set the Unicode string associated with a keyboard event.
    ///
    /// By default, the system translates the virtual key code in a keyboard
    /// event into a Unicode string based on the keyboard ID in the event
    /// source.  This function allows you to manually override this string.
    /// Note that application frameworks may ignore the Unicode string in a
    /// keyboard event and do their own translation based on the virtual
    /// keycode and perceived event state.
    fn CGEventKeyboardSetUnicodeString(event: CGEventRef,
                                       length: libc::c_ulong,
                                       string: *const u16);
}
<commit_msg>Add universal key code constants<commit_after>use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use geometry::CGPoint;
use event_source::{CGEventSource,CGEventSourceRef};

use libc;
use std::mem;
use std::ptr;

pub type CGKeyCode = libc::uint16_t;

/// Flags for events
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
bitflags! {
    #[repr(C)]
    pub struct CGEventFlags: u64 {
        const CGEventFlagNull = 0;

        // Device-independent modifier key bits.
        const CGEventFlagAlphaShift = 0x00010000;
        const CGEventFlagShift = 0x00020000;
        const CGEventFlagControl = 0x00040000;
        const CGEventFlagAlternate = 0x00080000;
        const CGEventFlagCommand = 0x00100000;

        // Special key identifiers.
        const CGEventFlagHelp = 0x00400000;
        const CGEventFlagSecondaryFn = 0x00800000;

        // Identifies key events from numeric keypad area on extended keyboards.
        const CGEventFlagNumericPad = 0x00200000;

        // Indicates if mouse/pen movement events are not being coalesced
        const CGEventFlagNonCoalesced = 0x00000100;
    }
}

/// Key codes for keys that are independent of keyboard layout.
///
/// [Ref](https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX10.13.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h)
#[repr(C)]
pub struct KeyCode;
impl KeyCode {
    pub const RETURN: CGKeyCode = 0x24;
    pub const TAB: CGKeyCode = 0x30;
    pub const SPACE: CGKeyCode = 0x31;
    pub const DELETE: CGKeyCode = 0x33;
    pub const ESCAPE: CGKeyCode = 0x35;
    pub const COMMAND: CGKeyCode = 0x37;
    pub const SHIFT: CGKeyCode = 0x38;
    pub const CAPS_LOCK: CGKeyCode = 0x39;
    pub const OPTION: CGKeyCode = 0x3A;
    pub const CONTROL: CGKeyCode = 0x3B;
    pub const RIGHT_COMMAND: CGKeyCode = 0x36;
    pub const RIGHT_SHIFT: CGKeyCode = 0x3C;
    pub const RIGHT_OPTION: CGKeyCode = 0x3D;
    pub const RIGHT_CONTROL: CGKeyCode = 0x3E;
    pub const FUNCTION: CGKeyCode = 0x3F;
    pub const VOLUME_UP: CGKeyCode = 0x48;
    pub const VOLUME_DOWN: CGKeyCode = 0x49;
    pub const MUTE: CGKeyCode = 0x4A;
    pub const F1: CGKeyCode = 0x7A;
    pub const F2: CGKeyCode = 0x78;
    pub const F3: CGKeyCode = 0x63;
    pub const F4: CGKeyCode = 0x76;
    pub const F5: CGKeyCode = 0x60;
    pub const F6: CGKeyCode = 0x61;
    pub const F7: CGKeyCode = 0x62;
    pub const F8: CGKeyCode = 0x64;
    pub const F9: CGKeyCode = 0x65;
    pub const F10: CGKeyCode = 0x6D;
    pub const F11: CGKeyCode = 0x67;
    pub const F12: CGKeyCode = 0x6F;
    pub const F13: CGKeyCode = 0x69;
    pub const F14: CGKeyCode = 0x6B;
    pub const F15: CGKeyCode = 0x71;
    pub const F16: CGKeyCode = 0x6A;
    pub const F17: CGKeyCode = 0x40;
    pub const F18: CGKeyCode = 0x4F;
    pub const F19: CGKeyCode = 0x50;
    pub const F20: CGKeyCode = 0x5A;
    pub const HELP: CGKeyCode = 0x72;
    pub const HOME: CGKeyCode = 0x73;
    pub const PAGE_UP: CGKeyCode = 0x74;
    pub const FORWARD_DELETE: CGKeyCode = 0x75;
    pub const END: CGKeyCode = 0x77;
    pub const PAGE_DOWN: CGKeyCode = 0x79;
    pub const LEFT_ARROW: CGKeyCode = 0x7B;
    pub const RIGHT_ARROW: CGKeyCode = 0x7C;
    pub const DOWN_ARROW: CGKeyCode = 0x7D;
    pub const UP_ARROW: CGKeyCode = 0x7E;
}

/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
    Null = 0,

    // Mouse events.
    LeftMouseDown = 1,
    LeftMouseUp = 2,
    RightMouseDown = 3,
    RightMouseUp = 4,
    MouseMoved = 5,
    LeftMouseDragged = 6,
    RightMouseDragged = 7,

    // Keyboard events.
    KeyDown = 10,
    KeyUp = 11,
    FlagsChanged = 12,

    // Specialized control devices.
    ScrollWheel = 22,
    TabletPointer = 23,
    TabletProximity = 24,
    OtherMouseDown = 25,
    OtherMouseUp = 26,
    OtherMouseDragged = 27,

    // Out of band event types. These are delivered to the event tap callback
    // to notify it of unusual conditions that disable the event tap.
    TapDisabledByTimeout = 0xFFFFFFFE,
    TapDisabledByUserInput = 0xFFFFFFFF,
}

// Constants that specify buttons on a one, two, or three-button mouse.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
    Left,
    Right,
    Center,
}

/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
    HID,
    Session,
    AnnotatedSession,
}

// This is an enum due to zero-sized types warnings.
// For more details see https://github.com/rust-lang/rust/issues/27303
pub enum __CGEvent {}

pub type CGEventRef = *const __CGEvent;

pub struct CGEvent {
    obj: CGEventRef,
}

impl Clone for CGEvent {
    #[inline]
    fn clone(&self) -> CGEvent {
        unsafe {
            TCFType::wrap_under_get_rule(self.obj)
        }
    }
}

impl Drop for CGEvent {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.as_CFTypeRef();
            assert!(ptr != ptr::null());
            CFRelease(ptr);
        }
    }
}

impl TCFType<CGEventRef> for CGEvent {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CGEventRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CGEventRef) -> CGEvent {
        let reference: CGEventRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CGEventRef) -> CGEvent {
        CGEvent {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CGEventGetTypeID()
        }
    }
}

impl CGEvent {
    pub fn new(source: CGEventSource) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreate(source.as_concrete_TypeRef());
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_keyboard_event(
        source: CGEventSource,
        keycode: CGKeyCode,
        keydown: bool
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_mouse_event(
        source: CGEventSource,
        mouse_type: CGEventType,
        mouse_cursor_position: CGPoint,
        mouse_button: CGMouseButton
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouse_type,
                mouse_cursor_position, mouse_button);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn post(&self, tap_location: CGEventTapLocation) {
        unsafe {
            CGEventPost(tap_location, self.as_concrete_TypeRef());
        }
    }

    pub fn location(&self) -> CGPoint {
        unsafe {
            CGEventGetLocation(self.as_concrete_TypeRef())
        }
    }

    #[cfg(feature = "elcapitan")]
    pub fn post_to_pid(&self, pid: libc::pid_t) {
        unsafe {
            CGEventPostToPid(pid, self.as_concrete_TypeRef());
        }
    }

    pub fn set_flags(&self, flags: CGEventFlags) {
        unsafe {
            CGEventSetFlags(self.as_concrete_TypeRef(), flags);
        }
    }

    pub fn get_flags(&self) -> CGEventFlags {
        unsafe {
            CGEventGetFlags(self.as_concrete_TypeRef())
        }
    }

    pub fn set_type(&self, event_type: CGEventType) {
        unsafe {
            CGEventSetType(self.as_concrete_TypeRef(), event_type);
        }
    }

    pub fn get_type(&self) -> CGEventType {
        unsafe {
            CGEventGetType(self.as_concrete_TypeRef())
        }
    }

    pub fn set_string_from_utf16_unchecked(&self, buf: &[u16]) {
        let buflen = buf.len() as libc::c_ulong;
        unsafe {
            CGEventKeyboardSetUnicodeString(self.as_concrete_TypeRef(), buflen, buf.as_ptr());
        }
    }

    pub fn set_string(&self, string: &str) {
        let buf: Vec<u16> = string.encode_utf16().collect();
        self.set_string_from_utf16_unchecked(&buf);
    }
}

#[link(name = "ApplicationServices", kind = "framework")]
extern {
    /// Return the type identifier for the opaque type `CGEventRef'.
    fn CGEventGetTypeID() -> CFTypeID;

    /// Return a new event using the event source `source'. If `source' is NULL,
    /// the default source is used.
    fn CGEventCreate(source: CGEventSourceRef) -> CGEventRef;

    /// Return a new keyboard event.
    ///
    /// The event source may be taken from another event, or may be NULL. Based
    /// on the virtual key code values entered, the appropriate key down, key up,
    /// or flags changed events are generated.
    ///
    /// All keystrokes needed to generate a character must be entered, including
    /// SHIFT, CONTROL, OPTION, and COMMAND keys. For example, to produce a 'Z',
    /// the SHIFT key must be down, the 'z' key must go down, and then the SHIFT
    /// and 'z' key must be released:
    fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, keycode: CGKeyCode,
        keydown: bool) -> CGEventRef;

    /// Return a new mouse event.
    ///
    /// The event source may be taken from another event, or may be NULL.
    /// `mouseType' should be one of the mouse event types. `mouseCursorPosition'
    /// should be the position of the mouse cursor in global coordinates.
    /// `mouseButton' should be the button that's changing state; `mouseButton'
    /// is ignored unless `mouseType' is one of `kCGEventOtherMouseDown',
    /// `kCGEventOtherMouseDragged', or `kCGEventOtherMouseUp'.
    ///
    /// The current implemementation of the event system supports a maximum of
    /// thirty-two buttons. Mouse button 0 is the primary button on the mouse.
    /// Mouse button 1 is the secondary mouse button (right). Mouse button 2 is
    /// the center button, and the remaining buttons are in USB device order.
    fn CGEventCreateMouseEvent(source: CGEventSourceRef, mouseType: CGEventType,
        mouseCursorPosition: CGPoint, mouseButton: CGMouseButton) -> CGEventRef;

    /// Post an event into the event stream at a specified location.
    ///
    /// This function posts the specified event immediately before any event taps
    /// instantiated for that location, and the event passes through any such
    /// taps.
    fn CGEventPost(tapLocation: CGEventTapLocation, event: CGEventRef);

    #[cfg(feature = "elcapitan")]
    /// Post an event to a specified process ID
    fn CGEventPostToPid(pid: libc::pid_t, event: CGEventRef);

    /// Set the event flags of an event.
    fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);

    /// Return the event flags of an event.
    fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;

    /// Return the location of an event in global display coordinates.
    /// CGPointZero is returned if event is not a valid CGEventRef.
    fn CGEventGetLocation(event: CGEventRef) -> CGPoint;

    /// Set the event type of an event.
    fn CGEventSetType(event: CGEventRef, eventType: CGEventType);

    /// Return the event type of an event (left mouse down, for example).
    fn CGEventGetType(event: CGEventRef) -> CGEventType;

    /// Set the Unicode string associated with a keyboard event.
    ///
    /// By default, the system translates the virtual key code in a keyboard
    /// event into a Unicode string based on the keyboard ID in the event
    /// source.  This function allows you to manually override this string.
    /// Note that application frameworks may ignore the Unicode string in a
    /// keyboard event and do their own translation based on the virtual
    /// keycode and perceived event state.
    fn CGEventKeyboardSetUnicodeString(event: CGEventRef,
                                       length: libc::c_ulong,
                                       string: *const u16);
}
<|endoftext|>


<commit_before>use board_logic::{Board, BoardMarker};

#[derive(Debug, PartialEq)]
pub enum Direction {
    Horizontal,
    Vertical,
    Diagonal, // ´\´
    AntiDiagonal, // ´/´
}

#[derive(PartialEq, Debug)]
pub enum EvalError {
    NoMatch,
    OutOfBounds,
}

pub fn is_five(board: &Board, marker: BoardMarker) -> Result<Direction, EvalError> {
    // OK this will be hard, let's do it.
    debug!("In is_five, checking {:?}", marker);
    let mut n_line = 1;
    { // Horizontal
        debug!("Horiz right:");
        debug!("\tStart: {}", marker.point.x+1);
        'right: for i in marker.point.x+1..board.boardsize {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'right;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Horiz left:");
        debug!("\tStart: {}", marker.point.x-1);
        'left: for i in (0..marker.point.x).rev() {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'left;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }

        debug!("Horizontal Line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Horizontal);
        }
        n_line=0;
    }
    { // Vertical
        debug!("Vert down:");
        debug!("\tStart: {}", marker.point.y+1);
        'down: for i in marker.point.y+1..board.boardsize {
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'down;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Vert up: ");
        debug!("\tStart: {}", marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'up: for i in (0..marker.point.y).rev() { // if it is suppossed to be 0..y+1 or 0..y is not clear
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'up;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds) // Shouldn't happen
            }
        }

        debug!("Vertical line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Vertical);
        }
        n_line = 0;
    }
    { // Diagonal '\'
        debug!("Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x+i, marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_down;
                }
            }
        }
        debug!("Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x-1, marker.point.y-1);
        'diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'diag_up;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_up;
                }
            }
        }

        debug!("Diagonal line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Diagonal);
        }
        n_line = 0;
    }
    { // AntiDiagonal '/'
        debug!("Anti-Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'anti_diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                        break 'anti_diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'anti_diag_down;
                }
            }
        }
        debug!("Anti-Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'anti_diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x + i, marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'anti_diag_up;
                    }
                },
                Err(_) => {
                    break 'anti_diag_up;
                }
            }
        }

        debug!("Anti-Diagonal line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::AntiDiagonal);
        }
        n_line = 0;
    }
    Err(EvalError::NoMatch)
}

#[cfg(test)]
mod tests {
    use super::*;
    use board_logic::{Board, BoardMarker, Stone, Point};

    #[test]
    #[ignore]
    fn check_if_illegal_move() {
        let mut board = Board::new(15);
        for pos in [7 * 15 + 7, 7 * 15 + 6, 6 * 15 + 6, 5 * 15 + 7].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let illegal = Point::from_1d(7 * 15 + 5, 15);
        println!("{:?}", illegal);
    }

    #[test]
    fn is_horizontal_five_in_a_row() {
        let mut board = Board::new(15);
        let y = 7u32;
        let p1 = BoardMarker { point: Point::new(4, y), color: Stone::Black };
        for x in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(8, y + 2), color: Stone::White };
        for x in (7..12).filter(|x| *x != 8) {
            board.set_point(Point::new(x, y + 2), Stone::White);
        }
        println!("\n{}\nChecks,{:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Horizontal));
        // assert_eq!(is_five(&board, p2).unwrap(), Direction::Horizontal);
    }

    #[test]
    fn is_vertical_five_in_a_row() {
        let mut board = Board::new(15);
        let x = 7u32;
        let p1 = BoardMarker { point: Point::new(x, 4), color: Stone::Black };
        for y in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(x + 2, 8), color: Stone::White };
        for y in (7..12).filter(|y| *y != 8) {
            board.set_point(Point::new(x+2, y), Stone::White);
        }
        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Vertical));
        assert_eq!(is_five(&board, p2), Ok(Direction::Vertical));
    }
    #[test]
    fn is_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        // A diagonal is '\'
        for pos in [2u32 + 7*15, 3u32 + 8*15, 4u32 + 9*15, 5u32 + 10*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        for pos in [9u32 + 0*15, 10u32 + 1*15, 11u32 + 2*15, 13u32 + 4*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::White);
        }
        let p1 = BoardMarker { point: Point::from_1d(11*15+6, 15), color: Stone::Black };
        let p2 = BoardMarker { point: Point::from_1d(12+3*15, 15), color: Stone::White };

        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Diagonal));
        assert_eq!(is_five(&board, p2), Ok(Direction::Diagonal));
    }
    #[test]
    fn is_anti_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        for pos in [6u32+6*15,5u32+7*15, 4u32+8*15, 3u32+9*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let p1 = BoardMarker { point: Point::from_1d(2u32+10*15, 15), color: Stone::Black };

        println!("\n{}\nChecks; {:?}",
                 board.board, p1);

        assert_eq!(is_five(&board, p1), Ok(Direction::AntiDiagonal));
    }
}
<commit_msg>Changed `is_five` to `is_line`. Some things still have to be done but it<commit_after>use board_logic::{Board, BoardMarker};

/// Describes direction for `is_line`, etc.
///
/// # Notes
/// Each field will probably in the future hold two integers, these will signify how many stones
/// were found around the matched `BoardMarker`.
#[derive(Debug, PartialEq)]
pub enum Direction {
    Horizontal,
    Vertical,
    Diagonal, // ´\´
    AntiDiagonal, // ´/´
}

/// Evaluation Error. Used by `is_line`, etc.
///
/// # Fields
/// - NoMatch
///     Used when the function did not find any match.
/// - OutOfBounds
///     *DEPRECATED* This should not be used.
#[derive(PartialEq, Debug)]
pub enum Error {
    NoMatch,
    OutOfBounds,
}

pub fn is_line(board: &Board, marker: BoardMarker) -> Result<Direction, Error> {
    debug!("In is_line, checking {:?}", marker);

    { // Horizontal
        let mut n_horiz_right = 0;
        let mut n_horiz_left = 0;
        debug!("Horiz right:");
        debug!("\tStart: {}", marker.point.x+1);
        'right: for i in marker.point.x+1..board.boardsize {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_horiz_right += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'right;
                    }
                },
                Err(_) => return Err(Error::OutOfBounds)
            }
        }
        debug!("Horiz left:");
        debug!("\tStart: {}", marker.point.x.checked_sub(1).unwrap_or(marker.point.x));
        'left: for i in (0..marker.point.x).rev() {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_horiz_left += 1;
                    } else {
                        break 'left;
                    }
                },
                Err(_) => return Err(Error::OutOfBounds)
            }
        }

        debug!("Horizontal Line length: {}", n_horiz_right + n_horiz_left);
        if n_horiz_right + n_horiz_left >= 4 {
            return Ok(Direction::Horizontal);
        }
    }
    { // Vertical
        let mut n_vert_down = 0;
        let mut n_vert_up = 0;
        debug!("Vert down:");
        debug!("\tStart: {}", marker.point.y+1);
        'down: for i in marker.point.y+1..board.boardsize {
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_vert_down += 1;
                    } else {
                        break 'down;
                    }
                },
                Err(_) => return Err(Error::OutOfBounds)
            }
        }
        debug!("Vert up: ");
        debug!("\tStart: {}", marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'up: for i in (0..marker.point.y).rev() { // if it is suppossed to be 0..y+1 or 0..y is not clear
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_vert_up += 1;
                    } else {
                        break 'up;
                    }
                },
                Err(_) => return Err(Error::OutOfBounds) // Shouldn't happen
            }
        }

        debug!("Vertical line length: {}", n_vert_down + n_vert_up);
        if n_vert_down + n_vert_up >= 4 {
            return Ok(Direction::Vertical);
        }
    }
    { // Diagonal '\'
        let mut n_diag_down = 0;
        let mut n_diag_up = 0;
        debug!("Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x+i, marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_diag_down += 1;
                    } else {
                        break 'diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_down;
                }
            }
        }
        debug!("Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x-1, marker.point.y-1);
        'diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_diag_up += 1;
                    } else {
                        break 'diag_up;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_up;
                }
            }
        }

        debug!("Diagonal line length: {}", n_diag_down + n_diag_up);
        if n_diag_down + n_diag_up >= 4 {
            return Ok(Direction::Diagonal);
        }
    }
    { // AntiDiagonal '/'
        let mut n_anti_diag_down = 0;
        let mut n_anti_diag_up = 0;
        debug!("Anti-Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'anti_diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_anti_diag_down += 1;
                    } else {
                        debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                        break 'anti_diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'anti_diag_down;
                }
            }
        }
        debug!("Anti-Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'anti_diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x + i, marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_anti_diag_up += 1;
                    } else {
                        break 'anti_diag_up;
                    }
                },
                Err(_) => {
                    break 'anti_diag_up;
                }
            }
        }

        debug!("Anti-Diagonal line length: {}", n_anti_diag_down + n_anti_diag_up);
        if n_anti_diag_down + n_anti_diag_up >= 4 {
            return Ok(Direction::AntiDiagonal);
        }
    }
    Err(Error::NoMatch)
}

#[cfg(test)]
mod tests {
    use super::*;
    use board_logic::{Board, BoardMarker, Stone, Point};

    #[test]
    #[ignore]
    fn check_if_illegal_move() {
        let mut board = Board::new(15);
        for pos in [7 * 15 + 7, 7 * 15 + 6, 6 * 15 + 6, 5 * 15 + 7].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let illegal = Point::from_1d(7 * 15 + 5, 15);
        println!("{:?}", illegal);
    }

    #[test]
    fn is_horizontal_five_in_a_row() {
        let mut board = Board::new(15);
        let y = 7u32;
        let p1 = BoardMarker { point: Point::new(4, y), color: Stone::Black };
        for x in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(8, y + 2), color: Stone::White };
        for x in (7..12).filter(|x| *x != 8) {
            board.set_point(Point::new(x, y + 2), Stone::White);
        }
        println!("\n{}\nChecks,{:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_line(&board, p1), Ok(Direction::Horizontal));
        // assert_eq!(is_line(&board, p2).unwrap(), Direction::Horizontal);
    }

    #[test]
    fn is_vertical_five_in_a_row() {
        let mut board = Board::new(15);
        let x = 7u32;
        let p1 = BoardMarker { point: Point::new(x, 4), color: Stone::Black };
        for y in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(x + 2, 8), color: Stone::White };
        for y in (7..12).filter(|y| *y != 8) {
            board.set_point(Point::new(x+2, y), Stone::White);
        }
        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_line(&board, p1), Ok(Direction::Vertical));
        assert_eq!(is_line(&board, p2), Ok(Direction::Vertical));
    }
    #[test]
    fn is_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        // A diagonal is '\'
        for pos in [2u32 + 7*15, 3u32 + 8*15, 4u32 + 9*15, 5u32 + 10*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        for pos in [9u32 + 0*15, 10u32 + 1*15, 11u32 + 2*15, 13u32 + 4*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::White);
        }
        let p1 = BoardMarker { point: Point::from_1d(11*15+6, 15), color: Stone::Black };
        let p2 = BoardMarker { point: Point::from_1d(12+3*15, 15), color: Stone::White };

        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_line(&board, p1), Ok(Direction::Diagonal));
        assert_eq!(is_line(&board, p2), Ok(Direction::Diagonal));
    }
    #[test]
    fn is_anti_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        for pos in [6u32+6*15,5u32+7*15, 4u32+8*15, 3u32+9*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let p1 = BoardMarker { point: Point::from_1d(2u32+10*15, 15), color: Stone::Black };

        println!("\n{}\nChecks; {:?}",
                 board.board, p1);

        assert_eq!(is_line(&board, p1), Ok(Direction::AntiDiagonal));
    }
}
<|endoftext|>


<commit_before>use lexer::{Symbol, Token, Tokens};
use function::{Function, Functions, functions};
use error::EvaluatorError;
use constants::{VERSION, KEYWORDS, HELP, LICENSE};
use std::u32;

pub struct Evaluator {
    functions: Functions,
}

fn is_keyword(variable: &String) -> Option<&str> {
    for keyword in KEYWORDS {
        if keyword.to_string() == variable.to_lowercase() {
            return Some(keyword);
        }
    }

    None
}

impl Evaluator {
    pub fn new() -> Evaluator {
        Evaluator { functions: Functions::new() }
    }

    pub fn evaluate(&self, tokens: Tokens) -> Result<Vec<String>, EvaluatorError> {
        let mut result: Vec<String> = Vec::new();
        let mut stack: Vec<u32> = Vec::new();

        if tokens.is_empty() {
            return Ok(result);
        }

        for (position, token) in tokens {
            match token {
                Token::Decimal(ref dec) => {
                    stack.push(dec.parse().unwrap());
                }
                Token::Hexadecimal(ref hex) => {
                    stack.push(u32::from_str_radix(hex.as_str().split_at(2).1, 16).unwrap());
                }
                Token::Keyword(ref kw) => {
                    if let Some(keyword) = is_keyword(kw) {
                        match keyword {
                            "version" => result.push(VERSION.to_string()),
                            "help" => {
                                result = HELP.lines().map(|line| line.to_string()).collect();
                            }
                            "license" => {
                                result = LICENSE.lines().map(|line| line.to_string()).collect();
                            }
                            "exit" => return Err(EvaluatorError::Exit),
                            _ => unreachable!(),
                        }
                        return Ok(result);
                    } else {
                        return Err(EvaluatorError::UnknownKeyword(position));
                    }
                }
                Token::Operator(ref op) => {
                    let function = self.functions.get(&op).unwrap();
                    if stack.len() >= function.arity {
                        let stack_len = stack.len();
                        let args: Vec<u32> = stack.split_off(stack_len - function.arity);
                        let interm_result = try!((function.handle)(args, position));
                        stack.push(interm_result.0);
                        result.push(interm_result.1);
                        result.push(interm_result.0.to_string());
                    } else {
                        return Err(EvaluatorError::MissingArgument(position));
                    }
                }
                _ => continue,

            }
        }

        // The last element on the stack should be the final result
        if stack.len() != 1 {
            return Err(EvaluatorError::TooManyArguments);
        } else if result.is_empty() {
            result.push(stack.pop().unwrap().to_string());
        }

        Ok(result)
    }
}

impl Default for Evaluator {
    fn default() -> Evaluator {
        let mut evaluator = Evaluator::new();

        evaluator.functions.insert(Symbol::NOT, Function::new(1, Box::new(functions::not)));
        evaluator.functions.insert(Symbol::XOR, Function::new(2, Box::new(functions::xor)));
        evaluator.functions.insert(Symbol::OR, Function::new(2, Box::new(functions::or)));
        evaluator.functions.insert(Symbol::AND, Function::new(2, Box::new(functions::and)));
        evaluator.functions.insert(Symbol::LSHIFT,
                                   Function::new(2, Box::new(functions::lshift)));
        evaluator.functions.insert(Symbol::RSHIFT,
                                   Function::new(2, Box::new(functions::rshift)));

        evaluator
    }
}
<commit_msg>Remove else conditional when there are missing arguments<commit_after>use lexer::{Symbol, Token, Tokens};
use function::{Function, Functions, functions};
use error::EvaluatorError;
use constants::{VERSION, KEYWORDS, HELP, LICENSE};
use std::u32;

pub struct Evaluator {
    functions: Functions,
}

fn is_keyword(variable: &String) -> Option<&str> {
    for keyword in KEYWORDS {
        if keyword.to_string() == variable.to_lowercase() {
            return Some(keyword);
        }
    }

    None
}

impl Evaluator {
    pub fn new() -> Evaluator {
        Evaluator { functions: Functions::new() }
    }

    pub fn evaluate(&self, tokens: Tokens) -> Result<Vec<String>, EvaluatorError> {
        let mut result: Vec<String> = Vec::new();
        let mut stack: Vec<u32> = Vec::new();

        if tokens.is_empty() {
            return Ok(result);
        }

        for (position, token) in tokens {
            match token {
                Token::Decimal(ref dec) => {
                    stack.push(dec.parse().unwrap());
                }
                Token::Hexadecimal(ref hex) => {
                    stack.push(u32::from_str_radix(hex.as_str().split_at(2).1, 16).unwrap());
                }
                Token::Keyword(ref kw) => {
                    if let Some(keyword) = is_keyword(kw) {
                        match keyword {
                            "version" => result.push(VERSION.to_string()),
                            "help" => {
                                result = HELP.lines().map(|line| line.to_string()).collect();
                            }
                            "license" => {
                                result = LICENSE.lines().map(|line| line.to_string()).collect();
                            }
                            "exit" => return Err(EvaluatorError::Exit),
                            _ => unreachable!(),
                        }
                        return Ok(result);
                    } else {
                        return Err(EvaluatorError::UnknownKeyword(position));
                    }
                }
                Token::Operator(ref op) => {
                    let function = self.functions.get(&op).unwrap();
                    if stack.len() < function.arity {
                        return Err(EvaluatorError::MissingArgument(position));
                    }
                    let stack_len = stack.len();
                    let args: Vec<u32> = stack.split_off(stack_len - function.arity);
                    let interm_result = try!((function.handle)(args, position));
                    stack.push(interm_result.0);
                    result.push(interm_result.1);
                    result.push(interm_result.0.to_string());
                }
                _ => continue,

            }
        }

        // The last element on the stack should be the final result
        if stack.len() != 1 {
            return Err(EvaluatorError::TooManyArguments);
        } else if result.is_empty() {
            result.push(stack.pop().unwrap().to_string());
        }

        Ok(result)
    }
}

impl Default for Evaluator {
    fn default() -> Evaluator {
        let mut evaluator = Evaluator::new();

        evaluator.functions.insert(Symbol::NOT, Function::new(1, Box::new(functions::not)));
        evaluator.functions.insert(Symbol::XOR, Function::new(2, Box::new(functions::xor)));
        evaluator.functions.insert(Symbol::OR, Function::new(2, Box::new(functions::or)));
        evaluator.functions.insert(Symbol::AND, Function::new(2, Box::new(functions::and)));
        evaluator.functions.insert(Symbol::LSHIFT,
                                   Function::new(2, Box::new(functions::lshift)));
        evaluator.functions.insert(Symbol::RSHIFT,
                                   Function::new(2, Box::new(functions::rshift)));

        evaluator
    }
}
<|endoftext|>


<commit_before>//! Binary lambda calculus execution

use lambda_calculus::*;
use binary_encoding::from_binary;
use lambda_encoding::{encode, decode};
use self::Error::*;

/// An error that can occur during BLC execution.
#[derive(Debug, PartialEq)]
pub enum Error {
    /// invalid BLC program
    InvalidProgram,
    /// invalid BLC argument
    InvalidArgument
}

/// The type of input for BLC execution.
pub enum Input<'a> {
    /// no input parameter
    Nothing,
    /// BLC input
    Bits(&'a [u8]),
    /// unencoded byte input
    Bytes(&'a [u8])
}

/// Executes a binary lambda calculus program, optionally feeding it the given argument.
///
/// # Example
/// ```
/// use blc::execution::run;
/// use blc::execution::Input::Bytes;
///
/// let reverse_blc = b"0001011001000110100000000001011100111110111100001011011110110000010";
///
/// assert_eq!(run(&*reverse_blc, Bytes(b"herp derp")), Ok("pred preh".into()));
/// ```
pub fn run(blc_program: &[u8], input: Input) -> Result<String, Error> {
    let program = from_binary(blc_program);
    if program.is_err() { return Err(InvalidProgram) }
    let program = program.unwrap(); // safe

    let calculation = match input {
        Input::Nothing     => beta(program, NOR, 0),
        Input::Bytes(arg)  => beta(app(program, encode(arg)), NOR, 0),
        Input::Bits(arg) => {
            let arg = from_binary(arg);
            if arg.is_ok() {
                beta(app(program, arg.unwrap()), NOR, 0) // safe
            } else {
                return Err(InvalidArgument)
            }
        }
    };

    decode(calculation).or(Err(InvalidProgram))
}

#[cfg(test)]
mod test {
    use super::*;
    use binary_encoding::{decompress, to_binary};
    use lambda_calculus::data::num::church::{is_zero, rem};

    #[test]
    fn id() {
        let id_compressed = b" ";
        let id_blc        = decompress(&*id_compressed);
        let input         = b"herp derp";

        assert_eq!(run(&*id_blc, Input::Bytes(&*input)).unwrap(), "herp derp".to_owned());
    }

    #[test]
    fn sort() {
        let sort_compressed =
            [0x15, 0x46, 0x84, 0x06, 0x05, 0x46, 0x81, 0x60, 0x15, 0xfb, 0xec, 0x2f, 0x80, 0x01,
             0x5b, 0xf9, 0x7f, 0x0b, 0x7e, 0xf7, 0x2f, 0xec, 0x2d, 0xfb, 0x80, 0x56, 0x05, 0xfd,
             0x85, 0xbb, 0x76, 0x11, 0x5d, 0x50, 0x5c, 0x00, 0xbe, 0x7f, 0xc1, 0x2b, 0xff, 0x0f,
             0xfc, 0x2c, 0x1b, 0x72, 0xbf, 0xf0, 0xff, 0xc2, 0xc1, 0x6d, 0x34, 0x50, 0x40];

        let sort_blc = decompress(&sort_compressed);
        let input  = b"3241";

        assert_eq!(run(&*sort_blc, Input::Bytes(&*input)).unwrap(), "1234".to_owned());
    }

    #[test]
    fn quine() {
        // program code from https://tromp.github.io/cl/Binary_lambda_calculus.html#A_quine
        let quine_blc = b"000101100100011010000000000001011\
            011110010111100111111011111011010";
        let input = b"hurr";

        assert_eq!(run(&quine_blc[..], Input::Bytes(&input[..])), Ok("hurrhurr".to_owned()));
    }

    #[test]
    fn inflating() {
        // program code from http://www.ioccc.org/2012/tromp/inflate.Blc
        let inflate_compressed =
            [0x44, 0x44, 0x68, 0x16, 0x01, 0x79, 0x1a, 0x00, 0x16, 0x7f, 0xfb, 0xcb, 0xcf, 0xdf,
             0x65, 0xfb, 0xed, 0x0f, 0x3c, 0xe7, 0x3c, 0xf3, 0xc2, 0xd8, 0x20, 0x58, 0x2c, 0x0b,
             0x06, 0xc0];

        let inflate_blc = decompress(&inflate_compressed);
        let s_compressed = [0x1, 0x7a, 0x74];

        assert_eq!(run(&*inflate_blc, Input::Bytes(&s_compressed[..])).unwrap(), "000000010111101001110100".to_owned());
    }

    #[test]
    fn deflating() {
        // program code from http://www.ioccc.org/2012/tromp/deflate.Blc
        let deflate_compressed =
            [0x44, 0x68, 0x16, 0x05, 0x7e, 0x01, 0x17, 0x00, 0xbe, 0x55, 0xff, 0xf0, 0x0d, 0xc1,
             0x8b, 0xb2, 0xc1, 0xb0, 0xf8, 0x7c, 0x2d, 0xd8, 0x05, 0x9e, 0x09, 0x7f, 0xbf, 0xb1,
             0x48, 0x39, 0xce, 0x81, 0xce, 0x80];

        let deflate_blc = decompress(&deflate_compressed);
        let s_blc = b"00000001011110100111010";

        assert_eq!(run(&*deflate_blc, Input::Bytes(&s_blc[..])).unwrap().as_bytes(), [0x1, 0x7a, 0x74]);
    }

    #[test]
    fn fizz_buzz() {
        let fizzbuzz_single =
            abs(
                app!(
                    is_zero(),
                    app!(rem(), Var(1), 15.into_church()),
                    encode(&b"FizzBuzz"[..]),
                    app!(
                        is_zero(),
                        app!(rem(), Var(1), 3.into_church()),
                        encode(&b"Fizz"[..]),
                        app!(
                            is_zero(),
                            app!(rem(), Var(1), 5.into_church()),
                            encode(&b"Buzz"[..]),
                            Var(1)
                        )
                    )
                )
            );
        let fizzbuzz_blc = to_binary(&fizzbuzz_single);

        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&1.into_church()))).unwrap(),  "(λλ21)");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&2.into_church()))).unwrap(),  "(λλ2(21))");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&3.into_church()))).unwrap(),  "Fizz");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&4.into_church()))).unwrap(),  "(λλ2(2(2(21))))");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&5.into_church()))).unwrap(),  "Buzz");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&15.into_church()))).unwrap(), "FizzBuzz");
    }

/*
    #[test] /* WIP; this one parses properly, but doesn't return the expected result */
    fn hilbert() {
        // program code from http://www.ioccc.org/2012/tromp/hilbert.Blc
        let hilbert_compressed =
            [0x18, 0x18, 0x18, 0x18, 0x11, 0x11, 0x54, 0x68, 0x06, 0x04, 0x15, 0x5f, 0xf0, 0x41,
             0x9d, 0xf9, 0xde, 0x16, 0xff, 0xfe, 0x5f, 0x3f, 0xef, 0xf6, 0x15, 0xff, 0x94, 0x68,
             0x40, 0x58, 0x11, 0x7e, 0x05, 0xcb, 0xfe, 0xbc, 0xbf, 0xee, 0x86, 0xcb, 0x94, 0x68,
             0x16, 0x00, 0x5c, 0x0b, 0xfa, 0xcb, 0xfb, 0xf7, 0x1a, 0x85, 0xe0, 0x5c, 0xf4, 0x14,
             0xd5, 0xfe, 0x08, 0x18, 0x0b, 0x04, 0x8d, 0x08, 0x00, 0xe0, 0x78, 0x01, 0x64, 0x45,
             0xff, 0xe5, 0xff, 0x7f, 0xff, 0xfe, 0x5f, 0xff, 0x2f, 0xc0, 0x2f, 0x7a, 0xd9, 0x7f,
             0x5b, 0xff, 0xff, 0xfb, 0xff, 0xfc, 0xaa, 0xff, 0xf7, 0x81, 0x7f, 0xfa, 0xdf, 0x76,
             0x69, 0x54, 0x68, 0x06, 0x01, 0x57, 0xf7, 0xe1, 0x60, 0x5c, 0x13, 0xfe, 0x80, 0xb2,
             0x2c, 0x18, 0x58, 0x1b, 0xfe, 0x5c, 0x10, 0x42, 0xff, 0x80, 0x5d, 0xee, 0xc0, 0x6c,
             0x2c, 0x0c, 0x06, 0x08, 0x19, 0x1a, 0x00, 0x16, 0x7f, 0xbc, 0xbc, 0xfd, 0xf6, 0x5f,
             0x7c, 0x0a, 0x20];

        let hilbert_blc = decompress(&hilbert_compressed);
        let arg = b"1234";

        assert_eq!(run(&hilbert_blc[..], Bytes(&arg[..])), Ok("WIP".into()));
    }
*/
/*
    #[test] /* WIP; this just returns λ1 */
    fn brainfuck() {
        // program code from http://www.ioccc.org/2012/tromp/bf.Blc
        let bf_interpreter_compressed =
            [0x44, 0x51, 0xa1, 0x01, 0x84, 0x55, 0xd5, 0x02, 0xb7, 0x70, 0x30, 0x22, 0xff, 0x32,
             0xf0, 0x00, 0xbf, 0xf9, 0x85, 0x7f, 0x5e, 0xe1, 0x6f, 0x95, 0x7f, 0x7d, 0xee, 0xc0,
             0xe5, 0x54, 0x68, 0x00, 0x58, 0x55, 0xfd, 0xfb, 0xe0, 0x45, 0x57, 0xfd, 0xeb, 0xfb,
             0xf0, 0xb6, 0xf0, 0x2f, 0xd6, 0x07, 0xe1, 0x6f, 0x73, 0xd7, 0xf1, 0x14, 0xbc, 0xc0,
             0x0b, 0xff, 0x2e, 0x1f, 0xa1, 0x6f, 0x66, 0x17, 0xe8, 0x5b, 0xef, 0x2f, 0xcf, 0xff,
             0x13, 0xff, 0xe1, 0xca, 0x34, 0x20, 0x0a, 0xc8, 0xd0, 0x0b, 0x99, 0xee, 0x1f, 0xe5,
             0xff, 0x7f, 0x5a, 0x6a, 0x1f, 0xff, 0x0f, 0xff, 0x87, 0x9d, 0x04, 0xd0, 0xab, 0x00,
             0x05, 0xdb, 0x23, 0x40, 0xb7, 0x3b, 0x28, 0xcc, 0xc0, 0xb0, 0x6c, 0x0e, 0x74, 0x10];

        let bf_interpreter_blc = decompress(&bf_interpreter_compressed);
        let bf_hello =
            b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

        assert_eq!(run(&bf_interpreter_blc, Bytes(&bf_hello[..])), Ok("Hello World!".into()));
    }
*/
}
<commit_msg>minor test tweaks<commit_after>//! Binary lambda calculus execution

use lambda_calculus::*;
use binary_encoding::from_binary;
use lambda_encoding::{encode, decode};
use self::Error::*;

/// An error that can occur during BLC execution.
#[derive(Debug, PartialEq)]
pub enum Error {
    /// invalid BLC program
    InvalidProgram,
    /// invalid BLC argument
    InvalidArgument
}

/// The type of input for BLC execution.
pub enum Input<'a> {
    /// no input parameter
    Nothing,
    /// BLC input
    Bits(&'a [u8]),
    /// unencoded byte input
    Bytes(&'a [u8])
}

/// Executes a binary lambda calculus program, optionally feeding it the given argument.
///
/// # Example
/// ```
/// use blc::execution::run;
/// use blc::execution::Input::Bytes;
///
/// let reverse_blc = b"0001011001000110100000000001011100111110111100001011011110110000010";
///
/// assert_eq!(run(&*reverse_blc, Bytes(b"herp derp")), Ok("pred preh".into()));
/// ```
pub fn run(blc_program: &[u8], input: Input) -> Result<String, Error> {
    let program = from_binary(blc_program);
    if program.is_err() { return Err(InvalidProgram) }
    let program = program.unwrap(); // safe

    let calculation = match input {
        Input::Nothing     => beta(program, NOR, 0),
        Input::Bytes(arg)  => beta(app(program, encode(arg)), NOR, 0),
        Input::Bits(arg) => {
            let arg = from_binary(arg);
            if arg.is_ok() {
                beta(app(program, arg.unwrap()), NOR, 0) // safe
            } else {
                return Err(InvalidArgument)
            }
        }
    };

    decode(calculation).or(Err(InvalidProgram))
}

#[cfg(test)]
mod test {
    use super::*;
    use binary_encoding::{decompress, to_binary};
    use lambda_calculus::data::num::church::{is_zero, rem};

    #[test]
    fn id() {
        let id_compressed = b" ";
        let id_blc        = decompress(&*id_compressed);
        let input         = b"herp derp";

        assert_eq!(run(&*id_blc, Input::Bytes(&*input)).unwrap(), "herp derp".to_owned());
    }

    #[test]
    fn sort() {
        let sort_compressed =
            [0x15, 0x46, 0x84, 0x06, 0x05, 0x46, 0x81, 0x60, 0x15, 0xfb, 0xec, 0x2f, 0x80, 0x01,
             0x5b, 0xf9, 0x7f, 0x0b, 0x7e, 0xf7, 0x2f, 0xec, 0x2d, 0xfb, 0x80, 0x56, 0x05, 0xfd,
             0x85, 0xbb, 0x76, 0x11, 0x5d, 0x50, 0x5c, 0x00, 0xbe, 0x7f, 0xc1, 0x2b, 0xff, 0x0f,
             0xfc, 0x2c, 0x1b, 0x72, 0xbf, 0xf0, 0xff, 0xc2, 0xc1, 0x6d, 0x34, 0x50, 0x40];

        let sort_blc = decompress(&sort_compressed);
        let input  = b"3241";

        assert_eq!(run(&*sort_blc, Input::Bytes(&*input)).unwrap(), "1234".to_owned());
    }

    #[test]
    fn quine() {
        // program code from https://tromp.github.io/cl/Binary_lambda_calculus.html#A_quine
        let quine_blc = b"000101100100011010000000000001011\
            011110010111100111111011111011010";
        let input = b"hurr";

        assert_eq!(run(&quine_blc[..], Input::Bytes(&*input)).unwrap(), "hurrhurr");
    }

    #[test]
    fn inflating() {
        // program code from http://www.ioccc.org/2012/tromp/inflate.Blc
        let inflate_compressed =
            [0x44, 0x44, 0x68, 0x16, 0x01, 0x79, 0x1a, 0x00, 0x16, 0x7f, 0xfb, 0xcb, 0xcf, 0xdf,
             0x65, 0xfb, 0xed, 0x0f, 0x3c, 0xe7, 0x3c, 0xf3, 0xc2, 0xd8, 0x20, 0x58, 0x2c, 0x0b,
             0x06, 0xc0];

        let inflate_blc = decompress(&inflate_compressed);
        let s_compressed = [0x1, 0x7a, 0x74];

        assert_eq!(run(&*inflate_blc, Input::Bytes(&s_compressed)).unwrap(), "000000010111101001110100".to_owned());
    }

    #[test]
    fn deflating() {
        // program code from http://www.ioccc.org/2012/tromp/deflate.Blc
        let deflate_compressed =
            [0x44, 0x68, 0x16, 0x05, 0x7e, 0x01, 0x17, 0x00, 0xbe, 0x55, 0xff, 0xf0, 0x0d, 0xc1,
             0x8b, 0xb2, 0xc1, 0xb0, 0xf8, 0x7c, 0x2d, 0xd8, 0x05, 0x9e, 0x09, 0x7f, 0xbf, 0xb1,
             0x48, 0x39, 0xce, 0x81, 0xce, 0x80];

        let deflate_blc = decompress(&deflate_compressed);
        let s_blc = b"00000001011110100111010";

        assert_eq!(run(&*deflate_blc, Input::Bytes(&*s_blc)).unwrap().as_bytes(), [0x1, 0x7a, 0x74]);
    }

    #[test]
    fn fizz_buzz() {
        let fizzbuzz_single =
            abs(
                app!(
                    is_zero(),
                    app!(rem(), Var(1), 15.into_church()),
                    encode(&*b"FizzBuzz"),
                    app!(
                        is_zero(),
                        app!(rem(), Var(1), 3.into_church()),
                        encode(&*b"Fizz"),
                        app!(
                            is_zero(),
                            app!(rem(), Var(1), 5.into_church()),
                            encode(&*b"Buzz"),
                            Var(1)
                        )
                    )
                )
            );
        let fizzbuzz_blc = to_binary(&fizzbuzz_single);

        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&1.into_church()))).unwrap(),  "(λλ21)");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&2.into_church()))).unwrap(),  "(λλ2(21))");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&3.into_church()))).unwrap(),  "Fizz");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&4.into_church()))).unwrap(),  "(λλ2(2(2(21))))");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&5.into_church()))).unwrap(),  "Buzz");
        assert_eq!(run(&*fizzbuzz_blc, Input::Bits(&to_binary(&15.into_church()))).unwrap(), "FizzBuzz");
    }

/*
    #[test] /* WIP; this one parses properly, but doesn't return the expected result */
    fn hilbert() {
        // program code from http://www.ioccc.org/2012/tromp/hilbert.Blc
        let hilbert_compressed =
            [0x18, 0x18, 0x18, 0x18, 0x11, 0x11, 0x54, 0x68, 0x06, 0x04, 0x15, 0x5f, 0xf0, 0x41,
             0x9d, 0xf9, 0xde, 0x16, 0xff, 0xfe, 0x5f, 0x3f, 0xef, 0xf6, 0x15, 0xff, 0x94, 0x68,
             0x40, 0x58, 0x11, 0x7e, 0x05, 0xcb, 0xfe, 0xbc, 0xbf, 0xee, 0x86, 0xcb, 0x94, 0x68,
             0x16, 0x00, 0x5c, 0x0b, 0xfa, 0xcb, 0xfb, 0xf7, 0x1a, 0x85, 0xe0, 0x5c, 0xf4, 0x14,
             0xd5, 0xfe, 0x08, 0x18, 0x0b, 0x04, 0x8d, 0x08, 0x00, 0xe0, 0x78, 0x01, 0x64, 0x45,
             0xff, 0xe5, 0xff, 0x7f, 0xff, 0xfe, 0x5f, 0xff, 0x2f, 0xc0, 0x2f, 0x7a, 0xd9, 0x7f,
             0x5b, 0xff, 0xff, 0xfb, 0xff, 0xfc, 0xaa, 0xff, 0xf7, 0x81, 0x7f, 0xfa, 0xdf, 0x76,
             0x69, 0x54, 0x68, 0x06, 0x01, 0x57, 0xf7, 0xe1, 0x60, 0x5c, 0x13, 0xfe, 0x80, 0xb2,
             0x2c, 0x18, 0x58, 0x1b, 0xfe, 0x5c, 0x10, 0x42, 0xff, 0x80, 0x5d, 0xee, 0xc0, 0x6c,
             0x2c, 0x0c, 0x06, 0x08, 0x19, 0x1a, 0x00, 0x16, 0x7f, 0xbc, 0xbc, 0xfd, 0xf6, 0x5f,
             0x7c, 0x0a, 0x20];

        let hilbert_blc = decompress(&hilbert_compressed);
        let arg = b"1234";

        assert_eq!(run(&hilbert_blc[..], Bytes(&arg[..])), Ok("WIP".into()));
    }
*/
/*
    #[test] /* WIP; this just returns λ1 */
    fn brainfuck() {
        // program code from http://www.ioccc.org/2012/tromp/bf.Blc
        let bf_interpreter_compressed =
            [0x44, 0x51, 0xa1, 0x01, 0x84, 0x55, 0xd5, 0x02, 0xb7, 0x70, 0x30, 0x22, 0xff, 0x32,
             0xf0, 0x00, 0xbf, 0xf9, 0x85, 0x7f, 0x5e, 0xe1, 0x6f, 0x95, 0x7f, 0x7d, 0xee, 0xc0,
             0xe5, 0x54, 0x68, 0x00, 0x58, 0x55, 0xfd, 0xfb, 0xe0, 0x45, 0x57, 0xfd, 0xeb, 0xfb,
             0xf0, 0xb6, 0xf0, 0x2f, 0xd6, 0x07, 0xe1, 0x6f, 0x73, 0xd7, 0xf1, 0x14, 0xbc, 0xc0,
             0x0b, 0xff, 0x2e, 0x1f, 0xa1, 0x6f, 0x66, 0x17, 0xe8, 0x5b, 0xef, 0x2f, 0xcf, 0xff,
             0x13, 0xff, 0xe1, 0xca, 0x34, 0x20, 0x0a, 0xc8, 0xd0, 0x0b, 0x99, 0xee, 0x1f, 0xe5,
             0xff, 0x7f, 0x5a, 0x6a, 0x1f, 0xff, 0x0f, 0xff, 0x87, 0x9d, 0x04, 0xd0, 0xab, 0x00,
             0x05, 0xdb, 0x23, 0x40, 0xb7, 0x3b, 0x28, 0xcc, 0xc0, 0xb0, 0x6c, 0x0e, 0x74, 0x10];

        let bf_interpreter_blc = decompress(&bf_interpreter_compressed);
        let bf_hello =
            b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

        assert_eq!(run(&bf_interpreter_blc, Bytes(&bf_hello[..])), Ok("Hello World!".into()));
    }
*/
}
<|endoftext|>


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

#![allow(dead_code)]

use libc::{c_int, c_ulong, ioctl};
use std::marker;
use std::mem::size_of;

use super::*;

const TYPE_SPI: c_ulong = (b'k' as c_ulong) << TYPESHIFT;

const NR_MESSAGE: c_ulong = 0 << NRSHIFT;
const NR_MODE: c_ulong = 1 << NRSHIFT;
const NR_LSB_FIRST: c_ulong = 2 << NRSHIFT;
const NR_BITS_PER_WORD: c_ulong = 3 << NRSHIFT;
const NR_MAX_SPEED_HZ: c_ulong = 4 << NRSHIFT;
const NR_MODE32: c_ulong = 5 << NRSHIFT;

const REQ_RD_MODE: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE | SIZE_U8);
const REQ_RD_LSB_FIRST: c_ulong = (DIR_READ | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
const REQ_RD_BITS_PER_WORD: c_ulong = (DIR_READ | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
const REQ_RD_MAX_SPEED_HZ: c_ulong = (DIR_READ | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
const REQ_RD_MODE_32: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE32 | SIZE_U32);

const REQ_WR_MESSAGE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MESSAGE);
const REQ_WR_MODE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE | SIZE_U8);
const REQ_WR_LSB_FIRST: c_ulong = (DIR_WRITE | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
const REQ_WR_BITS_PER_WORD: c_ulong = (DIR_WRITE | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
const REQ_WR_MAX_SPEED_HZ: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
const REQ_WR_MODE_32: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE32 | SIZE_U32);

pub const MODE_CPHA: u8 = 0x01;
pub const MODE_CPOL: u8 = 0x02;

pub const MODE_0: u8 = 0;
pub const MODE_1: u8 = MODE_CPHA;
pub const MODE_2: u8 = MODE_CPOL;
pub const MODE_3: u8 = MODE_CPOL | MODE_CPHA;

pub const MODE_CS_HIGH: u8 = 0x04; // Set SS to active high
pub const MODE_LSB_FIRST: u8 = 0x08; // Set bit order to LSB first
pub const MODE_3WIRE: u8 = 0x10; // Set bidirectional mode
pub const MODE_LOOP: u8 = 0x20; // Set loopback mode
pub const MODE_NO_CS: u8 = 0x40; // Don't assert SS
pub const MODE_READY: u8 = 0x80; // Slave sends a ready signal
pub const MODE_TX_DUAL: u32 = 0x100; // Send on 2 outgoing lines
pub const MODE_TX_QUAD: u32 = 0x200; // Send on 4 outgoing lines
pub const MODE_RX_DUAL: u32 = 0x400; // Receive on 2 incoming lines
pub const MODE_RX_QUAD: u32 = 0x800; // Receive on 4 incoming lines

/// TODO: Doc comments
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(C)]
pub struct TransferSegment<'a, 'b> {
    // Pointer to write buffer, or 0.
    tx_buf: u64,
    // Pointer to read buffer, or 0.
    rx_buf: u64,
    // Number of bytes to transfer in this segment.
    len: u32,
    // Set a different clock speed for this segment. Default = 0.
    speed_hz: u32,
    // Add a delay before the (optional) SS change and the next segment.
    delay_usecs: u16,
    // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
    bits_per_word: u8,
    // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
    cs_change: u8,
    // Number of outgoing lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    tx_nbits: u8,
    // Number of incoming lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    rx_nbits: u8,
    // Padding. Set to 0 for forward compatibility.
    pad: u16,
    // Zero-sized variable used to link this struct to the read buffer lifetime.
    read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
    // Zero-sized variable used to link this struct to the write buffer lifetime.
    write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
}

impl<'a, 'b> TransferSegment<'a, 'b> {
    /// TODO: Doc comments
    pub fn new(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
    ) -> TransferSegment<'a, 'b> {
        TransferSegment::with_settings(read_buffer, write_buffer, 0, 0, 0, false)
    }

    /// TODO: Doc comments
    pub fn with_settings(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
        clock_speed: u32,
        delay: u16,
        bits_per_word: u8,
        cs_change: bool,
    ) -> TransferSegment<'a, 'b> {
        // Len will contain the length of the shortest of the supplied buffers
        let mut len: u32 = 0;

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

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

        TransferSegment {
            tx_buf,
            rx_buf,
            len,
            speed_hz: clock_speed,
            delay_usecs: delay,
            bits_per_word,
            cs_change: cs_change as u8,
            tx_nbits: 0,
            rx_nbits: 0,
            pad: 0,
            read_buffer_lifetime: marker::PhantomData,
            write_buffer_lifetime: marker::PhantomData,
        }
    }

    /// Returns the number of bytes that will be transferred.
    ///
    /// If both a read buffer and write buffer are supplied, [`transfer_segments`] only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    pub fn len(&self) -> u32 {
        self.len
    }

    /// Returns `true` if this segment won't transfer any bytes.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Gets the alternate clock speed in hertz (Hz) for this segment.
    pub fn clock_speed(&self) -> u32 {
        self.speed_hz
    }

    /// Sets an alternate clock speed in hertz (Hz) for this segment.
    ///
    /// By default, `clock_speed` is set to 0, which means
    /// it will use the same value as configured for `Spi`.
    pub fn set_clock_speed(&mut self, clock_speed: u32) {
        self.speed_hz = clock_speed;
    }

    /// Gets the delay in microseconds (µs) for this segment.
    pub fn delay(&self) -> u16 {
        self.delay_usecs
    }

    /// Sets a delay in microseconds (µs) for this segment.
    ///
    /// `set_delay` adds a delay at the end of this segment,
    /// before the (optional) Slave Select change.
    ///
    /// By default, `delay` is set to 0.
    pub fn set_delay(&mut self, delay: u16) {
        self.delay_usecs = delay;
    }

    /// Gets the number of bits per word for this segment.
    pub fn bits_per_word(&self) -> u8 {
        self.bits_per_word
    }

    /// Sets the number of bits per word for this segment.
    ///
    /// The Raspberry Pi currently only supports 8 bit words.
    ///
    /// By default, `bits_per_word` is set to 0, which means
    /// it will use the same value as configured for `Spi`.
    pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
        self.bits_per_word = bits_per_word;
    }

    /// Gets the state of Slave Select change for this segment.
    pub fn ss_change(&self) -> bool {
        self.cs_change == 1
    }

    /// Sets alternate Slave Select behavior for this segment.
    ///
    /// If `ss_change` is set to `true`, and this is not the last
    /// segment of the transfer, the Slave Select line will briefly
    /// change to inactive between this segment and the next.
    /// If this is the last segment, setting `ss_change` to true will
    /// keep Slave Select active after the transfer ends.
    ///
    /// By default, `ss_change` is set to `false`.
    pub fn set_ss_change(&mut self, ss_change: bool) {
        self.cs_change = ss_change as u8;
    }
}

pub unsafe fn mode(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MODE, value))
}

pub unsafe fn set_mode(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MODE, &value))
}

pub unsafe fn lsb_first(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_LSB_FIRST, value))
}

pub unsafe fn set_lsb_first(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_LSB_FIRST, &value))
}

pub unsafe fn bits_per_word(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_BITS_PER_WORD, value))
}

pub unsafe fn set_bits_per_word(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_BITS_PER_WORD, &value))
}

pub unsafe fn clock_speed(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MAX_SPEED_HZ, value))
}

pub unsafe fn set_clock_speed(fd: c_int, value: u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MAX_SPEED_HZ, &value))
}

pub unsafe fn mode32(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MODE_32, value))
}

pub unsafe fn set_mode32(fd: c_int, value: u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MODE_32, &value))
}

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

#![allow(dead_code)]

use libc::{c_int, c_ulong, ioctl};
use std::marker;
use std::mem::size_of;

use super::*;

const TYPE_SPI: c_ulong = (b'k' as c_ulong) << TYPESHIFT;

const NR_MESSAGE: c_ulong = 0 << NRSHIFT;
const NR_MODE: c_ulong = 1 << NRSHIFT;
const NR_LSB_FIRST: c_ulong = 2 << NRSHIFT;
const NR_BITS_PER_WORD: c_ulong = 3 << NRSHIFT;
const NR_MAX_SPEED_HZ: c_ulong = 4 << NRSHIFT;
const NR_MODE32: c_ulong = 5 << NRSHIFT;

const REQ_RD_MODE: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE | SIZE_U8);
const REQ_RD_LSB_FIRST: c_ulong = (DIR_READ | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
const REQ_RD_BITS_PER_WORD: c_ulong = (DIR_READ | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
const REQ_RD_MAX_SPEED_HZ: c_ulong = (DIR_READ | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
const REQ_RD_MODE_32: c_ulong = (DIR_READ | TYPE_SPI | NR_MODE32 | SIZE_U32);

const REQ_WR_MESSAGE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MESSAGE);
const REQ_WR_MODE: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE | SIZE_U8);
const REQ_WR_LSB_FIRST: c_ulong = (DIR_WRITE | TYPE_SPI | NR_LSB_FIRST | SIZE_U8);
const REQ_WR_BITS_PER_WORD: c_ulong = (DIR_WRITE | TYPE_SPI | NR_BITS_PER_WORD | SIZE_U8);
const REQ_WR_MAX_SPEED_HZ: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MAX_SPEED_HZ | SIZE_U32);
const REQ_WR_MODE_32: c_ulong = (DIR_WRITE | TYPE_SPI | NR_MODE32 | SIZE_U32);

pub const MODE_CPHA: u8 = 0x01;
pub const MODE_CPOL: u8 = 0x02;

pub const MODE_0: u8 = 0;
pub const MODE_1: u8 = MODE_CPHA;
pub const MODE_2: u8 = MODE_CPOL;
pub const MODE_3: u8 = MODE_CPOL | MODE_CPHA;

pub const MODE_CS_HIGH: u8 = 0x04; // Set SS to active high
pub const MODE_LSB_FIRST: u8 = 0x08; // Set bit order to LSB first
pub const MODE_3WIRE: u8 = 0x10; // Set bidirectional mode
pub const MODE_LOOP: u8 = 0x20; // Set loopback mode
pub const MODE_NO_CS: u8 = 0x40; // Don't assert SS
pub const MODE_READY: u8 = 0x80; // Slave sends a ready signal
pub const MODE_TX_DUAL: u32 = 0x100; // Send on 2 outgoing lines
pub const MODE_TX_QUAD: u32 = 0x200; // Send on 4 outgoing lines
pub const MODE_RX_DUAL: u32 = 0x400; // Receive on 2 incoming lines
pub const MODE_RX_QUAD: u32 = 0x800; // Receive on 4 incoming lines

/// Part of a multi-segment transfer
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(C)]
pub struct TransferSegment<'a, 'b> {
    // Pointer to write buffer, or 0.
    tx_buf: u64,
    // Pointer to read buffer, or 0.
    rx_buf: u64,
    // Number of bytes to transfer in this segment.
    len: u32,
    // Set a different clock speed for this segment. Default = 0.
    speed_hz: u32,
    // Add a delay before the (optional) SS change and the next segment.
    delay_usecs: u16,
    // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
    bits_per_word: u8,
    // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
    cs_change: u8,
    // Number of outgoing lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    tx_nbits: u8,
    // Number of incoming lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    rx_nbits: u8,
    // Padding. Set to 0 for forward compatibility.
    pad: u16,
    // Zero-sized variable used to link this struct to the read buffer lifetime.
    read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
    // Zero-sized variable used to link this struct to the write buffer lifetime.
    write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
}

impl<'a, 'b> TransferSegment<'a, 'b> {
    /// Creates a new `TransferSegment` with the default settings.
    ///
    /// If `read_buffer` is set to `None`, any incoming data is discarded.
    ///
    /// If `write_buffer` is set to `None`, a zero-value byte will be sent for every
    /// byte read.
    ///
    /// If both `read_buffer` and `write_buffer` are specified, [`transfer_segments`]
    /// will only transfer as many bytes as the shortest of the two buffers contains.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`transfer_segments`]: index.html
    /// [`Spi`]: index.html
    pub fn new(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
    ) -> TransferSegment<'a, 'b> {
        TransferSegment::with_settings(read_buffer, write_buffer, 0, 0, 0, false)
    }

    /// Creates a new `TransferSegment` with the specified settings.
    ///
    /// These settings override the values set for [`Spi`], and are only used
    /// for this specific segment.
    ///
    /// If `read_buffer` is set to `None`, any incoming data is discarded.
    ///
    /// If `write_buffer` is set to `None`, a zero-value byte will be sent for every
    /// byte read.
    ///
    /// If both `read_buffer` and `write_buffer` are specified, [`transfer_segments`]
    /// will only transfer as many bytes as the shortest of the two buffers contains.
    ///
    /// `clock_speed` sets an alternate clock speed in hertz (Hz).
    ///
    /// `delay` sets a delay in microseconds (µs).
    ///
    /// `bits_per_word` sets the number of bits per word. The Raspberry Pi currently only supports 8 bits per word.
    ///
    /// `ss_change` changes how Slave Select behaves in between two segments (toggle SS), or after the final segment (keep SS active).
    ///
    /// [`transfer_segments`]: index.html
    /// [`Spi`]: index.html
    pub fn with_settings(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
        clock_speed: u32,
        delay: u16,
        bits_per_word: u8,
        ss_change: bool,
    ) -> TransferSegment<'a, 'b> {
        // Len will contain the length of the shortest of the supplied buffers
        let mut len: u32 = 0;

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

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

        TransferSegment {
            tx_buf,
            rx_buf,
            len,
            speed_hz: clock_speed,
            delay_usecs: delay,
            bits_per_word,
            cs_change: ss_change as u8,
            tx_nbits: 0,
            rx_nbits: 0,
            pad: 0,
            read_buffer_lifetime: marker::PhantomData,
            write_buffer_lifetime: marker::PhantomData,
        }
    }

    /// Returns the number of bytes that will be transferred.
    ///
    /// If both a read buffer and write buffer are supplied, [`transfer_segments`] only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    pub fn len(&self) -> u32 {
        self.len
    }

    /// Returns `true` if this segment won't transfer any bytes.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Gets the alternate clock speed in hertz (Hz) for this segment.
    pub fn clock_speed(&self) -> u32 {
        self.speed_hz
    }

    /// Sets an alternate clock speed in hertz (Hz) for this segment.
    ///
    /// By default, `clock_speed` is set to 0, which means
    /// it will use the same value as configured for `Spi`.
    pub fn set_clock_speed(&mut self, clock_speed: u32) {
        self.speed_hz = clock_speed;
    }

    /// Gets the delay in microseconds (µs) for this segment.
    pub fn delay(&self) -> u16 {
        self.delay_usecs
    }

    /// Sets a delay in microseconds (µs) for this segment.
    ///
    /// `set_delay` adds a delay at the end of this segment,
    /// before the (optional) Slave Select change.
    ///
    /// By default, `delay` is set to 0.
    pub fn set_delay(&mut self, delay: u16) {
        self.delay_usecs = delay;
    }

    /// Gets the number of bits per word for this segment.
    pub fn bits_per_word(&self) -> u8 {
        self.bits_per_word
    }

    /// Sets the number of bits per word for this segment.
    ///
    /// The Raspberry Pi currently only supports 8 bit words.
    ///
    /// By default, `bits_per_word` is set to 0, which means
    /// it will use the same value as configured for `Spi`.
    pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
        self.bits_per_word = bits_per_word;
    }

    /// Gets the state of Slave Select change for this segment.
    pub fn ss_change(&self) -> bool {
        self.cs_change == 1
    }

    /// Sets alternate Slave Select behavior for this segment.
    ///
    /// If `ss_change` is set to `true`, and this is not the last
    /// segment of the transfer, the Slave Select line will briefly
    /// change to inactive between this segment and the next.
    /// If this is the last segment, setting `ss_change` to true will
    /// keep Slave Select active after the transfer ends.
    ///
    /// By default, `ss_change` is set to `false`.
    pub fn set_ss_change(&mut self, ss_change: bool) {
        self.cs_change = ss_change as u8;
    }
}

pub unsafe fn mode(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MODE, value))
}

pub unsafe fn set_mode(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MODE, &value))
}

pub unsafe fn lsb_first(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_LSB_FIRST, value))
}

pub unsafe fn set_lsb_first(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_LSB_FIRST, &value))
}

pub unsafe fn bits_per_word(fd: c_int, value: &mut u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_BITS_PER_WORD, value))
}

pub unsafe fn set_bits_per_word(fd: c_int, value: u8) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_BITS_PER_WORD, &value))
}

pub unsafe fn clock_speed(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MAX_SPEED_HZ, value))
}

pub unsafe fn set_clock_speed(fd: c_int, value: u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MAX_SPEED_HZ, &value))
}

pub unsafe fn mode32(fd: c_int, value: &mut u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_RD_MODE_32, value))
}

pub unsafe fn set_mode32(fd: c_int, value: u32) -> Result<i32> {
    parse_retval(ioctl(fd, REQ_WR_MODE_32, &value))
}

pub unsafe fn transfer(fd: c_int, segments: &[TransferSegment]) -> Result<i32> {
    parse_retval(ioctl(
        fd,
        REQ_WR_MESSAGE
            | (((segments.len() * size_of::<TransferSegment>()) as c_ulong) << SIZESHIFT),
        segments,
    ))
}
<|endoftext|>