<commit_before>use handle::Owned;
use raw;
use raw::{ SSize_t };
use SV;

pub struct AV(Owned<raw::AV>);

impl AV {
    /// Construct new `AV` from a raw pointer without incrementing reference counter (raw pointer
    /// already "owns" one incref).
    pub unsafe fn from_raw_owned(pthx: raw::Interpreter, raw: *mut raw::AV) -> AV {
        AV(Owned::from_raw_owned(pthx, raw))
    }

    /// Construct new `AV` from a raw pointer and increment its reference counter (raw pointer is
    /// "borrowed" from another structure that owns one incref).
    pub unsafe fn from_raw_borrowed(pthx: raw::Interpreter, raw: *mut raw::AV) -> AV {
        AV(Owned::from_raw_borrowed(pthx, raw))
    }

    fn pthx(&self) -> raw::Interpreter { self.0.pthx() }

    fn as_ptr(&self) -> *mut raw::AV { self.0.as_ptr() }

    method! {
        /// Empty the array, preserving underlying allocation.
        ///
        /// See ['av_clear'](http://perldoc.perl.org/perlapi.html#av_clear).
        simple fn clear() = av_clear()
    }
    method! {
        /// Delete element of array and return it.
        ///
        /// See ['av_delete'](http://perldoc.perl.org/perlapi.html#av_delete).
        getter fn delete(key: SSize_t) = av_delete(key, 0)
    }
    method! {
        /// Delete element of array without returning it.
        ///
        /// See ['av_delete'](http://perldoc.perl.org/perlapi.html#av_delete).
        simple fn discard(key: SSize_t) = av_delete(key, raw::G_DISCARD as raw::I32)
    }
    method! {
        /// Return true when element exists in the array.
        ///
        /// See ['av_exists'](http://perldoc.perl.org/perlapi.html#av_exists).
        simple fn exists(key: SSize_t) -> bool = av_exists(key) != 0
    }
    method! {
        /// Grow array to contain at least `key` elements.
        ///
        /// See ['av_extend'](http://perldoc.perl.org/perlapi.html#av_extend).
        simple fn extend(key: SSize_t) = av_extend(key)
    }
    method! {
        /// Fetch element from array.
        ///
        /// See ['av_fetch'](http://perldoc.perl.org/perlapi.html#av_fetch).
        getptr fn fetch(key: SSize_t) = av_fetch(key, 0)
    }
    method! {
        /// Fetch element from array ready for assignment.
        ///
        /// See ['av_fetch'](http://perldoc.perl.org/perlapi.html#av_fetch).
        getptr fn fetch_lvalue(key: SSize_t) = av_fetch(key, 1)
    }
    method! {
        /// Set array length to `fill + 1`.
        ///
        /// See ['av_fill'](http://perldoc.perl.org/perlapi.html#av_fill).
        simple fn fill(fill: SSize_t) = av_fill(fill)
    }
    method! {
        /// Return the highest index in the array.
        ///
        /// See ['av_top_index'](http://perldoc.perl.org/perlapi.html#av_top_index).
        simple fn top_index() -> SSize_t = av_len()
    }
    method! {
        /// Remove and return one element at the end of the array.
        ///
        /// See ['av_pop'](http://perldoc.perl.org/perlapi.html#av_pop).
        getter fn pop() = av_pop()
    }
    method! {
        /// Append new element to the array.
        ///
        /// See ['av_push'](http://perldoc.perl.org/perlapi.html#av_push).
        simple fn push(val: SV) = av_push(val.into_raw())
    }
    method! {
        /// Remove and return one element at the beginning of the array.
        ///
        /// See ['av_shift'](http://perldoc.perl.org/perlapi.html#av_shift).
        getter fn shift() = av_shift()
    }
    method! {
        ///  Clear the array and release array memory.
        ///
        /// See ['av_undef'](http://perldoc.perl.org/perlapi.html#av_undef).
        simple fn undef() = av_undef()
    }
    method! {
        /// Insert one or more `undef` elements at the beginning of the array.
        ///
        /// See ['av_unshift'](http://perldoc.perl.org/perlapi.html#av_unshift).
        simple fn unshift(num: SSize_t) = av_unshift(num)
    }

    /// Set array element at index `key` to `val`.
    ///
    /// See ['av_store'](http://perldoc.perl.org/perlapi.html#av_store).
    pub fn store(&self, key: SSize_t, val: SV) {
        unsafe {
            let svpp = self.pthx().av_store(self.as_ptr(), key, val.into_raw());
            if !svpp.is_null() { self.pthx().sv_refcnt_dec(*svpp) }
        }
    }
}
<commit_msg>Move unsafe constructors to the end of the impl.<commit_after>use handle::Owned;
use raw;
use raw::{ SSize_t };
use SV;

pub struct AV(Owned<raw::AV>);

impl AV {
    fn pthx(&self) -> raw::Interpreter { self.0.pthx() }

    fn as_ptr(&self) -> *mut raw::AV { self.0.as_ptr() }

    method! {
        /// Empty the array, preserving underlying allocation.
        ///
        /// See ['av_clear'](http://perldoc.perl.org/perlapi.html#av_clear).
        simple fn clear() = av_clear()
    }
    method! {
        /// Delete element of array and return it.
        ///
        /// See ['av_delete'](http://perldoc.perl.org/perlapi.html#av_delete).
        getter fn delete(key: SSize_t) = av_delete(key, 0)
    }
    method! {
        /// Delete element of array without returning it.
        ///
        /// See ['av_delete'](http://perldoc.perl.org/perlapi.html#av_delete).
        simple fn discard(key: SSize_t) = av_delete(key, raw::G_DISCARD as raw::I32)
    }
    method! {
        /// Return true when element exists in the array.
        ///
        /// See ['av_exists'](http://perldoc.perl.org/perlapi.html#av_exists).
        simple fn exists(key: SSize_t) -> bool = av_exists(key) != 0
    }
    method! {
        /// Grow array to contain at least `key` elements.
        ///
        /// See ['av_extend'](http://perldoc.perl.org/perlapi.html#av_extend).
        simple fn extend(key: SSize_t) = av_extend(key)
    }
    method! {
        /// Fetch element from array.
        ///
        /// See ['av_fetch'](http://perldoc.perl.org/perlapi.html#av_fetch).
        getptr fn fetch(key: SSize_t) = av_fetch(key, 0)
    }
    method! {
        /// Fetch element from array ready for assignment.
        ///
        /// See ['av_fetch'](http://perldoc.perl.org/perlapi.html#av_fetch).
        getptr fn fetch_lvalue(key: SSize_t) = av_fetch(key, 1)
    }
    method! {
        /// Set array length to `fill + 1`.
        ///
        /// See ['av_fill'](http://perldoc.perl.org/perlapi.html#av_fill).
        simple fn fill(fill: SSize_t) = av_fill(fill)
    }
    method! {
        /// Return the highest index in the array.
        ///
        /// See ['av_top_index'](http://perldoc.perl.org/perlapi.html#av_top_index).
        simple fn top_index() -> SSize_t = av_len()
    }
    method! {
        /// Remove and return one element at the end of the array.
        ///
        /// See ['av_pop'](http://perldoc.perl.org/perlapi.html#av_pop).
        getter fn pop() = av_pop()
    }
    method! {
        /// Append new element to the array.
        ///
        /// See ['av_push'](http://perldoc.perl.org/perlapi.html#av_push).
        simple fn push(val: SV) = av_push(val.into_raw())
    }
    method! {
        /// Remove and return one element at the beginning of the array.
        ///
        /// See ['av_shift'](http://perldoc.perl.org/perlapi.html#av_shift).
        getter fn shift() = av_shift()
    }
    method! {
        ///  Clear the array and release array memory.
        ///
        /// See ['av_undef'](http://perldoc.perl.org/perlapi.html#av_undef).
        simple fn undef() = av_undef()
    }
    method! {
        /// Insert one or more `undef` elements at the beginning of the array.
        ///
        /// See ['av_unshift'](http://perldoc.perl.org/perlapi.html#av_unshift).
        simple fn unshift(num: SSize_t) = av_unshift(num)
    }

    /// Set array element at index `key` to `val`.
    ///
    /// See ['av_store'](http://perldoc.perl.org/perlapi.html#av_store).
    pub fn store(&self, key: SSize_t, val: SV) {
        unsafe {
            let svpp = self.pthx().av_store(self.as_ptr(), key, val.into_raw());
            if !svpp.is_null() { self.pthx().sv_refcnt_dec(*svpp) }
        }
    }

    /// Construct new AV from a raw pointer without incrementing reference counter (raw pointer
    /// already "owns" one incref).
    pub unsafe fn from_raw_owned(pthx: raw::Interpreter, raw: *mut raw::AV) -> AV {
        AV(Owned::from_raw_owned(pthx, raw))
    }

    /// Construct new AV from a raw pointer and increment its reference counter (raw pointer is
    /// "borrowed" from another structure that owns one incref).
    pub unsafe fn from_raw_borrowed(pthx: raw::Interpreter, raw: *mut raw::AV) -> AV {
        AV(Owned::from_raw_borrowed(pthx, raw))
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * 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.
 */

use ffiasync;

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::slice;
use std::sync::{Barrier, Mutex};

pub enum PersistenceType {
    Default = 0,
    Nothing = 1,
    User    = 2,
}

#[derive(Debug)]
pub enum ConnectReturnCode {
    Success               = 0,
    UnacceptableProtocol  = 1,
    IdentifierRejected    = 2,
    ServerUnavailable     = 3,
    BadUsernameOrPassword = 4,
    NotAuthorized         = 5,
    Reserved              = 6,
}

#[derive(Debug)]
pub enum ConnectErr {
    ReturnCode(ConnectReturnCode),
    Callback
}

#[derive(Debug, Copy, Clone)]
pub enum MqttError {
    Code(i32),
    Bad
}

#[derive(Debug)]
pub enum Qos {
    FireAndForget  = 0,
    AtLeastOnce    = 1,
    OnceAndOneOnly = 2,
}

#[derive(Debug, Copy, Clone)]
pub enum ActionResult {
    None,
    Ok,
    Err(MqttError)
}

#[derive(Debug)]
pub struct Message {
    pub topic     : String,
    pub payload   : Vec<u8>,
    pub qos       : Qos,
    pub retained  : bool,
    pub duplicate : bool,
}

pub struct AsyncClient {
    handle        : ffiasync::MQTTAsync,
    barrier       : Barrier,
    action_result : ActionResult,
    messages      : Mutex<Vec<Message>>,
}

impl AsyncClient {

    /// Ensures the FFI struct is consistent for callback invocation.
    ///
    /// Because the user may move this struct, the `context` pointer
    /// passed back to FFI callbacks might be invalidated. This function
    /// should be called before FFI actions that might fire callbacks to ensure
    /// the self-pointer is valid.
    fn context(&mut self) -> *mut c_void {
        self as *mut _ as *mut c_void
    }

    pub fn new(address: &str, clientid: &str, persistence: PersistenceType) -> Result<AsyncClient, MqttError> {
        let mut handle                      = unsafe{mem::zeroed()};
        let mut persistence_context: c_void = unsafe{mem::zeroed()};

        let c_url          = CString::new(address).unwrap();
        let c_clientid     = CString::new(clientid).unwrap();
        let array_url      = c_url.as_bytes_with_nul();
        let array_clientid = c_clientid.as_bytes_with_nul();

        let error = unsafe {
            ffiasync::MQTTAsync_create(&mut handle,
                                       mem::transmute::<&u8, *const c_char>(&array_url[0]),
                                       mem::transmute::<&u8, *const c_char>(&array_clientid[0]),
                                       persistence as i32,
                                       &mut persistence_context)
        };

        match error {
            0   => { Ok( AsyncClient {
                        handle          : handle,
                        barrier         : Barrier::new(2),
                        action_result   : ActionResult::None,
                        messages        : Mutex::new(Vec::new()),
                    })},
            err => Err(MqttError::Code(err))
        }
    }

    pub fn connect(&mut self, options: &mut AsyncConnectOptions) -> Result<(), ConnectErr> {
        info!("connect..");
        unsafe {
            ffiasync::MQTTAsync_setCallbacks(self.handle,
                                             self.context(),
                                             Some(Self::disconnected),
                                             Some(Self::received),
                                             None);
        }

        // fill in FFI private struct
        options.options.keepAliveInterval = options.keep_alive_interval;
        options.options.cleansession      = options.cleansession;
        options.options.maxInflight       = options.max_in_flight;
        options.options.connectTimeout    = options.connect_timeout;
        options.options.retryInterval     = options.retry_interval;

        // register callbacks
        options.options.context   = self.context();
        options.options.onSuccess = Some(Self::action_succeeded);
        options.options.onFailure = Some(Self::action_failed);

        self.action_result = ActionResult::None;
        let error = unsafe {
            ffiasync::MQTTAsync_connect(self.handle, &options.options)
        };
        if error == 0 {
            self.barrier.wait();
            if self.is_connected() {
                Ok(())
            } else {
                Err(ConnectErr::Callback)
            }
        } else {
            Err(ConnectErr::ReturnCode(
                match error {
                    1 => ConnectReturnCode::UnacceptableProtocol,
                    2 => ConnectReturnCode::IdentifierRejected,
                    3 => ConnectReturnCode::ServerUnavailable,
                    4 => ConnectReturnCode::BadUsernameOrPassword,
                    5 => ConnectReturnCode::NotAuthorized,
                    _ => ConnectReturnCode::Reserved,
            }))
        }
    }

    #[allow(unused_variables)]
    extern "C" fn disconnected(context: *mut c_void, cause: *mut c_char) -> () {
        warn!("disconnected");
        assert!(!context.is_null());
    }

    pub fn is_connected(&mut self) -> bool {
        let ret = unsafe {
            ffiasync::MQTTAsync_isConnected(self.handle)
        };

        match ret {
            1 => true,
            _ => false,
        }
    }

    pub fn send(&mut self, data: &[u8], topic: &str, qos: Qos) -> Result<(), MqttError> {
        info!("send..");
        let mut responseoption = ffiasync::MQTTAsync_responseOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8],
            struct_version  : 0,
            onSuccess       : Some(Self::action_succeeded),
            onFailure       : Some(Self::action_failed),
            context         : self.context(),
            token           : 0,
        };

        let mut message = ffiasync::MQTTAsync_message {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'M' as i8],
            struct_version  : 0,
            payloadlen      : data.len() as i32,
            payload         : unsafe {mem::transmute::<&u8, *mut c_void>(&data[0])},
            qos             : qos as c_int,
            retained        : 0,
            dup             : 0,
            msgid           : 0,
        };

        let c_topic        = CString::new(topic).unwrap();
        let array_topic    = c_topic.as_bytes_with_nul();
        self.action_result = ActionResult::None;

        let error = unsafe {
            ffiasync::MQTTAsync_sendMessage(self.handle,
                                            mem::transmute::<&u8, *const c_char>(&array_topic[0]),
                                            &mut message,
                                            &mut responseoption)
        };
        if error == 0 {
            self.barrier.wait();
            match self.action_result {
                ActionResult::None   => unreachable!(),
                ActionResult::Ok     => Ok(()),
                ActionResult::Err(x) => Err(x)
            }
        } else { Err(MqttError::Code(error)) }
    }

    pub fn subscribe(&mut self, topic: &str, qos: Qos) -> Result<(), MqttError> {
        info!("subscribe..");
        let mut responseoption = ffiasync::MQTTAsync_responseOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8],
            struct_version  : 0,
            onSuccess       : Some(Self::action_succeeded),
            onFailure       : Some(Self::action_failed),
            context         : self.context(),
            token           : 0,
        };

        let c_topic     = CString::new(topic).unwrap();
        let array_topic = c_topic.as_bytes_with_nul();

        let c_qos: i32 = match qos {
            Qos::FireAndForget  => 0,
            Qos::AtLeastOnce    => 1,
            Qos::OnceAndOneOnly => 2,
        };
        self.action_result = ActionResult::None;

        let error = unsafe {
            ffiasync::MQTTAsync_subscribe(self.handle,
                                          mem::transmute::<&u8, *const c_char>(&array_topic[0]),
                                          c_qos,
                                          &mut responseoption)
        };

        if error == 0 {
            self.barrier.wait();
            match self.action_result {
                ActionResult::None   => unreachable!(),
                ActionResult::Ok     => Ok(()),
                ActionResult::Err(x) => Err(x)
            }
        } else { Err(MqttError::Code(error)) }
    }

    #[allow(unused_variables)]
    extern "C" fn action_succeeded(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_successData) -> () {
        info!("success callback");
        assert!(!context.is_null());
        let selfclient: &mut AsyncClient = unsafe {mem::transmute(context)};
        selfclient.action_result = ActionResult::Ok;
        selfclient.barrier.wait();
    }

    extern "C" fn action_failed(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_failureData) -> () {
        info!("failure callback");
        assert!(!context.is_null());
        let selfclient : &mut AsyncClient = unsafe {mem::transmute(context)};
        if response.is_null() {
            selfclient.action_result = ActionResult::Err(MqttError::Bad);
        } else {
            let resp : &mut ffiasync::MQTTAsync_failureData = unsafe {mem::transmute(response)};
            selfclient.action_result = ActionResult::Err(MqttError::Code(resp.code));
        }
        selfclient.barrier.wait();
    }

    extern "C" fn received(context: *mut ::libc::c_void, topic_name: *mut ::libc::c_char, topic_len: ::libc::c_int, amessage: *mut ffiasync::MQTTAsync_message) -> i32 {
        let c_topic = unsafe {CStr::from_ptr(topic_name).to_bytes()};
        let topic   = String::from_utf8(c_topic.to_vec()).unwrap();
        assert_eq!(topic.len(), topic_len as usize);

        assert!(!amessage.is_null());
        let transmessage: &mut ffiasync::MQTTAsync_message = unsafe {mem::transmute(amessage)};
        let payload: &[u8] = unsafe {
                slice::from_raw_parts(transmessage.payload as *mut u8, transmessage.payloadlen as usize)
        };

        assert!(!context.is_null());
        let selfclient : &mut AsyncClient = unsafe {mem::transmute(context)};

        let qos = match transmessage.qos {
            0 => Qos::FireAndForget,
            1 => Qos::AtLeastOnce,
            2 => Qos::OnceAndOneOnly,
            _ => unreachable!(),
        };

        let retained: bool = match transmessage.retained {
            0 => false,
            1 => true,
            _ => unreachable!(),
        };

        let duplicate: bool = match transmessage.dup {
            0 => false,
            1 => true,
            _ => unreachable!(),
        };

        let msg = Message {
            topic     : topic,
            payload   : payload.to_vec(),
            qos       : qos,
            retained  : retained,
            duplicate : duplicate,
        };

        let mut messages = selfclient.messages.lock().unwrap();
        messages.push(msg);

        let mut msg = amessage;
        unsafe{ffiasync::MQTTAsync_freeMessage(&mut msg)};
        unsafe{ffiasync::MQTTAsync_free(mem::transmute(topic_name))};
        1
    }

    pub fn messages(&mut self) -> AsyncClientIntoIterator {
        AsyncClientIntoIterator {
            client: self,
        }
    }
}

pub struct AsyncClientIntoIterator<'a> {
    client: &'a mut AsyncClient,
}

impl <'a>Iterator for AsyncClientIntoIterator<'a> {
    type Item = Message;
    fn next(&mut self) -> Option<Message> {
        let mut messages = self.client.messages.lock().unwrap();
        if messages.len() > 0 {
            Some(messages.remove(0))
        }
        else {
            None
        }
    }
}

impl Drop for AsyncClient {
    fn drop(&mut self) {
        unsafe{ffiasync::MQTTAsync_destroy(&mut self.handle)};
    }
}

pub struct AsyncConnectOptions {
    options: ffiasync::MQTTAsync_connectOptions,

    pub keep_alive_interval : i32,
    pub cleansession        : i32,
    pub max_in_flight       : i32,
    pub connect_timeout     : i32,
    pub retry_interval      : i32,
}
impl AsyncConnectOptions {
    pub fn new() -> AsyncConnectOptions {
        let ffioptions = ffiasync::MQTTAsync_connectOptions {
            struct_id           : ['M' as i8, 'Q' as i8, 'T' as i8, 'C' as i8],
            struct_version      : 3,
            keepAliveInterval   : 60,
            cleansession        : 1,
            maxInflight         : 10,
            will                : ptr::null_mut(),
            username            : ptr::null_mut(),
            password            : ptr::null_mut(),
            connectTimeout      : 30,
            retryInterval       : 0,
            ssl                 : ptr::null_mut(),
            onSuccess           : None,
            onFailure           : None,
            context             : ptr::null_mut(),
            serverURIcount      : 0,
            serverURIs          : ptr::null_mut(),
            MQTTVersion         : 0,
        };

        let options = AsyncConnectOptions {
            options             : ffioptions,

            keep_alive_interval : 20,
            cleansession        : 1,
            max_in_flight       : 10,
            connect_timeout     : 30,
            retry_interval      : 0,
        };

        options
    }
}

// it is going to be used in the future
// just silence warning for now
#[allow(dead_code)]
pub struct AsyncDisconnectOptions {
    options: ffiasync::MQTTAsync_disconnectOptions,
}
impl AsyncDisconnectOptions {

    pub fn new() -> ffiasync::MQTTAsync_disconnectOptions {
        let options = ffiasync::MQTTAsync_disconnectOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'D' as i8],
            struct_version  : 0,
            timeout         : 0,
            onSuccess       : ptr::null_mut(),
            onFailure       : ptr::null_mut(),
            context         : ptr::null_mut(),
        };

        options
    }
}
<commit_msg>add ActionResult code to CallbackFailure, replace info! with debug!<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * 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.
 */

use ffiasync;

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::slice;
use std::sync::{Barrier, Mutex};

pub enum PersistenceType {
    Default = 0,
    Nothing = 1,
    User    = 2,
}

#[derive(Debug)]
pub enum ConnectReturnCode {
    Success               = 0,
    UnacceptableProtocol  = 1,
    IdentifierRejected    = 2,
    ServerUnavailable     = 3,
    BadUsernameOrPassword = 4,
    NotAuthorized         = 5,
    Reserved              = 6,
}

#[derive(Debug)]
pub enum ConnectErr {
    ReturnCode(ConnectReturnCode),
    CallbackFailure(ActionResult),
}

#[derive(Debug, Copy, Clone)]
pub enum MqttError {
    Code(i32),
    Null
}

#[derive(Debug)]
pub enum Qos {
    FireAndForget  = 0,
    AtLeastOnce    = 1,
    OnceAndOneOnly = 2,
}

#[derive(Debug, Copy, Clone)]
pub enum ActionResult {
    None,
    Ok,
    Err(MqttError)
}

#[derive(Debug)]
pub struct Message {
    pub topic     : String,
    pub payload   : Vec<u8>,
    pub qos       : Qos,
    pub retained  : bool,
    pub duplicate : bool,
}

pub struct AsyncClient {
    handle        : ffiasync::MQTTAsync,
    barrier       : Barrier,
    action_result : ActionResult,
    messages      : Mutex<Vec<Message>>,
}

impl AsyncClient {

    /// Ensures the FFI struct is consistent for callback invocation.
    ///
    /// Because the user may move this struct, the `context` pointer
    /// passed back to FFI callbacks might be invalidated. This function
    /// should be called before FFI actions that might fire callbacks to ensure
    /// the self-pointer is valid.
    fn context(&mut self) -> *mut c_void {
        self as *mut _ as *mut c_void
    }

    pub fn new(address: &str, clientid: &str, persistence: PersistenceType) -> Result<AsyncClient, MqttError> {
        let mut handle                      = unsafe{mem::zeroed()};
        let mut persistence_context: c_void = unsafe{mem::zeroed()};

        let c_url          = CString::new(address).unwrap();
        let c_clientid     = CString::new(clientid).unwrap();
        let array_url      = c_url.as_bytes_with_nul();
        let array_clientid = c_clientid.as_bytes_with_nul();

        let error = unsafe {
            ffiasync::MQTTAsync_create(&mut handle,
                                       mem::transmute::<&u8, *const c_char>(&array_url[0]),
                                       mem::transmute::<&u8, *const c_char>(&array_clientid[0]),
                                       persistence as i32,
                                       &mut persistence_context)
        };

        match error {
            0   => { Ok( AsyncClient {
                        handle          : handle,
                        barrier         : Barrier::new(2),
                        action_result   : ActionResult::None,
                        messages        : Mutex::new(Vec::new()),
                    })},
            err => Err(MqttError::Code(err))
        }
    }

    pub fn connect(&mut self, options: &mut AsyncConnectOptions) -> Result<(), ConnectErr> {
        debug!("connect..");
        unsafe {
            ffiasync::MQTTAsync_setCallbacks(self.handle,
                                             self.context(),
                                             Some(Self::disconnected),
                                             Some(Self::received),
                                             None);
        }

        // fill in FFI private struct
        options.options.keepAliveInterval = options.keep_alive_interval;
        options.options.cleansession      = options.cleansession;
        options.options.maxInflight       = options.max_in_flight;
        options.options.connectTimeout    = options.connect_timeout;
        options.options.retryInterval     = options.retry_interval;

        // register callbacks
        options.options.context   = self.context();
        options.options.onSuccess = Some(Self::action_succeeded);
        options.options.onFailure = Some(Self::action_failed);

        self.action_result = ActionResult::None;
        let error = unsafe {
            ffiasync::MQTTAsync_connect(self.handle, &options.options)
        };
        if error == 0 {
            self.barrier.wait();
            if self.is_connected() {
                Ok(())
            } else {
                Err(ConnectErr::CallbackFailure(self.action_result))
            }
        } else {
            Err(ConnectErr::ReturnCode(
                match error {
                    1 => ConnectReturnCode::UnacceptableProtocol,
                    2 => ConnectReturnCode::IdentifierRejected,
                    3 => ConnectReturnCode::ServerUnavailable,
                    4 => ConnectReturnCode::BadUsernameOrPassword,
                    5 => ConnectReturnCode::NotAuthorized,
                    _ => ConnectReturnCode::Reserved,
            }))
        }
    }

    #[allow(unused_variables)]
    extern "C" fn disconnected(context: *mut c_void, cause: *mut c_char) -> () {
        warn!("disconnected");
        assert!(!context.is_null());
    }

    pub fn is_connected(&mut self) -> bool {
        let ret = unsafe {
            ffiasync::MQTTAsync_isConnected(self.handle)
        };

        match ret {
            1 => true,
            _ => false,
        }
    }

    pub fn send(&mut self, data: &[u8], topic: &str, qos: Qos) -> Result<(), MqttError> {
        debug!("send..");
        let mut responseoption = ffiasync::MQTTAsync_responseOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8],
            struct_version  : 0,
            onSuccess       : Some(Self::action_succeeded),
            onFailure       : Some(Self::action_failed),
            context         : self.context(),
            token           : 0,
        };

        let mut message = ffiasync::MQTTAsync_message {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'M' as i8],
            struct_version  : 0,
            payloadlen      : data.len() as i32,
            payload         : unsafe {mem::transmute::<&u8, *mut c_void>(&data[0])},
            qos             : qos as c_int,
            retained        : 0,
            dup             : 0,
            msgid           : 0,
        };

        let c_topic        = CString::new(topic).unwrap();
        let array_topic    = c_topic.as_bytes_with_nul();
        self.action_result = ActionResult::None;

        let error = unsafe {
            ffiasync::MQTTAsync_sendMessage(self.handle,
                                            mem::transmute::<&u8, *const c_char>(&array_topic[0]),
                                            &mut message,
                                            &mut responseoption)
        };
        if error == 0 {
            self.barrier.wait();
            match self.action_result {
                ActionResult::None   => unreachable!(),
                ActionResult::Ok     => Ok(()),
                ActionResult::Err(x) => Err(x)
            }
        } else { Err(MqttError::Code(error)) }
    }

    pub fn subscribe(&mut self, topic: &str, qos: Qos) -> Result<(), MqttError> {
        debug!("subscribe..");
        let mut responseoption = ffiasync::MQTTAsync_responseOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8],
            struct_version  : 0,
            onSuccess       : Some(Self::action_succeeded),
            onFailure       : Some(Self::action_failed),
            context         : self.context(),
            token           : 0,
        };

        let c_topic     = CString::new(topic).unwrap();
        let array_topic = c_topic.as_bytes_with_nul();

        let c_qos: i32 = match qos {
            Qos::FireAndForget  => 0,
            Qos::AtLeastOnce    => 1,
            Qos::OnceAndOneOnly => 2,
        };
        self.action_result = ActionResult::None;

        let error = unsafe {
            ffiasync::MQTTAsync_subscribe(self.handle,
                                          mem::transmute::<&u8, *const c_char>(&array_topic[0]),
                                          c_qos,
                                          &mut responseoption)
        };

        if error == 0 {
            self.barrier.wait();
            match self.action_result {
                ActionResult::None   => unreachable!(),
                ActionResult::Ok     => Ok(()),
                ActionResult::Err(x) => Err(x)
            }
        } else { Err(MqttError::Code(error)) }
    }

    #[allow(unused_variables)]
    extern "C" fn action_succeeded(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_successData) -> () {
        debug!("success callback");
        assert!(!context.is_null());
        let selfclient: &mut AsyncClient = unsafe {mem::transmute(context)};
        selfclient.action_result = ActionResult::Ok;
        selfclient.barrier.wait();
    }

    extern "C" fn action_failed(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_failureData) -> () {
        debug!("failure callback");
        assert!(!context.is_null());
        let selfclient : &mut AsyncClient = unsafe {mem::transmute(context)};
        if response.is_null() {
            selfclient.action_result = ActionResult::Err(MqttError::Null);
        } else {
            let resp : &mut ffiasync::MQTTAsync_failureData = unsafe {mem::transmute(response)};
            selfclient.action_result = ActionResult::Err(MqttError::Code(resp.code));
        }
        selfclient.barrier.wait();
    }

    extern "C" fn received(context: *mut ::libc::c_void, topic_name: *mut ::libc::c_char, topic_len: ::libc::c_int, amessage: *mut ffiasync::MQTTAsync_message) -> i32 {
        let c_topic = unsafe {CStr::from_ptr(topic_name).to_bytes()};
        let topic   = String::from_utf8(c_topic.to_vec()).unwrap();
        assert_eq!(topic.len(), topic_len as usize);

        assert!(!amessage.is_null());
        let transmessage: &mut ffiasync::MQTTAsync_message = unsafe {mem::transmute(amessage)};
        let payload: &[u8] = unsafe {
                slice::from_raw_parts(transmessage.payload as *mut u8, transmessage.payloadlen as usize)
        };

        assert!(!context.is_null());
        let selfclient : &mut AsyncClient = unsafe {mem::transmute(context)};

        let qos = match transmessage.qos {
            0 => Qos::FireAndForget,
            1 => Qos::AtLeastOnce,
            2 => Qos::OnceAndOneOnly,
            _ => unreachable!(),
        };

        let retained: bool = match transmessage.retained {
            0 => false,
            1 => true,
            _ => unreachable!(),
        };

        let duplicate: bool = match transmessage.dup {
            0 => false,
            1 => true,
            _ => unreachable!(),
        };

        let msg = Message {
            topic     : topic,
            payload   : payload.to_vec(),
            qos       : qos,
            retained  : retained,
            duplicate : duplicate,
        };

        let mut messages = selfclient.messages.lock().unwrap();
        messages.push(msg);

        let mut msg = amessage;
        unsafe{ffiasync::MQTTAsync_freeMessage(&mut msg)};
        unsafe{ffiasync::MQTTAsync_free(mem::transmute(topic_name))};
        1
    }

    pub fn messages(&mut self) -> AsyncClientIntoIterator {
        AsyncClientIntoIterator {
            client: self,
        }
    }
}

pub struct AsyncClientIntoIterator<'a> {
    client: &'a mut AsyncClient,
}

impl <'a>Iterator for AsyncClientIntoIterator<'a> {
    type Item = Message;
    fn next(&mut self) -> Option<Message> {
        let mut messages = self.client.messages.lock().unwrap();
        if messages.len() > 0 {
            Some(messages.remove(0))
        }
        else {
            None
        }
    }
}

impl Drop for AsyncClient {
    fn drop(&mut self) {
        unsafe{ffiasync::MQTTAsync_destroy(&mut self.handle)};
    }
}

pub struct AsyncConnectOptions {
    options: ffiasync::MQTTAsync_connectOptions,

    pub keep_alive_interval : i32,
    pub cleansession        : i32,
    pub max_in_flight       : i32,
    pub connect_timeout     : i32,
    pub retry_interval      : i32,
}
impl AsyncConnectOptions {
    pub fn new() -> AsyncConnectOptions {
        let ffioptions = ffiasync::MQTTAsync_connectOptions {
            struct_id           : ['M' as i8, 'Q' as i8, 'T' as i8, 'C' as i8],
            struct_version      : 3,
            keepAliveInterval   : 60,
            cleansession        : 1,
            maxInflight         : 10,
            will                : ptr::null_mut(),
            username            : ptr::null_mut(),
            password            : ptr::null_mut(),
            connectTimeout      : 30,
            retryInterval       : 0,
            ssl                 : ptr::null_mut(),
            onSuccess           : None,
            onFailure           : None,
            context             : ptr::null_mut(),
            serverURIcount      : 0,
            serverURIs          : ptr::null_mut(),
            MQTTVersion         : 0,
        };

        let options = AsyncConnectOptions {
            options             : ffioptions,

            keep_alive_interval : 20,
            cleansession        : 1,
            max_in_flight       : 10,
            connect_timeout     : 30,
            retry_interval      : 0,
        };

        options
    }
}

// it is going to be used in the future
// just silence warning for now
#[allow(dead_code)]
pub struct AsyncDisconnectOptions {
    options: ffiasync::MQTTAsync_disconnectOptions,
}
impl AsyncDisconnectOptions {

    pub fn new() -> ffiasync::MQTTAsync_disconnectOptions {
        let options = ffiasync::MQTTAsync_disconnectOptions {
            struct_id       : ['M' as i8, 'Q' as i8, 'T' as i8, 'D' as i8],
            struct_version  : 0,
            timeout         : 0,
            onSuccess       : ptr::null_mut(),
            onFailure       : ptr::null_mut(),
            context         : ptr::null_mut(),
        };

        options
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};

use syn::{self, Data, Field, Ident, Lit, Meta};

use super::{Context, RustlerAttr};

pub fn transcoder_decorator(ast: &syn::DeriveInput) -> TokenStream {
    let ctx = Context::from_ast(ast);

    let elixir_module = get_module(ast, &ctx);

    let struct_fields = match ast.data {
        Data::Struct(ref data_struct) => &data_struct.fields,
        Data::Enum(_) => panic!("NifStruct can only be used with structs"),
        Data::Union(_) => panic!("NifStruct can only be used with structs"),
    };

    let num_lifetimes = ast.generics.lifetimes().count();
    if num_lifetimes > 1 {
        panic!("Struct can only have one lifetime argument");
    }
    let has_lifetime = num_lifetimes == 1;

    let field_atoms: Vec<TokenStream> = struct_fields
        .iter()
        .map(|field| {
            let ident = field.ident.as_ref().unwrap();
            let ident_str = ident.to_string();

            let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());

            quote! {
                atom #atom_fun = #ident_str;
            }
        })
        .collect();

    let atom_defs = quote! {
        ::rustler::rustler_atoms! {
            atom atom_struct = "__struct__";
            atom atom_module = #elixir_module;
            #(#field_atoms)*
        }
    };

    let struct_fields: Vec<_> = struct_fields.iter().collect();

    let decoder = if ctx.decode() {
        gen_decoder(&ast.ident, &struct_fields, &atom_defs, has_lifetime)
    } else {
        quote! {}
    };

    let encoder = if ctx.encode() {
        gen_encoder(&ast.ident, &struct_fields, &atom_defs, has_lifetime)
    } else {
        quote! {}
    };

    let gen = quote! {
        #decoder
        #encoder
    };

    gen
}

pub fn gen_decoder(
    struct_name: &Ident,
    fields: &[&Field],
    atom_defs: &TokenStream,
    has_lifetime: bool,
) -> TokenStream {
    let struct_type = if has_lifetime {
        quote! { #struct_name <'a> }
    } else {
        quote! { #struct_name }
    };

    let field_defs: Vec<TokenStream> = fields
        .iter()
        .map(|field| {
            let ident = field.ident.as_ref().unwrap();
            let ident_str = ident.to_string();
            let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());
            let error_message = format!("Could not decode field :{} on %{}{{}}", ident.to_string(), struct_name.to_string());
            quote! {
                #ident: match ::rustler::Decoder::decode(term.map_get(#atom_fun().encode(env))?) {
                    Err(_) => return Err(::rustler::Error::RaiseTerm(Box::new(#error_message))),
                    Ok(stuff) => stuff
                }
            }
        })
        .collect();

    let gen = quote! {
        impl<'a> ::rustler::Decoder<'a> for #struct_type {
            fn decode(term: ::rustler::Term<'a>) -> Result<Self, ::rustler::Error> {
                use ::rustler::Encoder;

                #atom_defs

                let env = term.get_env();

                let module: ::rustler::types::atom::Atom = term.map_get(atom_struct().to_term(env))?.decode()?;
                if module != atom_module() {
                    return Err(::rustler::Error::Atom("invalid_struct"));
                }

                Ok(#struct_name { #(#field_defs),* })
            }
        }
    };

    gen
}

pub fn gen_encoder(
    struct_name: &Ident,
    fields: &[&Field],
    atom_defs: &TokenStream,
    has_lifetime: bool,
) -> TokenStream {
    let struct_type = if has_lifetime {
        quote! { #struct_name <'b> }
    } else {
        quote! { #struct_name }
    };

    let field_defs: Vec<TokenStream> = fields.iter().map(|field| {
        let field_ident = field.ident.as_ref().unwrap();
        let field_ident_str = field_ident.to_string();
        let atom_fun = Ident::new(&format!("atom_{}", field_ident_str), Span::call_site());
        quote! {
            map = map.map_put(#atom_fun().encode(env), self.#field_ident.encode(env)).ok().unwrap();
        }
    }).collect();

    let gen = quote! {
        impl<'b> ::rustler::Encoder for #struct_type {
            fn encode<'a>(&self, env: ::rustler::Env<'a>) -> ::rustler::Term<'a> {
                #atom_defs

                let mut map = ::rustler::types::map::map_new(env);
                map = map.map_put(atom_struct().encode(env), atom_module().encode(env)).ok().unwrap();
                #(#field_defs)*
                map
            }
        }
    };

    gen
}

fn get_module(ast: &syn::DeriveInput, ctx: &Context) -> String {
    ctx.attrs
        .iter()
        .find_map(|attr| match attr {
            RustlerAttr::Module(ref module) => Some(module.clone()),
            _ => None,
        })
        .or_else(|| {
            let attr_value =
                &ast.attrs
                    .iter()
                    .map(|attr| attr.parse_meta())
                    .find(|meta| match meta {
                        Ok(Meta::NameValue(meta_name_value)) => meta_name_value.ident == "module",
                        _ => false,
                    });

            match *attr_value {
                Some(Ok(Meta::NameValue(ref meta_name_value))) => match meta_name_value.lit {
                    Lit::Str(ref module) => Some(format!("Elixir.{}", module.value())),
                    _ => panic!("Cannot parse module"),
                },
                _ => None,
            }
        })
        .expect("NifStruct requires a 'module' attribute")
}
<commit_msg>Better variable name<commit_after>use proc_macro2::{Span, TokenStream};

use syn::{self, Data, Field, Ident, Lit, Meta};

use super::{Context, RustlerAttr};

pub fn transcoder_decorator(ast: &syn::DeriveInput) -> TokenStream {
    let ctx = Context::from_ast(ast);

    let elixir_module = get_module(ast, &ctx);

    let struct_fields = match ast.data {
        Data::Struct(ref data_struct) => &data_struct.fields,
        Data::Enum(_) => panic!("NifStruct can only be used with structs"),
        Data::Union(_) => panic!("NifStruct can only be used with structs"),
    };

    let num_lifetimes = ast.generics.lifetimes().count();
    if num_lifetimes > 1 {
        panic!("Struct can only have one lifetime argument");
    }
    let has_lifetime = num_lifetimes == 1;

    let field_atoms: Vec<TokenStream> = struct_fields
        .iter()
        .map(|field| {
            let ident = field.ident.as_ref().unwrap();
            let ident_str = ident.to_string();

            let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());

            quote! {
                atom #atom_fun = #ident_str;
            }
        })
        .collect();

    let atom_defs = quote! {
        ::rustler::rustler_atoms! {
            atom atom_struct = "__struct__";
            atom atom_module = #elixir_module;
            #(#field_atoms)*
        }
    };

    let struct_fields: Vec<_> = struct_fields.iter().collect();

    let decoder = if ctx.decode() {
        gen_decoder(&ast.ident, &struct_fields, &atom_defs, has_lifetime)
    } else {
        quote! {}
    };

    let encoder = if ctx.encode() {
        gen_encoder(&ast.ident, &struct_fields, &atom_defs, has_lifetime)
    } else {
        quote! {}
    };

    let gen = quote! {
        #decoder
        #encoder
    };

    gen
}

pub fn gen_decoder(
    struct_name: &Ident,
    fields: &[&Field],
    atom_defs: &TokenStream,
    has_lifetime: bool,
) -> TokenStream {
    let struct_type = if has_lifetime {
        quote! { #struct_name <'a> }
    } else {
        quote! { #struct_name }
    };

    let field_defs: Vec<TokenStream> = fields
        .iter()
        .map(|field| {
            let ident = field.ident.as_ref().unwrap();
            let ident_str = ident.to_string();
            let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());
            let error_message = format!("Could not decode field :{} on %{}{{}}", ident.to_string(), struct_name.to_string());
            quote! {
                #ident: match ::rustler::Decoder::decode(term.map_get(#atom_fun().encode(env))?) {
                    Err(_) => return Err(::rustler::Error::RaiseTerm(Box::new(#error_message))),
                    Ok(value) => value
                }
            }
        })
        .collect();

    let gen = quote! {
        impl<'a> ::rustler::Decoder<'a> for #struct_type {
            fn decode(term: ::rustler::Term<'a>) -> Result<Self, ::rustler::Error> {
                use ::rustler::Encoder;

                #atom_defs

                let env = term.get_env();

                let module: ::rustler::types::atom::Atom = term.map_get(atom_struct().to_term(env))?.decode()?;
                if module != atom_module() {
                    return Err(::rustler::Error::Atom("invalid_struct"));
                }

                Ok(#struct_name { #(#field_defs),* })
            }
        }
    };

    gen
}

pub fn gen_encoder(
    struct_name: &Ident,
    fields: &[&Field],
    atom_defs: &TokenStream,
    has_lifetime: bool,
) -> TokenStream {
    let struct_type = if has_lifetime {
        quote! { #struct_name <'b> }
    } else {
        quote! { #struct_name }
    };

    let field_defs: Vec<TokenStream> = fields.iter().map(|field| {
        let field_ident = field.ident.as_ref().unwrap();
        let field_ident_str = field_ident.to_string();
        let atom_fun = Ident::new(&format!("atom_{}", field_ident_str), Span::call_site());
        quote! {
            map = map.map_put(#atom_fun().encode(env), self.#field_ident.encode(env)).ok().unwrap();
        }
    }).collect();

    let gen = quote! {
        impl<'b> ::rustler::Encoder for #struct_type {
            fn encode<'a>(&self, env: ::rustler::Env<'a>) -> ::rustler::Term<'a> {
                #atom_defs

                let mut map = ::rustler::types::map::map_new(env);
                map = map.map_put(atom_struct().encode(env), atom_module().encode(env)).ok().unwrap();
                #(#field_defs)*
                map
            }
        }
    };

    gen
}

fn get_module(ast: &syn::DeriveInput, ctx: &Context) -> String {
    ctx.attrs
        .iter()
        .find_map(|attr| match attr {
            RustlerAttr::Module(ref module) => Some(module.clone()),
            _ => None,
        })
        .or_else(|| {
            let attr_value =
                &ast.attrs
                    .iter()
                    .map(|attr| attr.parse_meta())
                    .find(|meta| match meta {
                        Ok(Meta::NameValue(meta_name_value)) => meta_name_value.ident == "module",
                        _ => false,
                    });

            match *attr_value {
                Some(Ok(Meta::NameValue(ref meta_name_value))) => match meta_name_value.lit {
                    Lit::Str(ref module) => Some(format!("Elixir.{}", module.value())),
                    _ => panic!("Cannot parse module"),
                },
                _ => None,
            }
        })
        .expect("NifStruct requires a 'module' attribute")
}
<|endoftext|>


<commit_before>use std::collections::LinkedList;

use token::Token;

use std::cell::Cell;
use std::rc::Rc;
use regex::Regex;
use context::Context;
use token::Posn;

pub trait ESCharExt {
    fn is_es_newline(self) -> bool;
    fn is_es_whitespace(self) -> bool;
}

impl ESCharExt for char {
    fn is_es_newline(self) -> bool {
        match self {
            '\u{000a}' | '\u{000d}' | '\u{2028}' | '\u{2029}' => true,
            _ => false
        }
    }

    fn is_es_whitespace(self) -> bool {
        match self {
              '\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}'
            | '\u{1680}' | '\u{2000}' | '\u{2001}' | '\u{2002}' | '\u{2003}' | '\u{2004}'
            | '\u{2005}' | '\u{2006}' | '\u{2009}' | '\u{200a}' | '\u{202f}' | '\u{205f}'
            | '\u{3000}' | '\u{feff}' => true,
            _ => false
        }
    }
}

struct LineOrientedReader<I> {
    chars: I,
    curr_char: Option<char>,
    next_char: Option<char>,
    curr_posn: Posn
}

impl<I> LineOrientedReader<I> where I: Iterator<Item=char> {
    pub fn new(mut chars: I) -> LineOrientedReader<I> {
        let curr_char = chars.next();
        let next_char = if curr_char.is_some() { chars.next() } else { None };
        LineOrientedReader {
            chars: chars,
            curr_char: curr_char,
            next_char: next_char,
            curr_posn: Posn {
                offset: 0,
                line: 0,
                column: 0
            }
        }
    }

    pub fn curr_char(&mut self) -> Option<char> { self.curr_char }
    pub fn curr_posn(&mut self) -> Posn { self.curr_posn }
    pub fn next_char(&mut self) -> Option<char> { self.next_char }

    pub fn bump(&mut self) {
        let curr_char = self.next_char;
        let next_char = if curr_char.is_some() { self.chars.next() } else { None };

        self.curr_char = curr_char;
        self.next_char = next_char;

        if (curr_char == Some('\r') && next_char != Some('\n')) ||
           curr_char == Some('\n') ||
           curr_char == Some('\u{2028}') ||
           curr_char == Some('\u{2029}') {
            self.curr_posn.line += 1;
            self.curr_posn.column = 0;
        } else {
            self.curr_posn.column += 1;
        }

        self.curr_posn.offset += 1;
    }
}

struct TokenBuffer {
    tokens: LinkedList<Token>
}

impl TokenBuffer {
    fn new() -> TokenBuffer {
        TokenBuffer {
            tokens: LinkedList::new()
        }
    }

    fn is_empty(&mut self) -> bool {
        self.tokens.len() == 0
    }

    fn push_token(&mut self, token: Token) {
        assert!(self.tokens.len() == 0);
        self.tokens.push_back(token);
    }

    fn read_token(&mut self) -> Token {
        assert!(self.tokens.len() > 0);
        self.tokens.pop_front().unwrap()
    }

    fn peek_token(&mut self) -> &Token {
        assert!(self.tokens.len() > 0);
        self.tokens.front().unwrap()
    }

    fn unread_token(&mut self, token: Token) {
        assert!(self.tokens.len() >= 0);
        assert!(self.tokens.len() < 3);
        self.tokens.push_front(token);
    }
}

pub struct Lexer<I> {
    reader: LineOrientedReader<I>,
    cx: Rc<Cell<Context>>,
    lookahead: TokenBuffer
}

impl<I> Lexer<I> where I: Iterator<Item=char> {
    // constructor

    pub fn new(chars: I, cx: Rc<Cell<Context>>) -> Lexer<I> {
        Lexer {
            reader: LineOrientedReader::new(chars),
            cx: cx,
            lookahead: TokenBuffer::new()
        }
    }

    // public methods

    pub fn is_eof(&mut self) -> bool {
        match *self.peek_token() {
            Token::EOF => true,
            _ => false
        }
    }

    pub fn peek_token(&mut self) -> &Token {
        if self.lookahead.is_empty() {
            let token = self.read_next_token();
            self.lookahead.push_token(token);
        }
        self.lookahead.peek_token()
    }

    pub fn read_token(&mut self) -> Token {
        if self.lookahead.is_empty() {
            self.read_next_token()
        } else {
            self.lookahead.read_token()
        }
    }

    pub fn unread_token(&mut self, token: Token) {
        self.lookahead.unread_token(token);
    }

    // private methods

    fn skip_line_comment(&mut self) {
    }

    fn skip_block_comment(&mut self) {
    }

    fn div_or_regexp(&mut self) -> Token {
        if self.cx.borrow().get().operator {
            unimplemented!()
        } else {
            unimplemented!()
        }
    }

    fn read_next_token(&mut self) -> Token {
        self.skip_whitespace();
        loop {
            match self.reader.curr_char() {
                None => return Token::EOF,
                Some(ch) => {
                    if ch == '/' {
                        match self.reader.next_char() {
                            Some('/') => self.skip_line_comment(),
                            Some('*') => self.skip_block_comment(),
                            _ => return self.div_or_regexp()
                        }
                    }
                    unimplemented!()
                }
            }
        }
    }

    fn is_whitespace(&mut self) -> bool {
        match self.reader.curr_char() {
            Some(ch) => ch.is_es_whitespace(),
            None => false
        }
    }

    fn eat(&mut self) -> Option<char> {
        let ch = self.reader.curr_char();
        self.reader.bump();
        ch
    }

    fn skip_whitespace(&mut self) {
        while self.is_whitespace() {
            self.reader.bump();
        }
    }
}

impl<I> Iterator for Lexer<I> where I: Iterator<Item=char> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.skip_whitespace();
        self.eat()
            .map(|ch| {
                match ch {
                    '1' => Token::DecimalInt(String::from_str("1")),
                    '+' => Token::Plus,
                    _ => Token::Error(ch)
                }
            })
    }
}
<commit_msg>it said I need collections::borrow::Borrow but that seemed wrong since I thought collections was std::collections; no luck src/lexer.rs:2:5: 2:37 error: unresolved import `std::collections::borrow::Borrow`. Could not find `borrow` in `std::collections` src/lexer.rs:2 use std::collections::borrow::Borrow;                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<commit_after>use std::collections::LinkedList;
use std::collections::borrow::Borrow;

use token::Token;

use std::cell::Cell;
use std::rc::Rc;
use regex::Regex;
use context::Context;
use token::Posn;

pub trait ESCharExt {
    fn is_es_newline(self) -> bool;
    fn is_es_whitespace(self) -> bool;
}

impl ESCharExt for char {
    fn is_es_newline(self) -> bool {
        match self {
            '\u{000a}' | '\u{000d}' | '\u{2028}' | '\u{2029}' => true,
            _ => false
        }
    }

    fn is_es_whitespace(self) -> bool {
        match self {
              '\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}'
            | '\u{1680}' | '\u{2000}' | '\u{2001}' | '\u{2002}' | '\u{2003}' | '\u{2004}'
            | '\u{2005}' | '\u{2006}' | '\u{2009}' | '\u{200a}' | '\u{202f}' | '\u{205f}'
            | '\u{3000}' | '\u{feff}' => true,
            _ => false
        }
    }
}

struct LineOrientedReader<I> {
    chars: I,
    curr_char: Option<char>,
    next_char: Option<char>,
    curr_posn: Posn
}

impl<I> LineOrientedReader<I> where I: Iterator<Item=char> {
    pub fn new(mut chars: I) -> LineOrientedReader<I> {
        let curr_char = chars.next();
        let next_char = if curr_char.is_some() { chars.next() } else { None };
        LineOrientedReader {
            chars: chars,
            curr_char: curr_char,
            next_char: next_char,
            curr_posn: Posn {
                offset: 0,
                line: 0,
                column: 0
            }
        }
    }

    pub fn curr_char(&mut self) -> Option<char> { self.curr_char }
    pub fn curr_posn(&mut self) -> Posn { self.curr_posn }
    pub fn next_char(&mut self) -> Option<char> { self.next_char }

    pub fn bump(&mut self) {
        let curr_char = self.next_char;
        let next_char = if curr_char.is_some() { self.chars.next() } else { None };

        self.curr_char = curr_char;
        self.next_char = next_char;

        if (curr_char == Some('\r') && next_char != Some('\n')) ||
           curr_char == Some('\n') ||
           curr_char == Some('\u{2028}') ||
           curr_char == Some('\u{2029}') {
            self.curr_posn.line += 1;
            self.curr_posn.column = 0;
        } else {
            self.curr_posn.column += 1;
        }

        self.curr_posn.offset += 1;
    }
}

struct TokenBuffer {
    tokens: LinkedList<Token>
}

impl TokenBuffer {
    fn new() -> TokenBuffer {
        TokenBuffer {
            tokens: LinkedList::new()
        }
    }

    fn is_empty(&mut self) -> bool {
        self.tokens.len() == 0
    }

    fn push_token(&mut self, token: Token) {
        assert!(self.tokens.len() == 0);
        self.tokens.push_back(token);
    }

    fn read_token(&mut self) -> Token {
        assert!(self.tokens.len() > 0);
        self.tokens.pop_front().unwrap()
    }

    fn peek_token(&mut self) -> &Token {
        assert!(self.tokens.len() > 0);
        self.tokens.front().unwrap()
    }

    fn unread_token(&mut self, token: Token) {
        assert!(self.tokens.len() >= 0);
        assert!(self.tokens.len() < 3);
        self.tokens.push_front(token);
    }
}

pub struct Lexer<I> {
    reader: LineOrientedReader<I>,
    cx: Rc<Cell<Context>>,
    lookahead: TokenBuffer
}

impl<I> Lexer<I> where I: Iterator<Item=char> {
    // constructor

    pub fn new(chars: I, cx: Rc<Cell<Context>>) -> Lexer<I> {
        Lexer {
            reader: LineOrientedReader::new(chars),
            cx: cx,
            lookahead: TokenBuffer::new()
        }
    }

    // public methods

    pub fn is_eof(&mut self) -> bool {
        match *self.peek_token() {
            Token::EOF => true,
            _ => false
        }
    }

    pub fn peek_token(&mut self) -> &Token {
        if self.lookahead.is_empty() {
            let token = self.read_next_token();
            self.lookahead.push_token(token);
        }
        self.lookahead.peek_token()
    }

    pub fn read_token(&mut self) -> Token {
        if self.lookahead.is_empty() {
            self.read_next_token()
        } else {
            self.lookahead.read_token()
        }
    }

    pub fn unread_token(&mut self, token: Token) {
        self.lookahead.unread_token(token);
    }

    // private methods

    fn skip_line_comment(&mut self) {
    }

    fn skip_block_comment(&mut self) {
    }

    fn div_or_regexp(&mut self) -> Token {
        if self.cx.borrow().get().operator {
            unimplemented!()
        } else {
            unimplemented!()
        }
    }

    fn read_next_token(&mut self) -> Token {
        self.skip_whitespace();
        loop {
            match self.reader.curr_char() {
                None => return Token::EOF,
                Some(ch) => {
                    if ch == '/' {
                        match self.reader.next_char() {
                            Some('/') => self.skip_line_comment(),
                            Some('*') => self.skip_block_comment(),
                            _ => return self.div_or_regexp()
                        }
                    }
                    unimplemented!()
                }
            }
        }
    }

    fn is_whitespace(&mut self) -> bool {
        match self.reader.curr_char() {
            Some(ch) => ch.is_es_whitespace(),
            None => false
        }
    }

    fn eat(&mut self) -> Option<char> {
        let ch = self.reader.curr_char();
        self.reader.bump();
        ch
    }

    fn skip_whitespace(&mut self) {
        while self.is_whitespace() {
            self.reader.bump();
        }
    }
}

impl<I> Iterator for Lexer<I> where I: Iterator<Item=char> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.skip_whitespace();
        self.eat()
            .map(|ch| {
                match ch {
                    '1' => Token::DecimalInt(String::from_str("1")),
                    '+' => Token::Plus,
                    _ => Token::Error(ch)
                }
            })
    }
}
<|endoftext|>


<commit_before>use crate::token;

// TODO: Why byte? Maybe just use char and support unicode language out of box? wtp?
#[derive(Default, Debug, Clone)]
pub struct Lexer {
    input: String,
    position: u32,
    read_position: u32,
    ch: u8,
}

impl Lexer {
    pub fn new(input: String) -> Self {
        let mut lexer = Lexer {
            input,
            ..Default::default()
        };

        // Setup lexer:
        // 1) load first character
        // 2) move reader position
        // ...see read_char for details.
        lexer.read_char();

        lexer
    }

    // At the beginning position and read_position are zeroes.
    // After the first iteration situation changes to position: 0, read_position: 1.
    fn read_char(&mut self) {
        if self.read_position >= self.input.len() as u32 {
            self.ch = 0
        } else {
            self.ch = self.input.as_bytes()[self.read_position as usize]
        }

        self.position = self.read_position;
        self.read_position += 1;
    }

    // TODO: rethink types. Current one are not very nice.
    pub fn next_token(&mut self) -> token::Token {
        self.skip_whitespace();

        let tok = match self.ch {
            b'=' => {
                if self.peek_char() == b'=' {
                    let ch = self.ch;
                    self.read_char();
                    let literal = [ch, self.ch].iter().map(|&c| c as char).collect::<String>();
                    token::Token {
                        token_type: token::EQ.to_string(),
                        literal,
                    }
                } else {
                    token::Token {
                        token_type: token::ASSIGN.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
            b'+' => token::Token {
                token_type: token::PLUS.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'-' => token::Token {
                token_type: token::MINUS.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'!' => {
                if self.peek_char() == b'=' {
                    let ch = self.ch;
                    self.read_char();
                    let literal = [ch, self.ch].iter().map(|&c| c as char).collect::<String>();
                    token::Token {
                        token_type: token::NOT_EQ.to_string(),
                        literal,
                    }
                } else {
                    token::Token {
                        token_type: token::BANG.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
            b'/' => token::Token {
                token_type: token::SLASH.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'*' => token::Token {
                token_type: token::ASTERISK.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'<' => token::Token {
                token_type: token::LT.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'>' => token::Token {
                token_type: token::GT.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b';' => token::Token {
                token_type: token::SEMICOLON.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'(' => token::Token {
                token_type: token::LPAREN.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b')' => token::Token {
                token_type: token::RPAREN.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b',' => token::Token {
                token_type: token::COMMA.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'+' => token::Token {
                token_type: token::PLUS.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'{' => token::Token {
                token_type: token::LBRACE.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'}' => token::Token {
                token_type: token::RBRACE.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            0 => token::Token {
                token_type: token::EOF.to_string(),
                literal: "".to_string(),
            },
            _ => {
                let character = self.ch as char;

                if character.is_ascii_alphabetic() || character == '_' {
                    let literal = self.read_identifier();
                    let token_type = token::lookup_ident(literal.clone());
                    // We need early return here because
                    // in the end of function we run read_char(),
                    // but read_identifier() already at last position
                    return token::Token {
                        token_type,
                        literal,
                    };
                } else if character.is_digit(10) {
                    // TODO: Add linenumber and position to track the error
                    // and show output to user.
                    let literal = self.read_number();
                    let token_type = token::INT.to_string();
                    // We need early return here because
                    // in the end of function we run read_char(),
                    // but read_number() already at last position
                    return token::Token {
                        token_type,
                        literal,
                    };
                } else {
                    token::Token {
                        token_type: token::ILLEGAL.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
        };

        self.read_char();

        tok
    }

    fn read_identifier(&mut self) -> String {
        let position = self.position;
        while self.is_letter(self.ch) {
            self.read_char()
        }
        self.input.as_bytes()[position as usize..self.position as usize]
            .iter()
            .map(|&c| c as char)
            .collect::<String>()
    }

    fn read_number(&mut self) -> String {
        let position = self.position;
        while (self.ch as char).is_digit(10) {
            self.read_char()
        }

        self.input.as_bytes()[position as usize..self.position as usize]
            .iter()
            .map(|&c| c as char)
            .collect::<String>()
    }

    fn peek_char(&self) -> u8 {
        if self.read_position >= self.input.len() as u32 {
            0
        } else {
            self.input.as_bytes()[self.read_position as usize]
        }
    }

    fn is_letter(&self, c: u8) -> bool {
        let character = c as char;
        character.is_ascii_alphabetic() || character == '_'
    }

    fn skip_whitespace(&mut self) {
        while self.ch == b' ' || self.ch == b'\t' || self.ch == b'\n' || self.ch == b'\r' {
            self.read_char()
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::lexer::Lexer;
    use crate::token;

    #[test]
    fn test_next_token_basic() {
        let input = String::from("=+(){},;");

        let expected = vec![
            (token::ASSIGN.to_string(), String::from("=")),
            (token::PLUS.to_string(), String::from("+")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::COMMA.to_string(), String::from(",")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::EOF.to_string(), String::from("")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_next_token_clojurium_snippet() {
        let input = r###"
          let five = 5;
          let ten = 10;

          let add = fn(x, y) {
            x + y;
          };

          let result = add(five, ten);
        "###
        .to_string();

        let expected = vec![
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("five")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("ten")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::INT.to_string(), String::from("10")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("add")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::FUNCTION.to_string(), String::from("fn")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::IDENT.to_string(), String::from("x")),
            (token::COMMA.to_string(), String::from(",")),
            (token::IDENT.to_string(), String::from("y")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::IDENT.to_string(), String::from("x")),
            (token::PLUS.to_string(), String::from("+")),
            (token::IDENT.to_string(), String::from("y")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("result")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::IDENT.to_string(), String::from("add")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::IDENT.to_string(), String::from("five")),
            (token::COMMA.to_string(), String::from(",")),
            (token::IDENT.to_string(), String::from("ten")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::EOF.to_string(), String::from("")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_arithmetic_operators() {
        let input = r###"
         !-/*5;
         5 < 10 > 5;
       "###
        .to_string();

        let mut lexer = Lexer::new(input);

        let expected = vec![
            (token::BANG.to_string(), String::from("!")),
            (token::MINUS.to_string(), String::from("-")),
            (token::SLASH.to_string(), String::from("/")),
            (token::ASTERISK.to_string(), String::from("*")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::INT.to_string(), String::from("5")),
            (token::LT.to_string(), String::from("<")),
            (token::INT.to_string(), String::from("10")),
            (token::GT.to_string(), String::from(">")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
        ];

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_flow_control_keywords() {
        let input = r###"
          if (2 < 3) {
              return true;
          } else {
              return false;
          }
        "###
        .to_string();

        let expected = vec![
            (token::IF.to_string(), String::from("if")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::INT.to_string(), String::from("2")),
            (token::LT.to_string(), String::from("<")),
            (token::INT.to_string(), String::from("3")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RETURN.to_string(), String::from("return")),
            (token::TRUE.to_string(), String::from("true")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::ELSE.to_string(), String::from("else")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RETURN.to_string(), String::from("return")),
            (token::FALSE.to_string(), String::from("false")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_multichar_operators() {
        let input = r###"1 == 1 1 != 2"###.to_string();

        let expected = vec![
            (token::INT.to_string(), String::from("1")),
            (token::EQ.to_string(), String::from("==")),
            (token::INT.to_string(), String::from("1")),
            (token::INT.to_string(), String::from("1")),
            (token::NOT_EQ.to_string(), String::from("!=")),
            (token::INT.to_string(), String::from("2")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }
}
<commit_msg>remove duplicate PLUS branch<commit_after>use crate::token;

// TODO: Why byte? Maybe just use char and support unicode language out of box? wtp?
#[derive(Default, Debug, Clone)]
pub struct Lexer {
    input: String,
    position: u32,
    read_position: u32,
    ch: u8,
}

impl Lexer {
    pub fn new(input: String) -> Self {
        let mut lexer = Lexer {
            input,
            ..Default::default()
        };

        // Setup lexer:
        // 1) load first character
        // 2) move reader position
        // ...see read_char for details.
        lexer.read_char();

        lexer
    }

    // At the beginning position and read_position are zeroes.
    // After the first iteration situation changes to position: 0, read_position: 1.
    fn read_char(&mut self) {
        if self.read_position >= self.input.len() as u32 {
            self.ch = 0
        } else {
            self.ch = self.input.as_bytes()[self.read_position as usize]
        }

        self.position = self.read_position;
        self.read_position += 1;
    }

    // TODO: rethink types. Current one are not very nice.
    pub fn next_token(&mut self) -> token::Token {
        self.skip_whitespace();

        let tok = match self.ch {
            b'=' => {
                if self.peek_char() == b'=' {
                    let ch = self.ch;
                    self.read_char();
                    let literal = [ch, self.ch].iter().map(|&c| c as char).collect::<String>();
                    token::Token {
                        token_type: token::EQ.to_string(),
                        literal,
                    }
                } else {
                    token::Token {
                        token_type: token::ASSIGN.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
            b'+' => token::Token {
                token_type: token::PLUS.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'-' => token::Token {
                token_type: token::MINUS.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'!' => {
                if self.peek_char() == b'=' {
                    let ch = self.ch;
                    self.read_char();
                    let literal = [ch, self.ch].iter().map(|&c| c as char).collect::<String>();
                    token::Token {
                        token_type: token::NOT_EQ.to_string(),
                        literal,
                    }
                } else {
                    token::Token {
                        token_type: token::BANG.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
            b'/' => token::Token {
                token_type: token::SLASH.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'*' => token::Token {
                token_type: token::ASTERISK.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'<' => token::Token {
                token_type: token::LT.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'>' => token::Token {
                token_type: token::GT.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b';' => token::Token {
                token_type: token::SEMICOLON.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'(' => token::Token {
                token_type: token::LPAREN.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b')' => token::Token {
                token_type: token::RPAREN.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b',' => token::Token {
                token_type: token::COMMA.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'{' => token::Token {
                token_type: token::LBRACE.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            b'}' => token::Token {
                token_type: token::RBRACE.to_string(),
                literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
            },
            0 => token::Token {
                token_type: token::EOF.to_string(),
                literal: "".to_string(),
            },
            _ => {
                let character = self.ch as char;

                if character.is_ascii_alphabetic() || character == '_' {
                    let literal = self.read_identifier();
                    let token_type = token::lookup_ident(literal.clone());
                    // We need early return here because
                    // in the end of function we run read_char(),
                    // but read_identifier() already at last position
                    return token::Token {
                        token_type,
                        literal,
                    };
                } else if character.is_digit(10) {
                    // TODO: Add linenumber and position to track the error
                    // and show output to user.
                    let literal = self.read_number();
                    let token_type = token::INT.to_string();
                    // We need early return here because
                    // in the end of function we run read_char(),
                    // but read_number() already at last position
                    return token::Token {
                        token_type,
                        literal,
                    };
                } else {
                    token::Token {
                        token_type: token::ILLEGAL.to_string(),
                        literal: [self.ch].iter().map(|&c| c as char).collect::<String>(),
                    }
                }
            }
        };

        self.read_char();

        tok
    }

    fn read_identifier(&mut self) -> String {
        let position = self.position;
        while self.is_letter(self.ch) {
            self.read_char()
        }
        self.input.as_bytes()[position as usize..self.position as usize]
            .iter()
            .map(|&c| c as char)
            .collect::<String>()
    }

    fn read_number(&mut self) -> String {
        let position = self.position;
        while (self.ch as char).is_digit(10) {
            self.read_char()
        }

        self.input.as_bytes()[position as usize..self.position as usize]
            .iter()
            .map(|&c| c as char)
            .collect::<String>()
    }

    fn peek_char(&self) -> u8 {
        if self.read_position >= self.input.len() as u32 {
            0
        } else {
            self.input.as_bytes()[self.read_position as usize]
        }
    }

    fn is_letter(&self, c: u8) -> bool {
        let character = c as char;
        character.is_ascii_alphabetic() || character == '_'
    }

    fn skip_whitespace(&mut self) {
        while self.ch == b' ' || self.ch == b'\t' || self.ch == b'\n' || self.ch == b'\r' {
            self.read_char()
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::lexer::Lexer;
    use crate::token;

    #[test]
    fn test_next_token_basic() {
        let input = String::from("=+(){},;");

        let expected = vec![
            (token::ASSIGN.to_string(), String::from("=")),
            (token::PLUS.to_string(), String::from("+")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::COMMA.to_string(), String::from(",")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::EOF.to_string(), String::from("")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_next_token_clojurium_snippet() {
        let input = r###"
          let five = 5;
          let ten = 10;

          let add = fn(x, y) {
            x + y;
          };

          let result = add(five, ten);
        "###
        .to_string();

        let expected = vec![
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("five")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("ten")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::INT.to_string(), String::from("10")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("add")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::FUNCTION.to_string(), String::from("fn")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::IDENT.to_string(), String::from("x")),
            (token::COMMA.to_string(), String::from(",")),
            (token::IDENT.to_string(), String::from("y")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::IDENT.to_string(), String::from("x")),
            (token::PLUS.to_string(), String::from("+")),
            (token::IDENT.to_string(), String::from("y")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::LET.to_string(), String::from("let")),
            (token::IDENT.to_string(), String::from("result")),
            (token::ASSIGN.to_string(), String::from("=")),
            (token::IDENT.to_string(), String::from("add")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::IDENT.to_string(), String::from("five")),
            (token::COMMA.to_string(), String::from(",")),
            (token::IDENT.to_string(), String::from("ten")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::EOF.to_string(), String::from("")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_arithmetic_operators() {
        let input = r###"
         !-/*5;
         5 < 10 > 5;
       "###
        .to_string();

        let mut lexer = Lexer::new(input);

        let expected = vec![
            (token::BANG.to_string(), String::from("!")),
            (token::MINUS.to_string(), String::from("-")),
            (token::SLASH.to_string(), String::from("/")),
            (token::ASTERISK.to_string(), String::from("*")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::INT.to_string(), String::from("5")),
            (token::LT.to_string(), String::from("<")),
            (token::INT.to_string(), String::from("10")),
            (token::GT.to_string(), String::from(">")),
            (token::INT.to_string(), String::from("5")),
            (token::SEMICOLON.to_string(), String::from(";")),
        ];

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_flow_control_keywords() {
        let input = r###"
          if (2 < 3) {
              return true;
          } else {
              return false;
          }
        "###
        .to_string();

        let expected = vec![
            (token::IF.to_string(), String::from("if")),
            (token::LPAREN.to_string(), String::from("(")),
            (token::INT.to_string(), String::from("2")),
            (token::LT.to_string(), String::from("<")),
            (token::INT.to_string(), String::from("3")),
            (token::RPAREN.to_string(), String::from(")")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RETURN.to_string(), String::from("return")),
            (token::TRUE.to_string(), String::from("true")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
            (token::ELSE.to_string(), String::from("else")),
            (token::LBRACE.to_string(), String::from("{")),
            (token::RETURN.to_string(), String::from("return")),
            (token::FALSE.to_string(), String::from("false")),
            (token::SEMICOLON.to_string(), String::from(";")),
            (token::RBRACE.to_string(), String::from("}")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }

    #[test]
    fn test_multichar_operators() {
        let input = r###"1 == 1 1 != 2"###.to_string();

        let expected = vec![
            (token::INT.to_string(), String::from("1")),
            (token::EQ.to_string(), String::from("==")),
            (token::INT.to_string(), String::from("1")),
            (token::INT.to_string(), String::from("1")),
            (token::NOT_EQ.to_string(), String::from("!=")),
            (token::INT.to_string(), String::from("2")),
        ];

        let mut lexer = Lexer::new(input);

        expected.into_iter().for_each(|(token_type, literal)| {
            let token = lexer.next_token();

            assert_eq!(token.token_type, token_type);
            assert_eq!(token.literal, literal);
        });
    }
}
<|endoftext|>