<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

/// See the documentation at https://github.com/pagekite/libpagekite/blob/master/doc/API.md

use ffi::*;
use std::ffi::{CStr, CString};
use std::mem::transmute;
use std::{convert, ptr};
use std::time::Duration;
#[derive(Clone, Copy)]
pub enum InitFlags {
    WithDefaults = 0x8000,
    WithoutDefaults = 0x4000,
    WithSsl = 0x0001,
    WithIpv4 = 0x0002,
    WithIpv6 = 0x0004,
    WithServiceFrontends = 0x0008,
    WithoutServiceFrontends = 0x0010,
    WithDynamicFeList = 0x0020,
    WithFrontendSni = 0x0040,
    AsFrontendRelay = 0x0100,
    WithSyslog = 0x0200,
}

#[derive(Clone, Copy)]
pub enum VerbosityFlags {
    LogTunnelData = 0x000100,
    LogTunnelHeaders = 0x000200,
    LogTunnelConns = 0x000400,
    LogBeData = 0x001000,
    LogBeHeaders = 0x002000,
    LogBeConns = 0x004000,
    LogManagerError = 0x010000,
    LogManagerInfo = 0x020000,
    LogManagerDebug = 0x040000,
    LogTrace = 0x080000,
    LogLuaDebug = 0x008000,
    LogLuaInfo = 0x000800,
    LogError = 0x100000,
}

#[repr(i32)]
pub enum Status {
    Startup = 10,
    Connecting = 20,
    UpdatingDns = 30,
    Flying = 40,
    PROProblems = 50,
    Rejected = 60,
    NoNetwork = 90,
}

impl convert::Into<Status> for i32 {
    fn into(self) -> Status {
        unsafe { transmute::<i32, Status>(self) }
    }
}

pub static LOG_ERRORS: [VerbosityFlags; 2] = [VerbosityFlags::LogError,
                                              VerbosityFlags::LogManagerError];
pub static LOG_MANAGER: [VerbosityFlags; 2] = [VerbosityFlags::LogManagerError,
                                               VerbosityFlags::LogManagerInfo];
pub static LOG_CONNS: [VerbosityFlags; 2] = [VerbosityFlags::LogBeConns,
                                             VerbosityFlags::LogTunnelConns];
pub static LOG_NORMAL: [VerbosityFlags; 7] = [VerbosityFlags::LogError,
                                              VerbosityFlags::LogManagerError,
                                              VerbosityFlags::LogBeConns,
                                              VerbosityFlags::LogTunnelConns,
                                              VerbosityFlags::LogManagerError,
                                              VerbosityFlags::LogManagerInfo,
                                              VerbosityFlags::LogLuaInfo];
pub static LOG_ALL: [VerbosityFlags; 13] = [VerbosityFlags::LogTunnelData,
                                            VerbosityFlags::LogTunnelHeaders,
                                            VerbosityFlags::LogTunnelConns,
                                            VerbosityFlags::LogBeData,
                                            VerbosityFlags::LogBeHeaders,
                                            VerbosityFlags::LogBeConns,
                                            VerbosityFlags::LogManagerError,
                                            VerbosityFlags::LogManagerInfo,
                                            VerbosityFlags::LogManagerDebug,
                                            VerbosityFlags::LogTrace,
                                            VerbosityFlags::LogLuaDebug,
                                            VerbosityFlags::LogLuaInfo,
                                            VerbosityFlags::LogError];

pub struct PageKite {
    inner: pagekite_mgr,
}

macro_rules! str_cstring(
    ($a:ident) => ( CString::new($a).unwrap().as_ptr() )
);

macro_rules! build_flag(
    ($flag:ident, $array:ident) => (
        let mut $flag = 0;
        for item in $array {
            $flag |= *item as i32;
        }
    )
);

#[inline]
fn bool2int(val: bool) -> i32 {
    if val { 1 } else { 0 }
}

impl PageKite {
    pub fn init(app_id: Option<&str>,
                max_kites: i32,
                max_frontends: i32,
                max_conns: i32,
                dyndns_url: Option<&str>,
                flags: &[InitFlags],
                verbosity: &[VerbosityFlags])
                -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let c_dyndns_url = match dyndns_url {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init(c_app_id,
                          max_kites,
                          max_frontends,
                          max_conns,
                          c_dyndns_url,
                          c_flags,
                          c_verbosity)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn init_pagekitenet(app_id: Option<&str>,
                            max_kites: i32,
                            max_conns: i32,
                            flags: &[InitFlags],
                            verbosity: &[VerbosityFlags])
                            -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init_pagekitenet(c_app_id, max_kites, max_conns, c_flags, c_verbosity)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn init_whitelabel(app_id: Option<&str>,
                           max_kites: i32,
                           max_conns: i32,
                           flags: &[InitFlags],
                           verbosity: &[VerbosityFlags],
                           whitelabel_tld: Option<&str>)
                           -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let c_tld = match whitelabel_tld {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init_whitelabel(c_app_id, max_kites, max_conns, c_flags, c_verbosity, c_tld)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn add_kite(&self,
                    proto: &str,
                    kitename: &str,
                    public_port: i32,
                    secret: &str,
                    backend: &str,
                    local_port: i32)
                    -> bool {

        return unsafe {
            pagekite_add_kite(self.inner,
                              str_cstring!(proto),
                              str_cstring!(kitename),
                              public_port,
                              str_cstring!(secret),
                              str_cstring!(backend),
                              local_port)
        } == 0;
    }

    /// Useful flags are WithDefaults, WithIpv4, WithIpv6, WithDynamicFeList
    /// Returns the number of frontend ips, or an empty error.
    pub fn add_service_frontends(&self, flags: &[InitFlags]) -> Result<usize, ()> {
        build_flag!(c_flags, flags);
        let ret = unsafe { pagekite_add_service_frontends(self.inner, c_flags) };
        if ret == -1 { Err(()) } else { Ok(ret as usize) }
    }

    pub fn add_whitelabel_frontends(&self,
                                    flags: &[InitFlags],
                                    whitelabel_tld: Option<&str>)
                                    -> Result<usize, ()> {
        build_flag!(c_flags, flags);
        let c_tld = match whitelabel_tld {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };
        let ret = unsafe { pagekite_add_whitelabel_frontends(self.inner, c_flags, c_tld) };
        if ret == -1 { Err(()) } else { Ok(ret as usize) }
    }

    pub fn lookup_and_add_frontend(&self,
                                   domain: &str,
                                   port: i32,
                                   update_from_dns: bool)
                                   -> Option<i32> {
        let count = unsafe {
            pagekite_lookup_and_add_frontend(self.inner,
                                             str_cstring!(domain),
                                             port,
                                             if update_from_dns { 1 } else { 0 })
        };
        if count == -1 { None } else { Some(count) }
    }

    pub fn add_frontend(&self, domain: &str, port: i32) -> Option<i32> {
        let count = unsafe { pagekite_add_frontend(self.inner, str_cstring!(domain), port) };
        if count == -1 { None } else { Some(count) }
    }

    pub fn set_log_mask(&self, flags: &[VerbosityFlags]) {
        build_flag!(c_verbosity, flags);
        unsafe {
            pagekite_set_log_mask(self.inner, c_verbosity);
        }
    }

    pub fn set_housekeeping_min_interval(&self, interval: Duration) -> Duration {
        let ret = unsafe {
            pagekite_set_housekeeping_min_interval(self.inner, interval.as_secs() as i32)
        };
        Duration::new(ret as u64, 0)
    }

    pub fn set_housekeeping_max_interval(&self, interval: Duration) -> Duration {
        let ret = unsafe {
            pagekite_set_housekeeping_max_interval(self.inner, interval.as_secs() as i32)
        };
        Duration::new(ret as u64, 0)
    }

    pub fn enable_http_forwarding_headers(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_http_forwarding_headers(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_fake_ping(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_fake_ping(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_watchdog(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_watchdog(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_tick_timer(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_tick_timer(self.inner, bool2int(enable));
        }
        self
    }

    pub fn set_conn_eviction_idle_s(&self, seconds: Duration) -> &Self {
        unsafe {
            pagekite_set_conn_eviction_idle_s(self.inner, seconds.as_secs() as i32);
        }
        self
    }

    pub fn set_openssl_ciphers(&self, ciphers: &str) -> &Self {
        unsafe {
            pagekite_set_openssl_ciphers(self.inner, str_cstring!(ciphers));
        }
        self
    }

    pub fn want_spare_frontends(&self, spares: i32) -> &Self {
        unsafe {
            pagekite_want_spare_frontends(self.inner, spares);
        }
        self
    }

    pub fn thread_start(&self) -> bool {
        return unsafe { pagekite_thread_start(self.inner) } == 0;
    }

    pub fn thread_wait(&self) -> bool {
        return unsafe { pagekite_thread_wait(self.inner) } == 0;
    }

    pub fn thread_stop(&self) -> bool {
        return unsafe { pagekite_thread_stop(self.inner) } == 0;
    }

    pub fn get_status(&self) -> Status {
        return unsafe { pagekite_get_status(self.inner) }.into();
    }

    pub fn get_log(&self) -> String {
        unsafe {
            CStr::from_ptr(pagekite_get_log(self.inner))
                .to_string_lossy()
                .into_owned()
        }
    }

    pub fn dump_state_to_log(&self) -> &Self {
        unsafe {
            pagekite_dump_state_to_log(self.inner);
        }
        self
    }

    pub fn poll(&self, timeout: Duration) -> bool {
        return unsafe { pagekite_poll(self.inner, timeout.as_secs() as i32) } == 0;
    }

    pub fn tick(&self) -> bool {
        return unsafe { pagekite_tick(self.inner) } == 0;
    }
}

impl Drop for PageKite {
    fn drop(&mut self) {
        unsafe {
            pagekite_thread_stop(self.inner);
            pagekite_free(self.inner);
        }
    }
}
<commit_msg>Make the PageKite struct clonable<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

/// See the documentation at https://github.com/pagekite/libpagekite/blob/master/doc/API.md

use ffi::*;
use std::ffi::{CStr, CString};
use std::mem::transmute;
use std::{convert, ptr};
use std::time::Duration;
#[derive(Clone, Copy)]
pub enum InitFlags {
    WithDefaults = 0x8000,
    WithoutDefaults = 0x4000,
    WithSsl = 0x0001,
    WithIpv4 = 0x0002,
    WithIpv6 = 0x0004,
    WithServiceFrontends = 0x0008,
    WithoutServiceFrontends = 0x0010,
    WithDynamicFeList = 0x0020,
    WithFrontendSni = 0x0040,
    AsFrontendRelay = 0x0100,
    WithSyslog = 0x0200,
}

#[derive(Clone, Copy)]
pub enum VerbosityFlags {
    LogTunnelData = 0x000100,
    LogTunnelHeaders = 0x000200,
    LogTunnelConns = 0x000400,
    LogBeData = 0x001000,
    LogBeHeaders = 0x002000,
    LogBeConns = 0x004000,
    LogManagerError = 0x010000,
    LogManagerInfo = 0x020000,
    LogManagerDebug = 0x040000,
    LogTrace = 0x080000,
    LogLuaDebug = 0x008000,
    LogLuaInfo = 0x000800,
    LogError = 0x100000,
}

#[repr(i32)]
pub enum Status {
    Startup = 10,
    Connecting = 20,
    UpdatingDns = 30,
    Flying = 40,
    PROProblems = 50,
    Rejected = 60,
    NoNetwork = 90,
}

impl convert::Into<Status> for i32 {
    fn into(self) -> Status {
        unsafe { transmute::<i32, Status>(self) }
    }
}

pub static LOG_ERRORS: [VerbosityFlags; 2] = [VerbosityFlags::LogError,
                                              VerbosityFlags::LogManagerError];
pub static LOG_MANAGER: [VerbosityFlags; 2] = [VerbosityFlags::LogManagerError,
                                               VerbosityFlags::LogManagerInfo];
pub static LOG_CONNS: [VerbosityFlags; 2] = [VerbosityFlags::LogBeConns,
                                             VerbosityFlags::LogTunnelConns];
pub static LOG_NORMAL: [VerbosityFlags; 7] = [VerbosityFlags::LogError,
                                              VerbosityFlags::LogManagerError,
                                              VerbosityFlags::LogBeConns,
                                              VerbosityFlags::LogTunnelConns,
                                              VerbosityFlags::LogManagerError,
                                              VerbosityFlags::LogManagerInfo,
                                              VerbosityFlags::LogLuaInfo];
pub static LOG_ALL: [VerbosityFlags; 13] = [VerbosityFlags::LogTunnelData,
                                            VerbosityFlags::LogTunnelHeaders,
                                            VerbosityFlags::LogTunnelConns,
                                            VerbosityFlags::LogBeData,
                                            VerbosityFlags::LogBeHeaders,
                                            VerbosityFlags::LogBeConns,
                                            VerbosityFlags::LogManagerError,
                                            VerbosityFlags::LogManagerInfo,
                                            VerbosityFlags::LogManagerDebug,
                                            VerbosityFlags::LogTrace,
                                            VerbosityFlags::LogLuaDebug,
                                            VerbosityFlags::LogLuaInfo,
                                            VerbosityFlags::LogError];

#[derive(Clone)]
pub struct PageKite {
    inner: pagekite_mgr,
}

macro_rules! str_cstring(
    ($a:ident) => ( CString::new($a).unwrap().as_ptr() )
);

macro_rules! build_flag(
    ($flag:ident, $array:ident) => (
        let mut $flag = 0;
        for item in $array {
            $flag |= *item as i32;
        }
    )
);

#[inline]
fn bool2int(val: bool) -> i32 {
    if val { 1 } else { 0 }
}

impl PageKite {
    pub fn init(app_id: Option<&str>,
                max_kites: i32,
                max_frontends: i32,
                max_conns: i32,
                dyndns_url: Option<&str>,
                flags: &[InitFlags],
                verbosity: &[VerbosityFlags])
                -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let c_dyndns_url = match dyndns_url {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init(c_app_id,
                          max_kites,
                          max_frontends,
                          max_conns,
                          c_dyndns_url,
                          c_flags,
                          c_verbosity)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn init_pagekitenet(app_id: Option<&str>,
                            max_kites: i32,
                            max_conns: i32,
                            flags: &[InitFlags],
                            verbosity: &[VerbosityFlags])
                            -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init_pagekitenet(c_app_id, max_kites, max_conns, c_flags, c_verbosity)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn init_whitelabel(app_id: Option<&str>,
                           max_kites: i32,
                           max_conns: i32,
                           flags: &[InitFlags],
                           verbosity: &[VerbosityFlags],
                           whitelabel_tld: Option<&str>)
                           -> Option<Self> {

        build_flag!(c_flags, flags);
        build_flag!(c_verbosity, verbosity);

        let c_app_id = match app_id {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let c_tld = match whitelabel_tld {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };

        let raw = unsafe {
            pagekite_init_whitelabel(c_app_id, max_kites, max_conns, c_flags, c_verbosity, c_tld)
        };
        if raw.is_null() {
            return None;
        }
        Some(PageKite { inner: raw })
    }

    pub fn add_kite(&self,
                    proto: &str,
                    kitename: &str,
                    public_port: i32,
                    secret: &str,
                    backend: &str,
                    local_port: i32)
                    -> bool {

        return unsafe {
            pagekite_add_kite(self.inner,
                              str_cstring!(proto),
                              str_cstring!(kitename),
                              public_port,
                              str_cstring!(secret),
                              str_cstring!(backend),
                              local_port)
        } == 0;
    }

    /// Useful flags are WithDefaults, WithIpv4, WithIpv6, WithDynamicFeList
    /// Returns the number of frontend ips, or an empty error.
    pub fn add_service_frontends(&self, flags: &[InitFlags]) -> Result<usize, ()> {
        build_flag!(c_flags, flags);
        let ret = unsafe { pagekite_add_service_frontends(self.inner, c_flags) };
        if ret == -1 { Err(()) } else { Ok(ret as usize) }
    }

    pub fn add_whitelabel_frontends(&self,
                                    flags: &[InitFlags],
                                    whitelabel_tld: Option<&str>)
                                    -> Result<usize, ()> {
        build_flag!(c_flags, flags);
        let c_tld = match whitelabel_tld {
            Some(value) => str_cstring!(value),
            None => ptr::null(),
        };
        let ret = unsafe { pagekite_add_whitelabel_frontends(self.inner, c_flags, c_tld) };
        if ret == -1 { Err(()) } else { Ok(ret as usize) }
    }

    pub fn lookup_and_add_frontend(&self,
                                   domain: &str,
                                   port: i32,
                                   update_from_dns: bool)
                                   -> Option<i32> {
        let count = unsafe {
            pagekite_lookup_and_add_frontend(self.inner,
                                             str_cstring!(domain),
                                             port,
                                             if update_from_dns { 1 } else { 0 })
        };
        if count == -1 { None } else { Some(count) }
    }

    pub fn add_frontend(&self, domain: &str, port: i32) -> Option<i32> {
        let count = unsafe { pagekite_add_frontend(self.inner, str_cstring!(domain), port) };
        if count == -1 { None } else { Some(count) }
    }

    pub fn set_log_mask(&self, flags: &[VerbosityFlags]) {
        build_flag!(c_verbosity, flags);
        unsafe {
            pagekite_set_log_mask(self.inner, c_verbosity);
        }
    }

    pub fn set_housekeeping_min_interval(&self, interval: Duration) -> Duration {
        let ret = unsafe {
            pagekite_set_housekeeping_min_interval(self.inner, interval.as_secs() as i32)
        };
        Duration::new(ret as u64, 0)
    }

    pub fn set_housekeeping_max_interval(&self, interval: Duration) -> Duration {
        let ret = unsafe {
            pagekite_set_housekeeping_max_interval(self.inner, interval.as_secs() as i32)
        };
        Duration::new(ret as u64, 0)
    }

    pub fn enable_http_forwarding_headers(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_http_forwarding_headers(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_fake_ping(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_fake_ping(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_watchdog(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_watchdog(self.inner, bool2int(enable));
        }
        self
    }

    pub fn enable_tick_timer(&self, enable: bool) -> &Self {
        unsafe {
            pagekite_enable_tick_timer(self.inner, bool2int(enable));
        }
        self
    }

    pub fn set_conn_eviction_idle_s(&self, seconds: Duration) -> &Self {
        unsafe {
            pagekite_set_conn_eviction_idle_s(self.inner, seconds.as_secs() as i32);
        }
        self
    }

    pub fn set_openssl_ciphers(&self, ciphers: &str) -> &Self {
        unsafe {
            pagekite_set_openssl_ciphers(self.inner, str_cstring!(ciphers));
        }
        self
    }

    pub fn want_spare_frontends(&self, spares: i32) -> &Self {
        unsafe {
            pagekite_want_spare_frontends(self.inner, spares);
        }
        self
    }

    pub fn thread_start(&self) -> bool {
        return unsafe { pagekite_thread_start(self.inner) } == 0;
    }

    pub fn thread_wait(&self) -> bool {
        return unsafe { pagekite_thread_wait(self.inner) } == 0;
    }

    pub fn thread_stop(&self) -> bool {
        return unsafe { pagekite_thread_stop(self.inner) } == 0;
    }

    pub fn get_status(&self) -> Status {
        return unsafe { pagekite_get_status(self.inner) }.into();
    }

    pub fn get_log(&self) -> String {
        unsafe {
            CStr::from_ptr(pagekite_get_log(self.inner))
                .to_string_lossy()
                .into_owned()
        }
    }

    pub fn dump_state_to_log(&self) -> &Self {
        unsafe {
            pagekite_dump_state_to_log(self.inner);
        }
        self
    }

    pub fn poll(&self, timeout: Duration) -> bool {
        return unsafe { pagekite_poll(self.inner, timeout.as_secs() as i32) } == 0;
    }

    pub fn tick(&self) -> bool {
        return unsafe { pagekite_tick(self.inner) } == 0;
    }
}

impl Drop for PageKite {
    fn drop(&mut self) {
        unsafe {
            pagekite_thread_stop(self.inner);
            pagekite_free(self.inner);
        }
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

use crate::error::{ConfigError, Result};
use crate::map::Map;
use crate::value::{Value, ValueKind};

mod parser;

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Expression {
    Identifier(String),
    Child(Box<Self>, String),
    Subscript(Box<Self>, isize),
}

impl FromStr for Expression {
    type Err = ConfigError;

    fn from_str(s: &str) -> Result<Self> {
        parser::from_str(s).map_err(ConfigError::PathParse)
    }
}

fn sindex_to_uindex(index: isize, len: usize) -> usize {
    if index >= 0 {
        index as usize
    } else {
        len - (index.abs() as usize)
    }
}

impl Expression {
    pub fn get(self, root: &Value) -> Option<&Value> {
        match self {
            Self::Identifier(id) => {
                match root.kind {
                    // `x` access on a table is equivalent to: map[x]
                    ValueKind::Table(ref map) => map.get(&id),

                    // all other variants return None
                    _ => None,
                }
            }

            Self::Child(expr, key) => {
                match expr.get(root) {
                    Some(value) => {
                        match value.kind {
                            // Access on a table is identical to Identifier, it just forwards
                            ValueKind::Table(ref map) => map.get(&key),

                            // all other variants return None
                            _ => None,
                        }
                    }

                    _ => None,
                }
            }

            Self::Subscript(expr, index) => match expr.get(root) {
                Some(value) => match value.kind {
                    ValueKind::Array(ref array) => {
                        let index = sindex_to_uindex(index, array.len());

                        if index >= array.len() {
                            None
                        } else {
                            Some(&array[index])
                        }
                    }

                    _ => None,
                },

                _ => None,
            },
        }
    }

    pub fn get_mut<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
        match *self {
            Self::Identifier(ref id) => match root.kind {
                ValueKind::Table(ref mut map) => map.get_mut(id),

                _ => None,
            },

            Self::Child(ref expr, ref key) => match expr.get_mut(root) {
                Some(value) => match value.kind {
                    ValueKind::Table(ref mut map) => map.get_mut(key),

                    _ => None,
                },

                _ => None,
            },

            Self::Subscript(ref expr, index) => match expr.get_mut(root) {
                Some(value) => match value.kind {
                    ValueKind::Array(ref mut array) => {
                        let index = sindex_to_uindex(index, array.len());

                        if index >= array.len() {
                            None
                        } else {
                            Some(&mut array[index])
                        }
                    }

                    _ => None,
                },

                _ => None,
            },
        }
    }

    pub fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
        match *self {
            Self::Identifier(ref id) => match root.kind {
                ValueKind::Table(ref mut map) => Some(
                    map.entry(id.clone())
                        .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                ),

                _ => None,
            },

            Self::Child(ref expr, ref key) => match expr.get_mut_forcibly(root) {
                Some(value) => {
                    if let ValueKind::Table(ref mut map) = value.kind {
                        Some(
                            map.entry(key.clone())
                                .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                        )
                    } else {
                        *value = Map::<String, Value>::new().into();

                        if let ValueKind::Table(ref mut map) = value.kind {
                            Some(
                                map.entry(key.clone())
                                    .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                            )
                        } else {
                            unreachable!();
                        }
                    }
                }

                _ => None,
            },

            Self::Subscript(ref expr, index) => match expr.get_mut_forcibly(root) {
                Some(value) => {
                    match value.kind {
                        ValueKind::Array(_) => (),
                        _ => *value = Vec::<Value>::new().into(),
                    }

                    match value.kind {
                        ValueKind::Array(ref mut array) => {
                            let index = sindex_to_uindex(index, array.len());

                            if index >= array.len() {
                                array
                                    .resize((index + 1) as usize, Value::new(None, ValueKind::Nil));
                            }

                            Some(&mut array[index])
                        }

                        _ => None,
                    }
                }
                _ => None,
            },
        }
    }

    pub fn set(&self, root: &mut Value, value: Value) {
        match *self {
            Self::Identifier(ref id) => {
                // Ensure that root is a table
                match root.kind {
                    ValueKind::Table(_) => {}

                    _ => {
                        *root = Map::<String, Value>::new().into();
                    }
                }

                match value.kind {
                    ValueKind::Table(ref incoming_map) => {
                        // Pull out another table
                        let target = if let ValueKind::Table(ref mut map) = root.kind {
                            map.entry(id.clone())
                                .or_insert_with(|| Map::<String, Value>::new().into())
                        } else {
                            unreachable!();
                        };

                        // Continue the deep merge
                        for (key, val) in incoming_map {
                            Self::Identifier(key.clone()).set(target, val.clone());
                        }
                    }

                    _ => {
                        if let ValueKind::Table(ref mut map) = root.kind {
                            // Just do a simple set
                            if let Some(existing) = map.get_mut(id) {
                                *existing = value;
                            } else {
                                map.insert(id.clone(), value);
                            }
                        }
                    }
                }
            }

            Self::Child(ref expr, ref key) => {
                if let Some(parent) = expr.get_mut_forcibly(root) {
                    if !matches!(parent.kind, ValueKind::Table(_)) {
                        // Didn't find a table. Oh well. Make a table and do this anyway
                        *parent = Map::<String, Value>::new().into();
                    }
                    Self::Identifier(key.clone()).set(parent, value);
                }
            }

            Self::Subscript(ref expr, index) => {
                if let Some(parent) = expr.get_mut_forcibly(root) {
                    if !matches!(parent.kind, ValueKind::Array(_)) {
                        *parent = Vec::<Value>::new().into()
                    }

                    if let ValueKind::Array(ref mut array) = parent.kind {
                        let uindex = sindex_to_uindex(index, array.len());
                        if uindex >= array.len() {
                            array.resize((uindex + 1) as usize, Value::new(None, ValueKind::Nil));
                        }

                        array[uindex] = value;
                    }
                }
            }
        }
    }
}
<commit_msg>turn keys to lowercase to enable cross overrides<commit_after>use std::str::FromStr;

use crate::error::{ConfigError, Result};
use crate::map::Map;
use crate::value::{Value, ValueKind};

mod parser;

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Expression {
    Identifier(String),
    Child(Box<Self>, String),
    Subscript(Box<Self>, isize),
}

impl FromStr for Expression {
    type Err = ConfigError;

    fn from_str(s: &str) -> Result<Self> {
        parser::from_str(s).map_err(ConfigError::PathParse)
    }
}

fn sindex_to_uindex(index: isize, len: usize) -> usize {
    if index >= 0 {
        index as usize
    } else {
        len - (index.abs() as usize)
    }
}

impl Expression {
    pub fn get(self, root: &Value) -> Option<&Value> {
        match self {
            Self::Identifier(id) => {
                match root.kind {
                    // `x` access on a table is equivalent to: map[x]
                    ValueKind::Table(ref map) => map.get(&id),

                    // all other variants return None
                    _ => None,
                }
            }

            Self::Child(expr, key) => {
                match expr.get(root) {
                    Some(value) => {
                        match value.kind {
                            // Access on a table is identical to Identifier, it just forwards
                            ValueKind::Table(ref map) => map.get(&key),

                            // all other variants return None
                            _ => None,
                        }
                    }

                    _ => None,
                }
            }

            Self::Subscript(expr, index) => match expr.get(root) {
                Some(value) => match value.kind {
                    ValueKind::Array(ref array) => {
                        let index = sindex_to_uindex(index, array.len());

                        if index >= array.len() {
                            None
                        } else {
                            Some(&array[index])
                        }
                    }

                    _ => None,
                },

                _ => None,
            },
        }
    }

    pub fn get_mut<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
        match *self {
            Self::Identifier(ref id) => match root.kind {
                ValueKind::Table(ref mut map) => map.get_mut(id),

                _ => None,
            },

            Self::Child(ref expr, ref key) => match expr.get_mut(root) {
                Some(value) => match value.kind {
                    ValueKind::Table(ref mut map) => map.get_mut(key),

                    _ => None,
                },

                _ => None,
            },

            Self::Subscript(ref expr, index) => match expr.get_mut(root) {
                Some(value) => match value.kind {
                    ValueKind::Array(ref mut array) => {
                        let index = sindex_to_uindex(index, array.len());

                        if index >= array.len() {
                            None
                        } else {
                            Some(&mut array[index])
                        }
                    }

                    _ => None,
                },

                _ => None,
            },
        }
    }

    pub fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
        match *self {
            Self::Identifier(ref id) => match root.kind {
                ValueKind::Table(ref mut map) => Some(
                    map.entry(id.to_lowercase())
                        .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                ),

                _ => None,
            },

            Self::Child(ref expr, ref key) => match expr.get_mut_forcibly(root) {
                Some(value) => {
                    if let ValueKind::Table(ref mut map) = value.kind {
                        Some(
                            map.entry(key.to_lowercase())
                                .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                        )
                    } else {
                        *value = Map::<String, Value>::new().into();

                        if let ValueKind::Table(ref mut map) = value.kind {
                            Some(
                                map.entry(key.to_lowercase())
                                    .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                            )
                        } else {
                            unreachable!();
                        }
                    }
                }

                _ => None,
            },

            Self::Subscript(ref expr, index) => match expr.get_mut_forcibly(root) {
                Some(value) => {
                    match value.kind {
                        ValueKind::Array(_) => (),
                        _ => *value = Vec::<Value>::new().into(),
                    }

                    match value.kind {
                        ValueKind::Array(ref mut array) => {
                            let index = sindex_to_uindex(index, array.len());

                            if index >= array.len() {
                                array
                                    .resize((index + 1) as usize, Value::new(None, ValueKind::Nil));
                            }

                            Some(&mut array[index])
                        }

                        _ => None,
                    }
                }
                _ => None,
            },
        }
    }

    pub fn set(&self, root: &mut Value, value: Value) {
        match *self {
            Self::Identifier(ref id) => {
                // Ensure that root is a table
                match root.kind {
                    ValueKind::Table(_) => {}

                    _ => {
                        *root = Map::<String, Value>::new().into();
                    }
                }

                match value.kind {
                    ValueKind::Table(ref incoming_map) => {
                        // Pull out another table
                        let target = if let ValueKind::Table(ref mut map) = root.kind {
                            map.entry(id.to_lowercase())
                                .or_insert_with(|| Map::<String, Value>::new().into())
                        } else {
                            unreachable!();
                        };

                        // Continue the deep merge
                        for (key, val) in incoming_map {
                            Self::Identifier(key.to_lowercase()).set(target, val.clone());
                        }
                    }

                    _ => {
                        if let ValueKind::Table(ref mut map) = root.kind {
                            // Just do a simple set
                            if let Some(existing) = map.get_mut(&id.to_lowercase()) {
                                *existing = value;
                            } else {
                                map.insert(id.to_lowercase(), value);
                            }
                        }
                    }
                }
            }

            Self::Child(ref expr, ref key) => {
                if let Some(parent) = expr.get_mut_forcibly(root) {
                    if !matches!(parent.kind, ValueKind::Table(_)) {
                        // Didn't find a table. Oh well. Make a table and do this anyway
                        *parent = Map::<String, Value>::new().into();
                    }
                    Self::Identifier(key.to_lowercase()).set(parent, value);
                }
            }

            Self::Subscript(ref expr, index) => {
                if let Some(parent) = expr.get_mut_forcibly(root) {
                    if !matches!(parent.kind, ValueKind::Array(_)) {
                        *parent = Vec::<Value>::new().into()
                    }

                    if let ValueKind::Array(ref mut array) = parent.kind {
                        let uindex = sindex_to_uindex(index, array.len());
                        if uindex >= array.len() {
                            array.resize((uindex + 1) as usize, Value::new(None, ValueKind::Nil));
                        }

                        array[uindex] = value;
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry::*;

use util::*;

/// Information about the position of a single term within a document
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable)]
pub struct Position {
    /// Pair of byte indexes into the document at the beginning (inclusive) and end (exclusive) of
    /// the term.
    pub offsets: (usize, usize),
    /// The token position of the term, i.e., the number of tokens that occur before it in the doc.
    /// For example, for the sentence "I have to go to the store",
    /// the term "to" has positions [2, 4].
    pub position: usize,
}

impl Position {
    /// Creates a new Position struct with the given offsets and position.
    pub fn new(offsets: (usize, usize), position: usize) -> Position {
        Position {
            offsets: offsets,
            position: position,
        }
    }
}

impl Merge for Position {
    fn merge(self, other: Position) -> Option<Position> {
        if self.position == other.position {
            self.offsets.merge(other.offsets).map(|offsets| Position::new(offsets, self.position))
        } else {
            None
        }
    }
}

/// Each document is assigned a unique string id.
pub type DocId = String;

/// A Postings map (doc id => positions) for a single term.
/// Records which Documents contain the term, and at which locations in the documents.
pub type PostingsMap = BTreeMap<DocId, Vec<Position>>;

/// An extension trait for iterables over PostingsMaps
/// that enables the computation of the union of Postingsmaps
pub trait PostingsMerge {
    /// Computes the map containing the union of the maps in self
    fn merge_postings(self) -> PostingsMap;
}

impl<'a, Iter> PostingsMerge for Iter
    where Iter: IntoIterator,
          Iter::Item: Borrow<PostingsMap> {
    fn merge_postings(self) -> PostingsMap {
        let mut map = PostingsMap::new();
        for tree in self {
            for (doc_id, positions) in tree.borrow() {
                match map.entry(doc_id.clone()) {
                    Vacant(entry) => {
                        entry.insert(positions.clone());
                    }
                    Occupied(mut entry) =>
                        entry.get_mut().merge_coalesce(positions.iter().cloned()),
                }
            }
        }
        map
    }
}

/// An extension trait for slices of PostingsMaps,
/// that enables the computation of the intersection
/// of PostingsMaps
pub trait PostingsIntersect {
    /// Computes the map containing the intersection of the the maps in self
    fn intersect_postings(self) -> PostingsMap;
}

impl<'a> PostingsIntersect for &'a [PostingsMap] {
    fn intersect_postings(self) -> PostingsMap {
        match self {
            [] => PostingsMap::new(),
            [ref posting] => posting.clone(),
            [ref posting0, rest..] => {
                self.intersection()
                    .map(|doc_id| {
                        let mut positions = posting0[doc_id].clone();
                        for posting in rest {
                            positions.merge_coalesce(posting[doc_id].iter().cloned());
                        }
                        (doc_id.clone(), positions)
                    })
                    .collect()
            }
        }
    }
}

/// An extension trait for PostingsMaps which is useful in phrase query intersections.
pub trait PositionalIntersect {
    /// Intersect two postings maps positionally, returning a PostingsMap containing postings lists
    /// whose terms are present at position X in self's postings list for document D
    /// and position X + 1 in the input's postings list for document D.
    fn intersect_positionally(&self, &Self) -> PostingsMap;
}

impl PositionalIntersect for PostingsMap {
    fn intersect_positionally(&self, other: &Self) -> PostingsMap {
        fn positional_intersect(left: &[Position], right: &[Position]) -> Vec<Position> {
            let mut intersection = vec![];
            let mut left = left.iter().cloned();
            let mut right = right.iter().cloned();
            let mut lval = left.next();
            let mut rval = right.next();
            loop {
                if let (Some(l), Some(r)) = (lval, rval) {
                    match l.position.cmp(&r.position) {
                        Ordering::Less => {
                            if l.position + 1 == r.position {
                                if !intersection.is_empty() {
                                    if intersection[intersection.len() - 1] != l {
                                        intersection.push(l);
                                    }
                                } else {
                                    intersection.push(l);
                                }
                                intersection.push(r);
                                rval = right.next();
                            }
                            lval = left.next();
                        }
                        Ordering::Greater | Ordering::Equal => rval = right.next(),
                    }
                } else {
                    return intersection;
                }
            }
        }

        let maps = &[self, other];
        maps.intersection()
            .map(|doc_id| {
                (doc_id.clone(),
                 positional_intersect(&self[doc_id], &other[doc_id]))
            })
            .collect()
    }
}

#[cfg(test)]
mod test {
    use std::iter;
    use super::super::Position;
    use super::PostingsMerge;

    #[test]
    fn test_merge() {
        let postings = [iter::once(("1".into(),
                                    vec![Position::new((0, 1), 0), Position::new((2, 3), 1)]))
                            .collect(),
                        iter::once(("1".into(),
                                    vec![Position::new((4, 5), 2), Position::new((6, 7), 3)]))
                            .collect()];
        assert_eq!(postings.iter().merge_postings(),
                   iter::once(("1".into(),
                               vec![Position::new((0, 1), 0),
                                    Position::new((2, 3), 1),
                                    Position::new((4, 5), 2),
                                    Position::new((6, 7), 3)]))
                       .collect());
    }
}
<commit_msg>Clean up a few comments<commit_after>use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry::*;

use util::*;

/// Information about the position of a single term within a document
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable)]
pub struct Position {
    /// Pair of byte indexes into the document at the beginning (inclusive) and end (exclusive) of
    /// the term.
    pub offsets: (usize, usize),
    /// The token position of the term, i.e., the number of tokens that occur before it in the doc.
    /// For example, for the sentence "I have to go to the store",
    /// the term "to" has positions [2, 4].
    pub position: usize,
}

impl Position {
    /// Creates a new Position struct with the given offsets and position.
    pub fn new(offsets: (usize, usize), position: usize) -> Position {
        Position {
            offsets: offsets,
            position: position,
        }
    }
}

impl Merge for Position {
    fn merge(self, other: Position) -> Option<Position> {
        if self.position == other.position {
            self.offsets.merge(other.offsets).map(|offsets| Position::new(offsets, self.position))
        } else {
            None
        }
    }
}

/// Each document is assigned a unique string id.
pub type DocId = String;

/// A postings map (doc id => positions) for a single term.
/// Records which Documents contain the term, and at which locations in the documents.
pub type PostingsMap = BTreeMap<DocId, Vec<Position>>;

/// An extension trait for iterables over `PostingsMap`s
/// that enables computing their union.
pub trait PostingsMerge {
    /// Computes the map containing the union of the maps in self
    fn merge_postings(self) -> PostingsMap;
}

impl<'a, Iter> PostingsMerge for Iter
    where Iter: IntoIterator,
          Iter::Item: Borrow<PostingsMap> {
    fn merge_postings(self) -> PostingsMap {
        let mut map = PostingsMap::new();
        for tree in self {
            for (doc_id, positions) in tree.borrow() {
                match map.entry(doc_id.clone()) {
                    Vacant(entry) => {
                        entry.insert(positions.clone());
                    }
                    Occupied(mut entry) =>
                        entry.get_mut().merge_coalesce(positions.iter().cloned()),
                }
            }
        }
        map
    }
}

/// An extension trait for slices of `PostingsMap`s
/// that enables computing their intersection.
pub trait PostingsIntersect {
    /// Computes the map containing the intersection of the the maps in self
    fn intersect_postings(self) -> PostingsMap;
}

impl<'a> PostingsIntersect for &'a [PostingsMap] {
    fn intersect_postings(self) -> PostingsMap {
        match self {
            [] => PostingsMap::new(),
            [ref posting] => posting.clone(),
            [ref posting0, rest..] => {
                self.intersection()
                    .map(|doc_id| {
                        let mut positions = posting0[doc_id].clone();
                        for posting in rest {
                            positions.merge_coalesce(posting[doc_id].iter().cloned());
                        }
                        (doc_id.clone(), positions)
                    })
                    .collect()
            }
        }
    }
}

/// An extension trait for PostingsMaps which is useful in phrase query intersections.
pub trait PositionalIntersect {
    /// Intersect two postings maps positionally, returning a PostingsMap containing postings lists
    /// whose terms are present at position X in self's postings list for document D
    /// and position X + 1 in the input's postings list for document D.
    fn intersect_positionally(&self, &Self) -> PostingsMap;
}

impl PositionalIntersect for PostingsMap {
    fn intersect_positionally(&self, other: &Self) -> PostingsMap {
        fn positional_intersect(left: &[Position], right: &[Position]) -> Vec<Position> {
            let mut intersection = vec![];
            let mut left = left.iter().cloned();
            let mut right = right.iter().cloned();
            let mut lval = left.next();
            let mut rval = right.next();
            loop {
                if let (Some(l), Some(r)) = (lval, rval) {
                    match l.position.cmp(&r.position) {
                        Ordering::Less => {
                            if l.position + 1 == r.position {
                                if !intersection.is_empty() {
                                    if intersection[intersection.len() - 1] != l {
                                        intersection.push(l);
                                    }
                                } else {
                                    intersection.push(l);
                                }
                                intersection.push(r);
                                rval = right.next();
                            }
                            lval = left.next();
                        }
                        Ordering::Greater | Ordering::Equal => rval = right.next(),
                    }
                } else {
                    return intersection;
                }
            }
        }

        let maps = &[self, other];
        maps.intersection()
            .map(|doc_id| {
                (doc_id.clone(),
                 positional_intersect(&self[doc_id], &other[doc_id]))
            })
            .collect()
    }
}

#[cfg(test)]
mod test {
    use std::iter;
    use super::super::Position;
    use super::PostingsMerge;

    #[test]
    fn test_merge() {
        let postings = [iter::once(("1".into(),
                                    vec![Position::new((0, 1), 0), Position::new((2, 3), 1)]))
                            .collect(),
                        iter::once(("1".into(),
                                    vec![Position::new((4, 5), 2), Position::new((6, 7), 3)]))
                            .collect()];
        assert_eq!(postings.iter().merge_postings(),
                   iter::once(("1".into(),
                               vec![Position::new((0, 1), 0),
                                    Position::new((2, 3), 1),
                                    Position::new((4, 5), 2),
                                    Position::new((6, 7), 3)]))
                       .collect());
    }
}
<|endoftext|>


<commit_before>#[cfg(target_arch = "wasm32")]
compile_error!("This module should not be included when compiling to wasm");

use std::sync::mpsc::{self, TryRecvError};
use std::thread;
use std::path;

use piston_window::{clear, context, line, polygon, AdvancedWindow, Event as PistonEvent, G2d,
    Input, PistonWindow, WindowSettings};

use svg;
use svg::node::element::{Line as SvgLine, Polygon as SvgPolygon, Rectangle as SvgRect};

use crate::app::TurtleApp;
use crate::event::from_piston_event;
use crate::extensions::ConvertScreenCoordinates;
use crate::query::DrawingCommand;
use crate::state::{DrawingState, Path, Pen, Polygon, TurtleState};
use crate::{color, Color, Event, Point};

fn update_window(window: &mut PistonWindow, current: DrawingState, next: DrawingState) -> DrawingState {
    if next.title != current.title {
        window.set_title(next.title.clone());
    }
    if next.width != current.width || next.height != current.height {
        window.set_size((next.width, next.height));
    }
    if next.maximized != current.maximized {
        window.window.window.set_maximized(next.maximized);
    }
    if next.fullscreen != current.fullscreen {
        if next.fullscreen {
            window
                .window
                .window
                .set_fullscreen(Some(window.window.window.get_current_monitor()));
        } else {
            window.window.window.set_fullscreen(None);
        }
    }
    next
}

#[derive(Debug)]
pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    app: TurtleApp,
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new(app: TurtleApp) -> Renderer {
        Self {
            app,
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(&mut self, drawing_rx: mpsc::Receiver<DrawingCommand>, events_tx: mpsc::Sender<Event>) {
        let state = self.app.read_only();

        // This check isn't foolproof. Someone can always create a thread named "main".
        if thread::current().name().unwrap_or("") != "main" {
            // In order to maintain compatibility with MacOS, we need to make sure that windows are
            // only created on the main thread. We do this check on all platforms so that no one
            // can accidentally make a change that creates the window off of the main thread.
            unreachable!("bug: windows can only be created on the main thread");
        }

        let mut window: PistonWindow = WindowSettings::new(
            &*state.drawing().title,
            (state.drawing().width, state.drawing().height)
        ).exit_on_esc(true).build().expect("bug: could not build window");

        // We keep a copy of the DrawingState so that we can tell when it is updated and we need
        // to change something on the window
        let mut current_drawing = DrawingState::default();

        let mut center = state.drawing().center;

        'renderloop: while let Some(event) = window.next() {
            match event {
                PistonEvent::Input(Input::Resize(width, height)) => {
                    let width = width as u32;
                    let height = height as u32;
                    if width != current_drawing.width || height != current_drawing.height {
                        let mut drawing = self.app.drawing_mut();
                        drawing.width = width;
                        drawing.height = height;
                    }
                }
                _ => {}
            }

            if let Some(event) = from_piston_event(&event, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {}
                    // Quit - the server thread must have quit
                    Err(_) => break,
                }
            }

            // Need to handle all of the queries we receive at once so that any lag caused by
            // how long rendering takes doesn't cause any problems
            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd),
                    Err(TryRecvError::Empty) => break,                    // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            // Update the window based on any changes in the DrawingState
            current_drawing = update_window(&mut window, current_drawing, state.drawing().clone());

            window.draw_2d(&event, |c, g| {
                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = state.drawing().center.to_screen_coords(Point {
                    x: width * 0.5,
                    y: height * 0.5,
                });

                // We clone the relevant state before rendering so that the rendering thread
                // doesn't need to keep locking, waiting or making the main thread wait
                let drawing = state.drawing().clone();
                let temporary_path = state.temporary_path().clone();
                let turtle = state.turtle().clone();

                self.render(c, g, center, &drawing, &temporary_path, &turtle);
            });
        }
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand) {
        //NOTE: Do not pass the ReadOnly state to this function. By the time a DrawingCommand is
        // handled, that state may be completely out of date
        use DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path { start, end, .. } = path;
                    if poly.vertices.last().map_or(true, |&v| v != start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                } else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill(fill_color) => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| {
                    Some((
                        Vec::new(),
                        Polygon {
                            vertices: Vec::new(),
                            fill_color,
                        },
                    ))
                });
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(
                    border
                        .into_iter()
                        .filter_map(|p| if p.pen.enabled { Some(Drawing::Path(p)) } else { None }),
                );
            },
            Clear => {
                self.drawings.clear();
                // Because clear doesn't actually shorten the capacity of the vector
                // We don't want to leak memory by leaving this Vec at whatever its size was before
                self.drawings.shrink_to_fit();
                self.fill_polygon.take();
            },
            SaveSVG(path_buf) => self.save_svg(path_buf),
        }
    }

    /// The main rendering route. Dispatches to other functions as needed.
    fn render(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        drawing: &DrawingState,
        temporary_path: &Option<Path>,
        turtle: &TurtleState,
    ) {
        let background = drawing.background;
        clear(background.into(), g);

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => self.render_path(c, g, center, path),
                Drawing::Polygon(ref poly) => self.render_polygon(c, g, center, poly.fill_color, poly.vertices.iter()),
            }
        }

        if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
            // If the temporary_path is not None, we need to add it to the polygon being
            // filled or else the polygon will fall one edge behind in the animation
            let extra = temporary_path.as_ref().map_or(Vec::new(), |&Path { start, end, .. }| {
                if poly.vertices.last().map_or(true, |&v| v != start) {
                    vec![start, end]
                } else {
                    vec![end]
                }
            });
            self.render_polygon(c, g, center, poly.fill_color, poly.vertices.iter().chain(extra.iter()));

            for path in border {
                if path.pen.enabled {
                    self.render_path(c, g, center, path);
                }
            }
        }

        if let Some(ref path) = *temporary_path {
            if path.pen.enabled {
                self.render_path(c, g, center, path);
            }
        }

        self.render_shell(c, g, center, turtle);
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d<'_>, center: Point, path: &Path) {
        let &Path { start, end, ref pen } = path;
        let &Pen { thickness, color, enabled } = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness, [start.x, start.y, end.x, end.y], c.transform, g);
    }

    /// Render a polygon given its vertices
    fn render_polygon<'a, T: Iterator<Item = &'a Point>>(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        fill_color: Color,
        verts: T,
    ) {
        // Performance note: Why make this function generic instead of just taking Polygon?
        // Answer: render_polygon is called multiple times. It's called repeatedly in a loop, and
        // it is also called when drawing the current fill_polygon. Rather than moving the
        // code to *maybe* add the temporary_path to the rendered polygon into this method, we
        // avoid branching unnecessarily by allowing the repeated caller to do what is fast, then
        // doing the slow thing (checking if the path is None) only once when it is needed.
        //
        // We pass in the points as an Iterator so that they do not need to be collected into any
        // struct. This avoids an allocation that really isn't needed since these are all temporary
        // anyway. Everything is going to get copied anyway on the next line. No need to do it
        // twice.
        //
        // See the commit before this comment was added for the approach that would have required
        // branching in every iteration of the loop where render_polygon is called over and over
        // again.
        let verts = verts.map(|p| p.to_screen_coords(center).into()).collect::<Vec<_>>();
        polygon(fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        &TurtleState {
            position,
            heading,
            visible,
            ..
        }: &TurtleState,
    ) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position.x;
        let turtle_y = position.y;
        let shell: Vec<_> = [[0., 15.], [10., 0.], [0., -15.]]
            .into_iter()
            .map(|pt| {
                // Rotate each point by the heading and add the current turtle position
                let x = cos * pt[0] - sin * pt[1] + turtle_x;
                let y = sin * pt[0] + cos * pt[1] + turtle_y;
                Point { x, y }.to_screen_coords(center).into()
            })
            .collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1., [start[0], start[1], end[0], end[1]], c.transform, g);
        }
    }

    /// Save the drawings to SVG
    fn save_svg(&mut self, path: &path::Path) {
        // Returns rgba string of Color in CSS functional syntax
        fn rgba_string(color: Color) -> String {
            format!("rgba({}, {}, {}, {})", color.red as u8, color.green as u8, color.blue as u8, color.alpha)
        }

        let drawing_state = self.app.drawing();
        let mut document = svg::Document::new()
            .set("viewBox", (0, 0, drawing_state.width, drawing_state.height));

        // set background color - https://stackoverflow.com/a/11293812/9276882
        let bg_color = drawing_state.background;
        let background = SvgRect::new()
            .set("width", "100%")
            .set("height", "100%")
            .set("fill", rgba_string(bg_color));

        document = document.add(background);

        let center = drawing_state.center.to_screen_coords(Point {
            x: drawing_state.width as f64 * 0.5,
            y: drawing_state.height as f64 * 0.5,
        });

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => {
                    let &Path { start, end, ref pen } = path;
                    let &Pen { thickness, color, enabled: _ } = pen;

                    let start = start.to_screen_coords(center);
                    let end = end.to_screen_coords(center);

                    let line = SvgLine::new()
                        .set("x1", start.x)
                        .set("y1", start.y)
                        .set("x2", end.x)
                        .set("y2", end.y)
                        .set("stroke", rgba_string(*color))
                        .set("stroke-width", format!("{}px", thickness * 2.0));

                    // Note that in above code `stroke-width` is twice the `thickness`.
                    // As per https://docs.piston.rs/piston_window/piston_window/fn.line.html,
                    // second param of piston_window::line is radius.
                    // So radius (thickness) = 1 takes 2 pixels on screen.
                    // Hence, stroke-width of svg element is double the thickness.

                    document = document.add(line);
                },
                Drawing::Polygon(ref poly) => {
                    let points = poly.vertices.iter()
                        .map(|p| {
                            let point = p.to_screen_coords(center);
                            format!("{},{} ", point.x, point.y)
                        })
                        .fold("".to_string(), |acc, x| acc + &x);

                    let color = poly.fill_color;
                    let polygon = SvgPolygon::new()
                        .set("fill", rgba_string(poly.fill_color))
                        .set("points", points);

                    document = document.add(polygon);
                },
            }
        }

        let export_to_svg = svg::save(path, &document);
        match export_to_svg {
            Ok(()) => (),
            Err(error) => eprintln!("Error saving SVG file : {:#?}", error),
        }
    }
}
<commit_msg>Fixing some compiler errors and warnings that got through on the last PR<commit_after>#[cfg(target_arch = "wasm32")]
compile_error!("This module should not be included when compiling to wasm");

use std::sync::mpsc::{self, TryRecvError};
use std::thread;
use std::path;

use piston_window::{clear, context, line, polygon, AdvancedWindow, Event as PistonEvent, G2d,
    Input, PistonWindow, WindowSettings};

use svg;
use svg::node::element::{Line as SvgLine, Polygon as SvgPolygon, Rectangle as SvgRect};

use crate::app::TurtleApp;
use crate::event::from_piston_event;
use crate::extensions::ConvertScreenCoordinates;
use crate::query::DrawingCommand;
use crate::state::{DrawingState, Path, Pen, Polygon, TurtleState};
use crate::{color, Color, Event, Point};

fn update_window(window: &mut PistonWindow, current: DrawingState, next: DrawingState) -> DrawingState {
    if next.title != current.title {
        window.set_title(next.title.clone());
    }
    if next.width != current.width || next.height != current.height {
        window.set_size((next.width, next.height));
    }
    if next.maximized != current.maximized {
        window.window.window.set_maximized(next.maximized);
    }
    if next.fullscreen != current.fullscreen {
        if next.fullscreen {
            window
                .window
                .window
                .set_fullscreen(Some(window.window.window.get_current_monitor()));
        } else {
            window.window.window.set_fullscreen(None);
        }
    }
    next
}

#[derive(Debug)]
pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    app: TurtleApp,
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new(app: TurtleApp) -> Renderer {
        Self {
            app,
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(&mut self, drawing_rx: mpsc::Receiver<DrawingCommand>, events_tx: mpsc::Sender<Event>) {
        let state = self.app.read_only();

        // This check isn't foolproof. Someone can always create a thread named "main".
        if thread::current().name().unwrap_or("") != "main" {
            // In order to maintain compatibility with MacOS, we need to make sure that windows are
            // only created on the main thread. We do this check on all platforms so that no one
            // can accidentally make a change that creates the window off of the main thread.
            unreachable!("bug: windows can only be created on the main thread");
        }

        let mut window: PistonWindow = WindowSettings::new(
            &*state.drawing().title,
            (state.drawing().width, state.drawing().height)
        ).exit_on_esc(true).build().expect("bug: could not build window");

        // We keep a copy of the DrawingState so that we can tell when it is updated and we need
        // to change something on the window
        let mut current_drawing = DrawingState::default();

        let mut center = state.drawing().center;

        'renderloop: while let Some(event) = window.next() {
            match event {
                PistonEvent::Input(Input::Resize(width, height)) => {
                    let width = width as u32;
                    let height = height as u32;
                    if width != current_drawing.width || height != current_drawing.height {
                        let mut drawing = self.app.drawing_mut();
                        drawing.width = width;
                        drawing.height = height;
                    }
                }
                _ => {}
            }

            if let Some(event) = from_piston_event(&event, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {}
                    // Quit - the server thread must have quit
                    Err(_) => break,
                }
            }

            // Need to handle all of the queries we receive at once so that any lag caused by
            // how long rendering takes doesn't cause any problems
            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd),
                    Err(TryRecvError::Empty) => break,                    // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            // Update the window based on any changes in the DrawingState
            current_drawing = update_window(&mut window, current_drawing, state.drawing().clone());

            window.draw_2d(&event, |c, g| {
                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = state.drawing().center.to_screen_coords(Point {
                    x: width * 0.5,
                    y: height * 0.5,
                });

                // We clone the relevant state before rendering so that the rendering thread
                // doesn't need to keep locking, waiting or making the main thread wait
                let drawing = state.drawing().clone();
                let temporary_path = state.temporary_path().clone();
                let turtle = state.turtle().clone();

                self.render(c, g, center, &drawing, &temporary_path, &turtle);
            });
        }
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand) {
        //NOTE: Do not pass the ReadOnly state to this function. By the time a DrawingCommand is
        // handled, that state may be completely out of date
        use DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path { start, end, .. } = path;
                    if poly.vertices.last().map_or(true, |&v| v != start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                } else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill(fill_color) => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| {
                    Some((
                        Vec::new(),
                        Polygon {
                            vertices: Vec::new(),
                            fill_color,
                        },
                    ))
                });
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(
                    border
                        .into_iter()
                        .filter_map(|p| if p.pen.enabled { Some(Drawing::Path(p)) } else { None }),
                );
            },
            Clear => {
                self.drawings.clear();
                // Because clear doesn't actually shorten the capacity of the vector
                // We don't want to leak memory by leaving this Vec at whatever its size was before
                self.drawings.shrink_to_fit();
                self.fill_polygon.take();
            },
            SaveSVG(path_buf) => self.save_svg(&path_buf),
        }
    }

    /// The main rendering route. Dispatches to other functions as needed.
    fn render(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        drawing: &DrawingState,
        temporary_path: &Option<Path>,
        turtle: &TurtleState,
    ) {
        let background = drawing.background;
        clear(background.into(), g);

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => self.render_path(c, g, center, path),
                Drawing::Polygon(ref poly) => self.render_polygon(c, g, center, poly.fill_color, poly.vertices.iter()),
            }
        }

        if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
            // If the temporary_path is not None, we need to add it to the polygon being
            // filled or else the polygon will fall one edge behind in the animation
            let extra = temporary_path.as_ref().map_or(Vec::new(), |&Path { start, end, .. }| {
                if poly.vertices.last().map_or(true, |&v| v != start) {
                    vec![start, end]
                } else {
                    vec![end]
                }
            });
            self.render_polygon(c, g, center, poly.fill_color, poly.vertices.iter().chain(extra.iter()));

            for path in border {
                if path.pen.enabled {
                    self.render_path(c, g, center, path);
                }
            }
        }

        if let Some(ref path) = *temporary_path {
            if path.pen.enabled {
                self.render_path(c, g, center, path);
            }
        }

        self.render_shell(c, g, center, turtle);
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d<'_>, center: Point, path: &Path) {
        let &Path { start, end, ref pen } = path;
        let &Pen { thickness, color, enabled } = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness, [start.x, start.y, end.x, end.y], c.transform, g);
    }

    /// Render a polygon given its vertices
    fn render_polygon<'a, T: Iterator<Item = &'a Point>>(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        fill_color: Color,
        verts: T,
    ) {
        // Performance note: Why make this function generic instead of just taking Polygon?
        // Answer: render_polygon is called multiple times. It's called repeatedly in a loop, and
        // it is also called when drawing the current fill_polygon. Rather than moving the
        // code to *maybe* add the temporary_path to the rendered polygon into this method, we
        // avoid branching unnecessarily by allowing the repeated caller to do what is fast, then
        // doing the slow thing (checking if the path is None) only once when it is needed.
        //
        // We pass in the points as an Iterator so that they do not need to be collected into any
        // struct. This avoids an allocation that really isn't needed since these are all temporary
        // anyway. Everything is going to get copied anyway on the next line. No need to do it
        // twice.
        //
        // See the commit before this comment was added for the approach that would have required
        // branching in every iteration of the loop where render_polygon is called over and over
        // again.
        let verts = verts.map(|p| p.to_screen_coords(center).into()).collect::<Vec<_>>();
        polygon(fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(
        &self,
        c: context::Context,
        g: &mut G2d<'_>,
        center: Point,
        &TurtleState {
            position,
            heading,
            visible,
            ..
        }: &TurtleState,
    ) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position.x;
        let turtle_y = position.y;
        let shell: Vec<_> = [[0., 15.], [10., 0.], [0., -15.]]
            .into_iter()
            .map(|pt| {
                // Rotate each point by the heading and add the current turtle position
                let x = cos * pt[0] - sin * pt[1] + turtle_x;
                let y = sin * pt[0] + cos * pt[1] + turtle_y;
                Point { x, y }.to_screen_coords(center).into()
            })
            .collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1., [start[0], start[1], end[0], end[1]], c.transform, g);
        }
    }

    /// Save the drawings to SVG
    fn save_svg(&mut self, path: &path::Path) {
        // Returns rgba string of Color in CSS functional syntax
        fn rgba_string(color: Color) -> String {
            format!("rgba({}, {}, {}, {})", color.red as u8, color.green as u8, color.blue as u8, color.alpha)
        }

        let drawing_state = self.app.drawing();
        let mut document = svg::Document::new()
            .set("viewBox", (0, 0, drawing_state.width, drawing_state.height));

        // set background color - https://stackoverflow.com/a/11293812/9276882
        let bg_color = drawing_state.background;
        let background = SvgRect::new()
            .set("width", "100%")
            .set("height", "100%")
            .set("fill", rgba_string(bg_color));

        document = document.add(background);

        let center = drawing_state.center.to_screen_coords(Point {
            x: drawing_state.width as f64 * 0.5,
            y: drawing_state.height as f64 * 0.5,
        });

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => {
                    let &Path { start, end, ref pen } = path;
                    let &Pen { thickness, color, enabled: _ } = pen;

                    let start = start.to_screen_coords(center);
                    let end = end.to_screen_coords(center);

                    let line = SvgLine::new()
                        .set("x1", start.x)
                        .set("y1", start.y)
                        .set("x2", end.x)
                        .set("y2", end.y)
                        .set("stroke", rgba_string(color))
                        .set("stroke-width", format!("{}px", thickness * 2.0));

                    // Note that in above code `stroke-width` is twice the `thickness`.
                    // As per https://docs.piston.rs/piston_window/piston_window/fn.line.html,
                    // second param of piston_window::line is radius.
                    // So radius (thickness) = 1 takes 2 pixels on screen.
                    // Hence, stroke-width of svg element is double the thickness.

                    document = document.add(line);
                },
                Drawing::Polygon(ref poly) => {
                    let points = poly.vertices.iter()
                        .map(|p| {
                            let point = p.to_screen_coords(center);
                            format!("{},{} ", point.x, point.y)
                        })
                        .fold("".to_string(), |acc, x| acc + &x);

                    let polygon = SvgPolygon::new()
                        .set("fill", rgba_string(poly.fill_color))
                        .set("points", points);

                    document = document.add(polygon);
                },
            }
        }

        let export_to_svg = svg::save(path, &document);
        match export_to_svg {
            Ok(()) => (),
            Err(error) => eprintln!("Error saving SVG file : {:#?}", error),
        }
    }
}
<|endoftext|>