<commit_before> use libc::{c_int, c_char};
use std::ffi::{CString, c_str_to_bytes};

use SdlResult;
use get_error;

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

#[derive(Copy, Clone, PartialEq, Show)]
#[repr(i32)]
pub enum Axis {
    Invalid      = ll::SDL_CONTROLLER_AXIS_INVALID,
    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) -> Axis {
        let name_c = CString::from_slice(axis.as_bytes()).as_ptr();

        let id = unsafe { ll::SDL_GameControllerGetAxisFromString(name_c) };

        wrap_controller_axis(id as u8)
    }

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

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

        c_str_to_string(string)
    }
}

pub fn wrap_controller_axis(bitflags: u8) -> Axis {
    match bitflags as c_int {
        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")
    }
}

#[derive(Copy, Clone, PartialEq, Show)]
#[repr(i32)]
pub enum Button {
    Invalid       = ll::SDL_CONTROLLER_BUTTON_INVALID,
    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) -> Button {
        let name_c = CString::from_slice(button.as_bytes()).as_ptr();

        let id = unsafe { ll::SDL_GameControllerGetButtonFromString(name_c) };

        wrap_controller_button(id as u8)
    }

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

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

        c_str_to_string(string)
    }

}

pub fn wrap_controller_button(bitflags: u8) -> Button {
    match bitflags as c_int {
        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")
    }
}

/// Return true if the joystick at index `id` is a game controller.
pub fn is_game_controller(id: i32) -> bool {
    unsafe { ll::SDL_IsGameController(id) != 0 }
}

/// Return the name of the controller at index `id` or an empty string
/// if no name is found.
pub fn name_for_index(id: i32) -> String {
    let name = unsafe { ll::SDL_GameControllerNameForIndex(id) };

    c_str_to_string(name)
}

/// Force controller update when not using the event loop
pub fn update() {
    unsafe { ll::SDL_GameControllerUpdate() };
}

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

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

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

/// Add a new mapping from a mapping string
pub fn add_mapping(mapping: &str) -> SdlResult<MappingStatus> {
    let mapping_c = CString::from_slice(mapping.as_bytes()).as_ptr();

    let result = unsafe { ll::SDL_GameControllerAddMapping(mapping_c) };

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

pub struct GameController {
    raw: *const ll::SDL_GameController,
}

impl GameController {

    /// 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(id: i32) -> SdlResult<GameController> {
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };

        if controller.is_null() {
            Err(get_error())
        } else {
            Ok(GameController { raw: controller })
        }
    }

    /// 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 }
    }
}

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 {
        let bytes = unsafe { c_str_to_bytes(&c_str) };

        String::from_utf8_lossy(bytes).to_string()
    }
}
<commit_msg>Controller API: Added get_axis and get_button<commit_after> use libc::{c_int, c_char};
use std::ffi::{CString, c_str_to_bytes};

use SdlResult;
use {get_error, clear_error};

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

#[derive(Copy, Clone, PartialEq, Show)]
#[repr(i32)]
pub enum Axis {
    Invalid      = ll::SDL_CONTROLLER_AXIS_INVALID,
    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) -> Axis {
        let name_c = CString::from_slice(axis.as_bytes()).as_ptr();

        let id = unsafe { ll::SDL_GameControllerGetAxisFromString(name_c) };

        wrap_controller_axis(id as u8)
    }

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

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

        c_str_to_string(string)
    }
}

pub fn wrap_controller_axis(bitflags: u8) -> Axis {
    match bitflags as c_int {
        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")
    }
}

#[derive(Copy, Clone, PartialEq, Show)]
#[repr(i32)]
pub enum Button {
    Invalid       = ll::SDL_CONTROLLER_BUTTON_INVALID,
    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) -> Button {
        let name_c = CString::from_slice(button.as_bytes()).as_ptr();

        let id = unsafe { ll::SDL_GameControllerGetButtonFromString(name_c) };

        wrap_controller_button(id as u8)
    }

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

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

        c_str_to_string(string)
    }

}

pub fn wrap_controller_button(bitflags: u8) -> Button {
    match bitflags as c_int {
        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")
    }
}

/// Return true if the joystick at index `id` is a game controller.
pub fn is_game_controller(id: i32) -> bool {
    unsafe { ll::SDL_IsGameController(id) != 0 }
}

/// Return the name of the controller at index `id` or an empty string
/// if no name is found.
pub fn name_for_index(id: i32) -> String {
    let name = unsafe { ll::SDL_GameControllerNameForIndex(id) };

    c_str_to_string(name)
}

/// Force controller update when not using the event loop
pub fn update() {
    unsafe { ll::SDL_GameControllerUpdate() };
}

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

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

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

/// Add a new mapping from a mapping string
pub fn add_mapping(mapping: &str) -> SdlResult<MappingStatus> {
    let mapping_c = CString::from_slice(mapping.as_bytes()).as_ptr();

    let result = unsafe { ll::SDL_GameControllerAddMapping(mapping_c) };

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

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

impl GameController {

    /// 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(id: i32) -> SdlResult<GameController> {
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };

        if controller.is_null() {
            Err(get_error())
        } else {
            Ok(GameController { raw: controller })
        }
    }

    /// 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 get_attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }

    /// Get the position of the given `axis`
    pub fn get_axis(&self, axis: Axis) -> SdlResult<i16> {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured. As far as I can
        // tell the only way to know if an error happened is to see if
        // get_error() returns a non-empty string.
        clear_error();

        let axis = axis as ll::SDL_GameControllerAxis;

        let pos = unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) };

        if pos != 0 {
            Ok(pos)
        } else {
            let err = get_error();

            if err.is_empty() {
                Ok(pos)
            } else {
                Err(err)
            }
        }
    }

    /// Return `Ok(true)` if `button` is pressed.
    pub fn get_button(&self, button: Button) -> SdlResult<bool> {
        // Same deal as get_axis, 0 can mean both unpressed or
        // error...
        clear_error();

        let button = button as ll::SDL_GameControllerButton;

        let pressed =
            unsafe { ll::SDL_GameControllerGetButton(self.raw, button) };

        match pressed {
            1 => Ok(true),
            0 => {
                let err = get_error();

                if err.is_empty() {
                    // Button is not pressed
                    Ok(false)
                } else {
                    Err(err)
                }
            }
            // Should be unreachable
            _ => Err(get_error()),
        }
    }
}

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 {
        let bytes = unsafe { c_str_to_bytes(&c_str) };

        String::from_utf8_lossy(bytes).to_string()
    }
}
<|endoftext|>


<commit_before>pub mod entrypoint;
pub use entrypoint::EntryPointAnalyzer;

pub mod exports;
pub use exports::ExportsAnalyzer;

pub mod imports;
pub use imports::ImportsAnalyzer;

pub mod ptrs;
pub use ptrs::PtrAnalyzer;

// TODO: analyzer for imports to set symbols
// TODO: analyzer to inspect operands for pointers into the text section, e.g. argument to CreateThread
// TODO: analyzer for RUNTIME_FUNCTION metadata
// TODO: analyzer for __guard_fids_table
// TODO: analyzer for pointers from data section into text section
// TODO: analyzer to scan for prologues
// TODO: analyzer for RTTI
// TODO: analyzer for FLIRT-like signatures to set function names
// TODO: analyzer for jump-tables
// TODO: analyzer for import thunks
// TODO: analyzer for global ctors, initializers (__initterm_e, __initterm)
// TODO: analyzer for TLS callbacks

// TODO: analyzer for non-contiguous functions
//   e.g. __security_init_cookie in 07FB252D2E853A9B1B32F30EDE411F2EFBB9F01E4A7782DB5EACF3F55CF34902

// for k32.dll, the entrypoint + exports does not catch 0x1800012d4
// this is because its called by sub_180001630
// this has no CALL xref-to.
// it does have:
//   - RUNTIME_FUNCTION at 1800AB06C (TODO: analysis pass: runtime functions)
//   - __guard_fids_table entry at 18007ABAC (TODO: analysis pass: guard fids table)
//   - function pointer at 1800779E0 (TODO: analysis pass: ptr from data->text)
<commit_msg>analysis: update TODO doc<commit_after>pub mod entrypoint;
pub use entrypoint::EntryPointAnalyzer;

pub mod exports;
pub use exports::ExportsAnalyzer;

pub mod imports;
pub use imports::ImportsAnalyzer;

pub mod ptrs;
pub use ptrs::PtrAnalyzer;

// TODO: analyzer for jump-tables, ptr tables, see vivisect/pointertables.py
// TODO: analyzer for RUNTIME_FUNCTION metadata
// TODO: analyzer for __guard_fids_table
// TODO: analyzer for global ctors, initializers (__initterm_e, __initterm)
// TODO: analyzer for import thunks
// TODO: analyzer for TLS callbacks
// TODO: analyzer to inspect operands for pointers into the text section, e.g. argument to CreateThread
// TODO: analyzer to scan for prologues
// TODO: analyzer for RTTI
// TODO: analyzer for non-returning functions
// TODO: analyzer for VEH
// TODO: analyzer for SEH
// TODO: analyzer for FLIRT-like signatures to set function names

// TODO: analyzer for non-contiguous functions
//   e.g. __security_init_cookie in 07FB252D2E853A9B1B32F30EDE411F2EFBB9F01E4A7782DB5EACF3F55CF34902

// for k32.dll, the entrypoint + exports does not catch 0x1800012d4
// this is because its called by sub_180001630
// this has no CALL xref-to.
// it does have:
//   - RUNTIME_FUNCTION at 1800AB06C (TODO: analysis pass: runtime functions)
//   - __guard_fids_table entry at 18007ABAC (TODO: analysis pass: guard fids table)
//   - function pointer at 1800779E0 (TODO: analysis pass: ptr from data->text)
<|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::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

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<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

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

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| FileID::from(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * 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<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(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, m: &'a Module, id: &FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, 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(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

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

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("  id      : '{}'", id);
        let idstr : String = id.into();
        self.prefix_of_files_for_module(owner) + "-" + &idstr[..] + ".imag"
    }

    fn prefix_of_files_for_module(&self, m: &Module) -> String {
        self.storepath.clone() + m.name()
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

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

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            data_dump:      data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

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

    fn cause(&self) -> Option<&Error> {
        self.caused_by.as_ref().map(|e| &**e)
    }

}

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

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<commit_msg>Add hash type to filepath<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::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

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<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

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

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| FileID::from(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * 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<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(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, m: &'a Module, id: &FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, 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(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

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

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        let idstr   : String        = id.clone().into();
        let idtype  : FileIDType    = id.into();
        let typestr : String        = idtype.into();

        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("        id: '{}'", idstr);
        debug!("      type: '{}'", typestr);

        self.prefix_of_files_for_module(owner) +
            "-" + &typestr[..] +
            "-" + &idstr[..] +
            ".imag"
    }

    fn prefix_of_files_for_module(&self, m: &Module) -> String {
        self.storepath.clone() + m.name()
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

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

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            data_dump:      data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

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

    fn cause(&self) -> Option<&Error> {
        self.caused_by.as_ref().map(|e| &**e)
    }

}

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

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    client_proxy::ClientProxy,
    commands::{blocking_cmd, report_error, Command},
};

/// Command to transfer coins between two accounts.
pub struct TransferCommand {}

impl Command for TransferCommand {
    fn get_aliases(&self) -> Vec<&'static str> {
        vec!["transfer", "transferb", "t", "tb"]
    }
    fn get_params_help(&self) -> &'static str {
        "\n\t<sender_account_address>|<sender_account_ref_id> \
         <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> \
         [gas_unit_price_in_micro_libras (default=0)] [max_gas_amount_in_micro_libras (default 140000)] \
         Suffix 'b' is for blocking. "
    }
    fn get_description(&self) -> &'static str {
        "Transfer coins (in libra) from account to another."
    }
    fn execute(&self, client: &mut ClientProxy, params: &[&str]) {
        if params.len() < 4 || params.len() > 6 {
            println!("Invalid number of arguments for transfer");
            println!(
                "{} {}",
                self.get_aliases().join(" | "),
                self.get_params_help()
            );
            return;
        }

        println!(">> Transferring");
        let is_blocking = blocking_cmd(&params[0]);
        match client.transfer_coins(&params, is_blocking) {
            Ok(index_and_seq) => {
                if is_blocking {
                    println!("Finished transaction!");
                } else {
                    println!("Transaction submitted to validator");
                }
                println!(
                    "To query for transaction status, run: query txn_acc_seq {} {} \
                     <fetch_events=true|false>",
                    index_and_seq.account_index, index_and_seq.sequence_number
                );
            }
            Err(e) => report_error("Failed to perform transaction", e),
        }
    }
}
<commit_msg>[client] fix: max_gas_amount changed to 400_000<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    client_proxy::ClientProxy,
    commands::{blocking_cmd, report_error, Command},
};

/// Command to transfer coins between two accounts.
pub struct TransferCommand {}

impl Command for TransferCommand {
    fn get_aliases(&self) -> Vec<&'static str> {
        vec!["transfer", "transferb", "t", "tb"]
    }
    fn get_params_help(&self) -> &'static str {
        "\n\t<sender_account_address>|<sender_account_ref_id> \
         <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> \
         [gas_unit_price_in_micro_libras (default=0)] [max_gas_amount_in_micro_libras (default 400_000)] \
         Suffix 'b' is for blocking. "
    }
    fn get_description(&self) -> &'static str {
        "Transfer coins (in libra) from account to another."
    }
    fn execute(&self, client: &mut ClientProxy, params: &[&str]) {
        if params.len() < 4 || params.len() > 6 {
            println!("Invalid number of arguments for transfer");
            println!(
                "{} {}",
                self.get_aliases().join(" | "),
                self.get_params_help()
            );
            return;
        }

        println!(">> Transferring");
        let is_blocking = blocking_cmd(&params[0]);
        match client.transfer_coins(&params, is_blocking) {
            Ok(index_and_seq) => {
                if is_blocking {
                    println!("Finished transaction!");
                } else {
                    println!("Transaction submitted to validator");
                }
                println!(
                    "To query for transaction status, run: query txn_acc_seq {} {} \
                     <fetch_events=true|false>",
                    index_and_seq.account_index, index_and_seq.sequence_number
                );
            }
            Err(e) => report_error("Failed to perform transaction", e),
        }
    }
}
<|endoftext|>


<commit_before>use std::string::String as StdString;

use Variants;
use primitives as prim;
use api::{generic, Generic, Getable, Array, MaybeError, primitive, WithVM};
use api::generic::A;
use gc::{Gc, Traverseable, DataDef, WriteOnly};
use vm::{Thread, DataStruct, VMInt, Status, Value, Result};

fn array_length(array: Array<generic::A>) -> VMInt {
    array.len() as VMInt
}

fn array_index<'vm>(array: Array<'vm, Generic<generic::A>>,
                    index: VMInt)
                    -> MaybeError<Generic<generic::A>, String> {
    match array.get(index) {
        Some(value) => MaybeError::Ok(value),
        None => MaybeError::Err(format!("{} is out of range", index)),
    }
}

fn array_append<'vm>(lhs: Array<'vm, Generic<generic::A>>,
                     rhs: Array<'vm, Generic<generic::A>>)
                     -> Array<'vm, Generic<generic::A>> {
    struct Append<'b> {
        lhs: &'b [Value],
        rhs: &'b [Value],
    }

    impl<'b> Traverseable for Append<'b> {
        fn traverse(&self, gc: &mut Gc) {
            self.lhs.traverse(gc);
            self.rhs.traverse(gc);
        }
    }

    unsafe impl<'b> DataDef for Append<'b> {
        type Value = DataStruct;
        fn size(&self) -> usize {
            use std::mem::size_of;
            let len = self.lhs.len() + self.rhs.len();
            size_of::<usize>() + ::array::Array::<Value>::size_of(len)
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, DataStruct>) -> &'w mut DataStruct {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.tag = 0;
                result.fields.initialize(self.lhs.iter().chain(self.rhs.iter()).cloned());
                result
            }
        }
    }
    let vm = lhs.vm();
    let value = {
        let stack = vm.current_frame();
        vm.alloc(&stack.stack,
                 Append {
                     lhs: &lhs.fields,
                     rhs: &rhs.fields,
                 })
    };
    Getable::from_value(lhs.vm(), Variants(&Value::Data(value))).expect("Array")
}

fn string_append(lhs: WithVM<&str>, rhs: &str) -> String {
    use array::Str;
    struct StrAppend<'b> {
        lhs: &'b str,
        rhs: &'b str,
    }

    impl<'b> Traverseable for StrAppend<'b> {}

    unsafe impl<'b> DataDef for StrAppend<'b> {
        type Value = Str;
        fn size(&self) -> usize {
            ::array::Str::size_of(self.lhs.len() + self.rhs.len())
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, Str>) -> &'w mut Str {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                {
                    let array = Str::as_mut_array(result);
                    ::array::Array::set_len(array, self.lhs.len() + self.rhs.len());
                    let (l, r) = array.split_at_mut(self.lhs.len());
                    l.clone_from_slice(self.lhs.as_bytes());
                    r.clone_from_slice(self.rhs.as_bytes());
                }
                result
            }
        }
    }

    let vm = lhs.vm;
    let lhs = lhs.value;
    let value = {
        let stack = vm.current_frame();
        vm.alloc(&stack.stack,
                 StrAppend {
                     lhs: lhs,
                     rhs: rhs,
                 })
    };
    Getable::from_value(vm, Variants(&Value::String(value))).expect("Array")
}

fn string_slice(s: &str, start: VMInt, end: VMInt) -> &str {
    &s[start as usize..end as usize]
}

fn trace(a: Generic<A>) {
    println!("{:?}", a.0);
}

fn show_int(i: VMInt) -> String {
    format!("{}", i)
}

fn show_float(f: f64) -> String {
    format!("{}", f)
}

fn show_char(c: char) -> String {
    format!("{}", c)
}

fn error(_: &Thread) -> Status {
    // We expect a string as an argument to this function but we only return Status::Error
    // and let the caller take care of printing the message
    Status::Error
}

fn f1<A, R>(f: fn(A) -> R) -> fn(A) -> R {
    f
}
fn f2<A, B, R>(f: fn(A, B) -> R) -> fn(A, B) -> R {
    f
}
fn f3<A, B, C, R>(f: fn(A, B, C) -> R) -> fn(A, B, C) -> R {
    f
}

pub fn load(vm: &Thread) -> Result<()> {
    use std::f64;
    use std::char;
    try!(vm.define_global("float",
                          record!(
        digits => f64::DIGITS,
        epsilon => f64::EPSILON,
        infinity => f64::INFINITY,
        mantissa_digits => f64::MANTISSA_DIGITS,
        max_ => f64::MAX,
        max_10_exp => f64::MAX_10_EXP,
        max_exp => f64::MAX_EXP,
        min_ => f64::MIN,
        min_10_exp => f64::MIN_10_EXP,
        min_exp => f64::MIN_EXP,
        min_positive => f64::MIN_POSITIVE,
        nan => f64::NAN,
        neg_infinity => f64::NEG_INFINITY,
        e => f64::consts::E,
        pi => f64::consts::PI,
        radix => f64::RADIX,
        is_nan => f1(f64::is_nan),
        is_infinite => f1(f64::is_infinite),
        is_finite => f1(f64::is_finite),
        is_normal => f1(f64::is_normal),
        floor => f1(f64::floor),
        ceil => f1(f64::ceil),
        round => f1(f64::round),
        trunc => f1(f64::trunc),
        fract => f1(f64::fract),
        abs => f1(f64::abs),
        signum => f1(f64::signum),
        is_sign_positive => f1(f64::is_sign_positive),
        is_sign_negative => f1(f64::is_sign_negative),
        mul_add => f3(f64::mul_add),
        recip => f1(f64::recip),
        powi => f2(f64::powi),
        powf => f2(f64::powf),
        sqrt => f1(f64::sqrt),
        exp => f1(f64::exp),
        exp2 => f1(f64::exp2),
        ln => f1(f64::ln),
        log2 => f1(f64::log2),
        log10 => f1(f64::log10),
        to_degrees => f1(f64::to_degrees),
        to_radians => f1(f64::to_radians),
        max => f2(f64::max),
        min => f2(f64::min),
        abs_sub => f2(f64::abs_sub),
        cbrt => f1(f64::cbrt),
        hypot => f2(f64::hypot),
        sin => f1(f64::sin),
        cos => f1(f64::cos),
        tan => f1(f64::tan),
        acos => f1(f64::acos),
        atan => f1(f64::atan),
        atan2 => f2(f64::atan2),
        sin_cos => f1(f64::sin_cos),
        exp_m1 => f1(f64::exp_m1),
        ln_1p => f1(f64::ln_1p),
        sinh => f1(f64::sinh),
        cosh => f1(f64::cosh),
        tanh => f1(f64::tanh),
        acosh => f1(f64::acosh),
        atanh => f1(f64::atanh)
    )));
    try!(vm.define_global("int",
                          record!(
        min_value => VMInt::min_value(),
        max_value => VMInt::max_value(),
        count_ones => f1(VMInt::count_ones),
        rotate_left => f2(VMInt::rotate_left),
        rotate_right => f2(VMInt::rotate_right),
        swap_bytes => f1(VMInt::swap_bytes),
        from_be => f1(VMInt::from_be),
        from_le => f1(VMInt::from_le),
        to_be => f1(VMInt::to_be),
        to_le => f1(VMInt::to_le),
        pow => f2(VMInt::pow),
        abs => f1(VMInt::abs),
        signum => f1(VMInt::signum),
        is_positive => f1(VMInt::is_positive),
        is_negative => f1(VMInt::is_negative)
    )));
    try!(vm.define_global("array",
                          record!(
        length => f1(prim::array_length),
        index => f2(prim::array_index),
        append => f2(prim::array_append)
    )));

    try!(vm.define_global("string_prim",
                          record!(
        length => f1(str::len),
        is_empty => f1(str::is_empty),
        split_at => f2(str::split_at),
        find => f2(str::find::<&str>),
        rfind => f2(str::rfind::<&str>),
        trim => f1(str::trim),
        trim_left => f1(str::trim_left),
        trim_right => f1(str::trim_right),
        compare => f2(str::cmp),
        append => f2(prim::string_append),
        eq => f2(<str as PartialEq>::eq),
        slice => f3(prim::string_slice)
    )));
    try!(vm.define_global("char",
                          record!(
        is_digit => f2(char::is_digit),
        to_digit => f2(char::to_digit),
        len_utf8 => f1(char::len_utf8),
        len_utf16 => f1(char::len_utf16),
        is_alphabetic => f1(char::is_alphabetic),
        is_lowercase => f1(char::is_lowercase),
        is_uppercase => f1(char::is_uppercase),
        is_whitespace => f1(char::is_whitespace),
        is_alphanumeric => f1(char::is_alphanumeric),
        is_control => f1(char::is_control),
        is_numeric => f1(char::is_numeric)
    )));
    try!(vm.define_global("prim",
                          record!(
        show_Int => f1(prim::show_int),
        show_Float => f1(prim::show_float),
        show_Char => f1(prim::show_char)
    )));

    try!(vm.define_global("#error",
                          primitive::<fn(StdString) -> A>("#error", prim::error)));
    try!(vm.define_global("error",
                          primitive::<fn(StdString) -> A>("error", prim::error)));
    try!(vm.define_global("trace", f1(prim::trace)));

    try!(::lazy::load(vm));
    try!(::reference::load(vm));
    Ok(())
}
<commit_msg>feat: Remove abs_sub from the float module<commit_after>use std::string::String as StdString;

use Variants;
use primitives as prim;
use api::{generic, Generic, Getable, Array, MaybeError, primitive, WithVM};
use api::generic::A;
use gc::{Gc, Traverseable, DataDef, WriteOnly};
use vm::{Thread, DataStruct, VMInt, Status, Value, Result};

fn array_length(array: Array<generic::A>) -> VMInt {
    array.len() as VMInt
}

fn array_index<'vm>(array: Array<'vm, Generic<generic::A>>,
                    index: VMInt)
                    -> MaybeError<Generic<generic::A>, String> {
    match array.get(index) {
        Some(value) => MaybeError::Ok(value),
        None => MaybeError::Err(format!("{} is out of range", index)),
    }
}

fn array_append<'vm>(lhs: Array<'vm, Generic<generic::A>>,
                     rhs: Array<'vm, Generic<generic::A>>)
                     -> Array<'vm, Generic<generic::A>> {
    struct Append<'b> {
        lhs: &'b [Value],
        rhs: &'b [Value],
    }

    impl<'b> Traverseable for Append<'b> {
        fn traverse(&self, gc: &mut Gc) {
            self.lhs.traverse(gc);
            self.rhs.traverse(gc);
        }
    }

    unsafe impl<'b> DataDef for Append<'b> {
        type Value = DataStruct;
        fn size(&self) -> usize {
            use std::mem::size_of;
            let len = self.lhs.len() + self.rhs.len();
            size_of::<usize>() + ::array::Array::<Value>::size_of(len)
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, DataStruct>) -> &'w mut DataStruct {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.tag = 0;
                result.fields.initialize(self.lhs.iter().chain(self.rhs.iter()).cloned());
                result
            }
        }
    }
    let vm = lhs.vm();
    let value = {
        let stack = vm.current_frame();
        vm.alloc(&stack.stack,
                 Append {
                     lhs: &lhs.fields,
                     rhs: &rhs.fields,
                 })
    };
    Getable::from_value(lhs.vm(), Variants(&Value::Data(value))).expect("Array")
}

fn string_append(lhs: WithVM<&str>, rhs: &str) -> String {
    use array::Str;
    struct StrAppend<'b> {
        lhs: &'b str,
        rhs: &'b str,
    }

    impl<'b> Traverseable for StrAppend<'b> {}

    unsafe impl<'b> DataDef for StrAppend<'b> {
        type Value = Str;
        fn size(&self) -> usize {
            ::array::Str::size_of(self.lhs.len() + self.rhs.len())
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, Str>) -> &'w mut Str {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                {
                    let array = Str::as_mut_array(result);
                    ::array::Array::set_len(array, self.lhs.len() + self.rhs.len());
                    let (l, r) = array.split_at_mut(self.lhs.len());
                    l.clone_from_slice(self.lhs.as_bytes());
                    r.clone_from_slice(self.rhs.as_bytes());
                }
                result
            }
        }
    }

    let vm = lhs.vm;
    let lhs = lhs.value;
    let value = {
        let stack = vm.current_frame();
        vm.alloc(&stack.stack,
                 StrAppend {
                     lhs: lhs,
                     rhs: rhs,
                 })
    };
    Getable::from_value(vm, Variants(&Value::String(value))).expect("Array")
}

fn string_slice(s: &str, start: VMInt, end: VMInt) -> &str {
    &s[start as usize..end as usize]
}

fn trace(a: Generic<A>) {
    println!("{:?}", a.0);
}

fn show_int(i: VMInt) -> String {
    format!("{}", i)
}

fn show_float(f: f64) -> String {
    format!("{}", f)
}

fn show_char(c: char) -> String {
    format!("{}", c)
}

fn error(_: &Thread) -> Status {
    // We expect a string as an argument to this function but we only return Status::Error
    // and let the caller take care of printing the message
    Status::Error
}

fn f1<A, R>(f: fn(A) -> R) -> fn(A) -> R {
    f
}
fn f2<A, B, R>(f: fn(A, B) -> R) -> fn(A, B) -> R {
    f
}
fn f3<A, B, C, R>(f: fn(A, B, C) -> R) -> fn(A, B, C) -> R {
    f
}

pub fn load(vm: &Thread) -> Result<()> {
    use std::f64;
    use std::char;
    try!(vm.define_global("float",
                          record!(
        digits => f64::DIGITS,
        epsilon => f64::EPSILON,
        infinity => f64::INFINITY,
        mantissa_digits => f64::MANTISSA_DIGITS,
        max_ => f64::MAX,
        max_10_exp => f64::MAX_10_EXP,
        max_exp => f64::MAX_EXP,
        min_ => f64::MIN,
        min_10_exp => f64::MIN_10_EXP,
        min_exp => f64::MIN_EXP,
        min_positive => f64::MIN_POSITIVE,
        nan => f64::NAN,
        neg_infinity => f64::NEG_INFINITY,
        e => f64::consts::E,
        pi => f64::consts::PI,
        radix => f64::RADIX,
        is_nan => f1(f64::is_nan),
        is_infinite => f1(f64::is_infinite),
        is_finite => f1(f64::is_finite),
        is_normal => f1(f64::is_normal),
        floor => f1(f64::floor),
        ceil => f1(f64::ceil),
        round => f1(f64::round),
        trunc => f1(f64::trunc),
        fract => f1(f64::fract),
        abs => f1(f64::abs),
        signum => f1(f64::signum),
        is_sign_positive => f1(f64::is_sign_positive),
        is_sign_negative => f1(f64::is_sign_negative),
        mul_add => f3(f64::mul_add),
        recip => f1(f64::recip),
        powi => f2(f64::powi),
        powf => f2(f64::powf),
        sqrt => f1(f64::sqrt),
        exp => f1(f64::exp),
        exp2 => f1(f64::exp2),
        ln => f1(f64::ln),
        log2 => f1(f64::log2),
        log10 => f1(f64::log10),
        to_degrees => f1(f64::to_degrees),
        to_radians => f1(f64::to_radians),
        max => f2(f64::max),
        min => f2(f64::min),
        cbrt => f1(f64::cbrt),
        hypot => f2(f64::hypot),
        sin => f1(f64::sin),
        cos => f1(f64::cos),
        tan => f1(f64::tan),
        acos => f1(f64::acos),
        atan => f1(f64::atan),
        atan2 => f2(f64::atan2),
        sin_cos => f1(f64::sin_cos),
        exp_m1 => f1(f64::exp_m1),
        ln_1p => f1(f64::ln_1p),
        sinh => f1(f64::sinh),
        cosh => f1(f64::cosh),
        tanh => f1(f64::tanh),
        acosh => f1(f64::acosh),
        atanh => f1(f64::atanh)
    )));
    try!(vm.define_global("int",
                          record!(
        min_value => VMInt::min_value(),
        max_value => VMInt::max_value(),
        count_ones => f1(VMInt::count_ones),
        rotate_left => f2(VMInt::rotate_left),
        rotate_right => f2(VMInt::rotate_right),
        swap_bytes => f1(VMInt::swap_bytes),
        from_be => f1(VMInt::from_be),
        from_le => f1(VMInt::from_le),
        to_be => f1(VMInt::to_be),
        to_le => f1(VMInt::to_le),
        pow => f2(VMInt::pow),
        abs => f1(VMInt::abs),
        signum => f1(VMInt::signum),
        is_positive => f1(VMInt::is_positive),
        is_negative => f1(VMInt::is_negative)
    )));
    try!(vm.define_global("array",
                          record!(
        length => f1(prim::array_length),
        index => f2(prim::array_index),
        append => f2(prim::array_append)
    )));

    try!(vm.define_global("string_prim",
                          record!(
        length => f1(str::len),
        is_empty => f1(str::is_empty),
        split_at => f2(str::split_at),
        find => f2(str::find::<&str>),
        rfind => f2(str::rfind::<&str>),
        trim => f1(str::trim),
        trim_left => f1(str::trim_left),
        trim_right => f1(str::trim_right),
        compare => f2(str::cmp),
        append => f2(prim::string_append),
        eq => f2(<str as PartialEq>::eq),
        slice => f3(prim::string_slice)
    )));
    try!(vm.define_global("char",
                          record!(
        is_digit => f2(char::is_digit),
        to_digit => f2(char::to_digit),
        len_utf8 => f1(char::len_utf8),
        len_utf16 => f1(char::len_utf16),
        is_alphabetic => f1(char::is_alphabetic),
        is_lowercase => f1(char::is_lowercase),
        is_uppercase => f1(char::is_uppercase),
        is_whitespace => f1(char::is_whitespace),
        is_alphanumeric => f1(char::is_alphanumeric),
        is_control => f1(char::is_control),
        is_numeric => f1(char::is_numeric)
    )));
    try!(vm.define_global("prim",
                          record!(
        show_Int => f1(prim::show_int),
        show_Float => f1(prim::show_float),
        show_Char => f1(prim::show_char)
    )));

    try!(vm.define_global("#error",
                          primitive::<fn(StdString) -> A>("#error", prim::error)));
    try!(vm.define_global("error",
                          primitive::<fn(StdString) -> A>("error", prim::error)));
    try!(vm.define_global("trace", f1(prim::trace)));

    try!(::lazy::load(vm));
    try!(::reference::load(vm));
    Ok(())
}
<|endoftext|>


<commit_before>/// Disclaimer: The lexer is basically spaghetti. What did you expect?

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    // Words
    Select, From, Where, Group, Having, By, Limit,
    Distinct,
    Order, Asc, Desc,
    As, Join, Inner, Outer, Left, Right, On,
    Insert, Into, Values, Update, Delete,
    Create, Table, Index, Constraint,
    Primary, Key, Unique, References,
    And, Or,
    Between, In,
    Is, Not, Null,

    // Non-letter tokens
    Equal,
    NotEqual,
    LessThan, LessThanOrEqual,
    GreaterThan, GreaterThanOrEqual,

    Plus, Minus,

    LeftParen, RightParen,
    LeftBracket, RightBracket,
    Dot, Comma, Semicolon,

    Ampersand, Pipe, ForwardSlash,

    /// ||, the concatenate operator
    DoublePipe,

    /// *, the wildcard for SELECT
    Asterisk,

    /// ?, the prepared statement placeholder
    PreparedStatementPlaceholder,

    // Tokens with values
    Number(String),
    Ident(String),
    StringLiteral(String)
}

fn character_to_token(c: char) -> Option<Token> {
    use self::Token::*;

    Some(match c {
        '=' => Equal,
        '<' => LessThan,
        '>' => GreaterThan,
        '+' => Plus,
        '-' => Minus,
        '(' => LeftParen,
        ')' => RightParen,
        '[' => LeftBracket,
        ']' => RightBracket,
        '.' => Dot,
        ',' => Comma,
        ';' => Semicolon,
        '&' => Ampersand,
        '|' => Pipe,
        '*' => Asterisk,
        '/' => ForwardSlash,
        '?' => PreparedStatementPlaceholder,
        _ => return None
    })
}

fn word_to_token(word: String) -> Token {
    use self::Token::*;

    // Make all letters lowercase for comparison
    let word_cmp: String = word.chars().flat_map( |c| c.to_lowercase() ).collect();

    match word_cmp.as_slice() {
        "select" => Select,
        "from" => From,
        "where" => Where,
        "group" => Group,
        "having" => Having,
        "by" => By,
        "limit" => Limit,
        "distinct" => Distinct,
        "order" => Order,
        "asc" => Asc,
        "desc" => Desc,
        "as" => As,
        "join" => Join,
        "inner" => Inner,
        "outer" => Outer,
        "left" => Left,
        "right" => Right,
        "on" => On,
        "insert" => Insert,
        "into" => Into,
        "values" => Values,
        "update" => Update,
        "delete" => Delete,
        "create" => Create,
        "table" => Table,
        "index" => Index,
        "constraint" => Constraint,
        "primary" => Primary,
        "key" => Key,
        "unique" => Unique,
        "references" => References,
        "and" => And,
        "or" => Or,
        "between" => Between,
        "in" => In,
        "is" => Is,
        "not" => Not,
        "null" => Null,
        _ => Ident(word)
    }
}

enum LexerState {
    NoState,
    Word,
    Backtick,
    Apostrophe { escaping: bool },
    Number { decimal: bool },
    /// Disambiguate an operator sequence.
    OperatorDisambiguate { first: char },
    LineComment,
    BlockComment { was_prev_char_asterisk: bool }
}

struct Lexer {
    state: LexerState,
    string_buffer: String,
    tokens: Vec<Token>
}

impl Lexer {
    fn no_state(&mut self, c: char) -> Result<LexerState, char> {
        match c {
            'a'...'z' | 'A'...'Z' | '_' => {
                self.string_buffer.push(c);
                Ok(LexerState::Word)
            },
            '`' => {
                Ok(LexerState::Backtick)
            }
            '\'' => {
                // string literal
                Ok(LexerState::Apostrophe { escaping: false })
            },
            '0'...'9' => {
                self.string_buffer.push(c);
                Ok(LexerState::Number { decimal: false })
            },
            ' ' | '\t' | '\n' => {
                // whitespace
                Ok(LexerState::NoState)
            },
            c => {
                use self::Token::*;

                match character_to_token(c) {
                    Some(LessThan) | Some(GreaterThan) | Some(Minus) | Some(Pipe) | Some(ForwardSlash) => {
                        Ok(LexerState::OperatorDisambiguate { first: c })
                    },
                    Some(token) => {
                        self.tokens.push(token);
                        Ok(LexerState::NoState)
                    },
                    None => {
                        // unknown character
                        Err(c)
                    }
                }
            }
        }
    }

    fn move_string_buffer(&mut self) -> String {
        use std::mem;
        mem::replace(&mut self.string_buffer, String::new())
    }

    pub fn feed_character(&mut self, c: Option<char>) {
        self.state = match self.state {
            LexerState::NoState => {
                match c {
                    Some(c) => self.no_state(c).unwrap(),
                    None => LexerState::NoState
                }
            },
            LexerState::Word => {
                match c {
                    Some(c) => match c {
                        'a'...'z' | 'A'...'Z' | '_' | '0'...'9' => {
                            self.string_buffer.push(c);
                            LexerState::Word
                        }
                        c => {
                            let buffer = self.move_string_buffer();
                            self.tokens.push(word_to_token(buffer));
                            self.no_state(c).unwrap()
                        }
                    },
                    None => {
                        let buffer = self.move_string_buffer();
                        self.tokens.push(word_to_token(buffer));
                        LexerState::NoState
                    }
                }
            },
            LexerState::Backtick => {
                match c {
                    Some('`') => {
                        let buffer = self.move_string_buffer();
                        self.tokens.push(Token::Ident(buffer));
                        LexerState::NoState
                    },
                    Some(c) => {
                        self.string_buffer.push(c);
                        LexerState::Backtick
                    },
                    None => {
                        // error: backtick did not finish
                        unimplemented!()
                    }
                }
            },
            LexerState::Apostrophe { escaping } => {
                if let Some(c) = c {
                    match (escaping, c) {
                        (false, '\'') => {
                            // unescaped apostrophe
                            let buffer = self.move_string_buffer();
                            self.tokens.push(Token::StringLiteral(buffer));
                            LexerState::NoState
                        },
                        (false, '\\') => {
                            // unescaped backslash
                            LexerState::Apostrophe { escaping: true }
                        },
                        (true, _) | _ => {
                            self.string_buffer.push(c);
                            LexerState::Apostrophe { escaping: false }
                        }
                    }
                } else {
                    // error: apostrophe did not finish
                    unimplemented!()
                }
            },
            LexerState::Number { decimal } => {
                if let Some(c) = c {
                    match c {
                        '0'...'9' => {
                            self.string_buffer.push(c);
                            LexerState::Number { decimal: decimal }
                        },
                        '.' if !decimal => {
                            // Add a decimal point. None has been added yet.
                            self.string_buffer.push(c);
                            LexerState::Number { decimal: true }
                        },
                        c => {
                            let buffer = self.move_string_buffer();
                            self.tokens.push(Token::Number(buffer));
                            self.no_state(c).unwrap()
                        }
                    }
                } else {
                    let buffer = self.move_string_buffer();
                    self.tokens.push(Token::Number(buffer));
                    LexerState::NoState
                }
            },
            LexerState::OperatorDisambiguate { first } => {
                use self::Token::*;

                if let Some(c) = c {
                    match (first, c) {
                        ('<', '>') => {
                            self.tokens.push(NotEqual);
                            LexerState::NoState
                        },
                        ('<', '=') => {
                            self.tokens.push(LessThanOrEqual);
                            LexerState::NoState
                        },
                        ('>', '=') => {
                            self.tokens.push(GreaterThanOrEqual);
                            LexerState::NoState
                        },
                        ('|', '|') => {
                            self.tokens.push(DoublePipe);
                            LexerState::NoState
                        },
                        ('-', '-') => {
                            LexerState::LineComment
                        },
                        ('/', '*') => {
                            LexerState::BlockComment { was_prev_char_asterisk: false }
                        },
                        _ => {
                            self.tokens.push(character_to_token(first).unwrap());
                            self.no_state(c).unwrap()
                        }
                    }
                } else {
                    self.tokens.push(character_to_token(first).unwrap());
                    LexerState::NoState
                }
            },
            LexerState::LineComment => {
                match c {
                    Some('\n') => LexerState::NoState,
                    _ => LexerState::LineComment
                }
            },
            LexerState::BlockComment { was_prev_char_asterisk } => {
                if was_prev_char_asterisk && c == Some('/') {
                    LexerState::NoState
                } else {
                    LexerState::BlockComment { was_prev_char_asterisk: c == Some('*') }
                }
            }
        };
    }
}

pub fn parse(sql: &str) -> Vec<Token> {
    let mut lexer = Lexer {
        state: LexerState::NoState,
        string_buffer: String::new(),
        tokens: Vec::new()
    };

    for c in sql.chars() {
        lexer.feed_character(Some(c));
    }

    lexer.feed_character(None);
    lexer.tokens
}

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

    fn id(value: &str) -> super::Token {
        super::Token::Ident(value.to_string())
    }

    fn number(value: &str) -> super::Token {
        super::Token::Number(value.to_string())
    }

    #[test]
    fn test_sql_lexer_dontconfuseidentswithkeywords() {
        use super::Token::*;
        // Not: AS, Ident("df")
        assert_eq!(parse("asdf"), vec![Ident("asdf".to_string())]);
    }

    #[test]
    fn test_sql_lexer_escape() {
        use super::Token::*;
        // Escaped apostrophe
        assert_eq!(parse(r"'\''"), vec![StringLiteral("'".to_string())]);
    }

    #[test]
    fn test_sql_lexer_numbers() {
        use super::Token::*;

        assert_eq!(parse("12345"), vec![number("12345")]);
        assert_eq!(parse("0.25"), vec![number("0.25")]);
        assert_eq!(parse("0.25 + -0.25"), vec![number("0.25"), Plus, Minus, number("0.25")]);
        assert_eq!(parse("-0.25 + 0.25"), vec![Minus, number("0.25"), Plus, number("0.25")]);
        assert_eq!(parse("- 0.25 - -0.25"), vec![Minus, number("0.25"), Minus, Minus, number("0.25")]);
        assert_eq!(parse("- 0.25 --0.25"), vec![Minus, number("0.25")]);
        assert_eq!(parse("0.25 -0.25"), vec![number("0.25"), Minus, number("0.25")]);
    }

    #[test]
    fn test_sql_lexer_query1() {
        use super::Token::*;

        assert_eq!(parse(" SeLECT a,    b as alias1 , c alias2, d ` alias three ` fRoM table1 WHERE a='Hello World'; "),
            vec![
                Select, id("a"), Comma, id("b"), As, id("alias1"), Comma,
                id("c"), id("alias2"), Comma, id("d"), id(" alias three "),
                From, id("table1"),
                Where, id("a"), Equal, StringLiteral("Hello World".to_string()), Semicolon
            ]
        );
    }

    #[test]
    fn test_sql_lexer_query2() {
        use super::Token::*;

        let query = r"
        -- Get employee count from each department
        SELECT d.id departmentId, count(e.id) employeeCount
        FROM department d
        LEFT JOIN employee e ON e.departmentId = d.id
        GROUP BY departmentId;
        ";

        assert_eq!(parse(query), vec![
            Select, id("d"), Dot, id("id"), id("departmentId"), Comma, id("count"), LeftParen, id("e"), Dot, id("id"), RightParen, id("employeeCount"),
            From, id("department"), id("d"),
            Left, Join, id("employee"), id("e"), On, id("e"), Dot, id("departmentId"), Equal, id("d"), Dot, id("id"),
            Group, By, id("departmentId"), Semicolon
        ]);
    }

    #[test]
    fn test_sql_lexer_operators() {
        use super::Token::*;

        assert_eq!(parse("> = >=< =><"),
            vec![
                GreaterThan, Equal, GreaterThanOrEqual, LessThan, Equal, GreaterThan, LessThan
            ]
        );

        assert_eq!(parse(" ><>> >< >"),
            vec![
                GreaterThan, NotEqual, GreaterThan, GreaterThan, LessThan, GreaterThan
            ]
        );
    }

    #[test]
    fn test_sql_lexer_blockcomment() {
        use super::Token::*;

        assert_eq!(parse("hello/*/a/**/,/*there, */world"), vec![
            id("hello"), Comma, id("world")
        ]);

        assert_eq!(parse("/ */"), vec![ForwardSlash, Asterisk, ForwardSlash]);

        assert_eq!(parse("/**/"), vec![]);

        assert_eq!(parse("a/* test\ntest** /\nb*/c"), vec![id("a"), id("c")]);
    }
}
<commit_msg>Add EXPLAIN keyword to lexer<commit_after>/// Disclaimer: The lexer is basically spaghetti. What did you expect?

#[derive(Clone, Debug, PartialEq)]
pub enum Token {
    // Words
    Select, From, Where, Group, Having, By, Limit,
    Distinct,
    Order, Asc, Desc,
    As, Join, Inner, Outer, Left, Right, On,
    Insert, Into, Values, Update, Delete,
    Create, Table, Index, Constraint,
    Primary, Key, Unique, References,
    And, Or,
    Between, In,
    Is, Not, Null,
    Explain,

    // Non-letter tokens
    Equal,
    NotEqual,
    LessThan, LessThanOrEqual,
    GreaterThan, GreaterThanOrEqual,

    Plus, Minus,

    LeftParen, RightParen,
    LeftBracket, RightBracket,
    Dot, Comma, Semicolon,

    Ampersand, Pipe, ForwardSlash,

    /// ||, the concatenate operator
    DoublePipe,

    /// *, the wildcard for SELECT
    Asterisk,

    /// ?, the prepared statement placeholder
    PreparedStatementPlaceholder,

    // Tokens with values
    Number(String),
    Ident(String),
    StringLiteral(String)
}

fn character_to_token(c: char) -> Option<Token> {
    use self::Token::*;

    Some(match c {
        '=' => Equal,
        '<' => LessThan,
        '>' => GreaterThan,
        '+' => Plus,
        '-' => Minus,
        '(' => LeftParen,
        ')' => RightParen,
        '[' => LeftBracket,
        ']' => RightBracket,
        '.' => Dot,
        ',' => Comma,
        ';' => Semicolon,
        '&' => Ampersand,
        '|' => Pipe,
        '*' => Asterisk,
        '/' => ForwardSlash,
        '?' => PreparedStatementPlaceholder,
        _ => return None
    })
}

fn word_to_token(word: String) -> Token {
    use self::Token::*;

    // Make all letters lowercase for comparison
    let word_cmp: String = word.chars().flat_map( |c| c.to_lowercase() ).collect();

    match word_cmp.as_slice() {
        "select" => Select,
        "from" => From,
        "where" => Where,
        "group" => Group,
        "having" => Having,
        "by" => By,
        "limit" => Limit,
        "distinct" => Distinct,
        "order" => Order,
        "asc" => Asc,
        "desc" => Desc,
        "as" => As,
        "join" => Join,
        "inner" => Inner,
        "outer" => Outer,
        "left" => Left,
        "right" => Right,
        "on" => On,
        "insert" => Insert,
        "into" => Into,
        "values" => Values,
        "update" => Update,
        "delete" => Delete,
        "create" => Create,
        "table" => Table,
        "index" => Index,
        "constraint" => Constraint,
        "primary" => Primary,
        "key" => Key,
        "unique" => Unique,
        "references" => References,
        "and" => And,
        "or" => Or,
        "between" => Between,
        "in" => In,
        "is" => Is,
        "not" => Not,
        "null" => Null,
        "explain" => Explain,
        _ => Ident(word)
    }
}

enum LexerState {
    NoState,
    Word,
    Backtick,
    Apostrophe { escaping: bool },
    Number { decimal: bool },
    /// Disambiguate an operator sequence.
    OperatorDisambiguate { first: char },
    LineComment,
    BlockComment { was_prev_char_asterisk: bool }
}

struct Lexer {
    state: LexerState,
    string_buffer: String,
    tokens: Vec<Token>
}

impl Lexer {
    fn no_state(&mut self, c: char) -> Result<LexerState, char> {
        match c {
            'a'...'z' | 'A'...'Z' | '_' => {
                self.string_buffer.push(c);
                Ok(LexerState::Word)
            },
            '`' => {
                Ok(LexerState::Backtick)
            }
            '\'' => {
                // string literal
                Ok(LexerState::Apostrophe { escaping: false })
            },
            '0'...'9' => {
                self.string_buffer.push(c);
                Ok(LexerState::Number { decimal: false })
            },
            ' ' | '\t' | '\n' => {
                // whitespace
                Ok(LexerState::NoState)
            },
            c => {
                use self::Token::*;

                match character_to_token(c) {
                    Some(LessThan) | Some(GreaterThan) | Some(Minus) | Some(Pipe) | Some(ForwardSlash) => {
                        Ok(LexerState::OperatorDisambiguate { first: c })
                    },
                    Some(token) => {
                        self.tokens.push(token);
                        Ok(LexerState::NoState)
                    },
                    None => {
                        // unknown character
                        Err(c)
                    }
                }
            }
        }
    }

    fn move_string_buffer(&mut self) -> String {
        use std::mem;
        mem::replace(&mut self.string_buffer, String::new())
    }

    pub fn feed_character(&mut self, c: Option<char>) {
        self.state = match self.state {
            LexerState::NoState => {
                match c {
                    Some(c) => self.no_state(c).unwrap(),
                    None => LexerState::NoState
                }
            },
            LexerState::Word => {
                match c {
                    Some(c) => match c {
                        'a'...'z' | 'A'...'Z' | '_' | '0'...'9' => {
                            self.string_buffer.push(c);
                            LexerState::Word
                        }
                        c => {
                            let buffer = self.move_string_buffer();
                            self.tokens.push(word_to_token(buffer));
                            self.no_state(c).unwrap()
                        }
                    },
                    None => {
                        let buffer = self.move_string_buffer();
                        self.tokens.push(word_to_token(buffer));
                        LexerState::NoState
                    }
                }
            },
            LexerState::Backtick => {
                match c {
                    Some('`') => {
                        let buffer = self.move_string_buffer();
                        self.tokens.push(Token::Ident(buffer));
                        LexerState::NoState
                    },
                    Some(c) => {
                        self.string_buffer.push(c);
                        LexerState::Backtick
                    },
                    None => {
                        // error: backtick did not finish
                        unimplemented!()
                    }
                }
            },
            LexerState::Apostrophe { escaping } => {
                if let Some(c) = c {
                    match (escaping, c) {
                        (false, '\'') => {
                            // unescaped apostrophe
                            let buffer = self.move_string_buffer();
                            self.tokens.push(Token::StringLiteral(buffer));
                            LexerState::NoState
                        },
                        (false, '\\') => {
                            // unescaped backslash
                            LexerState::Apostrophe { escaping: true }
                        },
                        (true, _) | _ => {
                            self.string_buffer.push(c);
                            LexerState::Apostrophe { escaping: false }
                        }
                    }
                } else {
                    // error: apostrophe did not finish
                    unimplemented!()
                }
            },
            LexerState::Number { decimal } => {
                if let Some(c) = c {
                    match c {
                        '0'...'9' => {
                            self.string_buffer.push(c);
                            LexerState::Number { decimal: decimal }
                        },
                        '.' if !decimal => {
                            // Add a decimal point. None has been added yet.
                            self.string_buffer.push(c);
                            LexerState::Number { decimal: true }
                        },
                        c => {
                            let buffer = self.move_string_buffer();
                            self.tokens.push(Token::Number(buffer));
                            self.no_state(c).unwrap()
                        }
                    }
                } else {
                    let buffer = self.move_string_buffer();
                    self.tokens.push(Token::Number(buffer));
                    LexerState::NoState
                }
            },
            LexerState::OperatorDisambiguate { first } => {
                use self::Token::*;

                if let Some(c) = c {
                    match (first, c) {
                        ('<', '>') => {
                            self.tokens.push(NotEqual);
                            LexerState::NoState
                        },
                        ('<', '=') => {
                            self.tokens.push(LessThanOrEqual);
                            LexerState::NoState
                        },
                        ('>', '=') => {
                            self.tokens.push(GreaterThanOrEqual);
                            LexerState::NoState
                        },
                        ('|', '|') => {
                            self.tokens.push(DoublePipe);
                            LexerState::NoState
                        },
                        ('-', '-') => {
                            LexerState::LineComment
                        },
                        ('/', '*') => {
                            LexerState::BlockComment { was_prev_char_asterisk: false }
                        },
                        _ => {
                            self.tokens.push(character_to_token(first).unwrap());
                            self.no_state(c).unwrap()
                        }
                    }
                } else {
                    self.tokens.push(character_to_token(first).unwrap());
                    LexerState::NoState
                }
            },
            LexerState::LineComment => {
                match c {
                    Some('\n') => LexerState::NoState,
                    _ => LexerState::LineComment
                }
            },
            LexerState::BlockComment { was_prev_char_asterisk } => {
                if was_prev_char_asterisk && c == Some('/') {
                    LexerState::NoState
                } else {
                    LexerState::BlockComment { was_prev_char_asterisk: c == Some('*') }
                }
            }
        };
    }
}

pub fn parse(sql: &str) -> Vec<Token> {
    let mut lexer = Lexer {
        state: LexerState::NoState,
        string_buffer: String::new(),
        tokens: Vec::new()
    };

    for c in sql.chars() {
        lexer.feed_character(Some(c));
    }

    lexer.feed_character(None);
    lexer.tokens
}

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

    fn id(value: &str) -> super::Token {
        super::Token::Ident(value.to_string())
    }

    fn number(value: &str) -> super::Token {
        super::Token::Number(value.to_string())
    }

    #[test]
    fn test_sql_lexer_dontconfuseidentswithkeywords() {
        use super::Token::*;
        // Not: AS, Ident("df")
        assert_eq!(parse("asdf"), vec![Ident("asdf".to_string())]);
    }

    #[test]
    fn test_sql_lexer_escape() {
        use super::Token::*;
        // Escaped apostrophe
        assert_eq!(parse(r"'\''"), vec![StringLiteral("'".to_string())]);
    }

    #[test]
    fn test_sql_lexer_numbers() {
        use super::Token::*;

        assert_eq!(parse("12345"), vec![number("12345")]);
        assert_eq!(parse("0.25"), vec![number("0.25")]);
        assert_eq!(parse("0.25 + -0.25"), vec![number("0.25"), Plus, Minus, number("0.25")]);
        assert_eq!(parse("-0.25 + 0.25"), vec![Minus, number("0.25"), Plus, number("0.25")]);
        assert_eq!(parse("- 0.25 - -0.25"), vec![Minus, number("0.25"), Minus, Minus, number("0.25")]);
        assert_eq!(parse("- 0.25 --0.25"), vec![Minus, number("0.25")]);
        assert_eq!(parse("0.25 -0.25"), vec![number("0.25"), Minus, number("0.25")]);
    }

    #[test]
    fn test_sql_lexer_query1() {
        use super::Token::*;

        assert_eq!(parse(" SeLECT a,    b as alias1 , c alias2, d ` alias three ` fRoM table1 WHERE a='Hello World'; "),
            vec![
                Select, id("a"), Comma, id("b"), As, id("alias1"), Comma,
                id("c"), id("alias2"), Comma, id("d"), id(" alias three "),
                From, id("table1"),
                Where, id("a"), Equal, StringLiteral("Hello World".to_string()), Semicolon
            ]
        );
    }

    #[test]
    fn test_sql_lexer_query2() {
        use super::Token::*;

        let query = r"
        -- Get employee count from each department
        SELECT d.id departmentId, count(e.id) employeeCount
        FROM department d
        LEFT JOIN employee e ON e.departmentId = d.id
        GROUP BY departmentId;
        ";

        assert_eq!(parse(query), vec![
            Select, id("d"), Dot, id("id"), id("departmentId"), Comma, id("count"), LeftParen, id("e"), Dot, id("id"), RightParen, id("employeeCount"),
            From, id("department"), id("d"),
            Left, Join, id("employee"), id("e"), On, id("e"), Dot, id("departmentId"), Equal, id("d"), Dot, id("id"),
            Group, By, id("departmentId"), Semicolon
        ]);
    }

    #[test]
    fn test_sql_lexer_operators() {
        use super::Token::*;

        assert_eq!(parse("> = >=< =><"),
            vec![
                GreaterThan, Equal, GreaterThanOrEqual, LessThan, Equal, GreaterThan, LessThan
            ]
        );

        assert_eq!(parse(" ><>> >< >"),
            vec![
                GreaterThan, NotEqual, GreaterThan, GreaterThan, LessThan, GreaterThan
            ]
        );
    }

    #[test]
    fn test_sql_lexer_blockcomment() {
        use super::Token::*;

        assert_eq!(parse("hello/*/a/**/,/*there, */world"), vec![
            id("hello"), Comma, id("world")
        ]);

        assert_eq!(parse("/ */"), vec![ForwardSlash, Asterisk, ForwardSlash]);

        assert_eq!(parse("/**/"), vec![]);

        assert_eq!(parse("a/* test\ntest** /\nb*/c"), vec![id("a"), id("c")]);
    }
}
<|endoftext|>