<commit_before>//! Code for the internal Lua thread which handles all Lua requests.

use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Mutex;
use std::sync::mpsc::{channel, Receiver, Sender};

use glib::MainLoop;
use glib::source::{idle_add, Continue};

use rlua;

use super::init_path;
use super::rust_interop;
use super::types::*;
use awesome::signal;

thread_local! {
    // NOTE The debug library does some powerful reflection that can do crazy things,
    // which is why it's unsafe to load.
    static LUA: RefCell<rlua::Lua> = RefCell::new(unsafe { rlua::Lua::new_with_debug() });
    static MAIN_LOOP: RefCell<MainLoop> = RefCell::new(MainLoop::new(None, false));
}

lazy_static! {
    /// Sends requests to the Lua thread
    static ref CHANNEL: ChannelToLua = ChannelToLua::default();
}

/// Sets up the Lua environment before running the compositor.
pub fn setup_lua() {
    LUA.with(|lua| {
                 rust_interop::register_libraries(&*lua.borrow()).expect("Could not register lua \
                                                                          libraries");
                 info!("Initializing lua...");
                 load_config(&mut *lua.borrow_mut());
             });
}

/// Main loop of the Lua thread:
///
/// * Initialise the Lua state
/// * Run a GMainLoop
pub fn enter_glib_loop() {
    let _guard = DropReceiver;
    MAIN_LOOP.with(|main_loop| main_loop.borrow().run());
}

pub fn terminate() {
    MAIN_LOOP.with(|main_loop| main_loop.borrow().quit())
}

fn load_config(mut lua: &mut rlua::Lua) {
    info!("Loading way-cooler libraries...");

    let maybe_init_file = init_path::get_config();
    match maybe_init_file {
        Some((init_dir, mut init_file)) => {
            if init_dir.components().next().is_some() {
                // Add the config directory to the package path.
                let globals = lua.globals();
                let package: rlua::Table =
                    globals.get("package").expect("package not defined in Lua");
                let paths: String = package.get("path")
                                           .expect("package.path not defined in Lua");
                package.set("path",
                            paths + ";"
                            + init_dir.join("?.lua")
                                      .to_str()
                                      .expect("init_dir not a valid UTF-8 string"))
                       .expect("Failed to set package.path");
            }
            let mut init_contents = String::new();
            init_file.read_to_string(&mut init_contents)
                     .expect("Could not read contents");
            lua.exec(init_contents.as_str(), Some("init.lua".into()))
                .map(|_:()| info!("Read init.lua successfully"))
                .or_else(|err| {
                    fn recursive_callback_print(error: ::std::sync::Arc<rlua::Error>) {
                        match *error {
                            rlua::Error::CallbackError {traceback: ref err, ref cause } => {
                                error!("{}", err);
                                recursive_callback_print(cause.clone())
                            },
                            ref err => error!("{:?}", err)
                        }
                    }
                    match err {
                        rlua::Error::RuntimeError(ref err) => {
                            error!("{}", err);
                        }
                        rlua::Error::CallbackError{traceback: ref err, ref cause } => {
                            error!("traceback: {}", err);
                            recursive_callback_print(cause.clone());
                        },
                        err => {
                            error!("init file error: {:?}", err);
                        }
                    }
                    // Keeping this an error, so that it is visible
                    // in release builds.
                    info!("Defaulting to pre-compiled init.lua");
                    unsafe { *lua = rlua::Lua::new_with_debug(); }
                    rust_interop::register_libraries(&mut lua)?;
                    lua.exec(init_path::DEFAULT_CONFIG,
                             Some("init.lua <DEFAULT>".into()))
                })
                .expect("Unable to load pre-compiled init file");
        }
        None => {
            warn!("Could not find an init file in any path!");
            warn!("Defaulting to pre-compiled init.lua");
            let _: () = lua.exec(init_path::DEFAULT_CONFIG,
                                 Some("init.lua <DEFAULT>".into()))
                .or_else(|err| {
                    fn recursive_callback_print(error: ::std::sync::Arc<rlua::Error>) {
                        match *error {
                            rlua::Error::CallbackError {traceback: ref err, ref cause } => {
                                error!("{}", err);
                                recursive_callback_print(cause.clone())
                            },
                            ref err => error!("{:?}", err)
                        }
                    }
                    match err {
                        rlua::Error::RuntimeError(ref err) => {
                            error!("{}", err);
                        }
                        rlua::Error::CallbackError{traceback: ref err, ref cause } => {
                            error!("traceback: {}", err);
                            recursive_callback_print(cause.clone());
                        },
                        err => {
                            error!("init file error: {:?}", err);
                        }
                    }
                    // Keeping this an error, so that it is visible
                    // in release builds.
                    info!("Defaulting to pre-compiled init.lua");
                    unsafe { *lua = rlua::Lua::new_with_debug(); }
                    rust_interop::register_libraries(&mut lua)?;
                    lua.exec(init_path::DEFAULT_CONFIG,
                             Some("init.lua <DEFAULT>".into()))
                })
                .expect("Unable to load pre-compiled init file");
        }
    }
    emit_refresh(lua);
}

/// Struct sent to the Lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage {}
unsafe impl Sync for LuaMessage {}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Struct used to communicate with the Lua thread.
struct ChannelToLua {
    sender: Mutex<Sender<LuaMessage>>,
    receiver: Mutex<Option<Receiver<LuaMessage>>>
}

impl Default for ChannelToLua {
    fn default() -> Self {
        let (sender, receiver) = channel();
        ChannelToLua { sender: Mutex::new(sender),
                       receiver: Mutex::new(Some(receiver)) }
    }
}

// Reexported in lua/mod.rs:11
/// Errors which may arise from attempting
/// to sending a message to the Lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The sender had an issue, most likely because the Lua thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

impl Into<rlua::Error> for LuaSendError {
    fn into(self) -> rlua::Error {
        rlua::Error::RuntimeError(match self {
                                      LuaSendError::Sender(query) => {
                                          format!("Could not send query to Lua thread: {:?}", query)
                                      }
                                  })
    }
}

// Reexported in lua/mod.rs
/// Run a closure with the Lua state. The closure will execute in the Lua
/// thread.
pub fn run_with_lua<F>(func: F) -> rlua::Result<()>
    where F: 'static + FnMut(&rlua::Lua) -> rlua::Result<()>
{
    let send_result = send(LuaQuery::ExecWithLua(Box::new(func))).map_err(|err|err.into())?
        .recv().map_err(|err| {
            rlua::Error::RuntimeError(format!("Could not receive from mspc: {:?}", err))
        })?;
    match send_result {
        LuaResponse::Error(err) => Err(err),
        LuaResponse::Pong => Ok(()),
        _ => Err(rlua::Error::CoroutineInactive)
    }
}

fn emit_refresh(lua: &rlua::Lua) {
    if let Err(err) = signal::global_emit_signal(lua, ("refresh".to_owned(), rlua::Value::Nil)) {
        error!("Internal error while emitting 'refresh' signal: {}", err);
    }
}

fn idle_add_once<F>(func: F)
    where F: Send + 'static + FnOnce() -> ()
{
    let mut cell = Cell::new(Some(func));
    idle_add(move || {
                 (&mut cell).get_mut().take().unwrap()();
                 Continue(false)
             });
}

// Reexported in lua/mod.rs:11
/// Attempts to send a LuaQuery to the Lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    // Create a response channel
    let (response_tx, response_rx) = channel();
    match CHANNEL.sender.lock() {
        Err(_) => Err(LuaSendError::Sender(query)),
        Ok(sender) => {
            let message = LuaMessage { reply: response_tx,
                                       query: query };
            sender.send(message)
                  .map_err(|e| LuaSendError::Sender(e.0.query))
        }
    }?;
    idle_add_once(|| {
                      let receiver = CHANNEL.receiver.lock().unwrap();
                      if let Some(ref receiver) = *receiver {
                          LUA.with(|lua| {
                                       let lua = &mut *lua.borrow_mut();
                                       for message in receiver.try_iter() {
                                           trace!("Handling a request");
                                           if !handle_message(message, lua) {
                                               MAIN_LOOP.with(|main_loop| main_loop.borrow().quit())
                                           }
                                       }
                                       emit_refresh(lua);
                                   });
                      }
                  });
    Ok(response_rx)
}

struct DropReceiver;

impl Drop for DropReceiver {
    fn drop(&mut self) {
        // Drop the receiver, if possible
        match CHANNEL.receiver.lock() {
            Ok(mut receiver) => {
                let _ = receiver.take();
            }
            _ => {}
        }
    }
}

/// Handle each LuaQuery option sent to the thread
fn handle_message(request: LuaMessage, lua: &mut rlua::Lua) -> bool {
    match request.query {
        LuaQuery::Terminate => {
            trace!("Received terminate signal");
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread terminating!");
            return false
        }
        LuaQuery::Restart => {
            trace!("Received restart signal!");
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread restarting");
            unsafe {
                *lua = rlua::Lua::new_with_debug();
            }
            rust_interop::register_libraries(lua).expect("Could not register libraries");
            load_config(lua);
            return true
        }
        LuaQuery::Execute(code) => {
            trace!("Received request to execute {}", code);

            match lua.exec::<()>(&code, None) {
                Err(error) => {
                    warn!("Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        }
        LuaQuery::ExecFile(name) => {
            info!("Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(mut file) = try_file {
                let mut file_contents = String::new();
                file.read_to_string(&mut file_contents).expect("Could not read file contents");
                let result = lua.exec::<rlua::Value>(file_contents.as_str(), Some(name.as_str()));
                if let Err(err) = result {
                    warn!("Error executing {}!", name);
                    thread_send(request.reply, LuaResponse::Error(err));
                } else {
                    trace!("Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            } else {
                // Could not open file
                // Unwrap_err is used because we're in the else of let Ok
                let read_error = rlua::Error::RuntimeError(format!("{:#?}", try_file.unwrap_err()));
                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        }
        LuaQuery::ExecRust(func) => {
            let result = func(lua);
            thread_send(request.reply, LuaResponse::Variable(Some(result)));
        }
        LuaQuery::ExecWithLua(mut func) => {
            match func(lua) {
                Ok(()) => thread_send(request.reply, LuaResponse::Pong),
                Err(e) => thread_send(request.reply, LuaResponse::Error(e))
            };
        }
        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        }
    }
    return true
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {} // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}
<commit_msg>Don't load init file twice on fallback failure<commit_after>//! Code for the internal Lua thread which handles all Lua requests.

use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Mutex;
use std::sync::mpsc::{channel, Receiver, Sender};

use glib::MainLoop;
use glib::source::{idle_add, Continue};

use rlua;

use super::init_path;
use super::rust_interop;
use super::types::*;
use awesome::signal;

thread_local! {
    // NOTE The debug library does some powerful reflection that can do crazy things,
    // which is why it's unsafe to load.
    static LUA: RefCell<rlua::Lua> = RefCell::new(unsafe { rlua::Lua::new_with_debug() });
    static MAIN_LOOP: RefCell<MainLoop> = RefCell::new(MainLoop::new(None, false));
}

lazy_static! {
    /// Sends requests to the Lua thread
    static ref CHANNEL: ChannelToLua = ChannelToLua::default();
}

/// Sets up the Lua environment before running the compositor.
pub fn setup_lua() {
    LUA.with(|lua| {
                 rust_interop::register_libraries(&*lua.borrow()).expect("Could not register lua \
                                                                          libraries");
                 info!("Initializing lua...");
                 load_config(&mut *lua.borrow_mut());
             });
}

/// Main loop of the Lua thread:
///
/// * Initialise the Lua state
/// * Run a GMainLoop
pub fn enter_glib_loop() {
    let _guard = DropReceiver;
    MAIN_LOOP.with(|main_loop| main_loop.borrow().run());
}

pub fn terminate() {
    MAIN_LOOP.with(|main_loop| main_loop.borrow().quit())
}

fn load_config(mut lua: &mut rlua::Lua) {
    info!("Loading way-cooler libraries...");

    let maybe_init_file = init_path::get_config();
    match maybe_init_file {
        Some((init_dir, mut init_file)) => {
            if init_dir.components().next().is_some() {
                // Add the config directory to the package path.
                let globals = lua.globals();
                let package: rlua::Table =
                    globals.get("package").expect("package not defined in Lua");
                let paths: String = package.get("path")
                                           .expect("package.path not defined in Lua");
                package.set("path",
                            paths + ";"
                            + init_dir.join("?.lua")
                                      .to_str()
                                      .expect("init_dir not a valid UTF-8 string"))
                       .expect("Failed to set package.path");
            }
            let mut init_contents = String::new();
            init_file.read_to_string(&mut init_contents)
                     .expect("Could not read contents");
            lua.exec(init_contents.as_str(), Some("init.lua".into()))
                .map(|_:()| info!("Read init.lua successfully"))
                .or_else(|err| {
                    fn recursive_callback_print(error: ::std::sync::Arc<rlua::Error>) {
                        match *error {
                            rlua::Error::CallbackError {traceback: ref err, ref cause } => {
                                error!("{}", err);
                                recursive_callback_print(cause.clone())
                            },
                            ref err => error!("{:?}", err)
                        }
                    }
                    match err {
                        rlua::Error::RuntimeError(ref err) => {
                            error!("{}", err);
                        }
                        rlua::Error::CallbackError{traceback: ref err, ref cause } => {
                            error!("traceback: {}", err);
                            recursive_callback_print(cause.clone());
                        },
                        err => {
                            error!("init file error: {:?}", err);
                        }
                    }
                    // Keeping this an error, so that it is visible
                    // in release builds.
                    info!("Defaulting to pre-compiled init.lua");
                    unsafe { *lua = rlua::Lua::new_with_debug(); }
                    rust_interop::register_libraries(&mut lua)?;
                    lua.exec(init_path::DEFAULT_CONFIG,
                             Some("init.lua <DEFAULT>".into()))
                })
                .expect("Unable to load pre-compiled init file");
        }
        None => {
            warn!("Could not find an init file in any path!");
            warn!("Defaulting to pre-compiled init.lua");
            let _: () = lua.exec(init_path::DEFAULT_CONFIG,
                                 Some("init.lua <DEFAULT>".into()))
                .or_else(|err| {
                    fn recursive_callback_print(error: ::std::sync::Arc<rlua::Error>) {
                        match *error {
                            rlua::Error::CallbackError {traceback: ref err, ref cause } => {
                                error!("{}", err);
                                recursive_callback_print(cause.clone())
                            },
                            ref err => error!("{:?}", err)
                        }
                    }
                    match err.clone() {
                        rlua::Error::RuntimeError(ref err) => {
                            error!("{}", err);
                        }
                        rlua::Error::CallbackError{traceback: ref err, ref cause } => {
                            error!("traceback: {}", err);
                            recursive_callback_print(cause.clone());
                        },
                        err => {
                            error!("init file error: {:?}", err);
                        }
                    }
                    Err(err)
                })
                .expect("Unable to load pre-compiled init file");
        }
    }
    emit_refresh(lua);
}

/// Struct sent to the Lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage {}
unsafe impl Sync for LuaMessage {}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Struct used to communicate with the Lua thread.
struct ChannelToLua {
    sender: Mutex<Sender<LuaMessage>>,
    receiver: Mutex<Option<Receiver<LuaMessage>>>
}

impl Default for ChannelToLua {
    fn default() -> Self {
        let (sender, receiver) = channel();
        ChannelToLua { sender: Mutex::new(sender),
                       receiver: Mutex::new(Some(receiver)) }
    }
}

// Reexported in lua/mod.rs:11
/// Errors which may arise from attempting
/// to sending a message to the Lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The sender had an issue, most likely because the Lua thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

impl Into<rlua::Error> for LuaSendError {
    fn into(self) -> rlua::Error {
        rlua::Error::RuntimeError(match self {
                                      LuaSendError::Sender(query) => {
                                          format!("Could not send query to Lua thread: {:?}", query)
                                      }
                                  })
    }
}

// Reexported in lua/mod.rs
/// Run a closure with the Lua state. The closure will execute in the Lua
/// thread.
pub fn run_with_lua<F>(func: F) -> rlua::Result<()>
    where F: 'static + FnMut(&rlua::Lua) -> rlua::Result<()>
{
    let send_result = send(LuaQuery::ExecWithLua(Box::new(func))).map_err(|err|err.into())?
        .recv().map_err(|err| {
            rlua::Error::RuntimeError(format!("Could not receive from mspc: {:?}", err))
        })?;
    match send_result {
        LuaResponse::Error(err) => Err(err),
        LuaResponse::Pong => Ok(()),
        _ => Err(rlua::Error::CoroutineInactive)
    }
}

fn emit_refresh(lua: &rlua::Lua) {
    if let Err(err) = signal::global_emit_signal(lua, ("refresh".to_owned(), rlua::Value::Nil)) {
        error!("Internal error while emitting 'refresh' signal: {}", err);
    }
}

fn idle_add_once<F>(func: F)
    where F: Send + 'static + FnOnce() -> ()
{
    let mut cell = Cell::new(Some(func));
    idle_add(move || {
                 (&mut cell).get_mut().take().unwrap()();
                 Continue(false)
             });
}

// Reexported in lua/mod.rs:11
/// Attempts to send a LuaQuery to the Lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    // Create a response channel
    let (response_tx, response_rx) = channel();
    match CHANNEL.sender.lock() {
        Err(_) => Err(LuaSendError::Sender(query)),
        Ok(sender) => {
            let message = LuaMessage { reply: response_tx,
                                       query: query };
            sender.send(message)
                  .map_err(|e| LuaSendError::Sender(e.0.query))
        }
    }?;
    idle_add_once(|| {
                      let receiver = CHANNEL.receiver.lock().unwrap();
                      if let Some(ref receiver) = *receiver {
                          LUA.with(|lua| {
                                       let lua = &mut *lua.borrow_mut();
                                       for message in receiver.try_iter() {
                                           trace!("Handling a request");
                                           if !handle_message(message, lua) {
                                               MAIN_LOOP.with(|main_loop| main_loop.borrow().quit())
                                           }
                                       }
                                       emit_refresh(lua);
                                   });
                      }
                  });
    Ok(response_rx)
}

struct DropReceiver;

impl Drop for DropReceiver {
    fn drop(&mut self) {
        // Drop the receiver, if possible
        match CHANNEL.receiver.lock() {
            Ok(mut receiver) => {
                let _ = receiver.take();
            }
            _ => {}
        }
    }
}

/// Handle each LuaQuery option sent to the thread
fn handle_message(request: LuaMessage, lua: &mut rlua::Lua) -> bool {
    match request.query {
        LuaQuery::Terminate => {
            trace!("Received terminate signal");
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread terminating!");
            return false
        }
        LuaQuery::Restart => {
            trace!("Received restart signal!");
            thread_send(request.reply, LuaResponse::Pong);

            info!("Lua thread restarting");
            unsafe {
                *lua = rlua::Lua::new_with_debug();
            }
            rust_interop::register_libraries(lua).expect("Could not register libraries");
            load_config(lua);
            return true
        }
        LuaQuery::Execute(code) => {
            trace!("Received request to execute {}", code);

            match lua.exec::<()>(&code, None) {
                Err(error) => {
                    warn!("Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        }
        LuaQuery::ExecFile(name) => {
            info!("Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(mut file) = try_file {
                let mut file_contents = String::new();
                file.read_to_string(&mut file_contents).expect("Could not read file contents");
                let result = lua.exec::<rlua::Value>(file_contents.as_str(), Some(name.as_str()));
                if let Err(err) = result {
                    warn!("Error executing {}!", name);
                    thread_send(request.reply, LuaResponse::Error(err));
                } else {
                    trace!("Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            } else {
                // Could not open file
                // Unwrap_err is used because we're in the else of let Ok
                let read_error = rlua::Error::RuntimeError(format!("{:#?}", try_file.unwrap_err()));
                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        }
        LuaQuery::ExecRust(func) => {
            let result = func(lua);
            thread_send(request.reply, LuaResponse::Variable(Some(result)));
        }
        LuaQuery::ExecWithLua(mut func) => {
            match func(lua) {
                Ok(()) => thread_send(request.reply, LuaResponse::Pong),
                Err(e) => thread_send(request.reply, LuaResponse::Error(e))
            };
        }
        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        }
    }
    return true
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {} // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Jason White
//
// 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.
extern crate num_cpus;

pub mod opts;

use std::path::PathBuf;

use clap;

/// Returns clap App arguments to use for parsing.
pub fn app<'a, 'b>() -> clap::App<'a, 'b> {

    // Common options
    let verbose_opt = clap::Arg::with_name("verbose")
        .help("Print additional information.")
        .long("verbose")
        .short("v");

    let file_opt = clap::Arg::with_name("file")
        .help("Path to the build description.")
        .takes_value(true)
        .long("file")
        .short("f");

    let dryrun_opt = clap::Arg::with_name("dryrun")
        .help("Print what might happen.")
        .long("dryrun")
        .short("n");

    let threads_opt = clap::Arg::with_name("threads")
        .help("The number of threads to use.")
        .takes_value(true)
        .long("threads")
        .short("t");

    let color_opt = clap::Arg::with_name("color")
        .help("When to colorize the output.")
        .takes_value(true)
        .long("color")
        .possible_values(&["auto", "never", "always"])
        .default_value("auto");

    let common_opts = &[verbose_opt.clone(), file_opt.clone(),
        dryrun_opt.clone(), threads_opt.clone(), color_opt.clone()];

    clap::App::new("button")
        .version(crate_version!())
        .author(crate_authors!())
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .setting(clap::AppSettings::VersionlessSubcommands)
        .subcommand(clap::SubCommand::with_name("build")
            .about("Builds your damn software.")
            .alias("update")
            .args(common_opts)
            .args(&[
                clap::Arg::with_name("auto")
                    .help("Wait for changes and build automatically.")
                    .long("auto"),

                clap::Arg::with_name("watchdir")
                    .help("Used with --auto. The directory to watch for changes in.")
                    .takes_value(true)
                    .long("watchdir"),

                clap::Arg::with_name("delay")
                    .help("Used with --auto. The number of milliseconds to wait before \
                          building.")
                    .takes_value(true)
                    .long("delay")
                    .default_value("50"),
            ])
        )
        .subcommand(clap::SubCommand::with_name("clean")
            .about("Cleans your damn software.")
            .args(common_opts)
            .args(&[
                clap::Arg::with_name("purge")
                    .help("Delete the build state too.")
                    .long("purge"),
            ])
        )
        .subcommand(clap::SubCommand::with_name("graph")
            .about("Graphs your damn software.")
            .arg(file_opt.clone())
            .arg(threads_opt.clone())
            .args(&[
                clap::Arg::with_name("changes")
                    .help("Only display the subgraph that will be traversed on \
                          an update. This has to query the file system for \
                          changes to resources.")
                    .long("changes"),
                clap::Arg::with_name("cached")
                    .help("Display the cached graph from the previous build.")
                    .long("cached"),
                clap::Arg::with_name("full")
                    .help("Displays the full name for each node.")
                    .long("full"),
                clap::Arg::with_name("edges")
                    .help("Type of edges to show.")
                    .takes_value(true)
                    .possible_values(&["explicit", "implicit", "both"])
                    .default_value("explicit")
                    .long("edges"),
            ])
        )
}

pub enum Command {
    Build(opts::Build),
    Clean(opts::Clean),
    Graph(opts::Graph),
}

pub fn subcommand<'a>(name: &str, matches: &clap::ArgMatches<'a>) -> clap::Result<Command> {
    let cpu_count = num_cpus::get();

    match name {
        "build" => Ok(Command::Build(opts::Build {
            file: matches.value_of("file").map(PathBuf::from),
            dryrun: matches.is_present("dryrun"),
            color: try!(value_t!(matches.value_of("color"), opts::Coloring)),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            autobuild: matches.is_present("auto"),
            delay: try!(value_t!(matches, "delay", usize)),
        })),

        "clean" => Ok(Command::Clean(opts::Clean {
            file: matches.value_of("file").map(PathBuf::from),
            dryrun: matches.is_present("dryrun"),
            color: try!(value_t!(matches.value_of("color"), opts::Coloring)),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            purge: matches.is_present("purge"),
        })),

        "graph" => Ok(Command::Graph(opts::Graph {
            file: matches.value_of("file").map(PathBuf::from),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            changes: matches.is_present("changes"),
            cached: matches.is_present("cached"),
            full: matches.is_present("full"),
            edges: try!(value_t!(matches.value_of("edges"), opts::Edges)),
        })),

        // If all subcommands are matched above, then this shouldn't happen.
        _ => unreachable!(),
    }
}
<commit_msg>Update --threads help<commit_after>// Copyright (c) 2017 Jason White
//
// 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.
extern crate num_cpus;

pub mod opts;

use std::path::PathBuf;

use clap;

/// Returns clap App arguments to use for parsing.
pub fn app<'a, 'b>() -> clap::App<'a, 'b> {

    // Common options
    let verbose_opt = clap::Arg::with_name("verbose")
        .help("Print additional information.")
        .long("verbose")
        .short("v");

    let file_opt = clap::Arg::with_name("file")
        .help("Path to the build description.")
        .takes_value(true)
        .long("file")
        .short("f");

    let dryrun_opt = clap::Arg::with_name("dryrun")
        .help("Print what might happen.")
        .long("dryrun")
        .short("n");

    let threads_opt = clap::Arg::with_name("threads")
        .help("The number of threads to use. Defaults to the number of cores \
               on this machine.")
        .takes_value(true)
        .long("threads")
        .short("t");

    let color_opt = clap::Arg::with_name("color")
        .help("When to colorize the output.")
        .takes_value(true)
        .long("color")
        .possible_values(&["auto", "never", "always"])
        .default_value("auto");

    let common_opts = &[verbose_opt.clone(), file_opt.clone(),
        dryrun_opt.clone(), threads_opt.clone(), color_opt.clone()];

    clap::App::new("button")
        .version(crate_version!())
        .author(crate_authors!())
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .setting(clap::AppSettings::VersionlessSubcommands)
        .subcommand(clap::SubCommand::with_name("build")
            .about("Builds your damn software.")
            .alias("update")
            .args(common_opts)
            .args(&[
                clap::Arg::with_name("auto")
                    .help("Wait for changes and build automatically.")
                    .long("auto"),

                clap::Arg::with_name("watchdir")
                    .help("Used with --auto. The directory to watch for changes in.")
                    .takes_value(true)
                    .long("watchdir"),

                clap::Arg::with_name("delay")
                    .help("Used with --auto. The number of milliseconds to wait before \
                          building.")
                    .takes_value(true)
                    .long("delay")
                    .default_value("50"),
            ])
        )
        .subcommand(clap::SubCommand::with_name("clean")
            .about("Cleans your damn software.")
            .args(common_opts)
            .args(&[
                clap::Arg::with_name("purge")
                    .help("Delete the build state too.")
                    .long("purge"),
            ])
        )
        .subcommand(clap::SubCommand::with_name("graph")
            .about("Graphs your damn software.")
            .arg(file_opt.clone())
            .arg(threads_opt.clone())
            .args(&[
                clap::Arg::with_name("changes")
                    .help("Only display the subgraph that will be traversed on \
                          an update. This has to query the file system for \
                          changes to resources.")
                    .long("changes"),
                clap::Arg::with_name("cached")
                    .help("Display the cached graph from the previous build.")
                    .long("cached"),
                clap::Arg::with_name("full")
                    .help("Displays the full name for each node.")
                    .long("full"),
                clap::Arg::with_name("edges")
                    .help("Type of edges to show.")
                    .takes_value(true)
                    .possible_values(&["explicit", "implicit", "both"])
                    .default_value("explicit")
                    .long("edges"),
            ])
        )
}

pub enum Command {
    Build(opts::Build),
    Clean(opts::Clean),
    Graph(opts::Graph),
}

pub fn subcommand<'a>(name: &str, matches: &clap::ArgMatches<'a>) -> clap::Result<Command> {
    let cpu_count = num_cpus::get();

    match name {
        "build" => Ok(Command::Build(opts::Build {
            file: matches.value_of("file").map(PathBuf::from),
            dryrun: matches.is_present("dryrun"),
            color: try!(value_t!(matches.value_of("color"), opts::Coloring)),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            autobuild: matches.is_present("auto"),
            delay: try!(value_t!(matches, "delay", usize)),
        })),

        "clean" => Ok(Command::Clean(opts::Clean {
            file: matches.value_of("file").map(PathBuf::from),
            dryrun: matches.is_present("dryrun"),
            color: try!(value_t!(matches.value_of("color"), opts::Coloring)),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            purge: matches.is_present("purge"),
        })),

        "graph" => Ok(Command::Graph(opts::Graph {
            file: matches.value_of("file").map(PathBuf::from),
            threads: value_t!(matches, "threads", usize).unwrap_or(cpu_count),
            changes: matches.is_present("changes"),
            cached: matches.is_present("cached"),
            full: matches.is_present("full"),
            edges: try!(value_t!(matches.value_of("edges"), opts::Edges)),
        })),

        // If all subcommands are matched above, then this shouldn't happen.
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>#![cfg(not(windows))]

use screen::Key;
use screen::Key::*;
use std::old_io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::old_io::process::{Command, InheritFd};
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;

pub struct Screen {
  tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
  pub visible_choices: u16,
  pub start_line: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    let visible_choices = min(20, rows - 1);
    let start_line = rows - visible_choices - 1;
    Screen {
      tty: tty,
      original_stty_state: current_stty_state,
      height: rows,
      width: cols,
      visible_choices: visible_choices,
      start_line: start_line,
    }
  }

  fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }

  pub fn move_cursor_to_bottom(&mut self) {
    let end_line = self.start_line + self.visible_choices;
    self.move_cursor(end_line, 0);
    self.write("\n");
  }

  pub fn blank_screen(&mut self) {
    let start_line = self.start_line;
    self.move_cursor(start_line, 0);
    let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
    for _ in range(0, self.height) {
      self.tty.write(blank_line.as_bytes());
    }
    self.move_cursor(start_line, 0);
  }

  pub fn show_cursor(&mut self) {
    self.tty.write(ansi::show_cursor().as_slice());
  }

  pub fn hide_cursor(&mut self) {
    self.tty.write(ansi::hide_cursor().as_slice());
  }

  pub fn write(&mut self, s: &str) {
    self.tty.write(s.as_bytes());
  }

  pub fn write_inverted(&mut self, s: &str) {
    self.tty.write(ansi::inverse().as_slice());
    self.tty.write(s.as_bytes());
    self.tty.write(ansi::reset().as_slice());
  }

  // Return all buffered keystrokes, or the next key if buffer is empty.
  pub fn get_buffered_keys(&mut self) -> Vec<Key> {
    let mut ret = Vec::new();
    while let Ok(byte) = self.tty.input.try_recv() {
      ret.push(Terminal::translate_byte(byte));
    }
    if ret.is_empty() {
      let byte = self.tty.input.recv().unwrap();
      ret.push(Terminal::translate_byte(byte));
    }
    ret
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

struct Terminal {
  input: Receiver<u8>,
  output: File,
}

impl Terminal {
  fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    let (tx, rx) = mpsc::channel();
    Thread::spawn(move || {
      loop {
        tx.send(input_file.read_byte().unwrap()).unwrap();
      }
    });
    Terminal {
      input: rx,
      output: output_file
    }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  fn getchar(&mut self) -> Key {
    let byte = self.input.recv().unwrap();
    Terminal::translate_byte(byte)
  }

  fn translate_byte(byte: u8) -> Key {
    if byte == '\r' as u8 {
      Enter
    } else if byte == 127 {
      Backspace
    } else if byte & 96 == 0 {
      Control((byte + 96u8) as char)
    } else {
      Char(byte as char)
    }
  }

  fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice()).unwrap();
  }

  fn writeln(&mut self, s: &str) {
    self.output.write_line(s).unwrap();
  }

  fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}
<commit_msg>Fix winsize on Linux<commit_after>#![cfg(not(windows))]

use screen::Key;
use screen::Key::*;
use std::old_io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::old_io::process::{Command, InheritFd};
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;

pub struct Screen {
  tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
  pub visible_choices: u16,
  pub start_line: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    let visible_choices = min(20, rows - 1);
    let start_line = rows - visible_choices - 1;
    Screen {
      tty: tty,
      original_stty_state: current_stty_state,
      height: rows,
      width: cols,
      visible_choices: visible_choices,
      start_line: start_line,
    }
  }

  fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }

  pub fn move_cursor_to_bottom(&mut self) {
    let end_line = self.start_line + self.visible_choices;
    self.move_cursor(end_line, 0);
    self.write("\n");
  }

  pub fn blank_screen(&mut self) {
    let start_line = self.start_line;
    self.move_cursor(start_line, 0);
    let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
    for _ in range(0, self.height) {
      self.tty.write(blank_line.as_bytes());
    }
    self.move_cursor(start_line, 0);
  }

  pub fn show_cursor(&mut self) {
    self.tty.write(ansi::show_cursor().as_slice());
  }

  pub fn hide_cursor(&mut self) {
    self.tty.write(ansi::hide_cursor().as_slice());
  }

  pub fn write(&mut self, s: &str) {
    self.tty.write(s.as_bytes());
  }

  pub fn write_inverted(&mut self, s: &str) {
    self.tty.write(ansi::inverse().as_slice());
    self.tty.write(s.as_bytes());
    self.tty.write(ansi::reset().as_slice());
  }

  // Return all buffered keystrokes, or the next key if buffer is empty.
  pub fn get_buffered_keys(&mut self) -> Vec<Key> {
    let mut ret = Vec::new();
    while let Ok(byte) = self.tty.input.try_recv() {
      ret.push(Terminal::translate_byte(byte));
    }
    if ret.is_empty() {
      let byte = self.tty.input.recv().unwrap();
      ret.push(Terminal::translate_byte(byte));
    }
    ret
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

struct Terminal {
  input: Receiver<u8>,
  output: File,
}

impl Terminal {
  fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    let (tx, rx) = mpsc::channel();
    Thread::spawn(move || {
      loop {
        tx.send(input_file.read_byte().unwrap()).unwrap();
      }
    });
    Terminal {
      input: rx,
      output: output_file
    }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  fn getchar(&mut self) -> Key {
    let byte = self.input.recv().unwrap();
    Terminal::translate_byte(byte)
  }

  fn translate_byte(byte: u8) -> Key {
    if byte == '\r' as u8 {
      Enter
    } else if byte == 127 {
      Backspace
    } else if byte & 96 == 0 {
      Control((byte + 96u8) as char)
    } else {
      Char(byte as char)
    }
  }

  fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice()).unwrap();
  }

  fn writeln(&mut self, s: &str) {
    self.output.write_line(s).unwrap();
  }

  fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[cfg(any(target_os = "linux", target_os = "android"))]
    const TIOCGWINSZ: c_ulong = 0x00005413;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}

#[test]
fn winsize_test() {
  let term = Terminal::open_terminal();
  let (cols, rows) = term.winsize().expect("Failed to get window size!");
  // We don't know the window size a priori, but we can at least
  // assert that it is within some kind of sensible range.
  assert!(cols > 40);
  assert!(rows > 40);
  assert!(cols < 1000);
  assert!(rows < 1000);
}
<|endoftext|>


<commit_before>use std::string::String;
use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};
use types::{BERT_LABEL, EXT_VERSION, BertTag, BertType, BertTuple};

pub struct Serializer;

pub struct Deserializer {
//    data: Vec<u8>
}

pub trait Serialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

trait Deserialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

impl Serializer {
    pub fn new() -> Serializer {
        Serializer {}
    }

    pub fn term_to_binary<T>(&self, data: T) -> Vec<u8> where Self: Serialize<T> {
        let mut binary = vec![EXT_VERSION];
        let serialized_data = self.to_bert(data);
        binary.extend(serialized_data.iter().clone());
        binary
    }

    fn generate_term(&self, tag: BertTag, data: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![tag as u8];
        binary.extend(data.iter().clone());
        binary
    }

    fn convert_string_to_binary(&self, data: &str) -> Vec<u8> {
        let binary_string = data.as_bytes();
        let binary_length = binary_string.len() as u8;
        let mut binary = vec![0u8, binary_length];
        binary.extend(binary_string.iter().clone());
        binary
    }

    fn merge_terms(&self, term_1: Vec<u8>, term_2: Vec<u8>) -> Vec<u8> {
        let mut binary: Vec<u8> = term_1.clone();
        binary.extend(term_2.iter().clone());
        binary
    }

    fn get_atom(&self, name: Vec<u8>) -> Vec<u8> {
        self.generate_term(BertTag::Atom, name)
    }

    fn get_nil(&self) -> Vec<u8> {
        vec![BertTag::Nil as u8]
    }

    fn get_bert_nil(&self) -> Vec<u8> {
        let bert_atom = self.get_bert_atom();

        let binary_nil_string = self.convert_string_to_binary("nil");
        let nil_atom = self.get_atom(binary_nil_string);

        let mut binary: Vec<u8> = vec![];
        binary.extend(bert_atom.iter().clone());
        binary.extend(nil_atom.iter().clone());
        self.get_small_tuple(2, binary)
    }

    fn get_bert_atom(&self) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(BERT_LABEL);
        self.get_atom(binary_string)
    }

    fn get_small_tuple(&self, arity: u8, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![arity];
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::SmallTuple, binary)
    }

    fn get_large_tuple(&self, arity: i32, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(arity).unwrap();
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::LargeTuple, binary)
    }

    fn enum_value_to_binary(&self, enum_value: BertType) -> Vec<u8> {
        match enum_value {
            BertType::SmallInteger(value_u8) => self.to_bert(value_u8),
            BertType::Integer(value_i32) => self.to_bert(value_i32),
            BertType::Float(value_f64) => self.to_bert(value_f64),
            BertType::String(string) => self.to_bert(string),
            BertType::Boolean(boolean) => self.to_bert(boolean),
            BertType::Tuple(tuple) => self.to_bert(tuple),
        }
    }
}

impl Serialize<u8> for Serializer {
    fn to_bert(&self, data: u8) -> Vec<u8> {
        self.generate_term(BertTag::SmallInteger, vec![data])
    }
}

impl Serialize<i32> for Serializer {
    fn to_bert(&self, data: i32) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::Integer, binary)
    }
}

impl Serialize<f64> for Serializer {
    fn to_bert(&self, data: f64) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_f64::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::NewFloat, binary)
    }
}

impl Serialize<String> for Serializer {
    fn to_bert(&self, data: String) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(&data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl<'a> Serialize<&'a str> for Serializer {
    fn to_bert(&self, data: &'a str) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl Serialize<bool> for Serializer {
    fn to_bert(&self, data: bool) -> Vec<u8> {
        let boolean_string = data.to_string();
        let binary_boolean = self.convert_string_to_binary(&boolean_string);

        let bert_atom = self.get_bert_atom();
        let boolean_atom = self.get_atom(binary_boolean);

        let binary = self.merge_terms(bert_atom, boolean_atom);
        self.get_small_tuple(2, binary)
    }
}

impl Serialize<BertTuple> for Serializer {
    fn to_bert(&self, data: BertTuple) -> Vec<u8> {
        let arity = data.values.len();
        let binary: Vec<u8> = data.values
            .into_iter()
            .flat_map(|item| self.enum_value_to_binary(item).into_iter())
            .collect();

        match arity {
            0...255 => self.get_small_tuple(arity as u8, binary),
            _ => self.get_large_tuple(arity as i32, binary),
        }
    }
}

#[cfg(test)]
mod test_serializer {
    use super::{Serializer};
    use types::{BertTag};

    #[test]
    fn test_serialize_u8() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(1u8),
            vec![131u8, 97, 1]
        );

        assert_eq!(
            serializer.term_to_binary(255u8),
            vec![131u8, 97, 255]
        );
    }

    #[test]
    fn test_serialize_i32() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-2147483648),
            vec![131u8, 98, 128, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(-1i32),
            vec![131u8, 98, 255, 255, 255, 255]
        );

        assert_eq!(
            serializer.term_to_binary(512i32),
            vec![131u8, 98, 0, 0, 2, 0]
        );

        assert_eq!(
            serializer.term_to_binary(2147483647),
            vec![131u8, 98, 127, 255, 255, 255]
        );
    }

    #[test]
    fn test_serializer_f64() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-3.14f64),
            vec![131u8, 70, 192, 9, 30, 184, 81, 235, 133, 31]
        );

        assert_eq!(
            serializer.term_to_binary(0.0f64),
            vec![131u8, 70, 0, 0, 0, 0, 0, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(3.14f64),
            vec![131u8, 70, 64, 9, 30, 184, 81, 235, 133, 31]
        )

    }

    #[test]
    fn test_serialize_bool() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(true),
            vec![
                131u8,
                104,                            // small tuple tag
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 4, 116, 114, 117, 101   // "true" as atom
            ]
        );

        assert_eq!(
            serializer.term_to_binary(false),
            vec![
                131u8,
                104,                               // small tuple tag
                2,                                 // tuple length
                100, 0, 4, 98, 101, 114, 116,      // "bert" as atom
                100, 0, 5, 102, 97, 108, 115, 101  // "false" as atom
            ]
        );
    }

    #[test]
    fn test_serialize_string() {
        let serializer = Serializer::new();

        // string by value
        assert_eq!(
            serializer.term_to_binary(String::from("test")),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );

        // string by reference
        assert_eq!(
            serializer.term_to_binary("test"),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_generate_term() {
        let serializer = Serializer::new();

        let data: Vec<u8> = vec![0, 4, 116, 101, 115, 116];  // bert as string
        assert_eq!(
            serializer.generate_term(BertTag::Atom, data),
            vec![100u8, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_merge_terms() {
        let serializer = Serializer::new();

        let term_1: Vec<u8> = vec![100, 0, 4,  98, 101, 114, 116];
        let term_2: Vec<u8> = vec![100, 0, 3, 110, 105, 108];
        assert_eq!(
            serializer.merge_terms(term_1, term_2),
            vec![
                100u8, 0, 4, 98, 101, 114, 116,  // "bert" as atom
                100,   0, 3, 110, 105, 108       // "nil" as atom
            ]
        );
    }

    #[test]
    fn test_convert_string_to_binary() {
        let serializer = Serializer::new();

        let data = "bert";
        assert_eq!(
            serializer.convert_string_to_binary(data),
            vec![0u8, 4, 98, 101, 114, 116]
        );
    }

    #[test]
    fn test_get_atom() {
        let serializer = Serializer::new();

        let data: Vec<u8> = vec![0, 4, 116, 101, 115, 116];  // bert as string
        assert_eq!(
            serializer.get_atom(data),
            vec![100u8, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_get_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_nil(),
            vec![106u8]
        );
    }

    #[test]
    fn test_get_bert_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_bert_nil(),
            vec![
                104u8,
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 3, 110, 105, 108        // "nil" as atom
            ]
        )
    }
}
<commit_msg>Fixed converting string length into binary format<commit_after>use std::string::String;
use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};
use types::{BERT_LABEL, EXT_VERSION, BertTag, BertType, BertTuple};

pub struct Serializer;

pub struct Deserializer {
//    data: Vec<u8>
}

pub trait Serialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

trait Deserialize<T> {
    fn to_bert(&self, data: T) -> Vec<u8>;
}

impl Serializer {
    pub fn new() -> Serializer {
        Serializer {}
    }

    pub fn term_to_binary<T>(&self, data: T) -> Vec<u8> where Self: Serialize<T> {
        let mut binary = vec![EXT_VERSION];
        let serialized_data = self.to_bert(data);
        binary.extend(serialized_data.iter().clone());
        binary
    }

    fn generate_term(&self, tag: BertTag, data: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![tag as u8];
        binary.extend(data.iter().clone());
        binary
    }

    fn convert_string_to_binary(&self, data: &str) -> Vec<u8> {
        let binary_string = data.as_bytes();
        let binary_length = binary_string.len() as i16;
        let mut binary = vec![];
        binary.write_i16::<BigEndian>(binary_length).unwrap();
        binary.extend(binary_string.iter().clone());
        binary
    }

    fn merge_terms(&self, term_1: Vec<u8>, term_2: Vec<u8>) -> Vec<u8> {
        let mut binary: Vec<u8> = term_1.clone();
        binary.extend(term_2.iter().clone());
        binary
    }

    fn get_atom(&self, name: Vec<u8>) -> Vec<u8> {
        self.generate_term(BertTag::Atom, name)
    }

    fn get_nil(&self) -> Vec<u8> {
        vec![BertTag::Nil as u8]
    }

    fn get_bert_nil(&self) -> Vec<u8> {
        let bert_atom = self.get_bert_atom();

        let binary_nil_string = self.convert_string_to_binary("nil");
        let nil_atom = self.get_atom(binary_nil_string);

        let mut binary: Vec<u8> = vec![];
        binary.extend(bert_atom.iter().clone());
        binary.extend(nil_atom.iter().clone());
        self.get_small_tuple(2, binary)
    }

    fn get_bert_atom(&self) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(BERT_LABEL);
        self.get_atom(binary_string)
    }

    fn get_small_tuple(&self, arity: u8, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![arity];
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::SmallTuple, binary)
    }

    fn get_large_tuple(&self, arity: i32, elements: Vec<u8>) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(arity).unwrap();
        binary.extend(elements.iter().clone());
        self.generate_term(BertTag::LargeTuple, binary)
    }

    fn enum_value_to_binary(&self, enum_value: BertType) -> Vec<u8> {
        match enum_value {
            BertType::SmallInteger(value_u8) => self.to_bert(value_u8),
            BertType::Integer(value_i32) => self.to_bert(value_i32),
            BertType::Float(value_f64) => self.to_bert(value_f64),
            BertType::String(string) => self.to_bert(string),
            BertType::Boolean(boolean) => self.to_bert(boolean),
            BertType::Tuple(tuple) => self.to_bert(tuple),
        }
    }
}

impl Serialize<u8> for Serializer {
    fn to_bert(&self, data: u8) -> Vec<u8> {
        self.generate_term(BertTag::SmallInteger, vec![data])
    }
}

impl Serialize<i32> for Serializer {
    fn to_bert(&self, data: i32) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_i32::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::Integer, binary)
    }
}

impl Serialize<f64> for Serializer {
    fn to_bert(&self, data: f64) -> Vec<u8> {
        let mut binary = vec![];
        binary.write_f64::<BigEndian>(data).unwrap();
        self.generate_term(BertTag::NewFloat, binary)
    }
}

impl Serialize<String> for Serializer {
    fn to_bert(&self, data: String) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(&data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl<'a> Serialize<&'a str> for Serializer {
    fn to_bert(&self, data: &'a str) -> Vec<u8> {
        let binary_string = self.convert_string_to_binary(data);
        self.generate_term(BertTag::String, binary_string)
    }
}

impl Serialize<bool> for Serializer {
    fn to_bert(&self, data: bool) -> Vec<u8> {
        let boolean_string = data.to_string();
        let binary_boolean = self.convert_string_to_binary(&boolean_string);

        let bert_atom = self.get_bert_atom();
        let boolean_atom = self.get_atom(binary_boolean);

        let binary = self.merge_terms(bert_atom, boolean_atom);
        self.get_small_tuple(2, binary)
    }
}

impl Serialize<BertTuple> for Serializer {
    fn to_bert(&self, data: BertTuple) -> Vec<u8> {
        let arity = data.values.len();
        let binary: Vec<u8> = data.values
            .into_iter()
            .flat_map(|item| self.enum_value_to_binary(item).into_iter())
            .collect();

        match arity {
            0...255 => self.get_small_tuple(arity as u8, binary),
            _ => self.get_large_tuple(arity as i32, binary),
        }
    }
}

#[cfg(test)]
mod test_serializer {
    use super::{Serializer};
    use types::{BertTag};

    #[test]
    fn test_serialize_u8() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(1u8),
            vec![131u8, 97, 1]
        );

        assert_eq!(
            serializer.term_to_binary(255u8),
            vec![131u8, 97, 255]
        );
    }

    #[test]
    fn test_serialize_i32() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-2147483648),
            vec![131u8, 98, 128, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(-1i32),
            vec![131u8, 98, 255, 255, 255, 255]
        );

        assert_eq!(
            serializer.term_to_binary(512i32),
            vec![131u8, 98, 0, 0, 2, 0]
        );

        assert_eq!(
            serializer.term_to_binary(2147483647),
            vec![131u8, 98, 127, 255, 255, 255]
        );
    }

    #[test]
    fn test_serializer_f64() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(-3.14f64),
            vec![131u8, 70, 192, 9, 30, 184, 81, 235, 133, 31]
        );

        assert_eq!(
            serializer.term_to_binary(0.0f64),
            vec![131u8, 70, 0, 0, 0, 0, 0, 0, 0, 0]
        );

        assert_eq!(
            serializer.term_to_binary(3.14f64),
            vec![131u8, 70, 64, 9, 30, 184, 81, 235, 133, 31]
        )

    }

    #[test]
    fn test_serialize_bool() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.term_to_binary(true),
            vec![
                131u8,
                104,                            // small tuple tag
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 4, 116, 114, 117, 101   // "true" as atom
            ]
        );

        assert_eq!(
            serializer.term_to_binary(false),
            vec![
                131u8,
                104,                               // small tuple tag
                2,                                 // tuple length
                100, 0, 4, 98, 101, 114, 116,      // "bert" as atom
                100, 0, 5, 102, 97, 108, 115, 101  // "false" as atom
            ]
        );
    }

    #[test]
    fn test_serialize_string() {
        let serializer = Serializer::new();

        // string by value
        assert_eq!(
            serializer.term_to_binary(String::from("test")),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );

        // string by reference
        assert_eq!(
            serializer.term_to_binary("test"),
            vec![131u8, 107, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_generate_term() {
        let serializer = Serializer::new();

        let data: Vec<u8> = vec![0, 4, 116, 101, 115, 116];  // bert as string
        assert_eq!(
            serializer.generate_term(BertTag::Atom, data),
            vec![100u8, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_merge_terms() {
        let serializer = Serializer::new();

        let term_1: Vec<u8> = vec![100, 0, 4,  98, 101, 114, 116];
        let term_2: Vec<u8> = vec![100, 0, 3, 110, 105, 108];
        assert_eq!(
            serializer.merge_terms(term_1, term_2),
            vec![
                100u8, 0, 4, 98, 101, 114, 116,  // "bert" as atom
                100,   0, 3, 110, 105, 108       // "nil" as atom
            ]
        );
    }

    #[test]
    fn test_convert_string_to_binary() {
        let serializer = Serializer::new();

        let data = "bert";
        assert_eq!(
            serializer.convert_string_to_binary(data),
            vec![0u8, 4, 98, 101, 114, 116]
        );
    }

    #[test]
    fn test_get_atom() {
        let serializer = Serializer::new();

        let data: Vec<u8> = vec![0, 4, 116, 101, 115, 116];  // bert as string
        assert_eq!(
            serializer.get_atom(data),
            vec![100u8, 0, 4, 116, 101, 115, 116]
        );
    }

    #[test]
    fn test_get_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_nil(),
            vec![106u8]
        );
    }

    #[test]
    fn test_get_bert_nil() {
        let serializer = Serializer::new();

        assert_eq!(
            serializer.get_bert_nil(),
            vec![
                104u8,
                2,                              // tuple length
                100, 0, 4,  98, 101, 114, 116,  // "bert" as atom
                100, 0, 3, 110, 105, 108        // "nil" as atom
            ]
        )
    }
}
<|endoftext|>


<commit_before>use error::{Result, Error};
use filters::Filter;
use std::collections::HashMap;
use token::Token::{self, Identifier, StringLiteral, NumberLiteral, BooleanLiteral};
use value::{Value, Object};

#[derive(Clone)]
pub enum Interrupt {
    Continue,
    Break,
}

#[derive(Default)]
pub struct Context {
    stack: Vec<Object>,
    globals: Object,

    /// The current interrupt state. The interrupt state is used by
    /// the `break` and `continue` tags to halt template rendering
    /// at a given point and unwind the `render` call stack until
    /// it reaches an enclosing `for_loop`. At that point the interrupt
    /// is cleared, and the `for_loop` carries on processing as directed.
    interrupt: Option<Interrupt>,

    /// The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,

    // Public for backwards compatability
    pub filters: HashMap<String, Box<Filter>>,
}

impl Context {
    /// Creates a new, empty rendering context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::Context;
    /// let ctx = Context::new();
    /// assert_eq!(ctx.get_val("test"), None);
    /// ```
    pub fn new() -> Context {
        Context::with_values_and_filters(Object::new(), HashMap::new())
    }

    pub fn with_values(values: Object) -> Context {
        Context::with_values_and_filters(values, HashMap::new())
    }

    pub fn with_filters(filters: HashMap<String, Box<Filter>>) -> Context {
        Context::with_values_and_filters(Object::new(), filters)
    }

    pub fn with_values_and_filters(values: Object,
                                   filters: HashMap<String, Box<Filter>>)
                                   -> Context {
        Context {
            stack: vec![Object::new()],
            interrupt: None,
            cycles: HashMap::new(),
            globals: values,
            filters: filters,
        }
    }

    pub fn cycle_element(&mut self, name: &str, values: &[Token]) -> Result<Option<Value>> {
        let index = {
            let i = self.cycles.entry(name.to_owned()).or_insert(0);
            let j = *i;
            *i = (*i + 1) % values.len();
            j
        };

        if index >= values.len() {
            return Err(Error::Render(format!("cycle index {} out of bounds {}",
                                             index,
                                             values.len())));
        }

        self.evaluate(&values[index])
    }

    /// Only add the given filter to the context if a filter with this name doesn't already exist.
    pub fn maybe_add_filter(&mut self, name: &str, filter: Box<Filter>) {
        if !self.filters.contains_key(name) {
            self.add_filter(name, filter)
        }
    }

    pub fn add_filter(&mut self, name: &str, filter: Box<Filter>) {
        self.filters.insert(name.to_owned(), filter);
    }

    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b Filter> {
        self.filters.get(name).map(|f| f.as_ref())
    }

    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt.clone();
        self.interrupt = None;
        rval
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_scope(&mut self) {
        self.stack.push(Object::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_scope(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    ///
    /// # Examples
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// ctx.run_in_scope(|mut stack_frame| {
    ///   // stack_frame inherits values from its parent context
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(42f32)));
    ///
    ///   // but can (optionally) override them
    ///   stack_frame.set_local_val("test", Value::Num(3.14f32));
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(3.14f32)));
    /// });
    /// // the original value is unchanged once the scope exits
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
        where FnT: FnOnce(&mut Context) -> RvalT
    {
        self.push_scope();
        let result = f(self);
        self.pop_scope();
        result
    }

    /// Internal part of get_val. Walks the scope stack to try and find the
    /// reqested variable, and failing that checks the global pool.
    fn get<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.get(name)
    }

    /// Gets a value from the rendering context. The name value can be a
    /// dot-separated path to a value. A value will only be returned if
    /// each link in the chain (excluding the final name) refers to a
    /// value of type Object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
    /// ```
    pub fn get_val<'b>(&'b self, name: &str) -> Option<&'b Value> {
        let mut path = name.split('.');
        let key = path.next().unwrap_or("");
        let mut rval = self.get(key);

        // walk the chain of Object values, as specified by the path
        // passed in name
        for id in path {
            match rval {
                Some(&Value::Object(ref x)) => rval = x.get(id),
                _ => return None,
            }
        }

        rval
    }

    /// Sets a value in the global context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn set_val(&mut self, name: &str, val: Value) -> Option<Value> {
        self.globals.insert(name.to_owned(), val)
    }

    /// Translates a Token to a Value, looking it up in the context if
    /// necessary
    pub fn evaluate(&self, t: &Token) -> Result<Option<Value>> {
        match *t {
            NumberLiteral(f) => Ok(Some(Value::Num(f))),
            StringLiteral(ref s) => Ok(Some(Value::Str(s.clone()))),
            BooleanLiteral(b) => Ok(Some(Value::Bool(b))),
            Identifier(ref id) => Ok(self.get_val(id).cloned()),
            _ => {
                let msg = format!("Cannot evaluate {}", t);
                Err(Error::Other(msg))
            }
        }
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.run_in_scope(|mut local_scope| {
    ///   local_scope.set_val("global", Value::Num(42f32));
    ///   local_scope.set_local_val("local", Value::Num(163f32));
    ///
    ///   assert_eq!(local_scope.get_val("global"), Some(&Value::Num(42f32)));
    ///   assert_eq!(local_scope.get_val("local"), Some(&Value::Num(163f32)));
    /// });
    /// assert_eq!(ctx.get_val("global"), Some(&Value::Num(42f32)));
    /// assert_eq!(ctx.get_val("local"), None);
    /// ```
    pub fn set_local_val(&mut self, name: &str, val: Value) -> Option<Value> {
        match self.stack.last_mut() {
            Some(frame) => frame.insert(name.to_owned(), val),
            None => panic!("Cannot insert into an empty stack"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::Context;
    use value::Value;
    use std::collections::HashMap;

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        let mut post = HashMap::new();
        post.insert("number".to_owned(), Value::Num(42f32));
        ctx.set_val("post", Value::Object(post));
        assert_eq!(ctx.get_val("post.number").unwrap(), &Value::Num(42f32));
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.set_val("test", Value::Num(42f32));
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));

        ctx.run_in_scope(|mut new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(42f32));

            // set a new local value, and assert that it overrides the previous value
            new_scope.set_local_val("test", Value::Num(3.14f32));
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(3.14f32));

            // sat a new val that we will pick up outside the scope
            new_scope.set_val("global", Value::str("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
        assert_eq!(ctx.get_val("global").unwrap(), &Value::str("some value"));
    }

    #[test]
    fn evaluate_handles_string_literals() {
        use token::Token::StringLiteral;

        let ctx = Context::new();
        let t = StringLiteral("hello".to_owned());
        assert_eq!(ctx.evaluate(&t).unwrap(), Some(Value::str("hello")));
    }

    #[test]
    fn evaluate_handles_number_literals() {
        use token::Token::NumberLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&NumberLiteral(42f32)).unwrap(),
                   Some(Value::Num(42f32)));
    }

    #[test]
    fn evaluate_handles_boolean_literals() {
        use token::Token::BooleanLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&BooleanLiteral(true)).unwrap(),
                   Some(Value::Bool(true)));

        assert_eq!(ctx.evaluate(&BooleanLiteral(false)).unwrap(),
                   Some(Value::Bool(false)));
    }

    #[test]
    fn evaluate_handles_identifiers() {
        use token::Token::Identifier;

        let mut ctx = Context::new();
        ctx.set_val("var0", Value::Num(42f32));
        assert_eq!(ctx.evaluate(&Identifier("var0".to_owned())).unwrap(),
                   Some(Value::Num(42f32)));
        assert_eq!(ctx.evaluate(&Identifier("nope".to_owned())).unwrap(), None);
    }

    #[test]
    fn evaluate_returns_none_on_invalid_token() {
        use token::Token::DotDot;
        let ctx = Context::new();
        assert!(ctx.evaluate(&DotDot).is_err());
    }
}
<commit_msg>chore: Resolve beta warning<commit_after>use error::{Result, Error};
use filters::Filter;
use std::collections::HashMap;
use token::Token::{self, Identifier, StringLiteral, NumberLiteral, BooleanLiteral};
use value::{Value, Object};

#[derive(Clone)]
pub enum Interrupt {
    Continue,
    Break,
}

#[derive(Default)]
pub struct Context {
    stack: Vec<Object>,
    globals: Object,

    /// The current interrupt state. The interrupt state is used by
    /// the `break` and `continue` tags to halt template rendering
    /// at a given point and unwind the `render` call stack until
    /// it reaches an enclosing `for_loop`. At that point the interrupt
    /// is cleared, and the `for_loop` carries on processing as directed.
    interrupt: Option<Interrupt>,

    /// The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,

    // Public for backwards compatability
    pub filters: HashMap<String, Box<Filter>>,
}

impl Context {
    /// Creates a new, empty rendering context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::Context;
    /// let ctx = Context::new();
    /// assert_eq!(ctx.get_val("test"), None);
    /// ```
    pub fn new() -> Context {
        Context::with_values_and_filters(Object::new(), HashMap::new())
    }

    pub fn with_values(values: Object) -> Context {
        Context::with_values_and_filters(values, HashMap::new())
    }

    pub fn with_filters(filters: HashMap<String, Box<Filter>>) -> Context {
        Context::with_values_and_filters(Object::new(), filters)
    }

    pub fn with_values_and_filters(values: Object,
                                   filters: HashMap<String, Box<Filter>>)
                                   -> Context {
        Context {
            stack: vec![Object::new()],
            interrupt: None,
            cycles: HashMap::new(),
            globals: values,
            filters: filters,
        }
    }

    pub fn cycle_element(&mut self, name: &str, values: &[Token]) -> Result<Option<Value>> {
        let index = {
            let i = self.cycles.entry(name.to_owned()).or_insert(0);
            let j = *i;
            *i = (*i + 1) % values.len();
            j
        };

        if index >= values.len() {
            return Err(Error::Render(format!("cycle index {} out of bounds {}",
                                             index,
                                             values.len())));
        }

        self.evaluate(&values[index])
    }

    /// Only add the given filter to the context if a filter with this name doesn't already exist.
    pub fn maybe_add_filter(&mut self, name: &str, filter: Box<Filter>) {
        if !self.filters.contains_key(name) {
            self.add_filter(name, filter)
        }
    }

    pub fn add_filter(&mut self, name: &str, filter: Box<Filter>) {
        self.filters.insert(name.to_owned(), filter);
    }

    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b Filter> {
        self.filters.get(name).map(|f| f.as_ref())
    }

    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt.clone();
        self.interrupt = None;
        rval
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_scope(&mut self) {
        self.stack.push(Object::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_scope(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    ///
    /// # Examples
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// ctx.run_in_scope(|mut stack_frame| {
    ///   // stack_frame inherits values from its parent context
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(42f32)));
    ///
    ///   // but can (optionally) override them
    ///   stack_frame.set_local_val("test", Value::Num(3.14f32));
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(3.14f32)));
    /// });
    /// // the original value is unchanged once the scope exits
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
        where FnT: FnOnce(&mut Context) -> RvalT
    {
        self.push_scope();
        let result = f(self);
        self.pop_scope();
        result
    }

    /// Internal part of get_val. Walks the scope stack to try and find the
    /// reqested variable, and failing that checks the global pool.
    fn get<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.get(name)
    }

    /// Gets a value from the rendering context. The name value can be a
    /// dot-separated path to a value. A value will only be returned if
    /// each link in the chain (excluding the final name) refers to a
    /// value of type Object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
    /// ```
    pub fn get_val<'b>(&'b self, name: &str) -> Option<&'b Value> {
        let mut path = name.split('.');
        let key = path.next().unwrap_or("");
        let mut rval = self.get(key);

        // walk the chain of Object values, as specified by the path
        // passed in name
        for id in path {
            match rval {
                Some(&Value::Object(ref x)) => rval = x.get(id),
                _ => return None,
            }
        }

        rval
    }

    /// Sets a value in the global context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn set_val(&mut self, name: &str, val: Value) -> Option<Value> {
        self.globals.insert(name.to_owned(), val)
    }

    /// Translates a Token to a Value, looking it up in the context if
    /// necessary
    pub fn evaluate(&self, t: &Token) -> Result<Option<Value>> {
        match *t {
            NumberLiteral(f) => Ok(Some(Value::Num(f))),
            StringLiteral(ref s) => Ok(Some(Value::Str(s.clone()))),
            BooleanLiteral(b) => Ok(Some(Value::Bool(b))),
            Identifier(ref id) => Ok(self.get_val(id).cloned()),
            _ => {
                let msg = format!("Cannot evaluate {}", t);
                Err(Error::Other(msg))
            }
        }
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.run_in_scope(|mut local_scope| {
    ///   local_scope.set_val("global", Value::Num(42f32));
    ///   local_scope.set_local_val("local", Value::Num(163f32));
    ///
    ///   assert_eq!(local_scope.get_val("global"), Some(&Value::Num(42f32)));
    ///   assert_eq!(local_scope.get_val("local"), Some(&Value::Num(163f32)));
    /// });
    /// assert_eq!(ctx.get_val("global"), Some(&Value::Num(42f32)));
    /// assert_eq!(ctx.get_val("local"), None);
    /// ```
    pub fn set_local_val(&mut self, name: &str, val: Value) -> Option<Value> {
        match self.stack.last_mut() {
            Some(frame) => frame.insert(name.to_owned(), val),
            None => panic!("Cannot insert into an empty stack"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::Context;
    use value::Value;
    use std::collections::HashMap;

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        let mut post = HashMap::new();
        post.insert("number".to_owned(), Value::Num(42f32));
        ctx.set_val("post", Value::Object(post));
        assert_eq!(ctx.get_val("post.number").unwrap(), &Value::Num(42f32));
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.set_val("test", Value::Num(42f32));
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));

        ctx.run_in_scope(|new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(42f32));

            // set a new local value, and assert that it overrides the previous value
            new_scope.set_local_val("test", Value::Num(3.14f32));
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(3.14f32));

            // sat a new val that we will pick up outside the scope
            new_scope.set_val("global", Value::str("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
        assert_eq!(ctx.get_val("global").unwrap(), &Value::str("some value"));
    }

    #[test]
    fn evaluate_handles_string_literals() {
        use token::Token::StringLiteral;

        let ctx = Context::new();
        let t = StringLiteral("hello".to_owned());
        assert_eq!(ctx.evaluate(&t).unwrap(), Some(Value::str("hello")));
    }

    #[test]
    fn evaluate_handles_number_literals() {
        use token::Token::NumberLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&NumberLiteral(42f32)).unwrap(),
                   Some(Value::Num(42f32)));
    }

    #[test]
    fn evaluate_handles_boolean_literals() {
        use token::Token::BooleanLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&BooleanLiteral(true)).unwrap(),
                   Some(Value::Bool(true)));

        assert_eq!(ctx.evaluate(&BooleanLiteral(false)).unwrap(),
                   Some(Value::Bool(false)));
    }

    #[test]
    fn evaluate_handles_identifiers() {
        use token::Token::Identifier;

        let mut ctx = Context::new();
        ctx.set_val("var0", Value::Num(42f32));
        assert_eq!(ctx.evaluate(&Identifier("var0".to_owned())).unwrap(),
                   Some(Value::Num(42f32)));
        assert_eq!(ctx.evaluate(&Identifier("nope".to_owned())).unwrap(), None);
    }

    #[test]
    fn evaluate_returns_none_on_invalid_token() {
        use token::Token::DotDot;
        let ctx = Context::new();
        assert!(ctx.evaluate(&DotDot).is_err());
    }
}
<|endoftext|>