<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, ExitStatus, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

const RUN_MEDIA_KALVIN: &'static str = "/run/media/kalvin";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
    Rsync,
    RunAll,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

fn exec_rsync(args: &[&str]) -> Result<(), AuricError> {
    let result = Exec::cmd("rsync").args(args).join()?;
    if !result.success() {
        match result {
            ExitStatus::Exited(code) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with code {}",
                    code
                )))
            }
            ExitStatus::Signaled(signum) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with signal {}",
                    signum
                )))
            }
            ExitStatus::Other(code) => {
                return Err(AuricError::Subprocess(format!(
                    "unknown rsync error: {}",
                    code
                )))
            }
            ExitStatus::Undetermined => {
                return Err(AuricError::Subprocess("unknown rsync error!".to_string()))
            }
        }
    }
    Ok(())
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect,ServerAliveInterval=26",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            "rsync" => return Ok(AuricOperationMode::Rsync),
            "push" => return Ok(AuricOperationMode::RunAll),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        println!("{}", "Mounting sshfs...");
        self.sshfs_manager.mount()?;
        println!("{}", "Attaching loop device...");
        self.loop_manager.attach()?;
        println!("{}", "Finding newly attached loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Unlocking volume...");
        self.luks_manager.unlock(&loop_device)?;
        println!("{}", "Mounting volume...");
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        println!("{}", "Unmounting volume...");
        self.luks_manager.unmount()?;
        println!("{}", "Finding backing loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Locking volume...");
        self.luks_manager.lock(&loop_device)?;
        println!("{}", "Detaching loop device...");
        self.loop_manager.detach()?;
        println!("{}", "Unmounting sshfs...");
        self.sshfs_manager.unmount()
    }

    fn rsync(&self) -> Result<(), AuricError> {
        // I'm not pulling in the `home_dir` crate just to grab a value
        // that I suspect will be immutable for many years to come.
        // Likewise, the user mount directory is lazily hardcoded.
        let mut remote_directory = PathBuf::from(RUN_MEDIA_KALVIN);
        remote_directory.push(LOOP_DEVICE_PARTITION_LABEL);
        if !remote_directory.is_dir() {
            return Err(AuricError::Invocation(format!(
                "remote directory ``{}'' not mounted",
                remote_directory.display()
            )));
        }

        exec_rsync(&[
            "--delete",
            "-avPS",
            "--exclude=/.cache/",
            "--exclude=/.cargo/",
            "--exclude=/.local/share/",
            "--exclude=/.mozilla/",
            "--exclude=/.thunderbird/",
            "--exclude=/Downloads/.firefox-nightly/",
            "--exclude=/Downloads/.thunderbird-beta/",
            "--delete-excluded",
            "/home/kalvin/",
            remote_directory.to_str().unwrap(),
        ])
    }

    fn run_all(&self) -> Result<(), AuricError> {
        self.mount()?;
        self.rsync()?;
        self.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
            AuricOperationMode::Rsync => return self.rsync(),
            AuricOperationMode::RunAll => return self.run_all(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Check for lingering ssh<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, ExitStatus, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

const RUN_MEDIA_KALVIN: &'static str = "/run/media/kalvin";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
    Rsync,
    RunAll,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

fn exec_rsync(args: &[&str]) -> Result<(), AuricError> {
    let result = Exec::cmd("rsync").args(args).join()?;
    if !result.success() {
        match result {
            ExitStatus::Exited(code) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with code {}",
                    code
                )))
            }
            ExitStatus::Signaled(signum) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with signal {}",
                    signum
                )))
            }
            ExitStatus::Other(code) => {
                return Err(AuricError::Subprocess(format!(
                    "unknown rsync error: {}",
                    code
                )))
            }
            ExitStatus::Undetermined => {
                return Err(AuricError::Subprocess("unknown rsync error!".to_string()))
            }
        }
    }
    Ok(())
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect,ServerAliveInterval=26",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }

    pub fn count_ssh_processes(&self) -> Result<usize, AuricError> {
        let pgrep_stdout = exec_with_stdout("pgrep", &["-af", "ssh"], "pgrep ssh")?;
        Ok(pgrep_stdout.lines().count())
    }

    pub fn wait_until_just_one_ssh_process(&self) -> Result<(), AuricError> {
        let mut tries: i32 = 5;
        let mut sleep_seconds: u64 = 1;
        let mut ssh_processes: usize = 0;
        while tries > 0 {
            ssh_processes = self.count_ssh_processes()?;
            if ssh_processes == 1 {
                return Ok(());
            }
            std::thread::sleep(std::time::Duration::from_secs(sleep_seconds));
            tries -= 1;
            sleep_seconds *= 2;
        }
        Err(AuricError::Subprocess(format!(
            "Timed out with {} ssh processes still present",
            ssh_processes
        )))
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            "rsync" => return Ok(AuricOperationMode::Rsync),
            "push" => return Ok(AuricOperationMode::RunAll),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        println!("{}", "Mounting sshfs...");
        self.sshfs_manager.mount()?;
        println!("{}", "Attaching loop device...");
        self.loop_manager.attach()?;
        println!("{}", "Finding newly attached loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Unlocking volume...");
        self.luks_manager.unlock(&loop_device)?;
        println!("{}", "Mounting volume...");
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        println!("{}", "Unmounting volume...");
        self.luks_manager.unmount()?;
        println!("{}", "Finding backing loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Locking volume...");
        self.luks_manager.lock(&loop_device)?;
        println!("{}", "Detaching loop device...");
        self.loop_manager.detach()?;
        println!("{}", "Unmounting sshfs...");
        self.sshfs_manager.unmount()?;
        println!("{}", "Waiting for ssh processes to disappear...");
        self.sshfs_manager.wait_until_just_one_ssh_process()
    }

    fn rsync(&self) -> Result<(), AuricError> {
        // I'm not pulling in the `home_dir` crate just to grab a value
        // that I suspect will be immutable for many years to come.
        // Likewise, the user mount directory is lazily hardcoded.
        let mut remote_directory = PathBuf::from(RUN_MEDIA_KALVIN);
        remote_directory.push(LOOP_DEVICE_PARTITION_LABEL);
        if !remote_directory.is_dir() {
            return Err(AuricError::Invocation(format!(
                "remote directory ``{}'' not mounted",
                remote_directory.display()
            )));
        }

        exec_rsync(&[
            "--delete",
            "-avPS",
            "--exclude=/.cache/",
            "--exclude=/.cargo/",
            "--exclude=/.local/share/",
            "--exclude=/.mozilla/",
            "--exclude=/.thunderbird/",
            "--exclude=/Downloads/.firefox-nightly/",
            "--exclude=/Downloads/.thunderbird-beta/",
            "--delete-excluded",
            "/home/kalvin/",
            remote_directory.to_str().unwrap(),
        ])
    }

    fn run_all(&self) -> Result<(), AuricError> {
        self.mount()?;
        self.rsync()?;
        self.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
            AuricOperationMode::Rsync => return self.rsync(),
            AuricOperationMode::RunAll => return self.run_all(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before><commit_msg>elf.section_header: additional workaround for 0-length sections<commit_after><|endoftext|>


<commit_before><commit_msg>Made compile.<commit_after><|endoftext|>


<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/. */

use controller::Controller;
use iron::{ Request, Response, IronResult };
use iron::headers::{ ContentType, AccessControlAllowOrigin };
use iron::status::Status;
use router::Router;
use service::{ Service, ServiceAdapter, ServiceProperties };
use std::sync::Arc;
use std::time::Duration;
use std::thread;
use upnp::{ UpnpListener, UpnpService };
use uuid::Uuid;

struct DummyService<T> {
    controller: T,
    properties: ServiceProperties,
    dont_kill: bool
}

impl<T: Controller> DummyService<T> {
    fn new(controller: T, id: u32) -> Self {
        debug!("Creating dummy service");
        let service_id = Uuid::new_v4().to_simple_string();
        DummyService {
            controller: controller.clone(),
            properties: ServiceProperties {
                id: service_id.clone(),
                name: "dummy service".to_owned(),
                description: "really nothing to see".to_owned(),
                http_url: controller.get_http_root_for_service(service_id.clone()),
                ws_url: controller.get_ws_root_for_service(service_id)
            },
            dont_kill: id % 3 == 0
        }
    }
}

impl<T: Controller> Service for DummyService<T> {
    fn get_properties(&self) -> ServiceProperties {
        self.properties.clone()
    }

    // Starts the service, it will just spawn a thread and send messages once
    // in a while.
    fn start(&self) {
        let props = self.properties.clone();
        let can_kill = !self.dont_kill;
        let controller = self.controller.clone();
        thread::spawn(move || {
            info!("Dummy service thread started");
            let mut i = 0;
            loop {
                thread::sleep(Duration::from_millis(1000));
                info!("Bip #{} from {}", i, props.id);
                i += 1;
                if i == 3 && can_kill {
                    break;
                }
            }
            controller.remove_service(props.id.to_string());
        });
    }

    fn stop(&self) {
        info!("Stopping dummy service");
    }

    // Processes a http request.
    fn process_request(&self, req: &mut Request) -> IronResult<Response> {
        let cmd = req.extensions.get::<Router>().unwrap().find("command").unwrap_or("");
        debug!("Dummy Adapter {} received command {}", req.url, cmd);
        let mut response = Response::with("{\"type\": \"device/dummy\"}".to_owned());
        response.status = Some(Status::Ok);
        response.headers.set(ContentType::json());
        response.headers.set(AccessControlAllowOrigin::Any);
        Ok(response)
    }
}

pub struct DummyAdapter<T> {
    name: String,
    controller: T
}

struct DummyListener;

impl DummyListener {
    pub fn new() -> Arc<Self> {
        Arc::new(DummyListener)
    }
}

impl UpnpListener for DummyListener {
    fn upnp_discover(&self, service: &UpnpService) -> bool {
        let owns = service.msearch.device_id == "uuid:2f402f80-da50-11e1-9b23-c86000788a05";
        if owns {
            debug!("Found Phillips Hue simulator upnp service: {:?}", service);
        }
        owns
    }
}

impl<T: Controller> DummyAdapter<T> {
    pub fn new(controller: T) -> Self {
        debug!("Creating dummy adapter");
        DummyAdapter { name: "DummyAdapter".to_owned(),
                       controller: controller }
    }
}

impl<T: Controller> ServiceAdapter for DummyAdapter<T> {
    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn start(&self) {
        let mut id = 0;
        let controller = self.controller.clone();
        {
            let listener = DummyListener::new();
            controller.get_upnp_manager().add_listener("dummy".to_owned(), listener);
        }
        thread::spawn(move || {
            controller.adapter_started("Dummy Service Adapter".to_owned());
            loop {
                thread::sleep(Duration::from_millis(2000));
                id += 1;
                let service = DummyService::new(controller.clone(), id);
                service.start();
                controller.add_service(Box::new(service));

                // Create at most 7 dummy services.
                if id == 7 {
                    break;
                }
            }
        });
    }

    fn stop(&self) {
        debug!("Stopping dummy adapter");
    }
}

<commit_msg>Removing obsolete dummy adapter<commit_after><|endoftext|>


<commit_before>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    };
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<commit_msg>Use full name for GType<commit_after>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    };
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

mod pipeline_struct;
mod index_struct;
mod perlin_document;

use pipeline_struct::generate_pipeline_struct;
use index_struct::generate_index_struct;
use perlin_document::generate_perlin_document_impl;

use proc_macro::TokenStream;

#[proc_macro_derive(PerlinDocument, attributes(ExternalId, NoPipe))]
pub fn perlin_document(input: TokenStream) -> TokenStream {
    // Standard procedure when it comes to custom derive
    // See https://doc.rust-lang.org/book/procedural-macros.html
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).expect("AST: WHAT!?");

    let gen = impl_perlin_document(&ast);
    gen.parse().expect("GEN: WHAT!?")
}

fn impl_perlin_document(ast: &syn::MacroInput) -> quote::Tokens {
    //We create three different things:
    //1. A struct that holds the pipelines
    // It looks like:
    // IdentPipes {
    //   text: Option<Pipeline<String, Ident>>
    // }
    let pipeline_struct = generate_pipeline_struct(ast);

    //2. A Wrapping struct that holds the indices as well as the pipes
    // Plus additional information
    // It looks like
    // IdentIndex {
    //    documents: Ident,
    //    pipes: IdentPipes,
    //    doc_counter: DocId,
    //    base_path: PathBuf,
    //    (external_ids: Vec<(DocId, TExternalId)>)
    //  }
    let index_struct = generate_index_struct(ast);

    //3. the impl of PerlinDocument for Ident
    let perlin_doc_impl = generate_perlin_document_impl(ast);

    let ident = &ast.ident;
    let index_ident = syn::Ident::from(format!("{}Index", ident).to_string());
    quote! {
        pub use self::perlin_impl::#index_ident;
        mod perlin_impl{
            use super::#ident;

            use std::path::{Path, PathBuf};
            use std::borrow::Cow;

            use perlin::document_index::Pipeline;
            use perlin_core::index::posting::DocId;

            #pipeline_struct

            #index_struct

            #perlin_doc_impl
        }
    }
}

// fn impl_perlin_document(ast: &syn::MacroInput) -> quote::Tokens {
//     let name = &ast.ident;
//     let index_name = syn::Ident::from(format!("{}Index", name).to_string());

//     if let syn::Body::Struct(ref variant_data) = ast.body {
//         let commit = commit(variant_data.fields());
//         let index_field = index_field(variant_data.fields());

//         let params = create_params(variant_data.fields());
//         let page_caches = create_page_caches(variant_data.fields());
//         let field_creations = create_field_creations(variant_data.fields());

//         quote! {
//             // pub struct #index_name {
//             //     documents: #name,

//             // }

//             pub mod perlin_document_impl {
//                 use super::*;

//                 use std::path::Path;

//                 impl #name {
//                     pub fn create(path: &Path #(,#params)*) -> Self {
//                         use perlin_core::page_manager::{RamPageCache, FsPageManager};
//                         #(#page_caches)*

//                         #name {
//                             #(#field_creations,)*
//                         }
//                     }
//                 }

//                 impl PerlinDocument for #name {
//                     fn commit(&mut self) {
//                         #(#commit)*
//                     }

//                     fn index_field(&mut self, doc_id: DocId, field_name: &str, field_contents: &str) {
//                         match field_name {
//                             #(#index_field,)*
//                             _ => {panic!("WHAT!?")}
//                         };
//                     }
//                 }
//             }
//         }
//     } else {
//         panic!("PerlinDocument is only implemented for structs not enums!");
//     }
// }

fn create_params(fields: &[syn::Field]) -> Vec<quote::Tokens> {
    let mut result = Vec::new();

    for field in fields {
        let ident = &field.ident;
        let gen_params = get_generics_from_field(&field.ty);

        result.push(quote!(#ident: Option<Pipeline#gen_params>));
    }
    result
}

fn get_generics_from_field(field: &syn::Ty) -> quote::Tokens {
    if let &syn::Ty::Path(_, ref path) = field {
        for segment in &path.segments {
            if segment.ident == "Field" {
                let params = &segment.parameters;;
                return quote!(#params);
            }
        }
    }
    panic!("NO FIELD FOUND!");
}

fn commit(fields: &[syn::Field]) -> Vec<quote::Tokens> {
    let mut result = Vec::new();

    for field in fields {
        let ident = &field.ident;
        result.push(quote! {
            self.#ident.index.commit();
        });
    }
    result
}

fn index_field(fields: &[syn::Field]) -> Vec<quote::Tokens> {
    let mut result = Vec::new();

    for field in fields {
        let ident = &field.ident;
        result.push(quote! {
            stringify!(#ident) =>
            {
                let pipe = if let Some(ref pipeline) = self.#ident.pipeline
                {
                    pipeline()
                }
                else {
                    panic!(concat!("No pipeline found for ", stringify!(#ident)))
                };
                pipe(doc_id, self, field_contents);
            }});
    }
    result
}
<commit_msg>perlin-derive cleanup<commit_after>extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

mod pipeline_struct;
mod index_struct;
mod perlin_document;

use pipeline_struct::generate_pipeline_struct;
use index_struct::generate_index_struct;
use perlin_document::generate_perlin_document_impl;

use proc_macro::TokenStream;

#[proc_macro_derive(PerlinDocument, attributes(ExternalId, NoPipe))]
pub fn perlin_document(input: TokenStream) -> TokenStream {
    // Standard procedure when it comes to custom derive
    // See https://doc.rust-lang.org/book/procedural-macros.html
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).expect("AST: WHAT!?");

    let gen = impl_perlin_document(&ast);
    gen.parse().expect("GEN: WHAT!?")
}

fn impl_perlin_document(ast: &syn::MacroInput) -> quote::Tokens {
    //We create three different things:
    //1. A struct that holds the pipelines
    // It looks like:
    // IdentPipes {
    //   text: Option<Pipeline<String, Ident>>
    // }
    let pipeline_struct = generate_pipeline_struct(ast);

    //2. A Wrapping struct that holds the indices as well as the pipes
    // Plus additional information
    // It looks like
    // IdentIndex {
    //    documents: Ident,
    //    pipes: IdentPipes,
    //    doc_counter: DocId,
    //    base_path: PathBuf,
    //    (external_ids: Vec<(DocId, TExternalId)>)
    //  }
    let index_struct = generate_index_struct(ast);

    //3. the impl of PerlinDocument for Ident
    let perlin_doc_impl = generate_perlin_document_impl(ast);

    let ident = &ast.ident;
    let index_ident = syn::Ident::from(format!("{}Index", ident).to_string());
    quote! {
        pub use self::perlin_impl::#index_ident;
        mod perlin_impl{
            use super::#ident;

            use std::path::{Path, PathBuf};
            use std::borrow::Cow;

            use perlin::document_index::Pipeline;
            use perlin_core::index::posting::DocId;

            #pipeline_struct

            #index_struct

            #perlin_doc_impl
        }
    }
}
<|endoftext|>


<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/. */

use eutil::fptr_is_null;
use libc::{c_int};
use std::mem;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_list_t,cef_string_t};

//cef_string_list

#[no_mangle]
extern "C" fn cef_string_list_alloc() -> *mut cef_string_list_t {
    unsafe {
         let lt: Box<Vec<*mut cef_string_t>> = box vec!();
         mem::transmute(lt)
    }
}
<commit_msg>embedding: cef_string_list_size()<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/. */

use eutil::fptr_is_null;
use libc::{c_int};
use std::mem;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_list_t,cef_string_t};

//cef_string_list

#[no_mangle]
extern "C" fn cef_string_list_alloc() -> *mut cef_string_list_t {
    unsafe {
         let lt: Box<Vec<*mut cef_string_t>> = box vec!();
         mem::transmute(lt)
    }
}

#[no_mangle]
extern "C" fn cef_string_list_size(lt: *const cef_string_list_t) -> c_int {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return 0; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        v.len() as c_int
    }
}
<|endoftext|>


<commit_before>

use syscalls::gui::KeyCode;

pub enum Action<'a>
{
	Puts(&'a str),
	Backspace,
	Delete,
}

#[derive(Default)]
pub struct InputStack
{
	//	caps: bool,	// Caps can DIAF
	shift: u8,
	ctrl: u8,
	alt: u8,
	altgr: bool,

	ime_ofs: u8,
	ime_val: u32,

	buffer: String,
}

impl InputStack
{
	pub fn new() -> InputStack {
		InputStack::default()
	}
	pub fn handle_key<F: FnOnce(Action)>(&mut self, release: bool, keycode: u8, puts: F) -> Option<String>
	{
		kernel_log!("handle_key: (release={},keycode={},...)", release, keycode);
		if release {
			match KeyCode::from(keycode)
			{
			KeyCode::Return | KeyCode::KpEnter => Some( ::std::mem::replace(&mut self.buffer, String::new()) ),
			KeyCode::LeftShift => { self.shift &= !1; None },
			KeyCode::RightShift => { self.shift &= !2; None },
			KeyCode::Backsp => {
				kernel_log!("Backspace");
				puts(Action::Backspace);
				self.buffer.pop();	// TODO: Pop a grapheme, not just a char
				kernel_log!("- self.buffer = {:?}", self.buffer);
				None
				},
			kc @ _ => {
				let val = self.get_string(kc);
				kernel_log!("val={:?}", val);
				if val != "" {
					puts(Action::Puts(val));
					self.buffer.push_str(val);
				}
				None
				},
			}
		}
		else {
			match KeyCode::from(keycode)
			{
			KeyCode::LeftShift => { self.shift |= 1; },
			KeyCode::RightShift => { self.shift |= 2; },
			_ => {},
			}
			None
		}
	}

	fn get_string(&self, keycode: KeyCode) -> &'static str
	{
		macro_rules! alpha { ($s:ident: $lower:expr, $upper:expr) => { if $s.upper() { $upper } else {$lower} }; }
		match keycode
		{
		KeyCode::A => alpha! { self: "a", "A" },
		KeyCode::B => alpha! { self: "b", "B" },
		KeyCode::C => alpha! { self: "c", "C" },
		KeyCode::D => alpha! { self: "d", "D" },
		KeyCode::E => alpha! { self: "e", "E" },
		KeyCode::F => alpha! { self: "f", "F" },
		KeyCode::G => alpha! { self: "g", "G" },
		KeyCode::H => alpha! { self: "h", "H" },
		KeyCode::I => alpha! { self: "i", "I" },
		KeyCode::J => alpha! { self: "j", "J" },
		KeyCode::K => alpha! { self: "k", "K" },
		KeyCode::L => alpha! { self: "l", "L" },
		KeyCode::M => alpha! { self: "m", "M" },
		KeyCode::N => alpha! { self: "n", "N" },
		KeyCode::O => alpha! { self: "o", "O" },
		KeyCode::P => alpha! { self: "p", "P" },
		KeyCode::Q => alpha! { self: "q", "Q" },
		KeyCode::R => alpha! { self: "r", "R" },
		KeyCode::S => alpha! { self: "s", "S" },
		KeyCode::T => alpha! { self: "t", "T" },
		KeyCode::U => alpha! { self: "u", "U" },
		KeyCode::V => alpha! { self: "v", "V" },
		KeyCode::W => alpha! { self: "w", "W" },
		KeyCode::X => alpha! { self: "x", "X" },
		KeyCode::Y => alpha! { self: "y", "Y" },
		KeyCode::Z => alpha! { self: "z", "Z" },
		KeyCode::Space => " ",
		_ => "",
		}

	}

	fn upper(&self) -> bool { self.shift() }
	fn shift(&self) -> bool { self.shift != 0 }
}

<commit_msg>Usermode/simple_console - Extra keys to key map<commit_after>

use syscalls::gui::KeyCode;

pub enum Action<'a>
{
	Puts(&'a str),
	Backspace,
	Delete,
}

#[derive(Default)]
pub struct InputStack
{
	//	caps: bool,	// Caps can DIAF
	shift: u8,
	ctrl: u8,
	alt: u8,
	altgr: bool,

	ime_ofs: u8,
	ime_val: u32,

	buffer: String,
}

impl InputStack
{
	pub fn new() -> InputStack {
		InputStack::default()
	}
	pub fn handle_key<F: FnOnce(Action)>(&mut self, release: bool, keycode: u8, puts: F) -> Option<String>
	{
		kernel_log!("handle_key: (release={},keycode={},...)", release, keycode);
		if release {
			match KeyCode::from(keycode)
			{
			KeyCode::Return | KeyCode::KpEnter => Some( ::std::mem::replace(&mut self.buffer, String::new()) ),
			KeyCode::LeftShift => { self.shift &= !1; None },
			KeyCode::RightShift => { self.shift &= !2; None },
			KeyCode::Backsp => {
				kernel_log!("Backspace");
				puts(Action::Backspace);
				self.buffer.pop();	// TODO: Pop a grapheme, not just a char
				kernel_log!("- self.buffer = {:?}", self.buffer);
				None
				},
			kc @ _ => {
				let val = self.get_string(kc);
				kernel_log!("val={:?}", val);
				if val != "" {
					puts(Action::Puts(val));
					self.buffer.push_str(val);
				}
				None
				},
			}
		}
		else {
			match KeyCode::from(keycode)
			{
			KeyCode::LeftShift => { self.shift |= 1; },
			KeyCode::RightShift => { self.shift |= 2; },
			_ => {},
			}
			None
		}
	}

	fn get_string(&self, keycode: KeyCode) -> &'static str
	{
		macro_rules! shift { ($s:ident: $lower:expr, $upper:expr) => { if $s.shift() { $upper } else {$lower} }; }
		macro_rules! alpha { ($s:ident: $lower:expr, $upper:expr) => { if $s.upper() { $upper } else {$lower} }; }
		match keycode
		{
		KeyCode::A => alpha!(self: "a", "A"),
		KeyCode::B => alpha!(self: "b", "B"),
		KeyCode::C => alpha!(self: "c", "C"),
		KeyCode::D => alpha!(self: "d", "D"),
		KeyCode::E => alpha!(self: "e", "E"),
		KeyCode::F => alpha!(self: "f", "F"),
		KeyCode::G => alpha!(self: "g", "G"),
		KeyCode::H => alpha!(self: "h", "H"),
		KeyCode::I => alpha!(self: "i", "I"),
		KeyCode::J => alpha!(self: "j", "J"),
		KeyCode::K => alpha!(self: "k", "K"),
		KeyCode::L => alpha!(self: "l", "L"),
		KeyCode::M => alpha!(self: "m", "M"),
		KeyCode::N => alpha!(self: "n", "N"),
		KeyCode::O => alpha!(self: "o", "O"),
		KeyCode::P => alpha!(self: "p", "P"),
		KeyCode::Q => alpha!(self: "q", "Q"),
		KeyCode::R => alpha!(self: "r", "R"),
		KeyCode::S => alpha!(self: "s", "S"),
		KeyCode::T => alpha!(self: "t", "T"),
		KeyCode::U => alpha!(self: "u", "U"),
		KeyCode::V => alpha!(self: "v", "V"),
		KeyCode::W => alpha!(self: "w", "W"),
		KeyCode::X => alpha!(self: "x", "X"),
		KeyCode::Y => alpha!(self: "y", "Y"),
		KeyCode::Z => alpha!(self: "z", "Z"),

		KeyCode::SquareOpen  => shift!(self: "[", "{"),
		KeyCode::SquareClose => shift!(self: "[", "{"),
		KeyCode::Backslash   => shift!(self: "\\","|"),
		KeyCode::Semicolon => shift!(self: ";", ":"),
		KeyCode::Quote     => shift!(self: "'", "\""),
		KeyCode::Comma  => shift!(self: ",", "<"),
		KeyCode::Period => shift!(self: ".", ">"),
		KeyCode::Slash  => shift!(self: "/", "?"),

		KeyCode::Kb1 => shift!(self: "1", "!"),
		KeyCode::Kb2 => shift!(self: "2", "@"),
		KeyCode::Kb3 => shift!(self: "3", "#"),
		KeyCode::Kb4 => shift!(self: "4", "$"),
		KeyCode::Kb5 => shift!(self: "5", "%"),
		KeyCode::Kb6 => shift!(self: "6", "^"),
		KeyCode::Kb7 => shift!(self: "7", "&"),
		KeyCode::Kb8 => shift!(self: "8", "*"),
		KeyCode::Kb9 => shift!(self: "9", "("),
		KeyCode::Kb0 => shift!(self: "0", ")"),
		KeyCode::Minus  => shift!(self: "-", "_"),
		KeyCode::Equals => shift!(self: "=", "+"),

		KeyCode::Space => " ",
		_ => "",
		}

	}

	fn upper(&self) -> bool { self.shift() }
	fn shift(&self) -> bool { self.shift != 0 }
}

<|endoftext|>


<commit_before>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use std::cell::RefCell;
use std::rc::Rc;

use regex::Regex;

use super::assets::MemoryCache;
use super::FileBuilder;

fn assert_build(input: &str) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    b.eval_string(input).unwrap();
    if !b.assert_collector.success {
        assert!(false, b.assert_collector.failures);
    }
}

fn assert_build_failure(input: &str, expect: Vec<Regex>) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    let err = b.eval_string(input);
    match err {
        Ok(_) => {
            for r in expect.iter() {
                if !b.assert_collector.success {
                    if let None = r.find(&b.assert_collector.failures) {
                        assert!(
                            false,
                            "[{}] was not found in Assertion Failures:\n{}",
                            r, b.assert_collector.failures
                        );
                    }
                } else {
                    assert!(false, "Building input Did not panic!");
                }
            }
        }
        Err(ref err) => {
            for r in expect.iter() {
                let stack_trace = format!("{}", err);
                // Look for each expect to match the string.
                if let None = r.find(&stack_trace) {
                    assert!(
                        false,
                        "[{}] was not found in stacktrace:\n{}",
                        r, stack_trace
                    );
                }
            }
        }
    }
}

#[test]
fn test_tuples() {
    assert_build(include_str!("../../integration_tests/tuple_test.ucg"));
}

#[test]
fn test_lists() {
    assert_build(include_str!("../../integration_tests/list_test.ucg"));
}

#[test]
fn test_comparisons() {
    assert_build(include_str!("../../integration_tests/comparisons_test.ucg"));
}

#[test]
fn test_funcs() {
    assert_build(include_str!("../../integration_tests/macros_test.ucg"));
}

#[test]
fn test_modules() {
    assert_build(include_str!("../../integration_tests/modules_test.ucg"));
}

#[test]
fn test_selectors() {
    assert_build(include_str!("../../integration_tests/selectors_test.ucg"));
}

#[test]
fn test_empty_value() {
    assert_build(include_str!("../../integration_tests/empty_test.ucg"));
}

#[test]
fn test_select_expressions() {
    assert_build(include_str!(
        "../../integration_tests/select_expressions_test.ucg"
    ));
}

#[test]
fn test_binary_operator_precedence() {
    assert_build(include_str!(
        "../../integration_tests/operator_precedence_test.ucg"
    ));
}

#[test]
fn test_functional_processing() {
    assert_build(include_str!(
        "../../integration_tests/functional_processing_test.ucg"
    ));
}

#[test]
fn test_concatenation() {
    assert_build(include_str!(
        "../../integration_tests/concatenation_test.ucg"
    ));
}

#[test]
fn test_format() {
    assert_build(include_str!("../../integration_tests/format_test.ucg"));
}

#[test]
fn test_type_checks() {
    assert_build(include_str!("../../integration_tests/types_test.ucg"));
}

#[test]
#[should_panic(expected = "UserDefined")]
fn test_declarative_failures_are_user_defined() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "Caused By:\n\tI am a failure!")]
fn test_declarative_failures_are_caused_by_msg() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "1 is a failure!")]
fn test_declarative_failures_can_with_format_expr() {
    assert_build("fail \"@ is a failure!\" % (1);");
}

#[test]
fn test_assert_just_keyword_compile_failures() {
    assert_build_failure(
        "assert ",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected Expression: at <eval> line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_compile_failures() {
    assert_build_failure(
        "assert {",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected \(\}\) but got \(\): at <eval> line: 1, column: 9").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_ok_compile_failures() {
    assert_build_failure(
        "assert {};",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_ok_compile_failures() {
    assert_build_failure(
        "assert { ok = 1, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, desc = 1 };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_import_missing_path_compile_failure() {
    assert_build_failure(
        "import ;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_import_path_not_a_string_compile_failure() {
    assert_build_failure(
        "import 1;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_binary_operator_missing_operand_compile_failure() {
    assert_build_failure(
        "1 +",
        vec![
            Regex::new(r"Abort while parsing operator expression: at <eval> line: 1, column: 1")
                .unwrap(),
            Regex::new(r"Missing operand for binary expression: at <eval> line: 1, column: 4")
                .unwrap(),
        ],
    )
}

#[test]
fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 + \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 - \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 * \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 / \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 > \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 < \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 <= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 >= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 == \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_compile_failure() {
    assert_build_failure(
        "{;",
        vec![
            Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 2").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_eq_compile_failure() {
    assert_build_failure(
        "{foo",
        vec![
            Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_value_compile_failure() {
    assert_build_failure(
        "{foo=",
        vec![
            Regex::new(r"Expected Expression").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_missing_expr_compile_error() {
    assert_build_failure(
        "\"foo\" %",
        vec![
            Regex::new(r"Expected format arguments").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_unclosed_parens_compile_failure() {
    assert_build_failure(
        "\"foo\" % (1",
        vec![
            Regex::new(r"Expected \(\)\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}
<commit_msg>MAINT: More error test coverage for list parsing.<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use std::cell::RefCell;
use std::rc::Rc;

use regex::Regex;

use super::assets::MemoryCache;
use super::FileBuilder;

fn assert_build(input: &str) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    b.eval_string(input).unwrap();
    if !b.assert_collector.success {
        assert!(false, b.assert_collector.failures);
    }
}

fn assert_build_failure(input: &str, expect: Vec<Regex>) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    let err = b.eval_string(input);
    match err {
        Ok(_) => {
            for r in expect.iter() {
                if !b.assert_collector.success {
                    if let None = r.find(&b.assert_collector.failures) {
                        assert!(
                            false,
                            "[{}] was not found in Assertion Failures:\n{}",
                            r, b.assert_collector.failures
                        );
                    }
                } else {
                    assert!(false, "Building input Did not panic!");
                }
            }
        }
        Err(ref err) => {
            for r in expect.iter() {
                let stack_trace = format!("{}", err);
                // Look for each expect to match the string.
                if let None = r.find(&stack_trace) {
                    assert!(
                        false,
                        "[{}] was not found in stacktrace:\n{}",
                        r, stack_trace
                    );
                }
            }
        }
    }
}

#[test]
fn test_tuples() {
    assert_build(include_str!("../../integration_tests/tuple_test.ucg"));
}

#[test]
fn test_lists() {
    assert_build(include_str!("../../integration_tests/list_test.ucg"));
}

#[test]
fn test_comparisons() {
    assert_build(include_str!("../../integration_tests/comparisons_test.ucg"));
}

#[test]
fn test_funcs() {
    assert_build(include_str!("../../integration_tests/macros_test.ucg"));
}

#[test]
fn test_modules() {
    assert_build(include_str!("../../integration_tests/modules_test.ucg"));
}

#[test]
fn test_selectors() {
    assert_build(include_str!("../../integration_tests/selectors_test.ucg"));
}

#[test]
fn test_empty_value() {
    assert_build(include_str!("../../integration_tests/empty_test.ucg"));
}

#[test]
fn test_select_expressions() {
    assert_build(include_str!(
        "../../integration_tests/select_expressions_test.ucg"
    ));
}

#[test]
fn test_binary_operator_precedence() {
    assert_build(include_str!(
        "../../integration_tests/operator_precedence_test.ucg"
    ));
}

#[test]
fn test_functional_processing() {
    assert_build(include_str!(
        "../../integration_tests/functional_processing_test.ucg"
    ));
}

#[test]
fn test_concatenation() {
    assert_build(include_str!(
        "../../integration_tests/concatenation_test.ucg"
    ));
}

#[test]
fn test_format() {
    assert_build(include_str!("../../integration_tests/format_test.ucg"));
}

#[test]
fn test_type_checks() {
    assert_build(include_str!("../../integration_tests/types_test.ucg"));
}

#[test]
#[should_panic(expected = "UserDefined")]
fn test_declarative_failures_are_user_defined() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "Caused By:\n\tI am a failure!")]
fn test_declarative_failures_are_caused_by_msg() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "1 is a failure!")]
fn test_declarative_failures_can_with_format_expr() {
    assert_build("fail \"@ is a failure!\" % (1);");
}

#[test]
fn test_assert_just_keyword_compile_failures() {
    assert_build_failure(
        "assert ",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected Expression: at <eval> line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_compile_failures() {
    assert_build_failure(
        "assert {",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected \(\}\) but got \(\): at <eval> line: 1, column: 9").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_ok_compile_failures() {
    assert_build_failure(
        "assert {};",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_ok_compile_failures() {
    assert_build_failure(
        "assert { ok = 1, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, desc = 1 };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_import_missing_path_compile_failure() {
    assert_build_failure(
        "import ;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_import_path_not_a_string_compile_failure() {
    assert_build_failure(
        "import 1;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_binary_operator_missing_operand_compile_failure() {
    assert_build_failure(
        "1 +",
        vec![
            Regex::new(r"Abort while parsing operator expression: at <eval> line: 1, column: 1")
                .unwrap(),
            Regex::new(r"Missing operand for binary expression: at <eval> line: 1, column: 4")
                .unwrap(),
        ],
    )
}

#[test]
fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 + \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 - \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 * \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 / \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 > \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 < \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 <= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 >= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 == \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_compile_failure() {
    assert_build_failure(
        "{;",
        vec![
            Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 2").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_eq_compile_failure() {
    assert_build_failure(
        "{foo",
        vec![
            Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_value_compile_failure() {
    assert_build_failure(
        "{foo=",
        vec![
            Regex::new(r"Expected Expression").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_missing_expr_compile_error() {
    assert_build_failure(
        "\"foo\" %",
        vec![
            Regex::new(r"Expected format arguments").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_unclosed_parens_compile_failure() {
    assert_build_failure(
        "\"foo\" % (1",
        vec![
            Regex::new(r"Expected \(\)\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_list_unclosed_bracket_compile_failure() {
    assert_build_failure(
        "[1",
        vec![
            Regex::new(r"Expected \(\]\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 3").unwrap(),
        ],
    )
}
<|endoftext|>


<commit_before>use std::pin::Pin;
use std::task::{Context, Poll};

#[cfg(not(debug_assertions))]
use actix_http::http::HeaderValue;
use actix_service::{Service, Transform};
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
use futures::future::{ok, Ready};
use futures::Future;
#[cfg(not(debug_assertions))]
use reqwest::header::{CACHE_CONTROL, EXPIRES};

use chrono::Duration;
#[cfg(not(debug_assertions))]
use chrono::Utc;

pub struct Caching {
    pub duration: Duration,
}

impl<S, B> Transform<S> for Caching
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = CachingMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CachingMiddleware {
            service,
            duration: self.duration,
        })
    }
}

pub struct CachingMiddleware<S> {
    service: S,
    #[allow(dead_code)]
    duration: Duration,
}

impl<S, B> Service for CachingMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(ctx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let fut = self.service.call(req);
        #[cfg(not(debug_assertions))]
        let duration = self.duration.clone();

        #[cfg(not(debug_assertions))]
        return Box::pin(async move {
            let dt = Utc::now() + duration;
            let mut res = fut.await?;
            let headers = res.headers_mut();
            let value = dt.format("%a, %d %b %Y %H:%M:%S GMT").to_string();

            headers.append(EXPIRES, HeaderValue::from_str(&value).unwrap());
            headers.append(CACHE_CONTROL, HeaderValue::from_static("public,max-age=31536000"));
            return Ok(res);
        });

        #[cfg(debug_assertions)]
        return Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        });
    }
}
<commit_msg>cache `duration` seconds, not a whole year<commit_after>use std::pin::Pin;
use std::task::{Context, Poll};

#[cfg(not(debug_assertions))]
use actix_http::http::HeaderValue;
use actix_service::{Service, Transform};
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
use futures::future::{ok, Ready};
use futures::Future;
#[cfg(not(debug_assertions))]
use reqwest::header::{CACHE_CONTROL, EXPIRES};

use chrono::Duration;
#[cfg(not(debug_assertions))]
use chrono::Utc;

pub struct Caching {
    pub duration: Duration,
}

impl<S, B> Transform<S> for Caching
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = CachingMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CachingMiddleware {
            service,
            duration: self.duration,
        })
    }
}

pub struct CachingMiddleware<S> {
    service: S,
    #[allow(dead_code)]
    duration: Duration,
}

impl<S, B> Service for CachingMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(ctx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let fut = self.service.call(req);
        #[cfg(not(debug_assertions))]
        let duration = self.duration.clone();

        #[cfg(not(debug_assertions))]
        return Box::pin(async move {
            let dt = Utc::now() + duration;
            let mut res = fut.await?;
            let headers = res.headers_mut();
            let value = dt.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
            let duration_seconds = format!("public,max-age={}", duration.num_seconds());

            headers.append(EXPIRES, HeaderValue::from_str(&value).unwrap());
            headers.append(CACHE_CONTROL, HeaderValue::from_str(&duration_seconds).unwrap());
            return Ok(res);
        });

        #[cfg(debug_assertions)]
        return Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        });
    }
}
<|endoftext|>