<commit_before>use traits::*;

#[derive(PartialEq, Clone, Debug, Copy)]
pub enum BFTokenType {
    IncrementPtr(usize),      // >
    DecrementPtr(usize),      // <
    IncrementData(usize),     // +
    DecrementData(usize),     // -
    Output,                   // .
    Input,                    // ,

    // These store their corresponding tokens
    //  aka LoopStart stores the position of
    //  the matching LoopEnd and vice versa.
    LoopStart(usize),         // [
    LoopEnd(usize),           // ]

    // Optimised instructions
    SetCurrent(i8),           // Sets the current cell to value
    AddCurrentUp(usize),      // Adds current cell to cell in [current + value]
    AddCurrentDown(usize)     // Adds current cell to cell in [current - value]
}

#[derive(Clone, Debug, Copy)]
pub struct BFToken {
    pub token_type : BFTokenType,

    // Used for matching brackets
    pub pos : usize
}

pub struct BFLexer {
}

impl Lexer<Vec<BFToken>> for BFLexer {

    fn parse(input_string:String) -> LexResult<Vec<BFToken>> {
        // Import enum -> Allows for using enum values without
        //  BFTokenType:: prefix
        use self::BFTokenType::*;

        // Create empty vector
        let mut tokens : Vec<BFToken> = vec![];

        // Store the index of the token in the vector
        let mut pos : usize = 0;

        // This stores the positions of open brackets
        // This is used to match up bracket pairs
        //  so that they can jump to each other in O(1)
        let mut loop_stack : Vec<usize> = vec![];

        // Store the previous 5 tokens, these are used
        //  for optimisations.
        let mut last_tokens = [BFToken { pos : 0, token_type : Input }; 5];

        // Loop through each character
        for character in input_string.chars() {
            // Match our character to a TokenType.
            let token_type = match character {
                '>' => {
                    // Batch up all the '>' tokens
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let IncrementPtr(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    IncrementPtr(new_x)
                },
                '<' => {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let DecrementPtr(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    DecrementPtr(new_x)
                },
                '+' =>  {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let IncrementData(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    IncrementData(new_x)
                },
                '-' => {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let DecrementData(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    DecrementData(new_x)
                },
                '.' => Output,
                ',' => Input,
                '[' => {
                    // Push this pos onto stack
                    loop_stack.push(pos);

                    // Store temp value of 0 for now - will be updated
                    //  once we know matching ]
                    LoopStart(0)
                },
                ']' => {

                    // Get matching bracket.
                    let index = loop_stack.pop().expect("Bracket mismatch");
                    let mut ret_token : BFTokenType = LoopEnd(index);

                    // Update it's data position to this ] token
                    tokens[index].token_type = LoopStart(pos);

                    match last_tokens[0].token_type {
                        // This currently checks for [-] or [+] and replaces
                        //  those with a set current cell to 0 instruction.
                        IncrementData(_)
                        | DecrementData(_) => {
                            if let LoopStart(_) = last_tokens[1].token_type {
                                ret_token = SetCurrent(0);
                                tokens.pop();
                                tokens.pop();
                                pos-=2;
                            }
                        },

                        //  Optimisation for [-<+>] or [->+<] pattern.
                        DecrementPtr(x)
                        | IncrementPtr(x) => {
                            if let IncrementData(a) = last_tokens[1].token_type {
                                match last_tokens[2].token_type {
                                    IncrementPtr(y)
                                    | DecrementPtr(y) => {
                                        if x == y {
                                            if let DecrementData(b) = last_tokens[3].token_type  {
                                                if a == b {
                                                    if let LoopStart(_) = last_tokens[4].token_type {
                                                        if last_tokens[0].token_type == DecrementPtr(x) {
                                                            ret_token = AddCurrentUp(x);
                                                        } else {
                                                            ret_token = AddCurrentDown(x);
                                                        }
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        pos-=5;
                                                    }
                                                }
                                            }
                                        }
                                    },
                                    _ => { }
                                }
                            };
                        },

                        // Empty Loop
                        LoopStart(_) => {
                            tokens.pop();
                            pos-=1;
                            continue;
                        },

                        _ => { }
                    }

                    ret_token
                },

                // BF is a very simple language - if there is an
                //  unrecognised character, it is ignored.
                _ => continue
            };

            let mut i = 1;
            // Recopy last tokens instead of shifting as they
            //  may have chagned.
            while i < 5 && pos > i-1 {
                last_tokens[i] = tokens[pos-i].clone();
                i+=1;
            }

            pos+=1;

            // Add it to the list of tokens
            let token = BFToken { token_type : token_type, pos : pos};

            last_tokens[0] = token.clone();

            tokens.push(token);
        }

        // No way for this to currently fail.
        LexResult::Success(tokens)
    }
}
<commit_msg>Added more comments to cell addition optimisation<commit_after>use traits::*;

#[derive(PartialEq, Clone, Debug, Copy)]
pub enum BFTokenType {
    IncrementPtr(usize),      // >
    DecrementPtr(usize),      // <
    IncrementData(usize),     // +
    DecrementData(usize),     // -
    Output,                   // .
    Input,                    // ,

    // These store their corresponding tokens
    //  aka LoopStart stores the position of
    //  the matching LoopEnd and vice versa.
    LoopStart(usize),         // [
    LoopEnd(usize),           // ]

    // Optimised instructions
    SetCurrent(i8),           // Sets the current cell to value
    AddCurrentUp(usize),      // Adds current cell to cell in [current + value]
    AddCurrentDown(usize)     // Adds current cell to cell in [current - value]
}

#[derive(Clone, Debug, Copy)]
pub struct BFToken {
    pub token_type : BFTokenType,

    // Used for matching brackets
    pub pos : usize
}

pub struct BFLexer {
}

impl Lexer<Vec<BFToken>> for BFLexer {

    fn parse(input_string:String) -> LexResult<Vec<BFToken>> {
        // Import enum -> Allows for using enum values without
        //  BFTokenType:: prefix
        use self::BFTokenType::*;

        // Create empty vector
        let mut tokens : Vec<BFToken> = vec![];

        // Store the index of the token in the vector
        let mut pos : usize = 0;

        // This stores the positions of open brackets
        // This is used to match up bracket pairs
        //  so that they can jump to each other in O(1)
        let mut loop_stack : Vec<usize> = vec![];

        // Store the previous 5 tokens, these are used
        //  for optimisations.
        let mut last_tokens = [BFToken { pos : 0, token_type : Input }; 5];

        // Loop through each character
        for character in input_string.chars() {
            // Match our character to a TokenType.
            let token_type = match character {
                '>' => {
                    // Batch up all the '>' tokens
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let IncrementPtr(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    IncrementPtr(new_x)
                },
                '<' => {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let DecrementPtr(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    DecrementPtr(new_x)
                },
                '+' =>  {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let IncrementData(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    IncrementData(new_x)
                },
                '-' => {
                    let mut new_x = 1;
                    if last_tokens[0].pos > 0 {
                        if let DecrementData(x) = last_tokens[0].token_type {
                            tokens.pop();
                            pos-=1;
                            new_x += x;
                        }
                    }
                    DecrementData(new_x)
                },
                '.' => Output,
                ',' => Input,
                '[' => {
                    // Push this pos onto stack
                    loop_stack.push(pos);

                    // Store temp value of 0 for now - will be updated
                    //  once we know matching ]
                    LoopStart(0)
                },
                ']' => {

                    // Get matching bracket.
                    let index = loop_stack.pop().expect("Bracket mismatch");
                    let mut ret_token : BFTokenType = LoopEnd(index);

                    // Update it's data position to this ] token
                    tokens[index].token_type = LoopStart(pos);

                    match last_tokens[0].token_type {
                        // This currently checks for [-] or [+] and replaces
                        //  those with a set current cell to 0 instruction.
                        IncrementData(_)
                        | DecrementData(_) => {
                            if let LoopStart(_) = last_tokens[1].token_type {
                                ret_token = SetCurrent(0);
                                tokens.pop();
                                tokens.pop();
                                pos-=2;
                            }
                        },

                        // Optimisation for [-<+>] or [->+<] pattern.
                        // These loops will add the current cell value
                        //  to the cell value offset by the amount of
                        //  ptr increments '>' and '<'.
                        //
                        // Examples:
                        //
                        //  [->+<] adds mem[current] to mem[current+1]
                        //
                        //  [->>>>+<<<<<] adds mem[current] to mem[current+4]
                        //
                        // Note that due to the combination optimisation
                        //  this only needs to check for [->+<] or [-<+>]
                        DecrementPtr(x)
                        | IncrementPtr(x) => {
                            if let IncrementData(a) = last_tokens[1].token_type {
                                match last_tokens[2].token_type {
                                    IncrementPtr(y)
                                    | DecrementPtr(y) => {
                                        if x == y {
                                            if let DecrementData(b) = last_tokens[3].token_type  {
                                                if a == b {
                                                    if let LoopStart(_) = last_tokens[4].token_type {
                                                        if last_tokens[0].token_type == DecrementPtr(x) {
                                                            ret_token = AddCurrentUp(x);
                                                        } else {
                                                            ret_token = AddCurrentDown(x);
                                                        }
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        tokens.pop();
                                                        pos-=5;
                                                    }
                                                }
                                            }
                                        }
                                    },
                                    _ => { }
                                }
                            };
                        },

                        // Empty Loop
                        LoopStart(_) => {
                            tokens.pop();
                            pos-=1;
                            continue;
                        },

                        _ => { }
                    }

                    ret_token
                },

                // BF is a very simple language - if there is an
                //  unrecognised character, it is ignored.
                _ => continue
            };

            let mut i = 1;
            // Recopy last tokens instead of shifting as they
            //  may have chagned.
            while i < 5 && pos > i-1 {
                last_tokens[i] = tokens[pos-i].clone();
                i+=1;
            }

            pos+=1;

            // Add it to the list of tokens
            let token = BFToken { token_type : token_type, pos : pos};

            last_tokens[0] = token.clone();

            tokens.push(token);
        }

        // No way for this to currently fail.
        LexResult::Success(tokens)
    }
}
<|endoftext|>


<commit_before>// run-pass
// only-linux - pidfds are a linux-specific concept

#![feature(linux_pidfd)]
#![feature(rustc_private)]

extern crate libc;

use std::io::Error;
use std::os::linux::process::{ChildExt, CommandExt};
use std::process::Command;

fn has_clone3() -> bool {
    let res = unsafe { libc::syscall(libc::SYS_clone3, 0, 0) };
    let err = (res == -1)
        .then(|| Error::last_os_error())
        .expect("probe syscall should not succeed");
    err.raw_os_error() != Some(libc::ENOSYS)
}

fn main() {
    // pidfds require the clone3 syscall
    if !has_clone3() {
        return;
    }

    // We don't assert the precise value, since the standard library
    // might have opened other file descriptors before our code runs.
    let _ = Command::new("echo")
        .create_pidfd(true)
        .spawn()
        .unwrap()
        .pidfd().expect("failed to obtain pidfd");

    let _ = Command::new("echo")
        .create_pidfd(false)
        .spawn()
        .unwrap()
        .pidfd().expect_err("pidfd should not have been created when create_pid(false) is set");

    let _ = Command::new("echo")
        .spawn()
        .unwrap()
        .pidfd().expect_err("pidfd should not have been created");
}
<commit_msg>Rollup merge of #87966 - pietroalbini:fix-pidfd-test, r=m-ou-se<commit_after>// run-pass
// only-linux - pidfds are a linux-specific concept

#![feature(linux_pidfd)]
#![feature(rustc_private)]

extern crate libc;

use std::io::Error;
use std::os::linux::process::{ChildExt, CommandExt};
use std::process::Command;

fn has_clone3() -> bool {
    let res = unsafe { libc::syscall(libc::SYS_clone3, 0, 0) };
    let err = (res == -1)
        .then(|| Error::last_os_error())
        .expect("probe syscall should not succeed");

    // If the `clone3` syscall is not implemented in the current kernel version it should return an
    // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and
    // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of
    // that we need to check for *both* `ENOSYS` and `EPERM`.
    //
    // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning
    // to update their filtering to return `ENOSYS` in a future release:
    //
    //     https://github.com/moby/moby/issues/42680
    //
    err.raw_os_error() != Some(libc::ENOSYS) && err.raw_os_error() != Some(libc::EPERM)
}

fn main() {
    // pidfds require the clone3 syscall
    if !has_clone3() {
        return;
    }

    // We don't assert the precise value, since the standard library
    // might have opened other file descriptors before our code runs.
    let _ = Command::new("echo")
        .create_pidfd(true)
        .spawn()
        .unwrap()
        .pidfd().expect("failed to obtain pidfd");

    let _ = Command::new("echo")
        .create_pidfd(false)
        .spawn()
        .unwrap()
        .pidfd().expect_err("pidfd should not have been created when create_pid(false) is set");

    let _ = Command::new("echo")
        .spawn()
        .unwrap()
        .pidfd().expect_err("pidfd should not have been created");
}
<|endoftext|>


<commit_before>use error::*;
use kernel32;
use os::windows::OkOrGetLastError;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use util;
use winapi::HMODULE;
use winapi::LPCSTR;

#[derive(Debug)]
pub struct Lib {
    handle: HMODULE
}

impl Lib {
    pub unsafe fn new<TPath>(path_to_lib: TPath) -> Result<Lib>
        where TPath: AsRef<Path> {
        let path_to_lib_vec: Vec<_> =
            path_to_lib
                .as_ref()
                .as_os_str()
                .encode_wide()
                .chain((0..1))
                .collect();
        let path_to_lib_ptr = path_to_lib_vec.as_ptr();

        util::error_guard(
            || {
                let handle = kernel32::LoadLibraryW(path_to_lib_ptr);
                let lib_option =
                    if handle.is_null()  {
                        None
                    } else {
                        let lib = Lib { handle: handle };
                        Some(lib)
                    };
                lib_option.ok_or_get_last_error("LoadLibraryW")
            }
        ).chain_err(
            || ErrorKind::LibraryOpen(path_to_lib.as_ref().to_path_buf())
        )

        // .map_err(
        //     |err| {
        //         let err = LibraryOpen::new(Box::new(err), path_to_lib.as_ref().to_path_buf());
        //         E::from(err)
        //     }
        // )
    }

    pub unsafe fn find<T, TStr>(&self, symbol_str: TStr) -> Result<*const T>
        where TStr: AsRef<str> {
        let symbol = symbol_str.as_ref();
        let symbol = symbol.as_ptr();
        let symbol = symbol as LPCSTR;

        util::error_guard(
            || {
                let symbol = kernel32::GetProcAddress(self.handle, symbol);
                if symbol.is_null() {
                    None
                } else {
                    Some(mem::transmute(symbol))
                }.ok_or_get_last_error("GetProcAddress")
            }
        ).chain_err(
            || ErrorKind::LibraryFindSymbol(symbol_str.as_ref().to_string())
        )
    }
}

unsafe impl Send for Lib { }

impl Drop for Lib {
    fn drop(&mut self) {
        util::error_guard(
            || {
                if unsafe { kernel32::FreeLibrary(self.handle) } == 0 {
                    None
                } else {
                    Some(())
                }.ok_or_get_last_error("FreeLibrary")
            }
        ).chain_err(
            || ErrorKind::LibraryClose
        ).unwrap()
    }
}
<commit_msg>Laid the foundation for error chain.<commit_after>use error::*;
use kernel32;
use os::windows::OkOrGetLastError;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use util;
use winapi::HMODULE;
use winapi::LPCSTR;

#[derive(Debug)]
pub struct Lib {
    handle: HMODULE
}

impl Lib {
    pub unsafe fn new<TPath>(path_to_lib: TPath) -> Result<Lib>
        where TPath: AsRef<Path> {
        let path_to_lib_vec: Vec<_> =
            path_to_lib
                .as_ref()
                .as_os_str()
                .encode_wide()
                .chain((0..1))
                .collect();
        let path_to_lib_ptr = path_to_lib_vec.as_ptr();

        util::error_guard(
            || {
                let handle = kernel32::LoadLibraryW(path_to_lib_ptr);
                let lib_option =
                    if handle.is_null()  {
                        None
                    } else {
                        let lib = Lib { handle: handle };
                        Some(lib)
                    };
                lib_option.ok_or_get_last_error("LoadLibraryW")
            }
        ).chain_err(
            || ErrorKind::LibraryOpen(path_to_lib.as_ref().to_path_buf())
        )
    }

    pub unsafe fn find<T, TStr>(&self, symbol_str: TStr) -> Result<*const T>
        where TStr: AsRef<str> {
        let symbol = symbol_str.as_ref();
        let symbol = symbol.as_ptr();
        let symbol = symbol as LPCSTR;

        util::error_guard(
            || {
                let symbol = kernel32::GetProcAddress(self.handle, symbol);
                if symbol.is_null() {
                    None
                } else {
                    Some(mem::transmute(symbol))
                }.ok_or_get_last_error("GetProcAddress")
            }
        ).chain_err(
            || ErrorKind::LibraryFindSymbol(symbol_str.as_ref().to_string())
        )
    }
}

unsafe impl Send for Lib { }

impl Drop for Lib {
    fn drop(&mut self) {
        util::error_guard(
            || {
                if unsafe { kernel32::FreeLibrary(self.handle) } == 0 {
                    None
                } else {
                    Some(())
                }.ok_or_get_last_error("FreeLibrary")
            }
        ).chain_err(
            || ErrorKind::LibraryClose
        ).unwrap()
    }
}
<|endoftext|>


<commit_before>//! Update parsing for console message updates.
use serde::de::{Deserialize, Deserializer};

/// Update for a user's log messages during the last tick.
#[derive(Clone, Hash, Debug)]
pub enum UserConsoleUpdate {
    /// All log messages which occurred during the last tick. Will always be sent
    /// once per tick unless an error occurs *and* this would be empty.
    Messages {
        /// All messages the screeps script code logged last tick.
        log_messages: Vec<String>,
        /// All result strings from console commands executed last tick.
        result_messages: Vec<String>,
    },
    /// An error occurred in the user script. May be sent multiple times per tick,
    /// and will not be sent unless an error did occur.
    Error {
        /// The error which occurred.
        message: String,
    },
}

// Separate representation for deserializing needed in order to have 'Error' variant be a
// struct-like variant rather than a tuple-like variant.

#[derive(Deserialize, Debug)]
enum InnerUpdateRepresentation {
    #[serde(rename = "messages")]
    Messages {
        #[serde(rename = "log")]
        log_messages: Vec<String>,
        #[serde(rename = "results")]
        result_messages: Vec<String>,
    },
    #[serde(rename = "error")]
    Error(String),
}

impl<'de> Deserialize<'de> for UserConsoleUpdate {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let intermediate = InnerUpdateRepresentation::deserialize(deserializer)?;

        Ok(match intermediate {
            InnerUpdateRepresentation::Messages {
                log_messages,
                result_messages,
            } => UserConsoleUpdate::Messages {
                log_messages: log_messages,
                result_messages: result_messages,
            },
            InnerUpdateRepresentation::Error(message) => UserConsoleUpdate::Error { message: message },
        })
    }
}
<commit_msg>Fix console websocket parsing for shards<commit_after>//! Update parsing for console message updates.
use serde::de::{Deserialize, Deserializer, Error};

/// Update for a user's log messages during the last tick.
#[derive(Clone, Hash, Debug)]
pub enum UserConsoleUpdate {
    /// All log messages which occurred during the last tick. Will always be sent
    /// once per tick unless an error occurs *and* this would be empty.
    Messages {
        /// All messages the screeps script code logged last tick.
        log_messages: Vec<String>,
        /// All result strings from console commands executed last tick.
        result_messages: Vec<String>,
        /// The shard the update is from
        shard: Option<String>,
    },
    /// An error occurred in the user script. May be sent multiple times per tick,
    /// and will not be sent unless an error did occur.
    Error {
        /// The error which occurred.
        message: String,
        /// The shard the update is from
        shard: Option<String>,
    },
}

impl UserConsoleUpdate {
    pub fn shard(&self) -> Option<&str> {
        match *self {
            UserConsoleUpdate::Messages { ref shard, .. } => shard.as_ref().map(|s| s.as_ref()),
            UserConsoleUpdate::Error { ref shard, .. } => shard.as_ref().map(|s| s.as_ref()),
        }
    }
}

// Separate representation for deserializing needed in order to have 'Error' variant be a
// struct-like variant rather than a tuple-like variant.

#[derive(Deserialize, Debug)]
struct InnerUpdateInnerMessages {
    log: Vec<String>,
    results: Vec<String>,
}

#[derive(Deserialize, Debug)]
struct InnerUpdateRepresentation {
    error: Option<String>,
    messages: Option<InnerUpdateInnerMessages>,
    shard: Option<String>,
}

impl<'de> Deserialize<'de> for UserConsoleUpdate {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let intermediate = InnerUpdateRepresentation::deserialize(deserializer)?;

        let InnerUpdateRepresentation {
            error: error_opt,
            messages: messages_opt,
            shard,
        } = intermediate;

        let parsed = match (error_opt, messages_opt) {
            (Some(e), None) => UserConsoleUpdate::Error {
                message: e,
                shard: shard,
            },
            (None, Some(m)) => UserConsoleUpdate::Messages {
                log_messages: m.log,
                result_messages: m.results,
                shard: shard,
            },
            (Some(_), Some(_)) => {
                return Err(D::Error::custom("expected either 'messages' property or 'error' property, not both"));
            }
            (None, None) => {
                return Err(D::Error::custom("expected either 'messages' property or 'error' property, neither found"));
            }
        };

        Ok(parsed)
    }
}
<|endoftext|>


<commit_before>// Need this for rusty_peg
#![recursion_limit="256"]

// I hate this lint.
#![allow(unused_parens)]

// The builtin tests don't cover the CLI and so forth, and it's just
// too darn annoying to try and make them do so.
#![cfg_attr(test, allow(dead_code))]

extern crate ascii_canvas;
extern crate atty;
extern crate bit_set;
extern crate diff;
extern crate ena;
extern crate itertools;
extern crate lalrpop_util;
extern crate petgraph;
extern crate regex;
extern crate regex_syntax;
extern crate string_cache;
extern crate term;
extern crate unicode_xid;

#[cfg(test)]
extern crate rand;

// hoist the modules that define macros up earlier
#[macro_use]
mod rust;
#[macro_use]
mod log;

mod api;
mod build;
mod collections;
mod file_text;
mod grammar;
mod lexer;
mod lr1;
mod message;
mod normalize;
mod parser;
mod kernel_set;
mod session;
mod tls;
mod tok;
mod util;

#[cfg(test)] mod generate;
#[cfg(test)] mod test_util;

pub use api::Configuration;
pub use api::process_root;
pub use api::process_root_unconditionally;
pub use ascii_canvas::style;
<commit_msg>Remove unused re-export.<commit_after>// Need this for rusty_peg
#![recursion_limit="256"]

// I hate this lint.
#![allow(unused_parens)]

// The builtin tests don't cover the CLI and so forth, and it's just
// too darn annoying to try and make them do so.
#![cfg_attr(test, allow(dead_code))]

extern crate ascii_canvas;
extern crate atty;
extern crate bit_set;
extern crate diff;
extern crate ena;
extern crate itertools;
extern crate lalrpop_util;
extern crate petgraph;
extern crate regex;
extern crate regex_syntax;
extern crate string_cache;
extern crate term;
extern crate unicode_xid;

#[cfg(test)]
extern crate rand;

// hoist the modules that define macros up earlier
#[macro_use]
mod rust;
#[macro_use]
mod log;

mod api;
mod build;
mod collections;
mod file_text;
mod grammar;
mod lexer;
mod lr1;
mod message;
mod normalize;
mod parser;
mod kernel_set;
mod session;
mod tls;
mod tok;
mod util;

#[cfg(test)] mod generate;
#[cfg(test)] mod test_util;

pub use api::Configuration;
pub use api::process_root;
pub use api::process_root_unconditionally;
use ascii_canvas::style;
<|endoftext|>


<commit_before>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use conn::Conn;
use conn::futures::Disconnect;
use conn::futures::DropQuery;
use conn::futures::DropResult;
use conn::futures::NewConn;
use errors::*;
use lib_futures::Async;
use lib_futures::Async::Ready;
use lib_futures::Async::NotReady;
use lib_futures::Future;
use std::fmt;
use opts::Opts;
use self::futures::*;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use tokio::reactor::Handle;
use time::Duration;
use time::SteadyTime;

pub mod futures;

pub struct Inner {
    closed: bool,
    new: Vec<NewConn>,
    idle: Vec<Conn>,
    disconnecting: Vec<Disconnect>,
    dropping: Vec<DropResult>,
    rollback: Vec<DropQuery>,
}

#[derive(Clone)]
/// Asynchronous pool of MySql connections.
pub struct Pool {
    handle: Handle,
    opts: Opts,
    inner: Rc<RefCell<Inner>>,
    min: usize,
    max: usize,
}

impl fmt::Debug for Pool {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Pool")
            .field("min", &self.min)
            .field("max", &self.max)
            .field("new connections count", &self.inner_ref().new.len())
            .field("idle connections count", &self.inner_ref().idle.len())
            .field("disconnecting connections count",
                   &self.inner_ref().disconnecting.len())
            .finish()
    }
}

impl Pool {
    /// Creates new pool of connections.
    pub fn new<O: Into<Opts>>(opts: O, handle: &Handle) -> Pool {
        let opts = opts.into();
        let pool_min = opts.get_pool_min();
        let pool_max = opts.get_pool_max();
        let mut new_conns = Vec::with_capacity(pool_min);
        for _ in 0..pool_min {
            let new_conn = Conn::new(opts.clone(), handle);
            new_conns.push(new_conn);
        }
        let pool = Pool {
            handle: handle.clone(),
            opts: opts,
            inner: Rc::new(RefCell::new(Inner {
                closed: false,
                new: new_conns,
                idle: Vec::new(),
                disconnecting: Vec::new(),
                dropping: Vec::new(),
                rollback: Vec::new(),
            })),
            min: pool_min,
            max: pool_max,
        };

        pool
    }

    /// Returns future that resolves to `Conn`.
    pub fn get_conn(&self) -> GetConn {
        new_get_conn(self)
    }

    /// Returns future that disconnects this pool from server and resolves to `()`.
    ///
    /// Active connections taken from this pool should be disconnected manually.
    /// Also all pending and new `GetConn`'s will resolve to error.
    pub fn disconnect(mut self) -> DisconnectPool {
        if !self.inner_ref().closed {
            self.inner_mut().closed = true;
            while let Some(conn) = self.take_conn() {
                self.inner_mut().disconnecting.push(conn.disconnect());
            }
        }
        new_disconnect_pool(self)
    }

    /// A way to take connection from a pool.
    fn take_conn(&mut self) -> Option<Conn> {
        let maybe_conn = self.inner_mut().idle.pop();
        maybe_conn.map(|mut conn| {
            conn.pool = Some(self.clone());
            conn
        })
    }

    /// A way to return connection taken from a pool.
    fn return_conn(&mut self, conn: Conn) {
        if self.inner_ref().closed {
            return;
        }

        let idle_duration: Duration = SteadyTime::now() - conn.last_io;
        let ttl = if let Some(conn_ttl) = self.opts.get_conn_ttl() {
            conn_ttl as i64
        } else {
            conn.wait_timeout as i64
        };
        if idle_duration.num_seconds() > ttl {
            self.inner_mut().disconnecting.push(conn.disconnect());
            return;
        }

        if conn.has_result.is_some() {
            self.inner_mut().dropping.push(conn.drop_result());
        } else if conn.in_transaction {
            self.inner_mut().rollback.push(conn.rollback_transaction())
        } else {
            let idle_len = self.inner_ref().idle.len();
            if idle_len >= self.min {
                self.inner_mut().disconnecting.push(conn.disconnect());
            } else {
                self.inner_mut().idle.push(conn);
            }
        }
    }

    fn inner_ref(&self) -> Ref<Inner> {
        self.inner.borrow()
    }

    fn inner_mut(&mut self) -> RefMut<Inner> {
        self.inner.borrow_mut()
    }

    /// Will manage lifetime of futures stored in a pool.
    fn handle_futures(&mut self) -> Result<()> {
        macro_rules! handle {
            ($vec:ident { $($p:pat => $b:block,)+ }) => ({
                let len = self.inner_ref().$vec.len();
                let mut done_fut_idxs = Vec::new();
                for i in 0..len {
                    let result = self.inner_mut().$vec.get_mut(i).unwrap().poll();
                    if let Ok(Ready(_)) = result {
                        done_fut_idxs.push(i);
                    }
                    match result {
                        $($p => $b),+
                        _ => (),
                    }
                }
                while let Some(i) = done_fut_idxs.pop() {
                    let _ = self.inner_mut().$vec.swap_remove(i);
                }
            });
        }

        let mut handled = false;

        // Handle closing connections.
        handle!(disconnecting {
            Ok(Ready(_)) => {},
            Err(_) => {},
        });

        // Handle dirty connections.
        handle!(dropping {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
            },
            Err(_) => {},
        });

        // Handle in-transaction connections
        handle!(rollback {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
            },
            Err(_) => {},
        });

        // Handle connecting connections.
        handle!(new {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
            },
            Err(err) => {
                if ! self.inner_ref().closed {
                    return Err(err);
                }
            },
        });

        if handled {
            self.handle_futures()
        } else {
            Ok(())
        }
    }

    /// Will poll pool for connection.
    fn poll(&mut self) -> Result<Async<Conn>> {
        if self.inner_ref().closed {
            return Err(ErrorKind::PoolDisconnected.into());
        }

        self.handle_futures()?;

        match self.take_conn() {
            Some(conn) => Ok(Ready(conn)),
            None => {
                let new_len = self.inner_ref().new.len();
                let idle_len = self.inner_ref().idle.len();
                if new_len == 0 && idle_len < self.max {
                    let new_conn = Conn::new(self.opts.clone(), &self.handle);
                    self.inner_mut().new.push(new_conn);
                    self.poll()
                } else {
                    Ok(NotReady)
                }
            },
        }
    }
}

impl Drop for Conn {
    fn drop(&mut self) {
        if let Some(mut pool) = self.pool.take() {
            let conn = self.take();
            pool.return_conn(conn)
        }
    }
}

#[cfg(test)]
mod test {
    use conn::pool::Pool;
    use lib_futures::Future;
    use test_misc::DATABASE_URL;
    use tokio::reactor::Core;

    #[test]
    fn should_connect() {
        let mut lp = Core::new().unwrap();

        let pool = Pool::new(&**DATABASE_URL, &lp.handle());
        let fut = pool.get_conn()
            .and_then(|conn| conn.ping().map(|_| ()))
            .and_then(|_| pool.disconnect());

        lp.run(fut).unwrap();
    }

    #[test]
    fn should_hold_bounds() {
        let mut lp = Core::new().unwrap();

        let pool = Pool::new(format!("{}?pool_min=1&pool_max=2", &**DATABASE_URL),
                             &lp.handle());
        let pool_clone = pool.clone();
        let fut = pool.get_conn()
            .join(pool.get_conn())
            .and_then(|(mut conn1, conn2)| {
                let new_conn = pool_clone.get_conn();
                conn1.pool.as_mut().unwrap().handle_futures().unwrap();
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().new.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().idle.len(), 0);
                assert_eq!(conn2.pool.as_ref().unwrap().inner_ref().disconnecting.len(), 0);
                assert_eq!(conn2.pool.as_ref().unwrap().inner_ref().dropping.len(), 0);
                new_conn
            })
            .and_then(|conn1| {
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().new.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().idle.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().disconnecting.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().dropping.len(), 0);
                Ok(())
            })
            .and_then(|_| {
                assert_eq!(pool.inner_ref().new.len(), 0);
                assert_eq!(pool.inner_ref().idle.len(), 1);
                assert_eq!(pool.inner_ref().disconnecting.len(), 0);
                assert_eq!(pool.inner_ref().dropping.len(), 0);
                pool.disconnect()
            });

        lp.run(fut).unwrap();
    }

    #[cfg(feature = "nightly")]
    mod bench {
        use test;
        use conn::pool::Pool;
        use tokio::reactor::Core;
        use lib_futures::Future;
        use test_misc::DATABASE_URL;

        #[bench]
        fn connect(bencher: &mut test::Bencher) {
            let mut lp = Core::new().unwrap();
            let pool = Pool::new(&**DATABASE_URL, &lp.handle());

            bencher.iter(|| {
                let fut = pool.get_conn().and_then(|conn| conn.ping());
                lp.run(fut).unwrap();
            })
        }
    }
}
<commit_msg>Remove resolved futures from pool<commit_after>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use conn::Conn;
use conn::futures::Disconnect;
use conn::futures::DropQuery;
use conn::futures::DropResult;
use conn::futures::NewConn;
use errors::*;
use lib_futures::Async;
use lib_futures::Async::Ready;
use lib_futures::Async::NotReady;
use lib_futures::Future;
use std::fmt;
use opts::Opts;
use self::futures::*;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use tokio::reactor::Handle;
use time::Duration;
use time::SteadyTime;

pub mod futures;

pub struct Inner {
    closed: bool,
    new: Vec<NewConn>,
    idle: Vec<Conn>,
    disconnecting: Vec<Disconnect>,
    dropping: Vec<DropResult>,
    rollback: Vec<DropQuery>,
}

#[derive(Clone)]
/// Asynchronous pool of MySql connections.
pub struct Pool {
    handle: Handle,
    opts: Opts,
    inner: Rc<RefCell<Inner>>,
    min: usize,
    max: usize,
}

impl fmt::Debug for Pool {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Pool")
            .field("min", &self.min)
            .field("max", &self.max)
            .field("new connections count", &self.inner_ref().new.len())
            .field("idle connections count", &self.inner_ref().idle.len())
            .field("disconnecting connections count",
                   &self.inner_ref().disconnecting.len())
            .finish()
    }
}

impl Pool {
    /// Creates new pool of connections.
    pub fn new<O: Into<Opts>>(opts: O, handle: &Handle) -> Pool {
        let opts = opts.into();
        let pool_min = opts.get_pool_min();
        let pool_max = opts.get_pool_max();
        let mut new_conns = Vec::with_capacity(pool_min);
        for _ in 0..pool_min {
            let new_conn = Conn::new(opts.clone(), handle);
            new_conns.push(new_conn);
        }
        let pool = Pool {
            handle: handle.clone(),
            opts: opts,
            inner: Rc::new(RefCell::new(Inner {
                closed: false,
                new: new_conns,
                idle: Vec::new(),
                disconnecting: Vec::new(),
                dropping: Vec::new(),
                rollback: Vec::new(),
            })),
            min: pool_min,
            max: pool_max,
        };

        pool
    }

    /// Returns future that resolves to `Conn`.
    pub fn get_conn(&self) -> GetConn {
        new_get_conn(self)
    }

    /// Returns future that disconnects this pool from server and resolves to `()`.
    ///
    /// Active connections taken from this pool should be disconnected manually.
    /// Also all pending and new `GetConn`'s will resolve to error.
    pub fn disconnect(mut self) -> DisconnectPool {
        if !self.inner_ref().closed {
            self.inner_mut().closed = true;
            while let Some(conn) = self.take_conn() {
                self.inner_mut().disconnecting.push(conn.disconnect());
            }
        }
        new_disconnect_pool(self)
    }

    /// A way to take connection from a pool.
    fn take_conn(&mut self) -> Option<Conn> {
        let maybe_conn = self.inner_mut().idle.pop();
        maybe_conn.map(|mut conn| {
            conn.pool = Some(self.clone());
            conn
        })
    }

    /// A way to return connection taken from a pool.
    fn return_conn(&mut self, conn: Conn) {
        if self.inner_ref().closed {
            return;
        }

        let idle_duration: Duration = SteadyTime::now() - conn.last_io;
        let ttl = if let Some(conn_ttl) = self.opts.get_conn_ttl() {
            conn_ttl as i64
        } else {
            conn.wait_timeout as i64
        };
        if idle_duration.num_seconds() > ttl {
            self.inner_mut().disconnecting.push(conn.disconnect());
            return;
        }

        if conn.has_result.is_some() {
            self.inner_mut().dropping.push(conn.drop_result());
        } else if conn.in_transaction {
            self.inner_mut().rollback.push(conn.rollback_transaction())
        } else {
            let idle_len = self.inner_ref().idle.len();
            if idle_len >= self.min {
                self.inner_mut().disconnecting.push(conn.disconnect());
            } else {
                self.inner_mut().idle.push(conn);
            }
        }
    }

    fn inner_ref(&self) -> Ref<Inner> {
        self.inner.borrow()
    }

    fn inner_mut(&mut self) -> RefMut<Inner> {
        self.inner.borrow_mut()
    }

    /// Will manage lifetime of futures stored in a pool.
    fn handle_futures(&mut self) -> Result<()> {
        macro_rules! handle {
            ($vec:ident { $($p:pat => $b:block,)+ }) => ({
                let len = self.inner_ref().$vec.len();
                let mut done_fut_idxs = Vec::new();
                for i in 0..len {
                    let result = self.inner_mut().$vec.get_mut(i).unwrap().poll();
                    match result {
                        Ok(Ready(_)) | Err(_) => done_fut_idxs.push(i),
                        _ => (),
                    }

                    let out: Result<()> = match result {
                        $($p => $b),+
                        _ => Ok(()),
                    };

                    match out {
                        Err(err) => {
                            // early return in case of error
                            while let Some(i) = done_fut_idxs.pop() {
                                let _ = self.inner_mut().$vec.swap_remove(i);
                            }
                            return Err(err)
                        }
                        _ => (),
                    }
                }

                while let Some(i) = done_fut_idxs.pop() {
                    let _ = self.inner_mut().$vec.swap_remove(i);
                }
            });
        }

        let mut handled = false;

        // Handle closing connections.
        handle!(disconnecting {
            Ok(Ready(_)) => { Ok(()) },
            Err(_) => { Ok(()) },
        });

        // Handle dirty connections.
        handle!(dropping {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
                Ok(())
            },
            Err(_) => { Ok(()) },
        });

        // Handle in-transaction connections
        handle!(rollback {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
                Ok(())
            },
            Err(_) => { Ok(()) },
        });

        // Handle connecting connections.
        handle!(new {
            Ok(Ready(conn)) => {
                let closed = self.inner_ref().closed;
                if closed {
                    self.inner_mut().disconnecting.push(conn.disconnect());
                } else {
                    self.return_conn(conn);
                }
                handled = true;
                Ok(())
            },
            Err(err) => {
                if ! self.inner_ref().closed {
                    Err(err)
                } else {
                    Ok(())
                }
            },
        });

        if handled {
            self.handle_futures()
        } else {
            Ok(())
        }
    }

    /// Will poll pool for connection.
    fn poll(&mut self) -> Result<Async<Conn>> {
        if self.inner_ref().closed {
            return Err(ErrorKind::PoolDisconnected.into());
        }

        self.handle_futures()?;

        match self.take_conn() {
            Some(conn) => Ok(Ready(conn)),
            None => {
                let new_len = self.inner_ref().new.len();
                let idle_len = self.inner_ref().idle.len();
                if new_len == 0 && idle_len < self.max {
                    let new_conn = Conn::new(self.opts.clone(), &self.handle);
                    self.inner_mut().new.push(new_conn);
                    self.poll()
                } else {
                    Ok(NotReady)
                }
            },
        }
    }
}

impl Drop for Conn {
    fn drop(&mut self) {
        if let Some(mut pool) = self.pool.take() {
            let conn = self.take();
            pool.return_conn(conn)
        }
    }
}

#[cfg(test)]
mod test {
    use conn::pool::Pool;
    use lib_futures::Future;
    use test_misc::DATABASE_URL;
    use tokio::reactor::Core;

    #[test]
    fn should_connect() {
        let mut lp = Core::new().unwrap();

        let pool = Pool::new(&**DATABASE_URL, &lp.handle());
        let fut = pool.get_conn()
            .and_then(|conn| conn.ping().map(|_| ()))
            .and_then(|_| pool.disconnect());

        lp.run(fut).unwrap();
    }

    #[test]
    fn should_hold_bounds() {
        let mut lp = Core::new().unwrap();

        let pool = Pool::new(format!("{}?pool_min=1&pool_max=2", &**DATABASE_URL),
                             &lp.handle());
        let pool_clone = pool.clone();
        let fut = pool.get_conn()
            .join(pool.get_conn())
            .and_then(|(mut conn1, conn2)| {
                let new_conn = pool_clone.get_conn();
                conn1.pool.as_mut().unwrap().handle_futures().unwrap();
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().new.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().idle.len(), 0);
                assert_eq!(conn2.pool.as_ref().unwrap().inner_ref().disconnecting.len(), 0);
                assert_eq!(conn2.pool.as_ref().unwrap().inner_ref().dropping.len(), 0);
                new_conn
            })
            .and_then(|conn1| {
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().new.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().idle.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().disconnecting.len(), 0);
                assert_eq!(conn1.pool.as_ref().unwrap().inner_ref().dropping.len(), 0);
                Ok(())
            })
            .and_then(|_| {
                assert_eq!(pool.inner_ref().new.len(), 0);
                assert_eq!(pool.inner_ref().idle.len(), 1);
                assert_eq!(pool.inner_ref().disconnecting.len(), 0);
                assert_eq!(pool.inner_ref().dropping.len(), 0);
                pool.disconnect()
            });

        lp.run(fut).unwrap();
    }

    #[cfg(feature = "nightly")]
    mod bench {
        use test;
        use conn::pool::Pool;
        use tokio::reactor::Core;
        use lib_futures::Future;
        use test_misc::DATABASE_URL;

        #[bench]
        fn connect(bencher: &mut test::Bencher) {
            let mut lp = Core::new().unwrap();
            let pool = Pool::new(&**DATABASE_URL, &lp.handle());

            bencher.iter(|| {
                let fut = pool.get_conn().and_then(|conn| conn.ping());
                lp.run(fut).unwrap();
            })
        }
    }
}
<|endoftext|>


<commit_before>use crate::{
    builtin_functions,
    compiler::hir::{self, Body, Expression, HirDb},
    input::Input,
};
use im::HashMap;
use itertools::Itertools;
use log;

use super::{
    builtin_functions::run_builtin_function,
    result::DiscoverResult,
    value::{Environment, Lambda, Value},
};

#[salsa::query_group(DiscoverStorage)]
pub trait Discover: HirDb {
    fn run_all(&self, input: Input) -> HashMap<hir::Id, DiscoverResult>;

    fn value_to_display_string(&self, value: Value) -> String;
}

fn run_all(db: &dyn Discover, input: Input) -> HashMap<hir::Id, DiscoverResult> {
    let (hir, _) = db.hir(input.clone()).unwrap();
    run_body(db, hir.as_ref(), Environment::new()).flatten()
}
fn run_body(db: &dyn Discover, body: &Body, environment: Environment) -> Environment {
    let mut environment = environment.new_child();
    for (id, _) in &body.expressions {
        environment = run(db, id.to_owned(), environment);
    }
    environment
}
fn run(db: &dyn Discover, id: hir::Id, environment: Environment) -> Environment {
    let mut environment = environment;
    let result = match db.find_expression(id.clone()).unwrap() {
        Expression::Int(int) => Value::Int(int.to_owned()).into(),
        Expression::Text(string) => Value::Text(string.to_owned()).into(),
        Expression::Reference(reference) => {
            if let &[builtin_function_index] = &reference.local[..] {
                if let Some(_) = builtin_functions::VALUES.get(builtin_function_index) {
                    panic!("References to built-in functions are not supported. Erroneous expression: {}", id)
                }
            }
            match db.find_expression(reference.to_owned()) {
                None => DiscoverResult::DependsOnParameter,
                Some(_) => environment.get(&reference).transitive(),
            }
        }
        Expression::Symbol(symbol) => Value::Symbol(symbol.to_owned()).into(),
        Expression::Struct(entries) => 'outer: loop {
            let mut struct_ = HashMap::new();
            for (key, value) in entries {
                let key = match environment.get(&key).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                let value = match environment.get(&value).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                struct_.insert(key, value);
            }
            break Value::Struct(struct_).into();
        },
        Expression::Lambda(hir::Lambda {
            first_id,
            parameters,
            body,
        }) => {
            let lambda = Lambda {
                captured_environment: environment.clone(),
                id: id.clone(),
            };

            for (index, _) in parameters.iter().enumerate() {
                environment.store(first_id.clone() + index, DiscoverResult::DependsOnParameter);
            }
            environment = run_body(db, &body, environment);
            Value::Lambda(lambda).into()
        }
        Expression::Body(body) => {
            environment = run_body(db, &body, environment);
            environment.get(body.out_id()).transitive()
        }
        Expression::Call {
            function,
            arguments,
        } => run_call(db, function, arguments, environment.clone()),
        Expression::Error => DiscoverResult::ErrorInHir,
    };
    environment.store(id, result);
    environment
}
pub(super) fn run_call(
    db: &dyn Discover,
    function: hir::Id,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    if let &[builtin_function_index] = &function.local[..] {
        if let Some(builtin_function) = builtin_functions::VALUES.get(builtin_function_index) {
            return run_builtin_function(
                db,
                builtin_function.to_owned(),
                arguments.to_owned(),
                environment,
            );
        }
    }

    let lambda = match environment.get(&function).transitive()? {
        Value::Lambda(lambda) => lambda,
        it => panic!("Tried to call something that wasn't a lambda: {:?}", it),
    };
    let lambda_hir = match db.find_expression(lambda.id)? {
        Expression::Lambda(lambda) => lambda,
        hir => panic!(
            "Discover lambda is not backed by a HIR lambda, but `{}`.",
            hir
        ),
    };

    let lambda_parent = if let Some(lambda_parent_id) = function.parent() {
        match db.find_expression(lambda_parent_id)? {
            Expression::Body(body) => body,
            hir => panic!("A called lambda's parent isn't a body, but `{}`", hir),
        }
    } else {
        let (hir, _) = db.hir(function.input.to_owned()).unwrap();
        hir.as_ref().to_owned()
    };
    match lambda_parent.identifiers.get(&function) {
        Some(function_name) => log::trace!("Calling function `{}`", &function_name),
        // TODO: Inline lambdas don't have names, but for some other lambdas the name is not found.
        None => log::trace!("Calling function {}", function),
    };

    if lambda_hir.parameters.len() != arguments.len() {
        return DiscoverResult::panic(format!(
            "Lambda parameter and argument counts don't match: {:?}.",
            lambda_hir
        ));
    }

    let mut inner_environment = lambda.captured_environment.to_owned();
    for (index, argument) in arguments.iter().enumerate() {
        inner_environment.store(
            lambda_hir.first_id.clone() + index,
            environment.get(argument),
        );
    }

    run_body(db, &lambda_hir.body, inner_environment).get(lambda_hir.body.out_id())
}

fn value_to_display_string(db: &dyn Discover, value: Value) -> String {
    match value {
        Value::Int(value) => format!("{}", value),
        Value::Text(value) => format!("\"{}\"", value),
        Value::Symbol(value) => format!("{}", value),
        Value::Struct(entries) => format!(
            "[{}]",
            entries
                .keys()
                .into_iter()
                .map(|it| (it, value_to_display_string(db, it.to_owned())))
                .sorted_by_key(|(_, it)| it.to_owned())
                .map(|(key, key_string)| format!(
                    "{}: {}",
                    key_string,
                    value_to_display_string(db, entries[key].to_owned())
                ))
                .join(", ")
        ),
        Value::Lambda(Lambda { id, .. }) => {
            let lambda = db.find_expression(id.clone()).unwrap();
            if let Expression::Lambda(hir::Lambda { parameters, .. }) = lambda {
                if parameters.is_empty() {
                    "{ … }".to_owned()
                } else {
                    format!("{{ {} -> … }}", parameters.join(" "))
                }
            } else {
                panic!("HIR of lambda {} is not a lambda.", id);
            }
        }
    }
}
<commit_msg>fix(compiler): disable noisy logging of all function calls<commit_after>use crate::{
    builtin_functions,
    compiler::hir::{self, Body, Expression, HirDb},
    input::Input,
};
use im::HashMap;
use itertools::Itertools;
use log;

use super::{
    builtin_functions::run_builtin_function,
    result::DiscoverResult,
    value::{Environment, Lambda, Value},
};

#[salsa::query_group(DiscoverStorage)]
pub trait Discover: HirDb {
    fn run_all(&self, input: Input) -> HashMap<hir::Id, DiscoverResult>;

    fn value_to_display_string(&self, value: Value) -> String;
}

fn run_all(db: &dyn Discover, input: Input) -> HashMap<hir::Id, DiscoverResult> {
    let (hir, _) = db.hir(input.clone()).unwrap();
    run_body(db, hir.as_ref(), Environment::new()).flatten()
}
fn run_body(db: &dyn Discover, body: &Body, environment: Environment) -> Environment {
    let mut environment = environment.new_child();
    for (id, _) in &body.expressions {
        environment = run(db, id.to_owned(), environment);
    }
    environment
}
fn run(db: &dyn Discover, id: hir::Id, environment: Environment) -> Environment {
    let mut environment = environment;
    let result = match db.find_expression(id.clone()).unwrap() {
        Expression::Int(int) => Value::Int(int.to_owned()).into(),
        Expression::Text(string) => Value::Text(string.to_owned()).into(),
        Expression::Reference(reference) => {
            if let &[builtin_function_index] = &reference.local[..] {
                if let Some(_) = builtin_functions::VALUES.get(builtin_function_index) {
                    panic!("References to built-in functions are not supported. Erroneous expression: {}", id)
                }
            }
            match db.find_expression(reference.to_owned()) {
                None => DiscoverResult::DependsOnParameter,
                Some(_) => environment.get(&reference).transitive(),
            }
        }
        Expression::Symbol(symbol) => Value::Symbol(symbol.to_owned()).into(),
        Expression::Struct(entries) => 'outer: loop {
            let mut struct_ = HashMap::new();
            for (key, value) in entries {
                let key = match environment.get(&key).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                let value = match environment.get(&value).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                struct_.insert(key, value);
            }
            break Value::Struct(struct_).into();
        },
        Expression::Lambda(hir::Lambda {
            first_id,
            parameters,
            body,
        }) => {
            let lambda = Lambda {
                captured_environment: environment.clone(),
                id: id.clone(),
            };

            for (index, _) in parameters.iter().enumerate() {
                environment.store(first_id.clone() + index, DiscoverResult::DependsOnParameter);
            }
            environment = run_body(db, &body, environment);
            Value::Lambda(lambda).into()
        }
        Expression::Body(body) => {
            environment = run_body(db, &body, environment);
            environment.get(body.out_id()).transitive()
        }
        Expression::Call {
            function,
            arguments,
        } => run_call(db, function, arguments, environment.clone()),
        Expression::Error => DiscoverResult::ErrorInHir,
    };
    environment.store(id, result);
    environment
}
pub(super) fn run_call(
    db: &dyn Discover,
    function: hir::Id,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    if let &[builtin_function_index] = &function.local[..] {
        if let Some(builtin_function) = builtin_functions::VALUES.get(builtin_function_index) {
            return run_builtin_function(
                db,
                builtin_function.to_owned(),
                arguments.to_owned(),
                environment,
            );
        }
    }

    let lambda = match environment.get(&function).transitive()? {
        Value::Lambda(lambda) => lambda,
        it => panic!("Tried to call something that wasn't a lambda: {:?}", it),
    };
    let lambda_hir = match db.find_expression(lambda.id)? {
        Expression::Lambda(lambda) => lambda,
        hir => panic!(
            "Discover lambda is not backed by a HIR lambda, but `{}`.",
            hir
        ),
    };

    // let lambda_parent = if let Some(lambda_parent_id) = function.parent() {
    //     match db.find_expression(lambda_parent_id)? {
    //         Expression::Body(body) => body,
    //         hir => panic!("A called lambda's parent isn't a body, but `{}`", hir),
    //     }
    // } else {
    //     let (hir, _) = db.hir(function.input.to_owned()).unwrap();
    //     hir.as_ref().to_owned()
    // };
    // match lambda_parent.identifiers.get(&function) {
    //     Some(function_name) => log::trace!("Calling function `{}`", &function_name),
    //     // TODO: Inline lambdas don't have names, but for some other lambdas the name is not found.
    //     None => log::trace!("Calling function {}", function),
    // };

    if lambda_hir.parameters.len() != arguments.len() {
        return DiscoverResult::panic(format!(
            "Lambda parameter and argument counts don't match: {:?}.",
            lambda_hir
        ));
    }

    let mut inner_environment = lambda.captured_environment.to_owned();
    for (index, argument) in arguments.iter().enumerate() {
        inner_environment.store(
            lambda_hir.first_id.clone() + index,
            environment.get(argument),
        );
    }

    run_body(db, &lambda_hir.body, inner_environment).get(lambda_hir.body.out_id())
}

fn value_to_display_string(db: &dyn Discover, value: Value) -> String {
    match value {
        Value::Int(value) => format!("{}", value),
        Value::Text(value) => format!("\"{}\"", value),
        Value::Symbol(value) => format!("{}", value),
        Value::Struct(entries) => format!(
            "[{}]",
            entries
                .keys()
                .into_iter()
                .map(|it| (it, value_to_display_string(db, it.to_owned())))
                .sorted_by_key(|(_, it)| it.to_owned())
                .map(|(key, key_string)| format!(
                    "{}: {}",
                    key_string,
                    value_to_display_string(db, entries[key].to_owned())
                ))
                .join(", ")
        ),
        Value::Lambda(Lambda { id, .. }) => {
            let lambda = db.find_expression(id.clone()).unwrap();
            if let Expression::Lambda(hir::Lambda { parameters, .. }) = lambda {
                if parameters.is_empty() {
                    "{ … }".to_owned()
                } else {
                    format!("{{ {} -> … }}", parameters.join(" "))
                }
            } else {
                panic!("HIR of lambda {} is not a lambda.", id);
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::{
    builtin_functions,
    compiler::hir::{self, Body, Expression, HirDb},
    input::Input,
};
use im::HashMap;
use itertools::Itertools;
use log;

use super::{
    builtin_functions::run_builtin_function,
    result::DiscoverResult,
    value::{Environment, Lambda, Value},
};

#[salsa::query_group(DiscoverStorage)]
pub trait Discover: HirDb {
    fn run_all(&self, input: Input) -> HashMap<hir::Id, DiscoverResult>;

    fn value_to_display_string(&self, value: Value) -> String;
}

fn run_all(db: &dyn Discover, input: Input) -> HashMap<hir::Id, DiscoverResult> {
    let (hir, _) = db.hir(input.clone()).unwrap();
    run_body(db, hir.as_ref(), Environment::new()).flatten()
}
fn run_body(db: &dyn Discover, body: &Body, environment: Environment) -> Environment {
    let mut environment = environment.new_child();
    for (id, _) in &body.expressions {
        environment = run(db, id.to_owned(), environment);
    }
    environment
}
fn run(db: &dyn Discover, id: hir::Id, environment: Environment) -> Environment {
    let mut environment = environment;
    let result = match db.find_expression(id.clone()).unwrap() {
        Expression::Int(int) => Value::Int(int.to_owned()).into(),
        Expression::Text(string) => Value::Text(string.to_owned()).into(),
        Expression::Reference(reference) => {
            if let &[builtin_function_index] = &reference.local[..] {
                if let Some(_) = builtin_functions::VALUES.get(builtin_function_index) {
                    panic!("References to built-in functions are not supported. Erroneous expression: {}", id)
                }
            }
            match db.find_expression(reference.to_owned()) {
                None => DiscoverResult::DependsOnParameter,
                Some(_) => environment.get(&reference).transitive(),
            }
        }
        Expression::Symbol(symbol) => Value::Symbol(symbol.to_owned()).into(),
        Expression::Struct(entries) => 'outer: loop {
            let mut struct_ = HashMap::new();
            for (key, value) in entries {
                let key = match environment.get(&key).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                let value = match environment.get(&value).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                struct_.insert(key, value);
            }
            break Value::Struct(struct_).into();
        },
        Expression::Lambda(hir::Lambda {
            first_id,
            parameters,
            body,
        }) => {
            let lambda = Lambda {
                captured_environment: environment.clone(),
                id: id.clone(),
            };

            for (index, _) in parameters.iter().enumerate() {
                environment.store(first_id.clone() + index, DiscoverResult::DependsOnParameter);
            }
            environment = run_body(db, &body, environment);
            Value::Lambda(lambda).into()
        }
        Expression::Body(body) => {
            environment = run_body(db, &body, environment);
            environment.get(body.out_id()).transitive()
        }
        Expression::Call {
            function,
            arguments,
        } => run_call(db, function, arguments, environment.clone()),
        Expression::Error => DiscoverResult::ErrorInHir,
    };
    environment.store(id, result);
    environment
}
pub(super) fn run_call(
    db: &dyn Discover,
    function: hir::Id,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    if let &[builtin_function_index] = &function.local[..] {
        if let Some(builtin_function) = builtin_functions::VALUES.get(builtin_function_index) {
            return run_builtin_function(
                db,
                builtin_function.to_owned(),
                arguments.to_owned(),
                environment,
            );
        }
    }

    let lambda = match environment.get(&function).transitive()? {
        Value::Lambda(lambda) => lambda,
        it => panic!("Tried to call something that wasn't a lambda: {:?}", it),
    };
    let lambda_hir = match db.find_expression(lambda.id)? {
        Expression::Lambda(lambda) => lambda,
        hir => panic!(
            "Discover lambda is not backed by a HIR lambda, but `{}`.",
            hir
        ),
    };

    // let lambda_parent = if let Some(lambda_parent_id) = function.parent() {
    //     match db.find_expression(lambda_parent_id)? {
    //         Expression::Body(body) => body,
    //         hir => panic!("A called lambda's parent isn't a body, but `{}`", hir),
    //     }
    // } else {
    //     let (hir, _) = db.hir(function.input.to_owned()).unwrap();
    //     hir.as_ref().to_owned()
    // };
    // match lambda_parent.identifiers.get(&function) {
    //     Some(function_name) => log::trace!("Calling function `{}`", &function_name),
    //     // TODO: Inline lambdas don't have names, but for some other lambdas the name is not found.
    //     None => log::trace!("Calling function {}", function),
    // };

    if lambda_hir.parameters.len() != arguments.len() {
        return DiscoverResult::panic(format!(
            "Lambda parameter and argument counts don't match: {:?}.",
            lambda_hir
        ));
    }

    let mut inner_environment = lambda.captured_environment.to_owned();
    for (index, argument) in arguments.iter().enumerate() {
        inner_environment.store(
            lambda_hir.first_id.clone() + index,
            environment.get(argument),
        );
    }

    run_body(db, &lambda_hir.body, inner_environment).get(lambda_hir.body.out_id())
}

fn value_to_display_string(db: &dyn Discover, value: Value) -> String {
    match value {
        Value::Int(value) => format!("{}", value),
        Value::Text(value) => format!("\"{}\"", value),
        Value::Symbol(value) => format!("{}", value),
        Value::Struct(entries) => format!(
            "[{}]",
            entries
                .keys()
                .into_iter()
                .map(|it| (it, value_to_display_string(db, it.to_owned())))
                .sorted_by_key(|(_, it)| it.to_owned())
                .map(|(key, key_string)| format!(
                    "{}: {}",
                    key_string,
                    value_to_display_string(db, entries[key].to_owned())
                ))
                .join(", ")
        ),
        Value::Lambda(Lambda { id, .. }) => {
            let lambda = db.find_expression(id.clone()).unwrap();
            if let Expression::Lambda(hir::Lambda { parameters, .. }) = lambda {
                if parameters.is_empty() {
                    "{ … }".to_owned()
                } else {
                    format!("{{ {} -> … }}", parameters.join(" "))
                }
            } else {
                panic!("HIR of lambda {} is not a lambda.", id);
            }
        }
    }
}
<commit_msg>refactor(compiler): remove verbose function call logging<commit_after>use crate::{
    builtin_functions,
    compiler::hir::{self, Body, Expression, HirDb},
    input::Input,
};
use im::HashMap;
use itertools::Itertools;
use log;

use super::{
    builtin_functions::run_builtin_function,
    result::DiscoverResult,
    value::{Environment, Lambda, Value},
};

#[salsa::query_group(DiscoverStorage)]
pub trait Discover: HirDb {
    fn run_all(&self, input: Input) -> HashMap<hir::Id, DiscoverResult>;

    fn value_to_display_string(&self, value: Value) -> String;
}

fn run_all(db: &dyn Discover, input: Input) -> HashMap<hir::Id, DiscoverResult> {
    let (hir, _) = db.hir(input.clone()).unwrap();
    run_body(db, hir.as_ref(), Environment::new()).flatten()
}
fn run_body(db: &dyn Discover, body: &Body, environment: Environment) -> Environment {
    let mut environment = environment.new_child();
    for (id, _) in &body.expressions {
        environment = run(db, id.to_owned(), environment);
    }
    environment
}
fn run(db: &dyn Discover, id: hir::Id, environment: Environment) -> Environment {
    let mut environment = environment;
    let result = match db.find_expression(id.clone()).unwrap() {
        Expression::Int(int) => Value::Int(int.to_owned()).into(),
        Expression::Text(string) => Value::Text(string.to_owned()).into(),
        Expression::Reference(reference) => {
            if let &[builtin_function_index] = &reference.local[..] {
                if let Some(_) = builtin_functions::VALUES.get(builtin_function_index) {
                    panic!("References to built-in functions are not supported. Erroneous expression: {}", id)
                }
            }
            match db.find_expression(reference.to_owned()) {
                None => DiscoverResult::DependsOnParameter,
                Some(_) => environment.get(&reference).transitive(),
            }
        }
        Expression::Symbol(symbol) => Value::Symbol(symbol.to_owned()).into(),
        Expression::Struct(entries) => 'outer: loop {
            let mut struct_ = HashMap::new();
            for (key, value) in entries {
                let key = match environment.get(&key).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                let value = match environment.get(&value).transitive() {
                    DiscoverResult::Value(value) => value,
                    it => break 'outer it,
                };
                struct_.insert(key, value);
            }
            break Value::Struct(struct_).into();
        },
        Expression::Lambda(hir::Lambda {
            first_id,
            parameters,
            body,
        }) => {
            let lambda = Lambda {
                captured_environment: environment.clone(),
                id: id.clone(),
            };

            for (index, _) in parameters.iter().enumerate() {
                environment.store(first_id.clone() + index, DiscoverResult::DependsOnParameter);
            }
            environment = run_body(db, &body, environment);
            Value::Lambda(lambda).into()
        }
        Expression::Body(body) => {
            environment = run_body(db, &body, environment);
            environment.get(body.out_id()).transitive()
        }
        Expression::Call {
            function,
            arguments,
        } => run_call(db, function, arguments, environment.clone()),
        Expression::Error => DiscoverResult::ErrorInHir,
    };
    environment.store(id, result);
    environment
}
pub(super) fn run_call(
    db: &dyn Discover,
    function: hir::Id,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    if let &[builtin_function_index] = &function.local[..] {
        if let Some(builtin_function) = builtin_functions::VALUES.get(builtin_function_index) {
            return run_builtin_function(
                db,
                builtin_function.to_owned(),
                arguments.to_owned(),
                environment,
            );
        }
    }

    let lambda = match environment.get(&function).transitive()? {
        Value::Lambda(lambda) => lambda,
        it => panic!("Tried to call something that wasn't a lambda: {:?}", it),
    };
    let lambda_hir = match db.find_expression(lambda.id)? {
        Expression::Lambda(lambda) => lambda,
        hir => panic!(
            "Discover lambda is not backed by a HIR lambda, but `{}`.",
            hir
        ),
    };

    if lambda_hir.parameters.len() != arguments.len() {
        return DiscoverResult::panic(format!(
            "Lambda parameter and argument counts don't match: {:?}.",
            lambda_hir
        ));
    }

    let mut inner_environment = lambda.captured_environment.to_owned();
    for (index, argument) in arguments.iter().enumerate() {
        inner_environment.store(
            lambda_hir.first_id.clone() + index,
            environment.get(argument),
        );
    }

    run_body(db, &lambda_hir.body, inner_environment).get(lambda_hir.body.out_id())
}

fn value_to_display_string(db: &dyn Discover, value: Value) -> String {
    match value {
        Value::Int(value) => format!("{}", value),
        Value::Text(value) => format!("\"{}\"", value),
        Value::Symbol(value) => format!("{}", value),
        Value::Struct(entries) => format!(
            "[{}]",
            entries
                .keys()
                .into_iter()
                .map(|it| (it, value_to_display_string(db, it.to_owned())))
                .sorted_by_key(|(_, it)| it.to_owned())
                .map(|(key, key_string)| format!(
                    "{}: {}",
                    key_string,
                    value_to_display_string(db, entries[key].to_owned())
                ))
                .join(", ")
        ),
        Value::Lambda(Lambda { id, .. }) => {
            let lambda = db.find_expression(id.clone()).unwrap();
            if let Expression::Lambda(hir::Lambda { parameters, .. }) = lambda {
                if parameters.is_empty() {
                    "{ … }".to_owned()
                } else {
                    format!("{{ {} -> … }}", parameters.join(" "))
                }
            } else {
                panic!("HIR of lambda {} is not a lambda.", id);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! TODO Fill in

use super::class::{Class, ClassBuilder};
use super::object::{self, Object, Objectable};
use cairo_sys::cairo_pattern_t;
use rlua::{self, LightUserData, Lua, Table, ToLua, UserData, UserDataMethods, Value};
use std::default::Default;
use std::fmt::{self, Display, Formatter};

/// Handle to the list of global key bindings
pub const ROOT_KEYS_HANDLE: &'static str = "__ROOT_KEYS";

#[derive(Clone, Debug)]
pub struct RootState {
    // TODO Fill in
    dummy: i32
}

pub struct Root<'lua>(Object<'lua>);

impl Default for RootState {
    fn default() -> Self {
        RootState { dummy: 0 }
    }
}

impl Display for RootState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Root: {:p}", self)
    }
}

impl<'lua> ToLua<'lua> for Root<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for RootState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        object::default_add_methods(methods);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    // FIXME: In awesome there is no root class
    method_setup(lua, Class::builder(lua, "FIXME", None)?)?.save_class("root")?
                                                           .build()
}

fn method_setup<'lua>(lua: &'lua Lua,
                      builder: ClassBuilder<'lua>)
                      -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy)?)?
           .method("buttons".into(), lua.create_function(dummy)?)?
           .method("wallpaper".into(), lua.create_function(wallpaper)?)?
           .method("tags".into(), lua.create_function(tags)?)?
           .method("keys".into(), lua.create_function(root_keys)?)?
           .method("size".into(), lua.create_function(dummy_double)?)?
           .method("size_mm".into(), lua.create_function(dummy_double)?)?
           .method("cursor".into(), lua.create_function(dummy)?)
}

impl_objectable!(Root, RootState);

fn dummy_double<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<(i32, i32)> {
    Ok((0, 0))
}

/// Gets the wallpaper as a cairo surface or set it as a cairo pattern
fn wallpaper<'lua>(lua: &'lua Lua, pattern: Option<LightUserData>) -> rlua::Result<Value<'lua>> {
    // TODO FIXME Implement for realz
    if let Some(pattern) = pattern {
        // TODO Wrap before giving it to set_wallpaper
        let pattern = pattern.0 as *mut cairo_pattern_t;
        return set_wallpaper(lua, pattern)?.to_lua(lua)
    }
    // TODO Look it up in global conf (e.g probably super secret lua value)
    return Ok(Value::Nil)
}

fn set_wallpaper<'lua>(_: &'lua Lua, _pattern: *mut cairo_pattern_t) -> rlua::Result<bool> {
    warn!("Fake setting the wallpaper");
    Ok(true)
}

fn tags<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Table<'lua>> {
    let table = lua.create_table()?;
    let activated_tags = lua.named_registry_value::<Table>(super::tag::TAG_LIST)?;
    for pair in activated_tags.pairs::<Value, Value>() {
        let (key, value) = pair?;
        table.set(key, value)?;
    }
    Ok(table)
}

/// Get or set global key bindings.
///
/// These bindings will be available when you press keys on the root window.
fn root_keys<'lua>(lua: &'lua Lua, key_array: rlua::Value<'lua>) -> rlua::Result<rlua::Value<'lua>> {
    match key_array {
        // Set the global keys
        Value::Table(key_array) => {
            let copy = lua.create_table()?;
            // NOTE We make a deep clone so they can't modify references.
            for entry in key_array.clone().pairs() {
                let (key, value) = entry?;
                copy.set::<Value, Value>(key, value)?;
            }
            lua.set_named_registry_value(ROOT_KEYS_HANDLE, copy)?;
            Ok(Value::Table(key_array))
        }
        // Get the global keys
        Value::Nil => {
            let res = lua.create_table()?;
            for entry in lua.named_registry_value::<Table>(ROOT_KEYS_HANDLE).or(lua.create_table())?.pairs() {
                let (key, value) = entry?;
                res.set::<Value, Value>(key, value)?;
            }
            Ok(Value::Table(res))
        }
        v => {
            Err(rlua::Error::RuntimeError(format!("Expected nil or array \
                                                   of keys, got {:?}",
                                                  v)))
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::root;
    use super::super::tag;
    use rlua::Lua;

    #[test]
    fn tags_none() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = root.tags()
assert(type(t) == "table")
assert(type(next(t)) == "nil")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_does_not_copy() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = tag{ activated = true }
local t2 = root.tags()[1]
assert(t == t2)
t2.name = "Foo"
assert(t.name == "Foo")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_some() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
local t = root.tags()
assert(t[1] == first)
assert(t[2] == second)
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_removal() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
first.activated = false
local t = root.tags()
assert(t[1] == second)
assert(type(t[2]) == "nil")
"#,
                 None
        ).unwrap()
    }
}
<commit_msg>Add a test for root.keys()<commit_after>//! TODO Fill in

use super::class::{Class, ClassBuilder};
use super::object::{self, Object, Objectable};
use cairo_sys::cairo_pattern_t;
use rlua::{self, LightUserData, Lua, Table, ToLua, UserData, UserDataMethods, Value};
use std::default::Default;
use std::fmt::{self, Display, Formatter};

/// Handle to the list of global key bindings
pub const ROOT_KEYS_HANDLE: &'static str = "__ROOT_KEYS";

#[derive(Clone, Debug)]
pub struct RootState {
    // TODO Fill in
    dummy: i32
}

pub struct Root<'lua>(Object<'lua>);

impl Default for RootState {
    fn default() -> Self {
        RootState { dummy: 0 }
    }
}

impl Display for RootState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Root: {:p}", self)
    }
}

impl<'lua> ToLua<'lua> for Root<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for RootState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        object::default_add_methods(methods);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    // FIXME: In awesome there is no root class
    method_setup(lua, Class::builder(lua, "FIXME", None)?)?.save_class("root")?
                                                           .build()
}

fn method_setup<'lua>(lua: &'lua Lua,
                      builder: ClassBuilder<'lua>)
                      -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy)?)?
           .method("buttons".into(), lua.create_function(dummy)?)?
           .method("wallpaper".into(), lua.create_function(wallpaper)?)?
           .method("tags".into(), lua.create_function(tags)?)?
           .method("keys".into(), lua.create_function(root_keys)?)?
           .method("size".into(), lua.create_function(dummy_double)?)?
           .method("size_mm".into(), lua.create_function(dummy_double)?)?
           .method("cursor".into(), lua.create_function(dummy)?)
}

impl_objectable!(Root, RootState);

fn dummy_double<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<(i32, i32)> {
    Ok((0, 0))
}

/// Gets the wallpaper as a cairo surface or set it as a cairo pattern
fn wallpaper<'lua>(lua: &'lua Lua, pattern: Option<LightUserData>) -> rlua::Result<Value<'lua>> {
    // TODO FIXME Implement for realz
    if let Some(pattern) = pattern {
        // TODO Wrap before giving it to set_wallpaper
        let pattern = pattern.0 as *mut cairo_pattern_t;
        return set_wallpaper(lua, pattern)?.to_lua(lua)
    }
    // TODO Look it up in global conf (e.g probably super secret lua value)
    return Ok(Value::Nil)
}

fn set_wallpaper<'lua>(_: &'lua Lua, _pattern: *mut cairo_pattern_t) -> rlua::Result<bool> {
    warn!("Fake setting the wallpaper");
    Ok(true)
}

fn tags<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Table<'lua>> {
    let table = lua.create_table()?;
    let activated_tags = lua.named_registry_value::<Table>(super::tag::TAG_LIST)?;
    for pair in activated_tags.pairs::<Value, Value>() {
        let (key, value) = pair?;
        table.set(key, value)?;
    }
    Ok(table)
}

/// Get or set global key bindings.
///
/// These bindings will be available when you press keys on the root window.
fn root_keys<'lua>(lua: &'lua Lua, key_array: rlua::Value<'lua>) -> rlua::Result<rlua::Value<'lua>> {
    match key_array {
        // Set the global keys
        Value::Table(key_array) => {
            let copy = lua.create_table()?;
            // NOTE We make a deep clone so they can't modify references.
            for entry in key_array.clone().pairs() {
                let (key, value) = entry?;
                copy.set::<Value, Value>(key, value)?;
            }
            lua.set_named_registry_value(ROOT_KEYS_HANDLE, copy)?;
            Ok(Value::Table(key_array))
        }
        // Get the global keys
        Value::Nil => {
            let res = lua.create_table()?;
            for entry in lua.named_registry_value::<Table>(ROOT_KEYS_HANDLE).or(lua.create_table())?.pairs() {
                let (key, value) = entry?;
                res.set::<Value, Value>(key, value)?;
            }
            Ok(Value::Table(res))
        }
        v => {
            Err(rlua::Error::RuntimeError(format!("Expected nil or array \
                                                   of keys, got {:?}",
                                                  v)))
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::root;
    use super::super::tag;
    use super::super::key;
    use rlua::Lua;

    #[test]
    fn tags_none() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = root.tags()
assert(type(t) == "table")
assert(type(next(t)) == "nil")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_does_not_copy() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local t = tag{ activated = true }
local t2 = root.tags()[1]
assert(t == t2)
t2.name = "Foo"
assert(t.name == "Foo")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_some() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
local t = root.tags()
assert(t[1] == first)
assert(t[2] == second)
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn tags_removal() {
        let lua = Lua::new();
        tag::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
local first = tag{ activated = true }
local second = tag{ activated = true }
first.activated = false
local t = root.tags()
assert(t[1] == second)
assert(type(t[2]) == "nil")
"#,
                 None
        ).unwrap()
    }

    #[test]
    fn keys() {
        let lua = Lua::new();
        key::init(&lua).unwrap();
        root::init(&lua).unwrap();
        lua.eval(
                 r#"
assert(next(root.keys()) == nil)

local first = key{}
local second = key{}
local keys = { first, second }

local res = root.keys(keys)
assert(res[1] == first)
assert(res[2] == second)
assert(res[3] == nil)

keys[3] = key{}
local res = root.keys()
assert(res[1] == first)
assert(res[2] == second)
assert(res[3] == nil)
"#,
                 None
        ).unwrap()
    }
}
<|endoftext|>