<commit_before>extern crate ring;

macro_rules! wrap_algorithm {
    ( $wrapper_name:ident, $t:ty, $alg:expr ) => {
        /// Do NOT free the resulting pointer.
        #[no_mangle]
        pub unsafe fn $wrapper_name() -> *const $t {
            $alg as *const $t
        }
    }
}

wrap_algorithm!(ring_digest_algorithm_sha1, ring::digest::Algorithm,
                &ring::digest::SHA1);
wrap_algorithm!(ring_digest_algorithm_sha256, ring::digest::Algorithm,
                &ring::digest::SHA256);
wrap_algorithm!(ring_digest_algorithm_sha384, ring::digest::Algorithm,
                &ring::digest::SHA384);
wrap_algorithm!(ring_digest_algorithm_sha512, ring::digest::Algorithm,
                &ring::digest::SHA512);

/// Returns a pointer to a heap-allocated `ring::digest::Context`. You must
/// call `ring_digest_context_finish` to free the returned context.
#[no_mangle]
pub unsafe fn ring_digest_context_new(algorithm: *const ring::digest::Algorithm)
                -> *mut ring::digest::Context {
    if algorithm.is_null() {
        return std::ptr::null_mut();
    }
    Box::into_raw(Box::new(ring::digest::Context::new(&*algorithm)))
}

/// Calls `ctx.update()` with the given data.
#[no_mangle]
pub unsafe fn ring_digest_context_update(ctx: *mut ring::digest::Context,
                                         p: *const u8, len: usize) {
    (*ctx).update(std::slice::from_raw_parts(p, len))
}

/// Frees a context created by `ring_digest_context_new`.
#[no_mangle]
pub unsafe fn ring_digest_context_finish(ctx: *mut ring::digest::Context,
                                         out: *mut u8, out_capacity: usize)
                                         -> usize {
    // We can't call `finish` on (*ctx). `x.finish()` consumes `x` but in the
    // FFI interface we can't rely on the caller honoring Rust's move semantics.
    let copy = (*ctx).clone();
    let digest = copy.finish();
    let digest = digest.as_ref();
    let len = digest.len();
    if len > out_capacity {
        return 0;
    }
    std::ptr::copy_nonoverlapping(digest.as_ptr(), out, len);
    len
}

#[no_mangle]
pub unsafe fn ring_digest_context_delete(ctx: *mut ring::digest::Context) {
    let _ = Box::from_raw(ctx);
}
<commit_msg>Add `ring_digest_context_clone` for cloning Digest contexts.<commit_after>extern crate ring;

/// Moves `x` onto the heap and returns a mutable pointer to it.
fn heap_mut_ptr<T>(x: T) -> *mut T {
    Box::into_raw(Box::new(x))
}

/// Clones `*x` onto the heap and returns a mutable pointer to it.
unsafe fn clone_heap_mut_ptr<T: Clone>(x: *const T) -> *mut T {
    heap_mut_ptr((*x).clone())
}

macro_rules! wrap_algorithm {
    ( $wrapper_name:ident, $t:ty, $alg:expr ) => {
        /// Do NOT free the resulting pointer.
        #[no_mangle]
        pub unsafe fn $wrapper_name() -> *const $t {
            $alg as *const $t
        }
    }
}

wrap_algorithm!(ring_digest_algorithm_sha1, ring::digest::Algorithm,
                &ring::digest::SHA1);
wrap_algorithm!(ring_digest_algorithm_sha256, ring::digest::Algorithm,
                &ring::digest::SHA256);
wrap_algorithm!(ring_digest_algorithm_sha384, ring::digest::Algorithm,
                &ring::digest::SHA384);
wrap_algorithm!(ring_digest_algorithm_sha512, ring::digest::Algorithm,
                &ring::digest::SHA512);

/// Returns a pointer to a heap-allocated `ring::digest::Context`. You must
/// call `ring_digest_context_finish` to free the returned context.
#[no_mangle]
pub unsafe fn ring_digest_context_new(algorithm: *const ring::digest::Algorithm)
                -> *mut ring::digest::Context {
    if algorithm.is_null() {
        return std::ptr::null_mut();
    }
    heap_mut_ptr(ring::digest::Context::new(&*algorithm))
}

/// Calls `ctx.update()` with the given data.
#[no_mangle]
pub unsafe fn ring_digest_context_update(ctx: *mut ring::digest::Context,
                                         p: *const u8, len: usize) {
    (*ctx).update(std::slice::from_raw_parts(p, len))
}

/// Frees a context created by `ring_digest_context_new`.
#[no_mangle]
pub unsafe fn ring_digest_context_finish(ctx: *mut ring::digest::Context,
                                         out: *mut u8, out_capacity: usize)
                                         -> usize {
    // We can't call `finish` on (*ctx). `x.finish()` consumes `x` but in the
    // FFI interface we can't rely on the caller honoring Rust's move semantics.
    let copy = (*ctx).clone();
    let digest = copy.finish();
    let digest = digest.as_ref();
    let len = digest.len();
    if len > out_capacity {
        return 0;
    }
    std::ptr::copy_nonoverlapping(digest.as_ptr(), out, len);
    len
}

#[no_mangle]
pub unsafe fn ring_digest_context_delete(ctx: *mut ring::digest::Context) {
    let _ = Box::from_raw(ctx);
}

/// Clones a digest context, returning a pointer to the clone.
#[no_mangle]
pub unsafe fn ring_digest_context_clone(ctx: *mut ring::digest::Context)
                                        -> *mut ring::digest::Context {
    clone_heap_mut_ptr(ctx)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

use std::io;
use std::io::prelude::*;
use std::io::{BufReader};
use std::fs::File;
use std::process::{ChildStdout, Command, Stdio, ExitStatus};
use std::sync::Mutex;
use std::ops::DerefMut;

use std::os::unix::io::FromRawFd;
use std::os::unix::io::AsRawFd;
use std::sync::mpsc;

pub enum Output {
    Parent,
    Ignore,
    // only works on Unix, the most efficient
    ToFd(File),
    /* The below would require additional error handling mechanisms
    // Write to this file in a background thread
    ToPath(PathBuf),
    // Check for any output in a background thread
    // Useful to assert that stderr has no output
    FailOnOutput,
    */
}

/*
pub fn output_to_path(path: &str) -> Output {
    Output::ToPath(PathBuf::from(path))
}
*/

pub struct DealWithOutput { stderr: Output, stdout: Output }

pub fn output() -> DealWithOutput {
    DealWithOutput { stdout: Output::Parent, stderr: Output::Parent }
}

impl DealWithOutput {
    pub fn stderr(&mut self, stderr: Output) -> &mut Self {
        self.stderr = stderr;
        self
    }

    pub fn stdout(&mut self, stdout: Output) -> &mut Self {
        self.stdout = stdout;
        self
    }
}

// Use a process as a consumer of a Read interface.
//
// Start a process for the given exe and args.
// Handle stderr and stdout in a non-blocking way according to the given DealWithOutput
//
// Feed the input as stdin to the process and then wait for the process to finish
// Return the result of the process.
pub fn process_read_consumer<R: Read>(
    deal_with: &mut DealWithOutput,
    mut input: R,
    cmd_args: (String, Vec<String>))
    -> io::Result<ExitStatus>
{
    let mut cmd = build_command(cmd_args);

    cmd.stdin(Stdio::piped());
    setup_stderr(&deal_with.stderr, &mut cmd)?;
    setup_stdout(&deal_with.stdout, &mut cmd)?;

    let mut process = cmd.spawn()?;

    // Introducing this scope will drop stdin
    // That will close the handle so the process can terminate
    {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");

        // output_optional_handle(&deal_with.stderr, &mut process.stderr)?;
        // output_optional_handle(&stdout, &mut process.stdout)?;
        io::copy(&mut input, &mut stdin)?;
    }

    let status = process.wait()?;
    Ok(status)
}

// Stream data through a process using readers
//
// Start a process for the given exe and args.
// Return a buffer of stdout or the error encountered when starting the process
//
// Feed input (if given) to its stdin (in a separate thread)
//
// Wait for the exit code on a separate thread
// Handle stderr in a non-blocking way according to the stderr option
//
// If any of the threads have failures, including if the process has a non-zero exit code,
// that will be reflected in `ChildStream.wait`
pub fn process_as_reader<R>(
	stdin_opt: Option<R>,
    stderr: Output,
	cmd_args: (String, Vec<String>)) -> io::Result<ChildStream>
    where R: Read + Send + 'static,
{
    let mut cmd = build_command(cmd_args);

    // setup stdout
    cmd.stdout(Stdio::piped());
    if let Some(_) = stdin_opt {
        cmd.stdin(Stdio::piped());
    }

    setup_stderr(&stderr, &mut cmd)?;

    let mut process = cmd.spawn()?;
    let stdout = process.stdout.take().expect("impossible! no stdout");

    // output_optional_handle(&stderr, &mut process.stderr)?;

    let (send_result, receiver) = mpsc::channel();
    // feed input to stdin
    if let Some(input) = stdin_opt {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");
        let input_mutex = Mutex::new(input);
        let sender = send_result.clone();
        let done_stdin = move |result| {
            match result {
                Err(err) => {
                    send_or_log_result(sender, Err(ProcessAsyncError::StdinError(err)))
                }
                Ok(Err(err)) => {
                    send_or_log_result(sender, Err(ProcessAsyncError::StdinError(err)))
                }
                // process wait returns the Ok
                Ok(Ok(_)) => {}
            }
        };
        concurrent::spawn_catch_panic(done_stdin, move || {
            // TODO: convert error types
            let mut inp = input_mutex.lock().expect("error locking stdin");
            let _ = io::copy(inp.deref_mut(), &mut stdin)?;
            Ok(())
        });
    }

    // wait for the process to exit successfully
    let sender = send_result.clone();
    let done_wait = move |result| {
        match result {
            Err(err) => {
                send_or_log_result(sender,
                    Err(ProcessAsyncError::WaitError(err)))
            }
            Ok(Err(err)) => {
                send_or_log_result(sender,
                    Err(ProcessAsyncError::WaitError(err)))
            }
            Ok(Ok(status)) => {
                send_or_log_result(sender, Ok(status));
            }
        }
    };
    concurrent::spawn_catch_panic(done_wait, move || {
        // assert that the process exits successfully
        let status = process.wait()?;
        Ok(status)
    });

    Ok(ChildStream {
      stdout: BufReader::new(stdout),
      wait_result: FutureExitResult::new(receiver)
    })
}

#[derive(Debug)]
pub enum ProcessAsyncError {
    RecvError(mpsc::RecvError),
    WaitError(io::Error),
    StdinError(io::Error),
    ExitStatusError(Option<i32>),
    AlreadyResolvedError,
}

type ProecessAsyncResult = Result<ExitStatus, ProcessAsyncError>;

pub struct FutureExitResult {
    recv: mpsc::Receiver<ProecessAsyncResult>,
    // This future should only be resolved once
    already: bool,
}

impl FutureExitResult {
   fn new(receiver: mpsc::Receiver<ProecessAsyncResult>) -> Self {
      FutureExitResult { recv: receiver, already: false }
   }

   fn exit_status(&mut self) -> ProecessAsyncResult {
       if self.already { return Err(ProcessAsyncError::AlreadyResolvedError) }
       self.already = true;
       match self.recv.recv() {
           Err(err) => {
               Err(ProcessAsyncError::RecvError(err))
           }
           Ok(stream_status) => {
               stream_status
           }
       }
   }

   pub fn wait(&mut self) -> Result<Option<i32>, ProcessAsyncError> {
        let status = self.exit_status()?;
        if status.success() {
            Ok(status.code())
        } else {
            Err(ProcessAsyncError::ExitStatusError(status.code()))
        }
   }
}

pub struct ChildStream {
    pub stdout: BufReader<ChildStdout>,
    pub wait_result: FutureExitResult
}
impl ChildStream {
   // Wait for the result of the process.
   // This should only be called once.
   // The second time it will return AlreadyResolvedError
   pub fn wait(&mut self) -> Result<Option<i32>, ProcessAsyncError> {
       self.wait_result.wait()
   }
}

fn send_or_log_result<T>(sender: mpsc::Sender<Result<T, ProcessAsyncError>>, result: Result<T, ProcessAsyncError>){
    match sender.send(result) {
        Ok(_) => {}
        Err(err) => error!("error sending done message: {}", err),
    }
}

fn setup_stderr(deal_with_stderr: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stderr {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stderr(Stdio::null());
      }
      &Output::ToFd(ref file) => {
          unsafe {
            cmd.stderr(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
      /*
      &Output::FailOnOutput => {
          cmd.stderr(Stdio::piped());
      }
      &Output::ToPath(_) => {
          cmd.stderr(Stdio::piped());
      }
      */
    }
    Ok(())
}

fn setup_stdout(deal_with_stdout: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stdout {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stdout(Stdio::null());
      }
      &Output::ToFd(ref file) => {
          unsafe {
            cmd.stdout(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
      /*
      &Output::FailOnOutput => {
          cmd.stdout(Stdio::piped());
      }
      &Output::ToPath(_) => {
          cmd.stdout(Stdio::piped());
      }
      */
    }
    Ok(())
}

// Build up the command from the arguments
fn build_command(cmd_args: (String, Vec<String>)) -> Command {
    // Build up the command from the arguments
    let (exe, args) = cmd_args;
    let mut cmd = Command::new(exe);
    for arg in args { cmd.arg(arg); }
    return cmd
}

/*
fn output_optional_handle<R: Read + Send + 'static>(deal_with_output: &Output, opt_handle: &mut Option<R>) -> io::Result<()> {
    if let &Output::FailOnOutput = deal_with_output {
        let handle = opt_handle.take().expect("impossible! no output handle");
        let _ = thread::spawn(move || {
            let mut output = Vec::new();
            let size = handle.take(1).read_to_end(&mut output).expect("error reading output");
            if size > 0 {
              panic!("got unexpected output");
            }
        });
    } else {
        if let &Output::ToPath(ref path) = deal_with_output {
            let mut handle = opt_handle.take().expect("impossible! no output handle");
            let mut file = File::create(path)?;
            let _ = thread::spawn(move || {
                io::copy(&mut handle, &mut file)
                  .expect("error writing output to a file");
            });
        }
    }

    Ok(())
}
*/

mod concurrent {
    use std::io;
    use std::io::{Error, ErrorKind};
    use std::panic;
    use std::thread;
    use std::thread::JoinHandle;
    use std::any::Any;

    pub fn caught_panic_to_io_error(err: Box<Any + Send + 'static>) -> io::Error {
        let msg = match err.downcast_ref::<&'static str>() {
            Some(s) => *s,
            None => {
                match err.downcast_ref::<String>() {
                    Some(s) => &s[..],
                    None => "Box<Any>",
                }
            }
        };

        Error::new(ErrorKind::Other, msg)
    }

    // Spawn a thread and catch any panic unwinds.
    // Call a callback function with the result
    // where Err is the message of the panic and
    // Ok is the return of the function
    pub fn spawn_catch_panic<Function, Returned, Finished>(done: Finished, f: Function) -> JoinHandle<()>
        where Function: FnOnce() -> Returned,
              Function: Send + 'static,
              Function: panic::UnwindSafe,
              Finished: FnOnce(io::Result<Returned>) -> (),
              Finished: Send + 'static,
    {
        thread::spawn(move || {
            let result = panic::catch_unwind(move || { f() });
            match result {
                Err(err) => { done(Err(caught_panic_to_io_error(err))) }
                Ok(ok)   => { done(Ok(ok)) }
            }
        })
    }
}
<commit_msg>document an API quirk<commit_after>#[macro_use]
extern crate log;

use std::io;
use std::io::prelude::*;
use std::io::{BufReader};
use std::fs::File;
use std::process::{ChildStdout, Command, Stdio, ExitStatus};
use std::sync::Mutex;
use std::ops::DerefMut;

use std::os::unix::io::FromRawFd;
use std::os::unix::io::AsRawFd;
use std::sync::mpsc;

pub enum Output {
    Parent,
    Ignore,
    // only works on Unix, the most efficient
    ToFd(File),
    /* The below would require additional error handling mechanisms
    // Write to this file in a background thread
    ToPath(PathBuf),
    // Check for any output in a background thread
    // Useful to assert that stderr has no output
    FailOnOutput,
    */
}

/*
pub fn output_to_path(path: &str) -> Output {
    Output::ToPath(PathBuf::from(path))
}
*/

pub struct DealWithOutput { stderr: Output, stdout: Output }

pub fn output() -> DealWithOutput {
    DealWithOutput { stdout: Output::Parent, stderr: Output::Parent }
}

impl DealWithOutput {
    pub fn stderr(&mut self, stderr: Output) -> &mut Self {
        self.stderr = stderr;
        self
    }

    pub fn stdout(&mut self, stdout: Output) -> &mut Self {
        self.stdout = stdout;
        self
    }
}

// Use a process as a consumer of a Read interface.
//
// Start a process for the given exe and args.
// Handle stderr and stdout in a non-blocking way according to the given DealWithOutput
//
// Feed the input as stdin to the process and then wait for the process to finish
// Return the result of the process.
pub fn process_read_consumer<R: Read>(
    deal_with: &mut DealWithOutput,
    mut input: R,
    cmd_args: (String, Vec<String>))
    -> io::Result<ExitStatus>
{
    let mut cmd = build_command(cmd_args);

    cmd.stdin(Stdio::piped());
    setup_stderr(&deal_with.stderr, &mut cmd)?;
    setup_stdout(&deal_with.stdout, &mut cmd)?;

    let mut process = cmd.spawn()?;

    // Introducing this scope will drop stdin
    // That will close the handle so the process can terminate
    {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");

        // output_optional_handle(&deal_with.stderr, &mut process.stderr)?;
        // output_optional_handle(&stdout, &mut process.stdout)?;
        io::copy(&mut input, &mut stdin)?;
    }

    let status = process.wait()?;
    Ok(status)
}

// Stream data through a process using readers
//
// Start a process for the given exe and args.
// Return a buffer of stdout or the error encountered when starting the process
//
// Feed input (if given) to its stdin (in a separate thread)
// If no stdin is given you will need to give None::<File>
//
// Wait for the exit code on a separate thread
// Handle stderr in a non-blocking way according to the stderr option
//
// If any of the threads have failures, including if the process has a non-zero exit code,
// that will be reflected in `ChildStream.wait`
pub fn process_as_reader<R>(
	stdin_opt: Option<R>,
    stderr: Output,
	cmd_args: (String, Vec<String>)) -> io::Result<ChildStream>
    where R: Read + Send + 'static,
{
    let mut cmd = build_command(cmd_args);

    // setup stdout
    cmd.stdout(Stdio::piped());
    if let Some(_) = stdin_opt {
        cmd.stdin(Stdio::piped());
    }

    setup_stderr(&stderr, &mut cmd)?;

    let mut process = cmd.spawn()?;
    let stdout = process.stdout.take().expect("impossible! no stdout");

    // output_optional_handle(&stderr, &mut process.stderr)?;

    let (send_result, receiver) = mpsc::channel();
    // feed input to stdin
    if let Some(input) = stdin_opt {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");
        let input_mutex = Mutex::new(input);
        let sender = send_result.clone();
        let done_stdin = move |result| {
            match result {
                Err(err) => {
                    send_or_log_result(sender, Err(ProcessAsyncError::StdinError(err)))
                }
                Ok(Err(err)) => {
                    send_or_log_result(sender, Err(ProcessAsyncError::StdinError(err)))
                }
                // process wait returns the Ok
                Ok(Ok(_)) => {}
            }
        };
        concurrent::spawn_catch_panic(done_stdin, move || {
            // TODO: convert error types
            let mut inp = input_mutex.lock().expect("error locking stdin");
            let _ = io::copy(inp.deref_mut(), &mut stdin)?;
            Ok(())
        });
    }

    // wait for the process to exit successfully
    let sender = send_result.clone();
    let done_wait = move |result| {
        match result {
            Err(err) => {
                send_or_log_result(sender,
                    Err(ProcessAsyncError::WaitError(err)))
            }
            Ok(Err(err)) => {
                send_or_log_result(sender,
                    Err(ProcessAsyncError::WaitError(err)))
            }
            Ok(Ok(status)) => {
                send_or_log_result(sender, Ok(status));
            }
        }
    };
    concurrent::spawn_catch_panic(done_wait, move || {
        // assert that the process exits successfully
        let status = process.wait()?;
        Ok(status)
    });

    Ok(ChildStream {
      stdout: BufReader::new(stdout),
      wait_result: FutureExitResult::new(receiver)
    })
}

#[derive(Debug)]
pub enum ProcessAsyncError {
    RecvError(mpsc::RecvError),
    WaitError(io::Error),
    StdinError(io::Error),
    ExitStatusError(Option<i32>),
    AlreadyResolvedError,
}

type ProecessAsyncResult = Result<ExitStatus, ProcessAsyncError>;

pub struct FutureExitResult {
    recv: mpsc::Receiver<ProecessAsyncResult>,
    // This future should only be resolved once
    already: bool,
}

impl FutureExitResult {
   fn new(receiver: mpsc::Receiver<ProecessAsyncResult>) -> Self {
      FutureExitResult { recv: receiver, already: false }
   }

   fn exit_status(&mut self) -> ProecessAsyncResult {
       if self.already { return Err(ProcessAsyncError::AlreadyResolvedError) }
       self.already = true;
       match self.recv.recv() {
           Err(err) => {
               Err(ProcessAsyncError::RecvError(err))
           }
           Ok(stream_status) => {
               stream_status
           }
       }
   }

   pub fn wait(&mut self) -> Result<Option<i32>, ProcessAsyncError> {
        let status = self.exit_status()?;
        if status.success() {
            Ok(status.code())
        } else {
            Err(ProcessAsyncError::ExitStatusError(status.code()))
        }
   }
}

pub struct ChildStream {
    pub stdout: BufReader<ChildStdout>,
    pub wait_result: FutureExitResult
}
impl ChildStream {
   // Wait for the result of the process.
   // This should only be called once.
   // The second time it will return AlreadyResolvedError
   pub fn wait(&mut self) -> Result<Option<i32>, ProcessAsyncError> {
       self.wait_result.wait()
   }
}

fn send_or_log_result<T>(sender: mpsc::Sender<Result<T, ProcessAsyncError>>, result: Result<T, ProcessAsyncError>){
    match sender.send(result) {
        Ok(_) => {}
        Err(err) => error!("error sending done message: {}", err),
    }
}

fn setup_stderr(deal_with_stderr: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stderr {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stderr(Stdio::null());
      }
      &Output::ToFd(ref file) => {
          unsafe {
            cmd.stderr(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
      /*
      &Output::FailOnOutput => {
          cmd.stderr(Stdio::piped());
      }
      &Output::ToPath(_) => {
          cmd.stderr(Stdio::piped());
      }
      */
    }
    Ok(())
}

fn setup_stdout(deal_with_stdout: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stdout {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stdout(Stdio::null());
      }
      &Output::ToFd(ref file) => {
          unsafe {
            cmd.stdout(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
      /*
      &Output::FailOnOutput => {
          cmd.stdout(Stdio::piped());
      }
      &Output::ToPath(_) => {
          cmd.stdout(Stdio::piped());
      }
      */
    }
    Ok(())
}

// Build up the command from the arguments
fn build_command(cmd_args: (String, Vec<String>)) -> Command {
    // Build up the command from the arguments
    let (exe, args) = cmd_args;
    let mut cmd = Command::new(exe);
    for arg in args { cmd.arg(arg); }
    return cmd
}

/*
fn output_optional_handle<R: Read + Send + 'static>(deal_with_output: &Output, opt_handle: &mut Option<R>) -> io::Result<()> {
    if let &Output::FailOnOutput = deal_with_output {
        let handle = opt_handle.take().expect("impossible! no output handle");
        let _ = thread::spawn(move || {
            let mut output = Vec::new();
            let size = handle.take(1).read_to_end(&mut output).expect("error reading output");
            if size > 0 {
              panic!("got unexpected output");
            }
        });
    } else {
        if let &Output::ToPath(ref path) = deal_with_output {
            let mut handle = opt_handle.take().expect("impossible! no output handle");
            let mut file = File::create(path)?;
            let _ = thread::spawn(move || {
                io::copy(&mut handle, &mut file)
                  .expect("error writing output to a file");
            });
        }
    }

    Ok(())
}
*/

mod concurrent {
    use std::io;
    use std::io::{Error, ErrorKind};
    use std::panic;
    use std::thread;
    use std::thread::JoinHandle;
    use std::any::Any;

    pub fn caught_panic_to_io_error(err: Box<Any + Send + 'static>) -> io::Error {
        let msg = match err.downcast_ref::<&'static str>() {
            Some(s) => *s,
            None => {
                match err.downcast_ref::<String>() {
                    Some(s) => &s[..],
                    None => "Box<Any>",
                }
            }
        };

        Error::new(ErrorKind::Other, msg)
    }

    // Spawn a thread and catch any panic unwinds.
    // Call a callback function with the result
    // where Err is the message of the panic and
    // Ok is the return of the function
    pub fn spawn_catch_panic<Function, Returned, Finished>(done: Finished, f: Function) -> JoinHandle<()>
        where Function: FnOnce() -> Returned,
              Function: Send + 'static,
              Function: panic::UnwindSafe,
              Finished: FnOnce(io::Result<Returned>) -> (),
              Finished: Send + 'static,
    {
        thread::spawn(move || {
            let result = panic::catch_unwind(move || { f() });
            match result {
                Err(err) => { done(Err(caught_panic_to_io_error(err))) }
                Ok(ok)   => { done(Ok(ok)) }
            }
        })
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;

#[derive(Debug)]
pub struct Entry<'a> {
    key: &'a[u8],
    value: &'a[u8],
    timestamp: u32,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec().sec as u32,
            deleted: false,
        }
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key,
            value: &[],
            timestamp: time::now().to_timespec().sec as u32,
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: &bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize],
            value: &bytes[STATIC_SIZE + key_size as usize..],
            timestamp: timestamp,
            deleted: value_size == TOMBSTONE
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>add tests for file entry<commit_after>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;

#[derive(Debug, Eq, PartialEq)]
pub struct Entry<'a> {
    key: &'a[u8],
    value: &'a[u8],
    timestamp: u32,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec().sec as u32,
            deleted: false,
        }
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key,
            value: &[],
            timestamp: time::now().to_timespec().sec as u32,
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: &bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize],
            value: &bytes[STATIC_SIZE + key_size as usize..],
            timestamp: timestamp,
            deleted: value_size == TOMBSTONE
        }
    }
}

#[cfg(test)]
mod tests {
    use Entry;

    #[test]
    fn test_serialization() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::new(&key, &value);

        assert_eq!(entry, Entry::from_bytes(&entry.to_bytes()));
        assert_eq!(entry.deleted(), Entry::from_bytes(&entry.deleted().to_bytes()));

        let empty_entry = Entry::new(&key, &[]);

        assert_eq!(empty_entry, Entry::from_bytes(&empty_entry.to_bytes()));
        assert_eq!(empty_entry.deleted(), Entry::from_bytes(&empty_entry.deleted().to_bytes()));

        assert!(Entry::from_bytes(&empty_entry.deleted().to_bytes()).deleted);
        assert!(empty_entry.deleted().deleted);
    }

    #[test]
    fn test_deleted() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::new(&key, &value);

        assert!(entry.deleted().deleted);
        assert_eq!(entry.deleted().value.len(), 0);
    }
}
<|endoftext|>


<commit_before>#[crate_id = "fragments#0.1-pre"];

#[comment = "A simple template library for Rust"];
#[license = "MIT"];
#[crate_type = "lib"];
#[crate_type = "rlib"];

#[doc(html_root_url = "http://www.rust-ci.org/Ogeon/fragments/doc/")];

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::{BufReader, IoError, EndOfFile};
use std::vec::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	content: HashMap<~str, ~fmt::Show>,
	///Conditional switches
	conditions: HashSet<~str>,
	priv tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content),
									next_parser: None
								}
							},
							Ok(c) => fail!("Unknown token type: '{}'", c),
							Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
							Err(e) => fail!("{}", e)
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => content.push_char('['),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => content.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: String(content),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e),
					_ => {}
				}
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e),
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => label.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Placeholder(label),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";
	let mut expected = true;
	let mut expected_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Conditional(label, expected, parse_block(b)),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Conditional(label, expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	#[should_fail]
	fn strange_tokens() {
		let _: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}
}<commit_msg>Changed to the new inner attribute syntax<commit_after>#![crate_id = "fragments#0.1-pre"]

#![comment = "A simple template library for Rust"]
#![license = "MIT"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

#![doc(html_root_url = "http://www.rust-ci.org/Ogeon/fragments/doc/")]

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::{BufReader, IoError, EndOfFile};
use std::vec::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	content: HashMap<~str, ~fmt::Show>,
	///Conditional switches
	conditions: HashSet<~str>,
	priv tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content),
									next_parser: None
								}
							},
							Ok(c) => fail!("Unknown token type: '{}'", c),
							Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
							Err(e) => fail!("{}", e)
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => content.push_char('['),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => content.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: String(content),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e),
					_ => {}
				}
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e),
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => label.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Placeholder(label),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";
	let mut expected = true;
	let mut expected_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Conditional(label, expected, parse_block(b)),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Conditional(label, expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	#[should_fail]
	fn strange_tokens() {
		let _: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}
}<|endoftext|>


<commit_before>#![allow(unstable)]

#[derive(Clone, Show, PartialEq)]
pub struct Error;

pub type ParseResult<O, I> = Result<(O, I), Error>;

pub trait Stream {
    type Item;
    fn uncons(self) -> ParseResult<<Self as Stream>::Item, Self>;
}

impl  Stream for I {
    type Item = ::Item;
    fn uncons(mut self) -> ParseResult<::Item, I> {
        match self.next() {
            Some(x) => Ok((x, self)),
            None => Err(Error)
        }
    }
}

impl <'a> Stream for &'a str {
    type Item = char;
    fn uncons(self) -> ParseResult<char, &'a str> {
        match self.slice_shift_char() {
            Some(x) => Ok(x),
            None => Err(Error)
        }
    }
}

impl <'a, T> Stream for &'a [T] {
    type Item = &'a T;
    fn uncons(self) -> ParseResult<&'a T, &'a [T]> {
        match self {
            [ref x, rest..] => Ok((x, rest)),
            [] => Err(Error)
        }
    }
}

pub trait Parser {
    type Input: Clone + Stream;
    type Output;
    fn parse(&mut self, input: <Self as Parser>::Input) -> ParseResult<<Self as Parser>::Output, <Self as Parser>::Input>;
}
impl <'a, I, O, P> Parser for &'a mut P
    where I: Clone + Stream, P: Parser<Input=I, Output=O> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        (*self).parse(input)
    }
}

pub fn char<'a, I>(input: I) -> ParseResult<char, I>
    where I: Stream<Item=char> + Clone {
    input.uncons()
}

pub struct ManyAppend<'a, O: 'a, P: Parser<Output=O> + 'a> {
    parser: P,
    vec: &'a mut Vec<O>
}
impl <'a, O, P: Parser<Output=O> + 'a> Parser for ManyAppend<'a, O, P> {
    type Input =
::Input;
    type Output = ();
    fn parse(&mut self, mut input:
::Input) -> ParseResult<(),
::Input> {
        loop {
            match self.parser.parse(input.clone()) {
                Ok((x, rest)) => {
                    self.vec.push(x);
                    input = rest;
                }
                Err(_) => break
            }
        }
        Ok(((), input))
    }
}

pub fn many_append<'a, O, P: Parser<Output=O>>(parser: P, vec: &'a mut Vec<O>) -> ManyAppend<'a, O, P> {
    ManyAppend { parser: parser, vec: vec }
}

#[derive(Clone)]
pub struct Many
 {
    parser: P
}
impl
 Parser for Many
 {
    type Input =
::Input;
    type Output = Vec<
::Output>;
    fn parse(&mut self, input:
::Input) -> ParseResult<Vec<
::Output>,
::Input> {
        let mut result = Vec::new();
        let ((), input) = try!(many_append(&mut self.parser, &mut result).parse(input));
        Ok((result, input))
    }
}
pub fn many
(p: P) -> Many
 {
    Many { parser: p }
}

pub fn many1<'a, P: Clone + 'a>(mut p: P) -> Box<Parser<Input=
::Input, Output=Vec<
::Output>> + 'a>
    where P: Parser {
    Box::new(FnParser(move |&mut:input| {
        let (first, input) = try!(p.parse(input));
        let mut result = vec![first];
        let ((), input) = try!(many_append(&mut p, &mut result).parse(input));
        Ok((result, input))
    }))
}

#[derive(Clone)]
pub struct SepBy
 {
    parser: P,
    separator: S
}
impl
 Parser for SepBy

    where P: Parser, S: Parser<Input=
::Input> {

    type Input =
::Input;
    type Output = Vec<
::Output>;
    fn parse(&mut self, mut input:
::Input) -> ParseResult<Vec<
::Output>,
::Input> {
        let mut result = Vec::new();
        match self.parser.parse(input.clone()) {
            Ok((x, rest)) => {
                result.push(x);
                input = rest;
            }
            Err(_) => return Ok((result, input))
        }
        let rest = FnParser(|input| {
            let mut env = Env::new(input);
            try!(env.with(&mut self.separator));
            let v = try!(env.with(&mut self.parser));
            env.result(v)
        });
        let ((), input) = try!(many_append(rest, &mut result).parse(input));
        Ok((result, input))
    }
}
pub fn sep_by
(parser: P, separator: S) -> SepBy
 {
    SepBy { parser: parser, separator: separator }
}

impl <'a, I: Clone + Stream, O> Parser for Box<FnMut(I) -> ParseResult<O, I> + 'a> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        self(input)
    }
}

#[derive(Clone)]
struct FnParser<'a, I: Stream, O, F: FnMut(I) -> ParseResult<O, I>>(F);

impl <'a, I, O, F> Parser for FnParser<'a, I, O, F>
    where I: Clone + Stream, F: FnMut(I) -> ParseResult<O, I> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        (self.0)(input)
    }
}

impl <'a, I, O> Parser for fn (I) -> ParseResult<O, I>
    where I: Clone + Stream {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        self(input)
    }
}

#[derive(Clone)]
pub struct Satisfy { pred: Pred }

impl <'a, I, Pred> Parser for Satisfy
    where I: Stream<Item=char> + Clone, Pred: FnMut(char) -> bool {

    type Input = I;
    type Output = char;
    fn parse(&mut self, input: I) -> ParseResult<char, I> {
        match input.uncons() {
            Ok((c, s)) => {
                if (self.pred)(c) { Ok((c, s)) }
                else { Err(Error) }
            }
            Err(err) => Err(err)
        }
    }
}

pub fn satisfy(pred: Pred) -> Satisfy
    where I: Stream + Clone, Pred: FnMut(char) -> bool {
    Satisfy { pred: pred }
}

pub fn space() -> Satisfy bool>
    where I: Stream + Clone {
    satisfy(CharExt::is_whitespace as fn (char) -> bool)
}

#[derive(Clone)]
pub struct StringP<'a, I> { s: &'a str }
impl <'a, 'b, I> Parser for StringP<'b, I>
    where I: Stream<Item=char> + Clone {
    type Input = I;
    type Output = &'b str;
    fn parse(&mut self, mut input: I) -> ParseResult<&'b str, I> {
        for c in self.s.chars() {
            match input.uncons() {
                Ok((other, rest)) => {
                    if c != other { return Err(Error);  }
                    input = rest;
                }
                Err(err) => return Err(err)
            }
        }
        Ok((self.s, input))
    }
}

pub fn string(s: &str) -> StringP
    where I: Stream + Clone {
    StringP { s: s }
}

#[derive(Clone)]
pub struct AndThen<P1, P2>(P1, P2);
impl  Parser for AndThen<P1, P2>
    where I: Clone + Stream, P1: Parser<Input=I, Output=A>, P2: Parser<Input=I, Output=B> {

    type Input = I;
    type Output = (A, B);
    fn parse(&mut self, input: I) -> ParseResult<(A, B), I> {
        let (a, rest) = try!(self.0.parse(input));
        let (b, rest) = try!(self.1.parse(rest));
        Ok(((a, b), rest))
    }
}
pub fn and_then<P1, P2>(p1: P1, p2: P2) -> AndThen<P1, P2>
    where P1: Parser, P2: Parser {
    AndThen(p1, p2)
}

#[derive(Clone)]
pub struct Optional
(P);
impl
 Parser for Optional

    where P: Parser {
    type Input =
::Input;
    type Output = Option<
::Output>;
    fn parse(&mut self, input:
::Input) -> ParseResult<Option<
::Output>,
::Input> {
        match self.0.parse(input.clone()) {
            Ok((x, rest)) => Ok((Some(x), rest)),
            Err(_) => Ok((None, input))
        }
    }
}
pub fn optional
(parser: P) -> Optional
 {
    Optional(parser)
}

pub struct Env {
    input: I
}

impl  Env {
    pub fn new(input: I) -> Env {
        Env { input: input }
    }

    pub fn with
(&mut self, mut parser: P) -> Result<O, Error>
        where P: Parser<Input=I, Output=O> {
        let (o, rest) = try!(parser.parse(self.input.clone()));
        self.input = rest;
        Ok(o)
    }

    pub fn result<O>(self, output: O) -> ParseResult<O, I> {
        Ok((output, self.input))
    }
}

pub fn digit<'a, I>(input: I) -> ParseResult<char, I>
    where I: Stream<Item=char> + Clone {
    match input.uncons() {
        Ok((c, rest)) => {
            if c.is_digit(10) { Ok((c, rest)) }
            else { Err(Error) }
        }
        Err(err) => Err(err)
    }
}

pub trait ParserExt {
    fn and_then
(self, P) -> AndThen<Self, P>
        where P: Parser;
}

impl
 ParserExt for P {
    fn and_then<P2>(self, p: P2) -> AndThen<Self, P2>
        where P2: Parser {
        and_then(self, p)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn integer<'a, I>(input: I) -> ParseResult<i64, I>
        where I: Stream<Item=char> + Clone {
        let mut env = Env::new(input);
        let chars = try!(env.with(many(digit as fn(_) -> _)));
        let mut n = 0;
        for &c in chars.iter() {
            n = n * 10 + (c as i64 - '0' as i64);
        }
        env.result(n)
    }

    #[test]
    fn test_integer() {
        assert_eq!((integer as fn(_) -> _).parse("123"), Ok((123i64, "")));
    }
    #[test]
    fn list() {
        let mut p = sep_by(integer as fn(_) -> _, satisfy(|c| c == ','));
        assert_eq!(p.parse("123,4,56"), Ok((vec![123, 4, 56], "")));
    }
    #[test]
    fn iterator() {
        let result = (integer as fn(_) -> _).parse("123".chars())
            .map(|(i, mut iter)| (i, iter.next()));
        assert_eq!(result, Ok((123i64, None)));
    }
    #[test]
    fn field() {
        let word = many(satisfy(|c| c.is_alphanumeric()));
        let word2 = many(satisfy(|c| c.is_alphanumeric()));
        let spaces = many(space());
        let c_decl = word
            .and_then(spaces.clone())
            .and_then(satisfy(|c| c == ':'))
            .and_then(spaces)
            .and_then(word2)
            .parse("x: int")
            .map(|(((((ret, _), _), _), name), rest)| ((ret, name), rest));
        assert_eq!(c_decl, Ok(((vec!['x'], vec!['i', 'n', 't']), "")));
    }
}
<commit_msg>Added ParserExt trait to allow chaining of combinators<commit_after>#![allow(unstable)]

#[derive(Clone, Show, PartialEq)]
pub struct Error;

pub type ParseResult<O, I> = Result<(O, I), Error>;

pub trait Stream {
    type Item;
    fn uncons(self) -> ParseResult<<Self as Stream>::Item, Self>;
}

impl  Stream for I {
    type Item = ::Item;
    fn uncons(mut self) -> ParseResult<::Item, I> {
        match self.next() {
            Some(x) => Ok((x, self)),
            None => Err(Error)
        }
    }
}

impl <'a> Stream for &'a str {
    type Item = char;
    fn uncons(self) -> ParseResult<char, &'a str> {
        match self.slice_shift_char() {
            Some(x) => Ok(x),
            None => Err(Error)
        }
    }
}

impl <'a, T> Stream for &'a [T] {
    type Item = &'a T;
    fn uncons(self) -> ParseResult<&'a T, &'a [T]> {
        match self {
            [ref x, rest..] => Ok((x, rest)),
            [] => Err(Error)
        }
    }
}

pub trait Parser {
    type Input: Clone + Stream;
    type Output;
    fn parse(&mut self, input: <Self as Parser>::Input) -> ParseResult<<Self as Parser>::Output, <Self as Parser>::Input>;
}
impl <'a, I, O, P> Parser for &'a mut P
    where I: Clone + Stream, P: Parser<Input=I, Output=O> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        (*self).parse(input)
    }
}

pub fn char<'a, I>(input: I) -> ParseResult<char, I>
    where I: Stream<Item=char> + Clone {
    input.uncons()
}

pub struct ManyAppend<'a, O: 'a, P: Parser<Output=O> + 'a> {
    parser: P,
    vec: &'a mut Vec<O>
}
impl <'a, O, P: Parser<Output=O> + 'a> Parser for ManyAppend<'a, O, P> {
    type Input =
::Input;
    type Output = ();
    fn parse(&mut self, mut input:
::Input) -> ParseResult<(),
::Input> {
        loop {
            match self.parser.parse(input.clone()) {
                Ok((x, rest)) => {
                    self.vec.push(x);
                    input = rest;
                }
                Err(_) => break
            }
        }
        Ok(((), input))
    }
}

pub fn many_append<'a, O, P: Parser<Output=O>>(parser: P, vec: &'a mut Vec<O>) -> ManyAppend<'a, O, P> {
    ManyAppend { parser: parser, vec: vec }
}

#[derive(Clone)]
pub struct Many
 {
    parser: P
}
impl
 Parser for Many
 {
    type Input =
::Input;
    type Output = Vec<
::Output>;
    fn parse(&mut self, input:
::Input) -> ParseResult<Vec<
::Output>,
::Input> {
        let mut result = Vec::new();
        let ((), input) = try!(many_append(&mut self.parser, &mut result).parse(input));
        Ok((result, input))
    }
}
pub fn many
(p: P) -> Many
 {
    Many { parser: p }
}

pub fn many1<'a, P: Clone + 'a>(mut p: P) -> Box<Parser<Input=
::Input, Output=Vec<
::Output>> + 'a>
    where P: Parser {
    Box::new(FnParser(move |&mut:input| {
        let (first, input) = try!(p.parse(input));
        let mut result = vec![first];
        let ((), input) = try!(many_append(&mut p, &mut result).parse(input));
        Ok((result, input))
    }))
}

#[derive(Clone)]
pub struct SepBy
 {
    parser: P,
    separator: S
}
impl
 Parser for SepBy

    where P: Parser, S: Parser<Input=
::Input> {

    type Input =
::Input;
    type Output = Vec<
::Output>;
    fn parse(&mut self, mut input:
::Input) -> ParseResult<Vec<
::Output>,
::Input> {
        let mut result = Vec::new();
        match self.parser.parse(input.clone()) {
            Ok((x, rest)) => {
                result.push(x);
                input = rest;
            }
            Err(_) => return Ok((result, input))
        }
        let rest = FnParser(|input| {
            let mut env = Env::new(input);
            try!(env.with(&mut self.separator));
            let v = try!(env.with(&mut self.parser));
            env.result(v)
        });
        let ((), input) = try!(many_append(rest, &mut result).parse(input));
        Ok((result, input))
    }
}
pub fn sep_by
(parser: P, separator: S) -> SepBy
 {
    SepBy { parser: parser, separator: separator }
}

impl <'a, I: Clone + Stream, O> Parser for Box<FnMut(I) -> ParseResult<O, I> + 'a> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        self(input)
    }
}

#[derive(Clone)]
struct FnParser<'a, I: Stream, O, F: FnMut(I) -> ParseResult<O, I>>(F);

impl <'a, I, O, F> Parser for FnParser<'a, I, O, F>
    where I: Clone + Stream, F: FnMut(I) -> ParseResult<O, I> {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        (self.0)(input)
    }
}

impl <'a, I, O> Parser for fn (I) -> ParseResult<O, I>
    where I: Clone + Stream {
    type Input = I;
    type Output = O;
    fn parse(&mut self, input: I) -> ParseResult<O, I> {
        self(input)
    }
}

#[derive(Clone)]
pub struct Satisfy { pred: Pred }

impl <'a, I, Pred> Parser for Satisfy
    where I: Stream<Item=char> + Clone, Pred: FnMut(char) -> bool {

    type Input = I;
    type Output = char;
    fn parse(&mut self, input: I) -> ParseResult<char, I> {
        match input.uncons() {
            Ok((c, s)) => {
                if (self.pred)(c) { Ok((c, s)) }
                else { Err(Error) }
            }
            Err(err) => Err(err)
        }
    }
}

pub fn satisfy(pred: Pred) -> Satisfy
    where I: Stream + Clone, Pred: FnMut(char) -> bool {
    Satisfy { pred: pred }
}

pub fn space() -> Satisfy bool>
    where I: Stream + Clone {
    satisfy(CharExt::is_whitespace as fn (char) -> bool)
}

#[derive(Clone)]
pub struct StringP<'a, I> { s: &'a str }
impl <'a, 'b, I> Parser for StringP<'b, I>
    where I: Stream<Item=char> + Clone {
    type Input = I;
    type Output = &'b str;
    fn parse(&mut self, mut input: I) -> ParseResult<&'b str, I> {
        for c in self.s.chars() {
            match input.uncons() {
                Ok((other, rest)) => {
                    if c != other { return Err(Error);  }
                    input = rest;
                }
                Err(err) => return Err(err)
            }
        }
        Ok((self.s, input))
    }
}

pub fn string(s: &str) -> StringP
    where I: Stream + Clone {
    StringP { s: s }
}

#[derive(Clone)]
pub struct AndThen<P1, P2>(P1, P2);
impl  Parser for AndThen<P1, P2>
    where I: Clone + Stream, P1: Parser<Input=I, Output=A>, P2: Parser<Input=I, Output=B> {

    type Input = I;
    type Output = (A, B);
    fn parse(&mut self, input: I) -> ParseResult<(A, B), I> {
        let (a, rest) = try!(self.0.parse(input));
        let (b, rest) = try!(self.1.parse(rest));
        Ok(((a, b), rest))
    }
}
pub fn and_then<P1, P2>(p1: P1, p2: P2) -> AndThen<P1, P2>
    where P1: Parser, P2: Parser {
    AndThen(p1, p2)
}

#[derive(Clone)]
pub struct Optional
(P);
impl
 Parser for Optional

    where P: Parser {
    type Input =
::Input;
    type Output = Option<
::Output>;
    fn parse(&mut self, input:
::Input) -> ParseResult<Option<
::Output>,
::Input> {
        match self.0.parse(input.clone()) {
            Ok((x, rest)) => Ok((Some(x), rest)),
            Err(_) => Ok((None, input))
        }
    }
}
pub fn optional
(parser: P) -> Optional
 {
    Optional(parser)
}

pub struct Env {
    input: I
}

impl  Env {
    pub fn new(input: I) -> Env {
        Env { input: input }
    }

    pub fn with
(&mut self, mut parser: P) -> Result<O, Error>
        where P: Parser<Input=I, Output=O> {
        let (o, rest) = try!(parser.parse(self.input.clone()));
        self.input = rest;
        Ok(o)
    }

    pub fn result<O>(self, output: O) -> ParseResult<O, I> {
        Ok((output, self.input))
    }
}

pub fn digit<'a, I>(input: I) -> ParseResult<char, I>
    where I: Stream<Item=char> + Clone {
    match input.uncons() {
        Ok((c, rest)) => {
            if c.is_digit(10) { Ok((c, rest)) }
            else { Err(Error) }
        }
        Err(err) => Err(err)
    }
}

pub struct With<P1, P2>(P1, P2) where P1: Parser, P2: Parser;
impl  Parser for With<P1, P2>
    where I: Clone + Stream, P1: Parser<Input=I>, P2: Parser<Input=I> {

    type Input = I;
    type Output = <P2 as Parser>::Output;
    fn parse(&mut self, input: I) -> ParseResult<<Self as Parser>::Output, I> {
        let ((_, b), rest) = try!((&mut self.0).and_then(&mut self.1).parse(input));
        Ok((b, rest))
    }
}
pub struct Skip<P1, P2>(P1, P2) where P1: Parser, P2: Parser;
impl  Parser for Skip<P1, P2>
    where I: Clone + Stream, P1: Parser<Input=I>, P2: Parser<Input=I> {

    type Input = I;
    type Output = <P1 as Parser>::Output;
    fn parse(&mut self, input: I) -> ParseResult<<Self as Parser>::Output, I> {
        let ((a, _), rest) = try!((&mut self.0).and_then(&mut self.1).parse(input));
        Ok((a, rest))
    }
}

pub trait ParserExt : Parser + Sized {
    fn and_then<P2>(self, p: P2) -> AndThen<Self, P2>
        where P2: Parser {
        and_then(self, p)
    }
    fn with<P2>(self, p: P2) -> With<Self, P2>
        where P2: Parser {
        With(self, p)
    }
    fn skip<P2>(self, p: P2) -> Skip<Self, P2>
        where P2: Parser {
        Skip(self, p)
    }
}

impl
 ParserExt for P { }

#[cfg(test)]
mod tests {
    use super::*;

    fn integer<'a, I>(input: I) -> ParseResult<i64, I>
        where I: Stream<Item=char> + Clone {
        let mut env = Env::new(input);
        let chars = try!(env.with(many(digit as fn(_) -> _)));
        let mut n = 0;
        for &c in chars.iter() {
            n = n * 10 + (c as i64 - '0' as i64);
        }
        env.result(n)
    }

    #[test]
    fn test_integer() {
        assert_eq!((integer as fn(_) -> _).parse("123"), Ok((123i64, "")));
    }
    #[test]
    fn list() {
        let mut p = sep_by(integer as fn(_) -> _, satisfy(|c| c == ','));
        assert_eq!(p.parse("123,4,56"), Ok((vec![123, 4, 56], "")));
    }
    #[test]
    fn iterator() {
        let result = (integer as fn(_) -> _).parse("123".chars())
            .map(|(i, mut iter)| (i, iter.next()));
        assert_eq!(result, Ok((123i64, None)));
    }
    #[test]
    fn field() {
        let word = many(satisfy(|c| c.is_alphanumeric()));
        let word2 = many(satisfy(|c| c.is_alphanumeric()));
        let spaces = many(space());
        let c_decl = word
            .skip(spaces.clone())
            .skip(satisfy(|c| c == ':'))
            .skip(spaces)
            .and_then(word2)
            .parse("x: int");
        assert_eq!(c_decl, Ok(((vec!['x'], vec!['i', 'n', 't']), "")));
    }
}
<|endoftext|>


<commit_before>//! Rust bindings for [libpcuid](https://github.com/anrieff/libcpuid)
//! CPU detection and feature extraction library.
//!
//! `rust-cpuid` provides a high-level interface for getting information
//! about the features of the CPU that runs your code. All the essential
//! work is done by the `libcpuid` C library and exposed through Rust's
//! FFI mechanism as a simple and convenient API.
//!
//! # Available features
//!
//! * CPU vendor, brand and codename detection
//! * information about number of cores (both physical and logical)
//! * cache size
//! * clock frequency
//!
//! # Installation
//!
//! First - download, and build libcpuid as [described in the readme](https://github.com/anrieff/libcpuid). Install it by running `make install` (you may want to run `ldconfig` afterwards).
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! cpuid = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! extern crate cpuid;
//!
//! fn main () {
//!     match cpuid::identify() {
//!         Ok(info) => {
//!             println!("Found: {} CPU, model: {}", info.vendor, info.codename);
//!             println!("The full brand string is: {}", info.brand);
//!             println!("Hardware AES support: {}", if info.has_feature(cpuid::CpuFeature::AES) { "yes" } else { "no" });
//!         },
//!         Err(err) => println!("cpuid error: {}", err),
//!     };
//!     match cpuid::clock_frequency() {
//!         Some(frequency) => println!("CPU speed: {} MHz", frequency),
//!         None => println!("Couldn't get CPU speed."),
//!     };
//! }
//! ```

extern crate libc;

use std::c_str::CString;
use std::mem;

mod ffi;

/// A struct holding information about CPU features.
///
/// This data structure is returned by `identify()`. You can consult
/// [libcpuid docs for cpu_id_t](http://libcpuid.sourceforge.net/doxy/structcpu__id__t.html)
/// for more detailed descriptions of these fields.
pub struct CpuInfo {
    /// CPU vendor string, for example *GenuineIntel*.
    pub vendor: String,
    /// Brand string, for example *Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz*.
    pub brand: String,
    /// Brief CPU codename, such as *Sandy Bridge (Core i5)*.
    pub codename: String,
    /// Number of physical cores of the current CPU.
    pub num_cores: int,
    /// Number of logical processors (may include HyperThreading or such).
    pub num_logical_cpus: int,
    /// Total number of logical processors.
    pub total_logical_cpus: int,
    /// L1 data cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_data_cache: Option<int>,
    /// L1 instruction cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_instruction_cache: Option<int>,
    /// L2 cache size in kB. `Some(0)` if the CPU lacks L2 cache, `None` if it couldn't be determined.
    pub l2_cache: Option<int>,
    /// L3 cache size in kB. `Some(0)` if the CPU lacks L3 cache, `None` if it couldn't be determined.
    pub l3_cache: Option<int>,
    flags: [u8, ..ffi::CPU_FLAGS_MAX],
}

/// CPU feature identifiers.
pub enum CpuFeature {
    FloatingPointUnit = 0,
    VirtualModeExtension,
    DebugingExtension,
    PageSizeExtension,
    TimestampCounter,
    ModelSpecificRegisters,
    PhysicalAddressExtension,
    MachineCheckException,
    CMPXCHG8B,
    APIC,
    MemoryTypeRangeRegisters,
    SysenterSysexit,
    PageGlobalEnable,
    MachineCheckArchitecture,
    CMOV,
    PageAttributeTable,
    PageAddressExtension36bit,
    ProcessorSerialNumber,
    CLFLUSH,
    DebugStore,
    ACPI,
    MMX,
    FXSAVE,
    SSE,
    SSE2,
    SelfSnoop,
    HyperThreading,
    ThermalMonitor,
    IA64,
    PendingBreakEnable,
    SSE3,
    PCLMULQDQ,
    DebugStore64,
    MONITOR,
    CplQualifiedDebugStore,
    VirtualisationTechnology,
    SaferModeExceptions,
    EnhancedSpeedStep,
    ThermalMonitor2,
    SSSE3,
    ContextId,
    CMPXCHG16B,
    SendTaskPriorityMessages,
    PerformanceCapabilitiesMSR,
    DirectCacheAccess,
    SSE41,
    SSE42,
    SyscallSysret,
    ExecuteDisableBit,
    MOVBE,
    POPCNT,
    AES,
    XSAVE,
    OSXSAVE,
    AdvancedVectorExtensions,
    MMXExtensions,
    AMD3DNow,
    AMD3DNowExtended,
    NoExecuteBit,
    FXSAVEOptimizations,
    RDTSCP,
    LongMode,
    LAHFLongMode,
    CoreMultiProcessingLegacyMode,
    AMDSecureVirtualMachine,
    LZCNT,
    MisalignedSSE,
    SSE4a,
    PREFETCH,
    OsVisibleWorkaround,
    InstructionBasedSampling,
    SSE5,
    SKINIT,
    WatchdogTimer,
    TemperatureSensor,
    FrequencyIDControl,
    VoltageIDControl,
    THERMTRIP,
    AMDThermalControl,
    SoftwareThermalControl,
    Multiplier100Mhz,
    HardwarePstateControl,
    ConstantTSCTicks,
    XOP,
    FMA3,
    FMA4,
    TrailingBitManipulation,
    FPConvert16Bit,
    RDRAND,
    X2APIC,
    CorePerformanceBoost,
    MPERF,
    ProcessorFeedbackInterface,
    ProcessorAccumulator,
    NumCpuFeatures,
}

impl CpuInfo {
    /// Checks if current CPU supports given feature.
    ///
    /// See `CpuFeature` for a list of available feature identifiers.
    pub fn has_feature(&self, feature: CpuFeature) -> bool {
        return self.flags[feature as uint] == 1u8
    }
}

/// Checks if the CPUID instruction is present.
pub fn is_present() -> bool {
    unsafe {
        ffi::cpuid_present() == 1
    }
}

/// Returns libcpuid version string.
pub fn version() -> String {
    let version_string = unsafe {
        let ptr = ffi::cpuid_lib_version();
        CString::new(ptr, false)
    };
    version_string.as_str().unwrap().to_string()
}

/// Returns last libcpuid error string.
pub fn error() -> String {
    let error_string = unsafe {
        let ptr = ffi::cpuid_error();
        CString::new(ptr, false)
    };
    error_string.as_str().unwrap().to_string()
}

/// Tries to identify the current CPU and its features.
///
/// In case of successful detection, a `CpuInfo` struct is returned (wrapped
/// with `Ok`) which contains all available data about the processor.
/// If libcpuid encounters an error, `identify` returns an `Err` with
/// the error message inside.
pub fn identify() -> Result<CpuInfo, String> {
    let mut raw: ffi::cpu_raw_data_t = unsafe { mem::uninitialized() };
    let raw_result = unsafe {
        ffi::cpuid_get_raw_data(&mut raw)
    };
    if raw_result != 0 {
        return Err(error());
    }
    let mut data: ffi::cpu_id_t = unsafe { mem::uninitialized() };
    let identify_result = unsafe {
        ffi::cpu_identify(&mut raw, &mut data)
    };
    if identify_result != 0 {
        Err(error())
    } else {
        Ok(CpuInfo {
            vendor: String::from_utf8(data.vendor_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid vendor string"),
            brand: String::from_utf8(data.brand_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid brand string"),
            codename: String::from_utf8(data.cpu_codename.iter().map(|&x| x as u8).collect()).ok().expect("Invalid codename string"),
            num_cores: data.num_cores as int,
            num_logical_cpus: data.num_logical_cpus as int,
            total_logical_cpus: data.total_logical_cpus as int,
            l1_data_cache: if data.l1_data_cache != -1 { Some(data.l1_data_cache as int) } else { None },
            l1_instruction_cache: if data.l1_instruction_cache != -1 { Some(data.l1_instruction_cache as int) } else { None },
            l2_cache: if data.l2_cache != -1 { Some(data.l2_cache as int) } else { None },
            l3_cache: if data.l3_cache != -1 { Some(data.l3_cache as int) } else { None },
            flags: data.flags,
        })
    }
}

/// Gets the CPU clock frequency in MHz.
///
/// The underlying implementation uses several methods to discover CPU
/// speed, including direct measurement. If all these methods fail, function
/// returns `None`.
pub fn clock_frequency() -> Option<int> {
    let frequency = unsafe {
        ffi::cpu_clock()
    };
    if frequency != -1 {
        Some(frequency as int)
    } else {
        None
    }
}

#[test]
fn test_is_present() {
    assert!(is_present());
}
<commit_msg>Added deriving(Copy) to CpuFeature.<commit_after>//! Rust bindings for [libpcuid](https://github.com/anrieff/libcpuid)
//! CPU detection and feature extraction library.
//!
//! `rust-cpuid` provides a high-level interface for getting information
//! about the features of the CPU that runs your code. All the essential
//! work is done by the `libcpuid` C library and exposed through Rust's
//! FFI mechanism as a simple and convenient API.
//!
//! # Available features
//!
//! * CPU vendor, brand and codename detection
//! * information about number of cores (both physical and logical)
//! * cache size
//! * clock frequency
//!
//! # Installation
//!
//! First - download, and build libcpuid as [described in the readme](https://github.com/anrieff/libcpuid). Install it by running `make install` (you may want to run `ldconfig` afterwards).
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! cpuid = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! extern crate cpuid;
//!
//! fn main () {
//!     match cpuid::identify() {
//!         Ok(info) => {
//!             println!("Found: {} CPU, model: {}", info.vendor, info.codename);
//!             println!("The full brand string is: {}", info.brand);
//!             println!("Hardware AES support: {}", if info.has_feature(cpuid::CpuFeature::AES) { "yes" } else { "no" });
//!         },
//!         Err(err) => println!("cpuid error: {}", err),
//!     };
//!     match cpuid::clock_frequency() {
//!         Some(frequency) => println!("CPU speed: {} MHz", frequency),
//!         None => println!("Couldn't get CPU speed."),
//!     };
//! }
//! ```

extern crate libc;

use std::c_str::CString;
use std::mem;

mod ffi;

/// A struct holding information about CPU features.
///
/// This data structure is returned by `identify()`. You can consult
/// [libcpuid docs for cpu_id_t](http://libcpuid.sourceforge.net/doxy/structcpu__id__t.html)
/// for more detailed descriptions of these fields.
pub struct CpuInfo {
    /// CPU vendor string, for example *GenuineIntel*.
    pub vendor: String,
    /// Brand string, for example *Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz*.
    pub brand: String,
    /// Brief CPU codename, such as *Sandy Bridge (Core i5)*.
    pub codename: String,
    /// Number of physical cores of the current CPU.
    pub num_cores: int,
    /// Number of logical processors (may include HyperThreading or such).
    pub num_logical_cpus: int,
    /// Total number of logical processors.
    pub total_logical_cpus: int,
    /// L1 data cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_data_cache: Option<int>,
    /// L1 instruction cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_instruction_cache: Option<int>,
    /// L2 cache size in kB. `Some(0)` if the CPU lacks L2 cache, `None` if it couldn't be determined.
    pub l2_cache: Option<int>,
    /// L3 cache size in kB. `Some(0)` if the CPU lacks L3 cache, `None` if it couldn't be determined.
    pub l3_cache: Option<int>,
    flags: [u8, ..ffi::CPU_FLAGS_MAX],
}

/// CPU feature identifiers.
#[deriving(Copy)]
pub enum CpuFeature {
    FloatingPointUnit = 0,
    VirtualModeExtension,
    DebugingExtension,
    PageSizeExtension,
    TimestampCounter,
    ModelSpecificRegisters,
    PhysicalAddressExtension,
    MachineCheckException,
    CMPXCHG8B,
    APIC,
    MemoryTypeRangeRegisters,
    SysenterSysexit,
    PageGlobalEnable,
    MachineCheckArchitecture,
    CMOV,
    PageAttributeTable,
    PageAddressExtension36bit,
    ProcessorSerialNumber,
    CLFLUSH,
    DebugStore,
    ACPI,
    MMX,
    FXSAVE,
    SSE,
    SSE2,
    SelfSnoop,
    HyperThreading,
    ThermalMonitor,
    IA64,
    PendingBreakEnable,
    SSE3,
    PCLMULQDQ,
    DebugStore64,
    MONITOR,
    CplQualifiedDebugStore,
    VirtualisationTechnology,
    SaferModeExceptions,
    EnhancedSpeedStep,
    ThermalMonitor2,
    SSSE3,
    ContextId,
    CMPXCHG16B,
    SendTaskPriorityMessages,
    PerformanceCapabilitiesMSR,
    DirectCacheAccess,
    SSE41,
    SSE42,
    SyscallSysret,
    ExecuteDisableBit,
    MOVBE,
    POPCNT,
    AES,
    XSAVE,
    OSXSAVE,
    AdvancedVectorExtensions,
    MMXExtensions,
    AMD3DNow,
    AMD3DNowExtended,
    NoExecuteBit,
    FXSAVEOptimizations,
    RDTSCP,
    LongMode,
    LAHFLongMode,
    CoreMultiProcessingLegacyMode,
    AMDSecureVirtualMachine,
    LZCNT,
    MisalignedSSE,
    SSE4a,
    PREFETCH,
    OsVisibleWorkaround,
    InstructionBasedSampling,
    SSE5,
    SKINIT,
    WatchdogTimer,
    TemperatureSensor,
    FrequencyIDControl,
    VoltageIDControl,
    THERMTRIP,
    AMDThermalControl,
    SoftwareThermalControl,
    Multiplier100Mhz,
    HardwarePstateControl,
    ConstantTSCTicks,
    XOP,
    FMA3,
    FMA4,
    TrailingBitManipulation,
    FPConvert16Bit,
    RDRAND,
    X2APIC,
    CorePerformanceBoost,
    MPERF,
    ProcessorFeedbackInterface,
    ProcessorAccumulator,
    NumCpuFeatures,
}

impl CpuInfo {
    /// Checks if current CPU supports given feature.
    ///
    /// See `CpuFeature` for a list of available feature identifiers.
    pub fn has_feature(&self, feature: CpuFeature) -> bool {
        return self.flags[feature as uint] == 1u8
    }
}

/// Checks if the CPUID instruction is present.
pub fn is_present() -> bool {
    unsafe {
        ffi::cpuid_present() == 1
    }
}

/// Returns libcpuid version string.
pub fn version() -> String {
    let version_string = unsafe {
        let ptr = ffi::cpuid_lib_version();
        CString::new(ptr, false)
    };
    version_string.as_str().unwrap().to_string()
}

/// Returns last libcpuid error string.
pub fn error() -> String {
    let error_string = unsafe {
        let ptr = ffi::cpuid_error();
        CString::new(ptr, false)
    };
    error_string.as_str().unwrap().to_string()
}

/// Tries to identify the current CPU and its features.
///
/// In case of successful detection, a `CpuInfo` struct is returned (wrapped
/// with `Ok`) which contains all available data about the processor.
/// If libcpuid encounters an error, `identify` returns an `Err` with
/// the error message inside.
pub fn identify() -> Result<CpuInfo, String> {
    let mut raw: ffi::cpu_raw_data_t = unsafe { mem::uninitialized() };
    let raw_result = unsafe {
        ffi::cpuid_get_raw_data(&mut raw)
    };
    if raw_result != 0 {
        return Err(error());
    }
    let mut data: ffi::cpu_id_t = unsafe { mem::uninitialized() };
    let identify_result = unsafe {
        ffi::cpu_identify(&mut raw, &mut data)
    };
    if identify_result != 0 {
        Err(error())
    } else {
        Ok(CpuInfo {
            vendor: String::from_utf8(data.vendor_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid vendor string"),
            brand: String::from_utf8(data.brand_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid brand string"),
            codename: String::from_utf8(data.cpu_codename.iter().map(|&x| x as u8).collect()).ok().expect("Invalid codename string"),
            num_cores: data.num_cores as int,
            num_logical_cpus: data.num_logical_cpus as int,
            total_logical_cpus: data.total_logical_cpus as int,
            l1_data_cache: if data.l1_data_cache != -1 { Some(data.l1_data_cache as int) } else { None },
            l1_instruction_cache: if data.l1_instruction_cache != -1 { Some(data.l1_instruction_cache as int) } else { None },
            l2_cache: if data.l2_cache != -1 { Some(data.l2_cache as int) } else { None },
            l3_cache: if data.l3_cache != -1 { Some(data.l3_cache as int) } else { None },
            flags: data.flags,
        })
    }
}

/// Gets the CPU clock frequency in MHz.
///
/// The underlying implementation uses several methods to discover CPU
/// speed, including direct measurement. If all these methods fail, function
/// returns `None`.
pub fn clock_frequency() -> Option<int> {
    let frequency = unsafe {
        ffi::cpu_clock()
    };
    if frequency != -1 {
        Some(frequency as int)
    } else {
        None
    }
}

#[test]
fn test_is_present() {
    assert!(is_present());
}
<|endoftext|>


<commit_before>// Needed for Christoffel symbols in arr! macro
// (64 numbers in a 4D spacetime)
#![recursion_limit = "70"]

pub extern crate diffgeom;
pub extern crate generic_array;
extern crate numeric_algs;

pub use generic_array::typenum;

pub mod coord_systems;
mod entity;
pub mod numeric;
mod particle;

pub use crate::entity::Entity;
pub use crate::particle::Particle;
<commit_msg>Export the PosAndVel trait<commit_after>// Needed for Christoffel symbols in arr! macro
// (64 numbers in a 4D spacetime)
#![recursion_limit = "70"]

pub extern crate diffgeom;
pub extern crate generic_array;
extern crate numeric_algs;

pub use generic_array::typenum;

pub mod coord_systems;
mod entity;
pub mod numeric;
mod particle;

pub use crate::entity::Entity;
pub use crate::particle::{Particle, PosAndVel};
<|endoftext|>