<commit_before>use std::collections::HashMap;
use std::time::Instant;
use nix::Error as NixErr;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::Errno;
use nix::Result;
use nix::unistd::Pid;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use config::Config;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: Instant,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: Instant,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
    /// An error occurred that indicates no future runs will succeed such as
    /// PIE issues in OS.
    Abort,
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable | TestState::Abort => true,
            _ => false,
        }
    }

    /// Convenience function for creating start states
    fn start_state() -> TestState {
        TestState::Start{start_time: Instant::now()}
    }

    /// Convenience function for creating wait states
    fn wait_state() -> TestState {
        TestState::Waiting{start_time: Instant::now()}
    }

    /// Updates the state machine state
    pub fn step<T:StateData>(self, data: &mut T, config: &Config) -> TestState {
        match self {
            TestState::Start{start_time} => {
                if let Some(s) = data.start() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out when starting test");
                    TestState::Timeout
                } else {
                    TestState::Start{start_time:start_time}
                }
            },
            TestState::Initialise => {
                data.init()
            },
            TestState::Waiting{start_time} => {
                if let Some(s) =data.wait() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out waiting for test response");
                    TestState::Timeout
                } else {
                    TestState::Waiting{start_time}
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::start_state(), data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    pub error_message: Option<String>,
    /// Thread count. Hopefully getting rid of in future
    thread_count: isize,
    /// Used to show anomalies noticed so hit counts disabled
    force_disable_hit_count: bool
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(_) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if trace_children(self.current).is_err() {
            println!("Failed to trace child threads");
        }
        let mut instrumented = true;
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) if e==NixErr::Sys(Errno::EIO) => {
                        println!("ERROR: Tarpaulin cannot find code addresses \
                                  check that pie is disabled for your linker. \
                                  If linking with gcc try adding -C link-args=-no-pie \
                                  to your rust flags");
                        instrumented = false;
                        break;
                    }
                    Err(_) => {
                        self.error_message = Some("Failed to instrument test executable".to_string());
                    },
                }
            }
        }
        if !instrumented {
            TestState::Abort
        }
        else if let Ok(_) = continue_exec(self.parent, None) {
            TestState::wait_state()
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(_) => {
                self.error_message = Some("An error occurred while waiting for response from test".to_string());
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(_,_,_) => {
                match self.handle_ptrace_event() {
                    Ok(s) => s,
                    Err(e) => {
                        let msg = format!("Error occurred when handling ptrace event: {}", e);
                        self.error_message = Some(msg);
                        TestState::Unrecoverable
                    },
                }
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                match self.collect_coverage_data() {
                    Ok(s) => s,
                    Err(e) => {
                        self.error_message = Some(format!("Error when collecting coverage: {}", e));
                        TestState::Unrecoverable
                    }
                }
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                if continue_exec(child, None).is_ok() {
                    TestState::wait_state()
                } else {
                    self.error_message = Some("Error processing SIGSTOP".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::wait_state()
            },
            WaitStatus::Signaled(_,_,_) => {
                if let Ok(s) = self.handle_signaled() {
                    s
                } else {
                    self.error_message = Some("Error attempting to handle tarpaulin being signaled".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Exited(child, _) => {
                for (_, ref mut value) in self.breakpoints.iter_mut() {
                    value.thread_killed(child);
                }
                if child == self.parent {
                    TestState::End
                } else {
                    // Process may have already been destroyed. This is just incase
                    let _ = continue_exec(self.parent, None);
                    TestState::wait_state()
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {
        if let Some(ref e) = self.error_message {
            println!("An error occurred during run. Coverage results may be inaccurate.");
            println!("{}", e);
        }
    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None,
            thread_count: 0,
            force_disable_hit_count: !config.no_count
        }
    }

    fn handle_ptrace_event(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_CLONE) => {
                if get_event_data(child).is_ok() {
                    self.thread_count += 1;
                    continue_exec(child, None)?;
                    Ok(TestState::wait_state())
                } else {
                    self.error_message = Some("Error occurred upon test executable thread creation".to_string());
                    Ok(TestState::Unrecoverable)
                }
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC) => {
                detach_child(child)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT) => {
                self.thread_count -= 1;
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => Ok(TestState::Unrecoverable),
        }
    }

    fn collect_coverage_data(&mut self) -> Result<TestState> {
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (self.thread_count < 2);
                if !enable && self.force_disable_hit_count {
                    println!("Code is mulithreaded, disabling hit count");
                    println!("Results may be improved by using the '--no-count' option when running tarpaulin");
                    self.force_disable_hit_count = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None)?;
            }
        } else {
            continue_exec(self.current, None)?;
        }
        Ok(TestState::wait_state())
    }

    fn handle_signaled(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => {
                self.error_message = Some("Unexpected stop".to_string());
                Ok(TestState::Unrecoverable)
            },
        }
    }
}
<commit_msg>Refactored handle_ptrace function<commit_after>use std::collections::HashMap;
use std::time::Instant;
use nix::Error as NixErr;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::sys::signal::Signal;
use nix::Errno;
use nix::Result;
use nix::unistd::Pid;
use nix::libc::c_int;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use config::Config;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: Instant,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: Instant,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
    /// An error occurred that indicates no future runs will succeed such as
    /// PIE issues in OS.
    Abort,
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable | TestState::Abort => true,
            _ => false,
        }
    }

    /// Convenience function for creating start states
    fn start_state() -> TestState {
        TestState::Start{start_time: Instant::now()}
    }

    /// Convenience function for creating wait states
    fn wait_state() -> TestState {
        TestState::Waiting{start_time: Instant::now()}
    }

    /// Updates the state machine state
    pub fn step<T:StateData>(self, data: &mut T, config: &Config) -> TestState {
        match self {
            TestState::Start{start_time} => {
                if let Some(s) = data.start() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out when starting test");
                    TestState::Timeout
                } else {
                    TestState::Start{start_time:start_time}
                }
            },
            TestState::Initialise => {
                data.init()
            },
            TestState::Waiting{start_time} => {
                if let Some(s) =data.wait() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out waiting for test response");
                    TestState::Timeout
                } else {
                    TestState::Waiting{start_time}
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::start_state(), data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    pub error_message: Option<String>,
    /// Thread count. Hopefully getting rid of in future
    thread_count: isize,
    /// Used to show anomalies noticed so hit counts disabled
    force_disable_hit_count: bool
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(_) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if trace_children(self.current).is_err() {
            println!("Failed to trace child threads");
        }
        let mut instrumented = true;
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) if e==NixErr::Sys(Errno::EIO) => {
                        println!("ERROR: Tarpaulin cannot find code addresses \
                                  check that pie is disabled for your linker. \
                                  If linking with gcc try adding -C link-args=-no-pie \
                                  to your rust flags");
                        instrumented = false;
                        break;
                    }
                    Err(_) => {
                        self.error_message = Some("Failed to instrument test executable".to_string());
                    },
                }
            }
        }
        if !instrumented {
            TestState::Abort
        }
        else if let Ok(_) = continue_exec(self.parent, None) {
            TestState::wait_state()
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(_) => {
                self.error_message = Some("An error occurred while waiting for response from test".to_string());
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(c,s,e) => {
                match self.handle_ptrace_event(c, s, e) {
                    Ok(s) => s,
                    Err(e) => {
                        let msg = format!("Error occurred when handling ptrace event: {}", e);
                        self.error_message = Some(msg);
                        TestState::Unrecoverable
                    },
                }
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                match self.collect_coverage_data() {
                    Ok(s) => s,
                    Err(e) => {
                        self.error_message = Some(format!("Error when collecting coverage: {}", e));
                        TestState::Unrecoverable
                    }
                }
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                if continue_exec(child, None).is_ok() {
                    TestState::wait_state()
                } else {
                    self.error_message = Some("Error processing SIGSTOP".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::wait_state()
            },
            WaitStatus::Signaled(_,_,_) => {
                if let Ok(s) = self.handle_signaled() {
                    s
                } else {
                    self.error_message = Some("Error attempting to handle tarpaulin being signaled".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Exited(child, _) => {
                for (_, ref mut value) in self.breakpoints.iter_mut() {
                    value.thread_killed(child);
                }
                if child == self.parent {
                    TestState::End
                } else {
                    // Process may have already been destroyed. This is just incase
                    let _ = continue_exec(self.parent, None);
                    TestState::wait_state()
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {
        if let Some(ref e) = self.error_message {
            println!("An error occurred during run. Coverage results may be inaccurate.");
            println!("{}", e);
        }
    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None,
            thread_count: 0,
            force_disable_hit_count: !config.no_count
        }
    }

    fn handle_ptrace_event(&mut self, child: Pid, signal: Signal, event: c_int) -> Result<TestState> {
        use nix::sys::signal::*;
        if signal == SIGTRAP {
            match event {
                PTRACE_EVENT_CLONE => {
                    if get_event_data(child).is_ok() {
                        self.thread_count += 1;
                        continue_exec(child, None)?;
                        Ok(TestState::wait_state())
                    } else {
                        self.error_message = Some("Error occurred upon test executable thread creation".to_string());
                        Ok(TestState::Unrecoverable)
                    }
                },
                PTRACE_EVENT_FORK | PTRACE_EVENT_VFORK => {
                    continue_exec(child, None)?;
                    Ok(TestState::wait_state())
                },
                PTRACE_EVENT_EXEC => {
                    detach_child(child)?;
                    Ok(TestState::wait_state())
                },
                PTRACE_EVENT_EXIT => {
                    self.thread_count -= 1;
                    continue_exec(child, None)?;
                    Ok(TestState::wait_state())
                },
                _ => Ok(TestState::Unrecoverable)
            }
        } else {
            self.error_message = Some("Unexpected ptrace event".to_string());
            Ok(TestState::Unrecoverable)
        }
    }

    fn collect_coverage_data(&mut self) -> Result<TestState> {
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (self.thread_count < 2);
                if !enable && self.force_disable_hit_count {
                    println!("Code is mulithreaded, disabling hit count");
                    println!("Results may be improved by using the '--no-count' option when running tarpaulin");
                    self.force_disable_hit_count = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None)?;
            }
        } else {
            continue_exec(self.current, None)?;
        }
        Ok(TestState::wait_state())
    }

    fn handle_signaled(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => {
                self.error_message = Some("Unexpected stop".to_string());
                Ok(TestState::Unrecoverable)
            },
        }
    }
}
<|endoftext|>


<commit_before>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use DocBuilderOptions;
use DocBuilder;

const DAEMON_PID_FILE_PATH: &'static str = "/var/run/cratesfyi.pid";

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect("Environment variable not found");
    }

    // check paths once
    opts().check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(DAEMON_PID_FILE_PATH).expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every 5 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(300));
            debug!("Checking new crates");
            let doc_builder = DocBuilder::new(opts());
            if let Err(e) = doc_builder.get_new_crates() {
                error!("Failed to get new crates: {}", e);
            }
        }
    });

    // build new crates every 3 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(180));

            let mut opts = opts();
            opts.skip_if_exists = true;

            // check lock file
            if opts.prefix.join("cratesfyi.lock").exists() {
                warn!("Lock file exits, skipping building new crates");
            }

            let mut doc_builder = DocBuilder::new(opts);
            if let Err(e) = doc_builder.load_cache() {
                error!("Failed to load cache: {}", e);
                continue;
            }

            debug!("Building new crates");
            if let Err(e) = doc_builder.build_packages_queue() {
                error!("Failed build new crates: {}", e);
            }

            if let Err(e) = doc_builder.save_cache() {
                error!("Failed to save cache: {}", e);
            }
        }
    });

    // update release activity everyday at 02:00
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 2 && now.tm_min == 0 {
                info!("Updating release activity");
                if let Err(e) = ::utils::update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    });

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
                               .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<commit_msg>Update sources before build<commit_after>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use DocBuilderOptions;
use DocBuilder;
use utils::{update_sources, update_release_activity};

const DAEMON_PID_FILE_PATH: &'static str = "/var/run/cratesfyi.pid";

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect("Environment variable not found");
    }

    // check paths once
    opts().check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(DAEMON_PID_FILE_PATH).expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every 5 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(300));
            debug!("Checking new crates");
            let doc_builder = DocBuilder::new(opts());
            if let Err(e) = doc_builder.get_new_crates() {
                error!("Failed to get new crates: {}", e);
            }
        }
    });

    // build new crates every 3 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(180));

            let mut opts = opts();
            opts.skip_if_exists = true;

            // check lock file
            if opts.prefix.join("cratesfyi.lock").exists() {
                warn!("Lock file exits, skipping building new crates");
                continue;
            }

            // update index
            if let Err(e) = update_sources() {
                error!("Failed to update sources: {}", e);
                continue;
            }

            let mut doc_builder = DocBuilder::new(opts);
            if let Err(e) = doc_builder.load_cache() {
                error!("Failed to load cache: {}", e);
                continue;
            }

            debug!("Building new crates");
            if let Err(e) = doc_builder.build_packages_queue() {
                error!("Failed build new crates: {}", e);
            }

            if let Err(e) = doc_builder.save_cache() {
                error!("Failed to save cache: {}", e);
            }
        }
    });

    // update release activity everyday at 02:00
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 2 && now.tm_min == 0 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    });

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
                               .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<|endoftext|>


<commit_before>extern crate rustbox;
extern crate regex;

use std::cmp::min;
use std::error::Error;
use self::rustbox::{Color, RustBox, Key};
use self::rustbox::Event::KeyEvent;
use self::regex::Regex;

// Screen Status
pub enum ScreenStatus {
    Selected(String),
    Escaped,
    Continue,
    Quit,
    Error,
}

// Screen parts
pub struct Screen {
    header:   String,
    lines:    Vec<String>,
    prompt:   String,
    // offset: usize,
    y_offset: usize,
    filtered: Vec<String>,
    query:    String,
    cursor:   usize,
    rustbox:  RustBox
}

impl Screen {

    pub fn new() -> Screen {

        let rustbox = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        Screen {
            header: String::new(),
            lines: vec!(),
            filtered: vec!(),
            prompt: "💠   ".to_owned(),
            y_offset: 1,
            query: String::new(),
            cursor: 0,
            // offset: 0,
            rustbox: rustbox
        }
    }

    pub fn update_screen(&mut self, header: &str, lines: &Vec<String>) {
        self.y_offset = header.lines().count() + 1;
        self.header   = header.to_string();
        self.cursor   = 0;
        self.query    = String::new();
        self.lines    = lines.to_owned();
        self.filtered = self.lines.clone();
    }

    pub fn render(&self, item: &str) -> ScreenStatus {

        self.rustbox.clear();
        self.rustbox.print_lines(0, &self.header, Color::Green, Color::Black);
        self.rustbox.print_lines(self.header.lines().count(), item, Color::Blue, Color::Black);
        self.rustbox.present();

        match self.rustbox.poll_event(false) {
            Ok(KeyEvent(Key::Char('q'))) => ScreenStatus::Quit,
            _ => ScreenStatus::Escaped
        }
    }

    pub fn select_line(&mut self) -> ScreenStatus {

        loop {

            self.render_items();

            match self.rustbox.poll_event(false) {
                Err(err) => panic!("{:?}", err),
                Ok(event) => match self.handle_event(event) {
                    Ok(ScreenStatus::Selected(s)) => return ScreenStatus::Selected(s),
                    Ok(ScreenStatus::Quit) => return ScreenStatus::Quit,
                    _ => (),
                }
            }
        }
    }

    fn handle_event(&mut self, event: rustbox::Event) -> Result<ScreenStatus, Box<Error>> {

        match event {
            KeyEvent(Key::Enter)     => Ok(ScreenStatus::Selected(self.filtered[self.cursor].to_owned())),
            KeyEvent(Key::Esc)       => Ok(ScreenStatus::Escaped),
            KeyEvent(Key::Char('q')) => Ok(ScreenStatus::Quit),
            KeyEvent(Key::Char(c))   => self.append_query(c).and(Ok(ScreenStatus::Continue)),
            KeyEvent(Key::Backspace) => self.remove_query().and(Ok(ScreenStatus::Continue)),
            KeyEvent(Key::Up) |
            KeyEvent(Key::Ctrl('p')) => {
                self.cursor_up();
                Ok(ScreenStatus::Continue)
            },
            KeyEvent(Key::Down) |
            KeyEvent(Key::Ctrl('n')) => {
                self.cursor_down();
                Ok(ScreenStatus::Continue)
            },
            _ => Ok(ScreenStatus::Continue),
        }
    }

    fn append_query(&mut self, c: char) -> Result<(), Box<Error>> {

        self.query.push(c);
        self.apply_filter()
    }

    fn remove_query(&mut self) -> Result<(), Box<Error>> {

        if self.query.is_empty() {
            return Ok(());
        }

        let idx = self.query.len() - 1;
        self.query.remove(idx);
        self.apply_filter()
    }

    fn apply_filter(&mut self) -> Result<(), Box<Error>> {
        self.filtered = if self.query.len() == 0 {
            self.lines.clone()
        } else {
            let re = try!(Regex::new(self.query.as_str()));
            self.lines.iter().filter(|&input| re.is_match(input)).cloned().collect()
        };

        self.cursor = 0;

        Ok(())
    }

    fn cursor_up(&mut self) {

        if self.cursor != 0 { self.cursor -= 1; }
    }

    fn cursor_down(&mut self) {

        let height = self.rustbox.height();

        self.cursor = min(
            (self.cursor + 1),
            min(
                (self.filtered.len() - 1),
                (height - 1)
            )
        );
    }

    fn render_items(&self) {

        self.rustbox.clear();

        for(y, item) in self.filtered.iter().take(&self.rustbox.height() - 1 - self.y_offset).enumerate() {
            if y == self.cursor {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Black, Color::Green);
            } else {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Blue, Color::Black);
            }
        }

        // print query line and move the cursor to end.
        let query_str = format!("{}{}", self.prompt, self.query);
        self.rustbox.print_lines(0, &self.header, Color::Green, Color::Black);
        self.rustbox.print_line(self.y_offset - 1, &query_str, Color::Blue, Color::Black);
        self.rustbox.present();
    }

}

trait Print {
    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color);
    fn print_lines(&self, start: usize, item: &str, fg: Color, bg: Color);
}

impl Print for RustBox {

    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color) {
        for x in 0..(self.width()) {
            let ch = item.chars().nth(x).unwrap_or(' ');
            self.print_char(x, y, rustbox::RB_NORMAL, fg, bg, ch);
        }
    }

    fn print_lines(&self, start: usize, item: &str, fg: Color, bg: Color) {

        let mut cursor = start;

        for line in item.split("\n") {

            // fold string if line lingth longer than window widht
            if line.chars().count() > self.width() {

                let (first, last) = line.split_at(self.width());
                self.print_line(cursor,    first, fg, bg);
                self.print_line(cursor + 1, last, fg, bg);
                cursor += 2;
                continue;

            }

            self.print_line(cursor, line, fg, bg);
            cursor += 1;
        }
    }
}
<commit_msg>Fix field visibility<commit_after>extern crate rustbox;
extern crate regex;

use std::cmp::min;
use std::error::Error;
use self::rustbox::{Color, RustBox, Key};
use self::rustbox::Event::KeyEvent;
use self::regex::Regex;

// Screen Status
pub enum ScreenStatus {
    Selected(String),
    Escaped,
    Continue,
    Quit,
    Error,
}

// Screen parts
pub struct Screen {
    pub header:   String,
    pub lines:    Vec<String>,
    prompt:   String,
    // offset: usize,
    y_offset: usize,
    filtered: Vec<String>,
    query:    String,
    cursor:   usize,
    rustbox:  RustBox
}

impl Screen {

    pub fn new() -> Screen {

        let rustbox = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        Screen {
            header: String::new(),
            lines: vec!(),
            filtered: vec!(),
            prompt: "💠   ".to_owned(),
            y_offset: 1,
            query: String::new(),
            cursor: 0,
            // offset: 0,
            rustbox: rustbox
        }
    }

    pub fn update_screen(&mut self, header: &str, lines: &Vec<String>) {
        self.y_offset = header.lines().count() + 1;
        self.header   = header.to_string();
        self.cursor   = 0;
        self.query    = String::new();
        self.lines    = lines.to_owned();
        self.filtered = self.lines.clone();
    }

    pub fn render(&self, item: &str) -> ScreenStatus {

        self.rustbox.clear();
        self.rustbox.print_lines(0, &self.header, Color::Green, Color::Black);
        self.rustbox.print_lines(self.header.lines().count(), item, Color::Blue, Color::Black);
        self.rustbox.present();

        match self.rustbox.poll_event(false) {
            Ok(KeyEvent(Key::Char('q'))) => ScreenStatus::Quit,
            _ => ScreenStatus::Escaped
        }
    }

    pub fn select_line(&mut self) -> ScreenStatus {

        loop {

            self.render_items();

            match self.rustbox.poll_event(false) {
                Err(err) => panic!("{:?}", err),
                Ok(event) => match self.handle_event(event) {
                    Ok(ScreenStatus::Selected(s)) => return ScreenStatus::Selected(s),
                    Ok(ScreenStatus::Quit) => return ScreenStatus::Quit,
                    _ => (),
                }
            }
        }
    }

    fn handle_event(&mut self, event: rustbox::Event) -> Result<ScreenStatus, Box<Error>> {

        match event {
            KeyEvent(Key::Enter)     => Ok(ScreenStatus::Selected(self.filtered[self.cursor].to_owned())),
            KeyEvent(Key::Esc)       => Ok(ScreenStatus::Escaped),
            KeyEvent(Key::Char('q')) => Ok(ScreenStatus::Quit),
            KeyEvent(Key::Char(c))   => self.append_query(c).and(Ok(ScreenStatus::Continue)),
            KeyEvent(Key::Backspace) => self.remove_query().and(Ok(ScreenStatus::Continue)),
            KeyEvent(Key::Up) |
            KeyEvent(Key::Ctrl('p')) => {
                self.cursor_up();
                Ok(ScreenStatus::Continue)
            },
            KeyEvent(Key::Down) |
            KeyEvent(Key::Ctrl('n')) => {
                self.cursor_down();
                Ok(ScreenStatus::Continue)
            },
            _ => Ok(ScreenStatus::Continue),
        }
    }

    fn append_query(&mut self, c: char) -> Result<(), Box<Error>> {

        self.query.push(c);
        self.apply_filter()
    }

    fn remove_query(&mut self) -> Result<(), Box<Error>> {

        if self.query.is_empty() {
            return Ok(());
        }

        let idx = self.query.len() - 1;
        self.query.remove(idx);
        self.apply_filter()
    }

    fn apply_filter(&mut self) -> Result<(), Box<Error>> {
        self.filtered = if self.query.len() == 0 {
            self.lines.clone()
        } else {
            let re = try!(Regex::new(self.query.as_str()));
            self.lines.iter().filter(|&input| re.is_match(input)).cloned().collect()
        };

        self.cursor = 0;

        Ok(())
    }

    fn cursor_up(&mut self) {

        if self.cursor != 0 { self.cursor -= 1; }
    }

    fn cursor_down(&mut self) {

        let height = self.rustbox.height();

        self.cursor = min(
            (self.cursor + 1),
            min(
                (self.filtered.len() - 1),
                (height - 1)
            )
        );
    }

    fn render_items(&self) {

        self.rustbox.clear();

        for(y, item) in self.filtered.iter().take(&self.rustbox.height() - 1 - self.y_offset).enumerate() {
            if y == self.cursor {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Black, Color::Green);
            } else {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Blue, Color::Black);
            }
        }

        // print query line and move the cursor to end.
        let query_str = format!("{}{}", self.prompt, self.query);
        self.rustbox.print_lines(0, &self.header, Color::Green, Color::Black);
        self.rustbox.print_line(self.y_offset - 1, &query_str, Color::Blue, Color::Black);
        self.rustbox.present();
    }

}

trait Print {
    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color);
    fn print_lines(&self, start: usize, item: &str, fg: Color, bg: Color);
}

impl Print for RustBox {

    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color) {
        for x in 0..(self.width()) {
            let ch = item.chars().nth(x).unwrap_or(' ');
            self.print_char(x, y, rustbox::RB_NORMAL, fg, bg, ch);
        }
    }

    fn print_lines(&self, start: usize, item: &str, fg: Color, bg: Color) {

        let mut cursor = start;

        for line in item.split("\n") {

            // fold string if line lingth longer than window widht
            if line.chars().count() > self.width() {

                let (first, last) = line.split_at(self.width());
                self.print_line(cursor,    first, fg, bg);
                self.print_line(cursor + 1, last, fg, bg);
                cursor += 2;
                continue;

            }

            self.print_line(cursor, line, fg, bg);
            cursor += 1;
        }
    }
}
<|endoftext|>


<commit_before>use document;
use document::YamlNodeData;
use ffi;
use parser::YamlMark;

use std::from_str::from_str;
use std::num::FromStrRadix;
use std::f64;
use std::char;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            document::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            document::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            document::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[deriving(PartialEq)]
#[deriving(Show)]
pub enum YamlStandardData {
    YamlInteger(int),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

pub struct YamlStandardConstructor;

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

    pub fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, String> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(format!("invalid escape sequence at line {:u}, col {:u}", mark.line, mark.column)),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u0085'),            // unicode next line
                        Some('_') => buf.push('\u00a0'),            // unicode non-breaking space
                        Some('L') => buf.push('\u2028'),            // unicode line separator
                        Some('P') => buf.push('\u2029'),            // unicode paragraph separator
                        Some('x') => {
                            let code:String = it.take(2).collect();
                            match parse_escape_sequence(code, 2) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some('u') => {
                            let code:String = it.take(4).collect();
                            match parse_escape_sequence(code, 4) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some('U') => {
                            let code:String = it.take(8).collect();
                            match parse_escape_sequence(code, 8) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: String, expected_len: uint) -> Option<char> {
    match FromStrRadix::from_str_radix(rep.as_slice(), 16) {
        Some(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: uint) -> int {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:int = FromStrRadix::from_str_radix(filtered.as_slice(), radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = from_str(data).unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

impl YamlConstructor<YamlStandardData, String> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, String> {
        let dec_int = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
        let oct_int = regex!(r"^([-+]?)0o?([0-7_]+)$");
        let hex_int = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
        let bin_int = regex!(r"^([-+]?)0b([0-1_]+)$");
        let float_pattern = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
        let pos_inf = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
        let neg_inf = regex!(r"^-(\.inf|\.Inf|\.INF)$");
        let nan = regex!(r"^(\.nan|\.NaN|\.NAN)$");
        let null = regex!(r"^(null|Null|NULL|~)$");
        let true_pattern = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
        let false_pattern = regex!(r"^(false|False|FALSE|no|No|NO)$");

        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            ffi::YamlPlainScalarStyle => {
                match bin_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 2))),
                    None => ()
                };
                match oct_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 8))),
                    None => ()
                };
                match hex_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 16))),
                    None => ()
                };

                if dec_int.is_match(value.as_slice()) {
                    return Ok(YamlInteger(parse_int("", value.as_slice(), 10)));
                }

                match float_pattern.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlFloat(parse_float(caps.at(1), caps.at(2)))),
                    None => ()
                };

                if pos_inf.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::INFINITY))
                } else if neg_inf.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::NEG_INFINITY))
                } else if nan.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::NAN))
                } else if null.is_match(value.as_slice()) {
                    Ok(YamlNull)
                } else if true_pattern.is_match(value.as_slice()) {
                    Ok(YamlBool(true))
                } else if false_pattern.is_match(value.as_slice()) {
                    Ok(YamlBool(false))
                } else {
                    Ok(YamlString(value))
                }
            },
            ffi::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(value.as_slice(), &mark).map(YamlString)
            },
            _ => {
                Ok(YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, String> {
        let res:Result<Vec<YamlStandardData>, String> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, String> {
        let mut pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, String> = pairs.collect();
        res.map(YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use ffi::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor, YamlInteger, YamlFloat, YamlBool, YamlNull, YamlString, YamlSequence};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match seq.as_slice() {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!((f1 - 0.3).abs() < 1.0e-6);
                                assert!((f2 + 0.4).abs() < 1.0e-6);
                                assert!((f3 - 1e+2).abs() < 1.0e-6);
                                assert!((f4 + 1.2e-3).abs() < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<commit_msg>Use abs_sub instead of abs<commit_after>use document;
use document::YamlNodeData;
use ffi;
use parser::YamlMark;

use std::from_str::from_str;
use std::num::FromStrRadix;
use std::f64;
use std::char;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            document::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            document::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            document::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[deriving(PartialEq)]
#[deriving(Show)]
pub enum YamlStandardData {
    YamlInteger(int),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

pub struct YamlStandardConstructor;

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

    pub fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, String> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(format!("invalid escape sequence at line {:u}, col {:u}", mark.line, mark.column)),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u0085'),            // unicode next line
                        Some('_') => buf.push('\u00a0'),            // unicode non-breaking space
                        Some('L') => buf.push('\u2028'),            // unicode line separator
                        Some('P') => buf.push('\u2029'),            // unicode paragraph separator
                        Some('x') => {
                            let code:String = it.take(2).collect();
                            match parse_escape_sequence(code, 2) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some('u') => {
                            let code:String = it.take(4).collect();
                            match parse_escape_sequence(code, 4) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some('U') => {
                            let code:String = it.take(8).collect();
                            match parse_escape_sequence(code, 8) {
                                Some(c) => buf.push(c),
                                None => return Err(format!("invalid x escape sequence at line {:u}, col {:u}", mark.line, mark.column))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: String, expected_len: uint) -> Option<char> {
    match FromStrRadix::from_str_radix(rep.as_slice(), 16) {
        Some(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: uint) -> int {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:int = FromStrRadix::from_str_radix(filtered.as_slice(), radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = from_str(data).unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

impl YamlConstructor<YamlStandardData, String> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, String> {
        let dec_int = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
        let oct_int = regex!(r"^([-+]?)0o?([0-7_]+)$");
        let hex_int = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
        let bin_int = regex!(r"^([-+]?)0b([0-1_]+)$");
        let float_pattern = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
        let pos_inf = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
        let neg_inf = regex!(r"^-(\.inf|\.Inf|\.INF)$");
        let nan = regex!(r"^(\.nan|\.NaN|\.NAN)$");
        let null = regex!(r"^(null|Null|NULL|~)$");
        let true_pattern = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
        let false_pattern = regex!(r"^(false|False|FALSE|no|No|NO)$");

        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            ffi::YamlPlainScalarStyle => {
                match bin_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 2))),
                    None => ()
                };
                match oct_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 8))),
                    None => ()
                };
                match hex_int.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlInteger(parse_int(caps.at(1), caps.at(2), 16))),
                    None => ()
                };

                if dec_int.is_match(value.as_slice()) {
                    return Ok(YamlInteger(parse_int("", value.as_slice(), 10)));
                }

                match float_pattern.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlFloat(parse_float(caps.at(1), caps.at(2)))),
                    None => ()
                };

                if pos_inf.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::INFINITY))
                } else if neg_inf.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::NEG_INFINITY))
                } else if nan.is_match(value.as_slice()) {
                    Ok(YamlFloat(f64::NAN))
                } else if null.is_match(value.as_slice()) {
                    Ok(YamlNull)
                } else if true_pattern.is_match(value.as_slice()) {
                    Ok(YamlBool(true))
                } else if false_pattern.is_match(value.as_slice()) {
                    Ok(YamlBool(false))
                } else {
                    Ok(YamlString(value))
                }
            },
            ffi::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(value.as_slice(), &mark).map(YamlString)
            },
            _ => {
                Ok(YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, String> {
        let res:Result<Vec<YamlStandardData>, String> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, String> {
        let mut pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, String> = pairs.collect();
        res.map(YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use std::num::FloatMath;
    use ffi::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor, YamlInteger, YamlFloat, YamlBool, YamlNull, YamlString, YamlSequence};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match seq.as_slice() {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!(f1.abs_sub(0.3) < 1.0e-6);
                                assert!(f2.abs_sub(-0.4) < 1.0e-6);
                                assert!(f3.abs_sub(1e+2) < 1.0e-6);
                                assert!(f4.abs_sub(-1.2e-3) < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<|endoftext|>


<commit_before>use quote::Tokens;
use syn::{Data, DeriveInput, Field, Ident, Fields};
use utils::{field_idents, get_field_types, numbered_vars};

/// Provides the hook to expand `#[derive(Constructor)]` into an implementation of `Constructor`
pub fn expand(input: &DeriveInput, _: &str) -> Tokens {
    let input_type = &input.ident;
    let empty_fields = &vec![];
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let ((body, vars), fields) = match input.body {
        Data::Struct(ref data_struct) => match data_struct.fields {
            Fields::Unnamed(ref fields) => (tuple_body(input_type, fields), fields),
            Fields::Named(ref fields) => (struct_body(input_type, fields), fields),
            Fields::Unit => (struct_body(input_type, empty_fields), empty_fields),
        },
        _ => panic!("Only structs can derive a constructor"),
    };
    let original_types = &get_field_types(fields);
    quote!{
        #[allow(missing_docs)]
        impl#impl_generics #input_type#ty_generics #where_clause {
            pub fn new(#(#vars: #original_types),*) -> #input_type#ty_generics {
                #body
            }
        }
    }
}

fn tuple_body(return_type: &Ident, fields: Vec<&Field>) -> (Tokens, Vec<Ident>) {
    let vars = &numbered_vars(fields.len(), "");
    (quote!(#return_type(#(#vars),*)), vars.clone())
}

fn struct_body(return_type: &Ident, fields: Vec<&Field>) -> (Tokens, Vec<Ident>) {
    let field_names: &Vec<Ident> = &field_idents(fields).iter().map(|f| (*f).clone()).collect();
    let vars = field_names;
    (quote!(#return_type{#(#field_names: #vars),*}), vars.clone())
}
<commit_msg>Fix constructor.rs<commit_after>use quote::Tokens;
use syn::{Data, DeriveInput, Field, Ident, Fields};
use utils::{field_idents, get_field_types, numbered_vars, named_to_vec, unnamed_to_vec};

/// Provides the hook to expand `#[derive(Constructor)]` into an implementation of `Constructor`
pub fn expand(input: &DeriveInput, _: &str) -> Tokens {
    let input_type = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let ((body, vars), fields) = match input.data {
        Data::Struct(ref data_struct) => match data_struct.fields {
            Fields::Unnamed(ref fields) => {
                let field_vec = unnamed_to_vec(fields);
                (tuple_body(input_type, field_vec), field_vec)
            },
            Fields::Named(ref fields) => {
                let field_vec = named_to_vec(fields);
                (struct_body(input_type, field_vec), field_vec)
            },
            Fields::Unit => (struct_body(input_type, vec![]), vec![]),
        },
        _ => panic!("Only structs can derive a constructor"),
    };
    let original_types = &get_field_types(fields);
    quote!{
        #[allow(missing_docs)]
        impl#impl_generics #input_type#ty_generics #where_clause {
            pub fn new(#(#vars: #original_types),*) -> #input_type#ty_generics {
                #body
            }
        }
    }
}

fn tuple_body(return_type: &Ident, fields: Vec<&Field>) -> (Tokens, Vec<Ident>) {
    let vars = &numbered_vars(fields.len(), "");
    (quote!(#return_type(#(#vars),*)), vars.clone())
}

fn struct_body(return_type: &Ident, fields: Vec<&Field>) -> (Tokens, Vec<Ident>) {
    let field_names: &Vec<Ident> = &field_idents(fields).iter().map(|f| (*f).clone()).collect();
    let vars = field_names;
    (quote!(#return_type{#(#field_names: #vars),*}), vars.clone())
}
<|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.

//! The conversion stage of the ucg compiler.
pub mod b64;
pub mod env;
pub mod exec;
pub mod flags;
pub mod json;
pub mod toml;
pub mod traits;
pub mod xml;
pub mod yaml;

use std::collections::HashMap;

/// ConverterRunner knows how to run a given converter on a Val.
pub struct ConverterRegistry {
    converters: HashMap<String, Box<dyn traits::Converter>>,
}

impl ConverterRegistry {
    /// new creates a new ConverterRunner with a converter for the provided output target.
    ///
    /// * flags
    /// * json
    /// * env
    /// * exec
    fn new() -> Self {
        ConverterRegistry {
            converters: HashMap::new(),
        }
    }

    pub fn make_registry() -> Self {
        let mut registry = Self::new();
        registry.register("json", Box::new(json::JsonConverter::new()));
        registry.register("env", Box::new(env::EnvConverter::new()));
        registry.register("flags", Box::new(flags::FlagConverter::new()));
        registry.register("exec", Box::new(exec::ExecConverter::new()));
        registry.register("yaml", Box::new(yaml::YamlConverter::new()));
        registry.register("toml", Box::new(toml::TomlConverter::new()));
        registry.register("xml", Box::new(xml::XmlConverter {}));
        registry
    }

    pub fn register<S: Into<String>>(&mut self, typ: S, converter: Box<traits::Converter>) {
        self.converters.insert(typ.into(), converter);
    }

    pub fn get_converter(&self, typ: &str) -> Option<&traits::Converter> {
        self.converters.get(typ).map(|c| c.as_ref())
    }

    pub fn get_converter_list(&self) -> Vec<(&String, &Box<traits::Converter>)> {
        self.converters.iter().collect()
    }

    // TODO(jwall): Support converter help descriptions.
}

pub struct ImporterRegistry {
    importers: HashMap<String, Box<dyn traits::Importer>>,
}

impl ImporterRegistry {
    /// new creates a new ConverterRunner with a converter for the provided output target.
    ///
    /// * flags
    /// * json
    /// * env
    /// * exec
    fn new() -> Self {
        ImporterRegistry {
            importers: HashMap::new(),
        }
    }

    pub fn make_registry() -> Self {
        let mut registry = Self::new();
        registry.register("b64", Box::new(b64::Base64Importer { url_safe: false }));
        registry.register(
            "b64urlsafe",
            Box::new(b64::Base64Importer { url_safe: true }),
        );
        registry
    }

    pub fn register<S: Into<String>>(&mut self, typ: S, importer: Box<dyn traits::Importer>) {
        self.importers.insert(typ.into(), importer);
    }

    pub fn get_importer(&self, typ: &str) -> Option<&dyn traits::Importer> {
        self.importers.get(typ).map(|c| c.as_ref())
    }

    pub fn get_importer_list(&self) -> Vec<(&String, &Box<dyn traits::Importer>)> {
        self.importers.iter().collect()
    }

    // TODO(jwall): Support converter help descriptions.
}
<commit_msg>DOC: Fix docstring for registry new constructors.<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.

//! The conversion stage of the ucg compiler.
pub mod b64;
pub mod env;
pub mod exec;
pub mod flags;
pub mod json;
pub mod toml;
pub mod traits;
pub mod xml;
pub mod yaml;

use std::collections::HashMap;

/// ConverterRunner knows how to run a given converter on a Val.
pub struct ConverterRegistry {
    converters: HashMap<String, Box<dyn traits::Converter>>,
}

impl ConverterRegistry {
    /// Creates a new ConverterRegistry.
    ///
    /// * flags
    /// * json
    /// * env
    /// * exec
    fn new() -> Self {
        ConverterRegistry {
            converters: HashMap::new(),
        }
    }

    pub fn make_registry() -> Self {
        let mut registry = Self::new();
        registry.register("json", Box::new(json::JsonConverter::new()));
        registry.register("env", Box::new(env::EnvConverter::new()));
        registry.register("flags", Box::new(flags::FlagConverter::new()));
        registry.register("exec", Box::new(exec::ExecConverter::new()));
        registry.register("yaml", Box::new(yaml::YamlConverter::new()));
        registry.register("toml", Box::new(toml::TomlConverter::new()));
        registry.register("xml", Box::new(xml::XmlConverter {}));
        registry
    }

    pub fn register<S: Into<String>>(&mut self, typ: S, converter: Box<traits::Converter>) {
        self.converters.insert(typ.into(), converter);
    }

    pub fn get_converter(&self, typ: &str) -> Option<&traits::Converter> {
        self.converters.get(typ).map(|c| c.as_ref())
    }

    pub fn get_converter_list(&self) -> Vec<(&String, &Box<traits::Converter>)> {
        self.converters.iter().collect()
    }

    // TODO(jwall): Support converter help descriptions.
}

pub struct ImporterRegistry {
    importers: HashMap<String, Box<dyn traits::Importer>>,
}

impl ImporterRegistry {
    /// Creates a new ImporterRegistry.
    ///
    /// * b64
    /// * b64urlsafe
    fn new() -> Self {
        ImporterRegistry {
            importers: HashMap::new(),
        }
    }

    pub fn make_registry() -> Self {
        let mut registry = Self::new();
        registry.register("b64", Box::new(b64::Base64Importer { url_safe: false }));
        registry.register(
            "b64urlsafe",
            Box::new(b64::Base64Importer { url_safe: true }),
        );
        registry
    }

    pub fn register<S: Into<String>>(&mut self, typ: S, importer: Box<dyn traits::Importer>) {
        self.importers.insert(typ.into(), importer);
    }

    pub fn get_importer(&self, typ: &str) -> Option<&dyn traits::Importer> {
        self.importers.get(typ).map(|c| c.as_ref())
    }

    pub fn get_importer_list(&self) -> Vec<(&String, &Box<dyn traits::Importer>)> {
        self.importers.iter().collect()
    }

    // TODO(jwall): Support converter help descriptions.
}
<|endoftext|>


<commit_before>// NOTE: Object is a term for an entity or a prop.

use time;
use opengl_graphics::*;
use core::{Vec2};

/// A trait for positioned objects
pub trait Position {
  /// Get the x coordinate
  fn get_pos(&self) -> Vec2<i64>;
  /// Set the Vec coordinate
  fn set_pos(&mut self, new_pos: Vec2<i64>);
}

/// The direction of a given object
#[derive(Clone, Copy)]
pub enum Dir {
  Left,
  Right,
  Up,
  Down,
}
impl Dir {
  fn to_vec(&self) -> Vec2<i64> {
    Vec2(
      match *self {
        Dir::Left => 1,
        Dir::Right => -1,
        _ => 0,
      },

      match *self {
        Dir::Up => 1,
        Dir::Down => -1,
      _ => 0,
      }
    )
  }
}

// TODO: Implement two directions at once.
// TODO: Make drawable trait

/// A movable object
pub trait Move: Position {
  /// Get the direction
  fn get_dir(&self) -> Dir;
  /// Set the direction
  fn set_dir(&mut self, new_dir: Dir);
  /// Is the object moving?
  fn is_moving(&self) -> bool;
  /// Can the object move? Or is it blocked?
  fn can_move(&self) -> bool;
  /// Move the object
  fn move_obj(&mut self, mov: Vec2<i64>) {
    let coord = self.get_pos();
    self.set_pos(coord + mov);
  }
  /// Get new coordinate
  fn get_new_pos(&self) -> Vec2<i64> {
    let dir = self.get_dir();
    self.get_pos() + dir.to_vec()
  }
  /// Move object in direction.
  fn move_obj_dir(&mut self) {
    let new_coord = self.get_new_pos();
    self.set_pos(new_coord)
  }
  /// Get the timestamp of the last move
  fn get_last_move(&self) -> f64;
  /// Set the timestamp of the last move
  fn set_last_move(&mut self, new: &f64);
  /// Get the speed of the object
  fn get_speed(&self) -> f64;
  /// Moves regularly
  fn move_reg(&mut self) {
    let now = &time::precise_time_s();
    if self.is_moving() && self.can_move()
       && self.get_last_move() - now > 1.0 / self.get_speed() {
      self.set_last_move(now);
      self.move_obj_dir();
    }
  }
  /// Get transitition point, which is in the interval [0,1]
  fn get_trans_state(&self) -> f64;
  /// Get animation frame
  fn get_animation_frame(&self) -> i16;
}

/// Trait for sprited objects
pub trait Sprite: Move {
  /// Get current sprite
  fn get_sprite(&self) -> &Texture;
  /// Get width, not neccesarily the width of the sprite, but rather the space
  /// the given object occupies. Given in fields.
  fn get_width(&self) -> i16;
  /// Get height, see note above
  fn get_height(&self) -> i16;
  /// Get the opacity of the object
  fn get_opacity(&self) -> f64;

  // TODO: Add draw method.
}

// TODO: Add event trait, for objects you can click on etc.

<commit_msg>Add draw method to Sprite trait<commit_after>// NOTE: Object is a term for an entity or a prop.

use time;
use opengl_graphics::*;
use core::{Vec2};

/// A trait for positioned objects
pub trait Position {
  /// Get the x coordinate
  fn get_pos(&self) -> Vec2<i64>;
  /// Set the Vec coordinate
  fn set_pos(&mut self, new_pos: Vec2<i64>);
}

/// The direction of a given object
#[derive(Clone, Copy)]
pub enum Dir {
  Left,
  Right,
  Up,
  Down,
}
impl Dir {
  fn to_vec(&self) -> Vec2<i64> {
    Vec2(
      match *self {
        Dir::Left => 1,
        Dir::Right => -1,
        _ => 0,
      },

      match *self {
        Dir::Up => 1,
        Dir::Down => -1,
      _ => 0,
      }
    )
  }
}

// TODO: Implement two directions at once.
// TODO: Make drawable trait

/// A movable object
pub trait Move: Position {
  /// Get the direction
  fn get_dir(&self) -> Dir;
  /// Set the direction
  fn set_dir(&mut self, new_dir: Dir);
  /// Is the object moving?
  fn is_moving(&self) -> bool;
  /// Can the object move? Or is it blocked?
  fn can_move(&self) -> bool;
  /// Move the object
  fn move_obj(&mut self, mov: Vec2<i64>) {
    let coord = self.get_pos();
    self.set_pos(coord + mov);
  }
  /// Get new coordinate
  fn get_new_pos(&self) -> Vec2<i64> {
    let dir = self.get_dir();
    self.get_pos() + dir.to_vec()
  }
  /// Move object in direction.
  fn move_obj_dir(&mut self) {
    let new_coord = self.get_new_pos();
    self.set_pos(new_coord)
  }
  /// Get the timestamp of the last move
  fn get_last_move(&self) -> f64;
  /// Set the timestamp of the last move
  fn set_last_move(&mut self, new: &f64);
  /// Get the speed of the object
  fn get_speed(&self) -> f64;
  /// Moves regularly
  fn move_reg(&mut self) {
    let now = &time::precise_time_s();
    if self.is_moving() && self.can_move()
       && self.get_last_move() - now > 1.0 / self.get_speed() {
      self.set_last_move(now);
      self.move_obj_dir();
    }
  }
  /// Get transitition point, which is in the interval [0,1]
  fn get_trans_state(&self) -> f64;
  /// Get animation frame
  fn get_animation_frame(&self) -> i16;
}

/// Trait for sprited objects
pub trait Sprite: Move {
  /// Get current sprite
  fn get_sprite(&self) -> &Texture;
  /// Get width, not neccesarily the width of the sprite, but rather the space
  /// the given object occupies. Given in fields.
  fn get_width(&self) -> i16;
  /// Get height, see note above
  fn get_height(&self) -> i16;
  /// Get the opacity of the object
  fn get_opacity(&self) -> f64;
  fn draw(&self, c: &Context, gl: &mut GlGraphics);

  // TODO: Add draw method.
}

// TODO: Add event trait, for objects you can click on etc.

<|endoftext|>