<commit_before>//***********************************************************************
//  Copyright 2006 John A. Trangenstein
//
//  This software is made available for research and instructional use
//  only.e available for research and instructional use
//  only.
//  You may copy and use this software without charge for these
//  non-commercial purposes, provided that the copyright notice and
//  associated text is reproduced on all copies.
//  For all other uses (including distribution of modified versions),
//  please contact the author at
//    John A. Trangenstein
//    Department of Mathematics
//    Duke University
//    Durham, NC 27708-0320
//    USA
//  or
//    johnt@math.duke.edu
//
//  This software is made available "as is" without any assurance that it
//  is completely correct, or that it will work for your purposes.
//  Use the software at your own risk.
//***********************************************************************

// Permission to publish the translation of the code
// was graciously granted by Prof. John A. Trangenstein

// The MIT License (MIT)
//
// Copyright (c) 2015 Alexander Samoilov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#![feature(duration_span)]

extern crate argparse;
extern crate num;
#[macro_use(tensor)]
extern crate numeric;

// http://numeric.rs/doc/numeric/tensor/struct.Tensor.html
use numeric::Tensor;

use num::{Num, Zero, One, Signed};

use std::time::Duration;
use std::cmp;
use std::process::exit;
use argparse::{ArgumentParser, StoreTrue, Store};

struct Options {
    verbose: bool,
    num_steps: usize,
    num_cells: usize,
    num_runs: usize,
}

fn min_dx(x: &[f64]) -> f64 {
    // https://doc.rust-lang.org/std/primitive.slice.html#method.windows
    let tdx = Tensor::new( x.windows(2) // iterator for adjacent pairs of a slice
                            .map(|w| w[1]-w[0]) // i.e. [x[0], x[1]], [x[1], x[2]] ...
                            .collect() );
    // fortunately `Tensor` has `min()`
    // `Vec` doesn't have so forced to use `fold` for it
    tdx.min()
}

#[test]
fn test_min_dx() {
    use std::iter::FromIterator;
    use std::cmp::Ordering;

    fn sub<T: Num + Copy>(lhs: &[T], rhs: &[T]) -> Vec<T> {
        Vec::from_iter (lhs.iter().zip(rhs.iter()) .map(|(&x,&y)| x - y ) )
    }

    // 1. prepare non-equidistant vector `x`
    let base_dx = std::f64::consts::FRAC_PI_4; // 0.785398
    let mut x = Vec::from_iter ((1..7) .map(|idx| ((idx as f64).sin() * base_dx).cos().abs() ) );

    x.sort_by(|&x, &y|
        if      x < y { Ordering::Less }
        else if x > y { Ordering::Greater }
        else { Ordering::Equal } );

    let delta_x = sub(&x, &[0.7295467223444416, 0.7556433468867207, 0.7894463883160331,
                            0.8284895327495356, 0.9760168013869176, 0.9938640428738624]);

    let inf_norm = delta_x .iter() .fold(delta_x[0].abs(), |x1, x2| (x1).max((*x2).abs()));

    assert!(inf_norm < 1e-10, "inf_norm");

    // classical way
    let mut min_dx1 = std::f64::MAX;
    for i in 0 .. x.len()-1 {
        min_dx1 = min_dx1.min(x[i+1]-x[i]);
    }

    // functional way
    let min_dx2 = min_dx(&x);
    let abs_delta = (min_dx1 - min_dx2).abs();
    println!("abs_delta : {} min_dx1 : {} min_dx2 : {} x : {:?}", abs_delta, min_dx1, min_dx2, x);
    assert!(abs_delta < 1e-10, "min_dx");
}

fn do_computation(nsteps: usize, ncells: usize, tmax: f64, ifirst: usize, ilast: usize,
                  statelft: f64, statergt: f64, velocity: f64, dt: f64,
                  fc: usize, lc: usize, x: &Tensor<f64>, u: &mut Tensor<f64>)
{
    let mut istep   =   0;
    let mut t       =   0.0f64;
    let mut flux    =   Tensor::new(vec![0.0f64; x.dim(0)]);

    // loop over timesteps
    while istep < nsteps && t < tmax {

        // right boundary condition: outgoing wave
        for ic in ncells .. lc {
            u[ic]=u[ncells-1];
        }
        // left boundary condition: specified value
        for ic in 0 .. fc {
            u[ic]=statelft;
        }

        // upwind fluxes times dt (ie, flux time integral over cell side)
        // assumes velocity > 0
        let vdt=velocity*dt;
        for ie in ifirst .. ilast+1 {
            flux[ie]=vdt*u[ie-1];
        }

        // conservative difference
        for ic in ifirst .. ilast {
            u[ic] -= (flux[ic+1]-flux[ic]) / (x[ic+1]-x[ic])
        }

        // update time and step number
        t       +=  dt;
        istep   +=  1
    }
}

fn main() {
    let mut options = Options {
        verbose:   false,
        num_steps: 2000,
        num_cells: 2000,
        num_runs:  10,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing of model PDE for scalar conservation law.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.num_steps)
            .add_option(&["-t", "--num_steps"], Store,
            "set number of time steps");
        ap.refer(&mut options.num_cells)
            .add_option(&["-n", "--num_cells"], Store,
            "set number of grid cells");
        ap.refer(&mut options.num_runs)
            .add_option(&["-b", "--num_runs"], Store,
            "set number of runs");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }

    if options.verbose {
        println!("number of time steps : {} number of grid cells : {} number of runs : {}",
                 options.num_steps, options.num_cells, options.num_runs);
    }

    // problem-specific parameters:
    const jump     : f64   =  0.0f64;
    const x_left   : f64   = -0.2f64;
    const x_right  : f64   =  1.0f64;
    const statelft : f64   =  2.0f64;
    const statergt : f64   =  0.0f64;
    const velocity : f64   =  1.0f64;

    const tmax     : f64   =  0.8f64;
    const cfl      : f64   =  0.9f64;

    // array bounds:
    let   nsteps           = options.num_steps;
    let   ncells           = options.num_cells;
    const fc       : usize = 2;
    let   lc               = ncells+1;
    const ifirst   : usize = 1;
    let   ilast            = ncells-1;

    // work arrays
    // #   double precision
    // #  &  u(-2:ncells+1),
    // #  &  x(0:ncells),
    // #  &  flux(0:ncells)

    let mut u    = Tensor::new(vec![0.0f64; ncells+4]);
    let mut x    = Tensor::new(vec![0.0f64; ncells+1]);

    //  uniform mesh:
    let dx = (x_right-x_left) / ncells as f64;
    for ie in ifirst .. ilast+1 {
        x[ie] = x_left + ie as f64 * dx;
    }

    // initial values for diffential equation:
    let ijump = cmp::max(ifirst as isize -1,
                         cmp::min((ncells as f64 * (jump-x_left)/(x_right-x_left) + 0.5) as isize,
                                  (ilast+1) as isize)) as usize;
    if options.verbose {
        println!("ijump : {}", ijump);
    }

    // stable timestep (independent of time for linear advection):
    let mindx = min_dx(&x.data());
    let dt = cfl*mindx/velocity.abs();

    let d = Duration::span(||{
        for r in 0..options.num_runs {
            if options.verbose {
                println!("run number : {}", r);
            }

            // left state to left of jump
            for ic in ifirst .. ijump-1 {
                u[ic+3] = statelft;
            }

            // volume-weighted average in cell containing jump
            let frac = (jump-x_left-ijump as f64 *dx)/(x_right-x_left);
            u[ijump+3] = statelft*frac+statergt*(1.0f64-frac);

            // right state to right of jump
            for ic in ijump+1 .. ilast {
                u[ic+3]=statergt;
            }

            do_computation(nsteps, ncells, tmax, ifirst, ilast,
                           statelft, statergt, velocity, dt, fc, lc, &x, &mut u);
        }
    });
    println!("elapsed time: {}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
}
<commit_msg>expand on `Tensor` usage.<commit_after>//***********************************************************************
//  Copyright 2006 John A. Trangenstein
//
//  This software is made available for research and instructional use
//  only.e available for research and instructional use
//  only.
//  You may copy and use this software without charge for these
//  non-commercial purposes, provided that the copyright notice and
//  associated text is reproduced on all copies.
//  For all other uses (including distribution of modified versions),
//  please contact the author at
//    John A. Trangenstein
//    Department of Mathematics
//    Duke University
//    Durham, NC 27708-0320
//    USA
//  or
//    johnt@math.duke.edu
//
//  This software is made available "as is" without any assurance that it
//  is completely correct, or that it will work for your purposes.
//  Use the software at your own risk.
//***********************************************************************

// Permission to publish the translation of the code
// was graciously granted by Prof. John A. Trangenstein

// The MIT License (MIT)
//
// Copyright (c) 2015 Alexander Samoilov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#![feature(duration_span)]

extern crate argparse;
extern crate num;
#[macro_use(tensor)]
extern crate numeric;

// http://numeric.rs/doc/numeric/tensor/struct.Tensor.html
use numeric::{Tensor, AxisIndex};

use num::{Num, Zero, One, Signed};

use std::time::Duration;
use std::cmp;
use std::process::exit;
use argparse::{ArgumentParser, StoreTrue, Store};

struct Options {
    verbose: bool,
    num_steps: usize,
    num_cells: usize,
    num_runs: usize,
}

fn min_dx(x: &[f64]) -> f64 {
    // https://doc.rust-lang.org/std/primitive.slice.html#method.windows
    let tdx = Tensor::new( x.windows(2) // iterator for adjacent pairs of a slice
                            .map(|w| w[1]-w[0]) // i.e. [x[0], x[1]], [x[1], x[2]] ...
                            .collect() );
    // fortunately `Tensor` has `min()`
    // `Vec` doesn't have so forced to use `fold` for it
    tdx.min()
}

#[test]
fn test_min_dx() {
    use std::iter::FromIterator;
    use std::cmp::Ordering;

    fn sub<T: Num + Copy>(lhs: &[T], rhs: &[T]) -> Vec<T> {
        Vec::from_iter (lhs.iter().zip(rhs.iter()) .map(|(&x,&y)| x - y ) )
    }

    // 1. prepare non-equidistant vector `x`
    let base_dx = std::f64::consts::FRAC_PI_4; // 0.785398
    let mut x = Vec::from_iter ((1..7) .map(|idx| ((idx as f64).sin() * base_dx).cos().abs() ) );

    x.sort_by(|&x, &y|
        if      x < y { Ordering::Less }
        else if x > y { Ordering::Greater }
        else { Ordering::Equal } );

    let delta_x = sub(&x, &[0.7295467223444416, 0.7556433468867207, 0.7894463883160331,
                            0.8284895327495356, 0.9760168013869176, 0.9938640428738624]);

    let inf_norm = delta_x .iter() .fold(delta_x[0].abs(), |x1, x2| (x1).max((*x2).abs()));

    assert!(inf_norm < 1e-10, "inf_norm");

    // classical way
    let mut min_dx1 = std::f64::MAX;
    for i in 0 .. x.len()-1 {
        min_dx1 = min_dx1.min(x[i+1]-x[i]);
    }

    // functional way
    let min_dx2 = min_dx(&x);
    let abs_delta = (min_dx1 - min_dx2).abs();
    println!("abs_delta : {} min_dx1 : {} min_dx2 : {} x : {:?}", abs_delta, min_dx1, min_dx2, x);
    assert!(abs_delta < 1e-10, "min_dx");
}

fn do_computation(nsteps: usize, ncells: usize, tmax: f64, ifirst: usize, ilast: usize,
                  statelft: f64, statergt: f64, velocity: f64, dt: f64,
                  fc: usize, lc: usize, x: &Tensor<f64>, u: &mut Tensor<f64>)
{
    let mut istep   =   0;
    let mut t       =   0.0f64;
    let mut flux    =   tensor![0.0f64; x.dim(0)];

    // loop over timesteps
    while istep < nsteps && t < tmax {

        // right boundary condition: outgoing wave
        let rbc = u[ncells-1];
        //u.slice_set( &[AxisIndex::SliceFrom(ncells as isize)], &tensor![rbc; fc] );
        for ic in ncells .. lc {
            u[ic]=u[ncells-1];
        }
        // left boundary condition: specified value
        u.slice_set( &[AxisIndex::Slice(0, fc as isize)], &tensor![statelft; fc] );
        //for ic in 0 .. fc {
        //    u[ic]=statelft;
        //}

        // upwind fluxes times dt (ie, flux time integral over cell side)
        // assumes velocity > 0
        let vdt=velocity*dt;
        for ie in ifirst .. ilast+1 {
            flux[ie]=vdt*u[ie-1];
        }

        // conservative difference
        for ic in ifirst .. ilast {
            u[ic] -= (flux[ic+1]-flux[ic]) / (x[ic+1]-x[ic])
        }

        // update time and step number
        t       +=  dt;
        istep   +=  1
    }
}

fn main() {
    let mut options = Options {
        verbose:   false,
        num_steps: 2000,
        num_cells: 2000,
        num_runs:  10,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing of model PDE for scalar conservation law.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.num_steps)
            .add_option(&["-t", "--num_steps"], Store,
            "set number of time steps");
        ap.refer(&mut options.num_cells)
            .add_option(&["-n", "--num_cells"], Store,
            "set number of grid cells");
        ap.refer(&mut options.num_runs)
            .add_option(&["-b", "--num_runs"], Store,
            "set number of runs");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }

    if options.verbose {
        println!("number of time steps : {} number of grid cells : {} number of runs : {}",
                 options.num_steps, options.num_cells, options.num_runs);
    }

    // problem-specific parameters:
    const jump     : f64   =  0.0f64;
    const x_left   : f64   = -0.2f64;
    const x_right  : f64   =  1.0f64;
    const statelft : f64   =  2.0f64;
    const statergt : f64   =  0.0f64;
    const velocity : f64   =  1.0f64;

    const tmax     : f64   =  0.8f64;
    const cfl      : f64   =  0.9f64;

    // array bounds:
    let   nsteps           = options.num_steps;
    let   ncells           = options.num_cells;
    const fc       : usize = 2;
    let   lc               = ncells+1;
    const ifirst   : usize = 1;
    let   ilast            = ncells-1;

    // work arrays
    // #   double precision
    // #  &  u(-2:ncells+1),
    // #  &  x(0:ncells),
    // #  &  flux(0:ncells)

    let mut u    = tensor![0.0f64; ncells+4];
    let mut x    = tensor![0.0f64; ncells+1];

    //  uniform mesh:
    let dx = (x_right-x_left) / ncells as f64;
    for ie in ifirst .. ilast+1 {
        x[ie] = x_left + ie as f64 * dx;
    }

    // initial values for diffential equation:
    let ijump = cmp::max(ifirst as isize -1,
                         cmp::min((ncells as f64 * (jump-x_left)/(x_right-x_left) + 0.5) as isize,
                                  (ilast+1) as isize)) as usize;
    if options.verbose {
        println!("ijump : {}", ijump);
    }

    // stable timestep (independent of time for linear advection):
    let mindx = min_dx(&x.data());
    let dt = cfl*mindx/velocity.abs();

    let d = Duration::span(||{
        for r in 0..options.num_runs {
            if options.verbose {
                println!("run number : {}", r);
            }

            // left state to left of jump
            for ic in ifirst .. ijump-1 {
                u[ic+3] = statelft;
            }

            // volume-weighted average in cell containing jump
            let frac = (jump-x_left-ijump as f64 *dx)/(x_right-x_left);
            u[ijump+3] = statelft*frac+statergt*(1.0f64-frac);

            // right state to right of jump
            for ic in ijump+1 .. ilast {
                u[ic+3]=statergt;
            }

            do_computation(nsteps, ncells, tmax, ifirst, ilast,
                           statelft, statergt, velocity, dt, fc, lc, &x, &mut u);
        }
    });
    println!("elapsed time: {}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
}
<|endoftext|>


<commit_before>/*===============================================================================================*/
// Copyright 2016 Kyle Finlay
//
// 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.
/*===============================================================================================*/

// Crate imports
extern crate num_traits;

// Module imports
use self::num_traits::{Num, NumCast};

use ::util::Clamp;

/*===============================================================================================*/
/*------LERP TRAIT-------------------------------------------------------------------------------*/
/*===============================================================================================*/

/// The lerp trait.
pub trait Lerp {

    /// Linearly interpolates between two values.
    ///
    /// # Examples
    /// ```
    /// # use ion_math::util::Lerp;
    /// println! ("{}", f32::lerp (1.0, 7.5, 0.5));
    /// ```
    fn lerp (start: &Self, end: &Self, percentage: f32) -> Self;

    /// Linearly interpolates between two values without clamping.
    ///
    /// # Examples
    /// ```
    /// # use ion_math::util::Lerp;
    /// println! ("{}", f32::lerp (1.0, 7.5, 0.5));
    /// ```
    fn lerp_unclamped (start: &Self, end: &Self, percentage: f32) -> Self;
}

/*===============================================================================================*/
/*------LERP TRAIT IMPLEMENTATIONS---------------------------------------------------------------*/
/*===============================================================================================*/

impl<T> Lerp for T where
    T: Copy + Num + NumCast {

    fn lerp (start: &Self, end: &Self, percentage: f32) -> Self {
        T::from ((*start + (*end - *start)).to_f32 ().unwrap () * percentage.clamp (0.0, 1.0)).unwrap ()
    }

/*-----------------------------------------------------------------------------------------------*/

    fn lerp_unclamped (start: &Self, end: &Self, percentage: f32) -> Self {
        T::from ((*start + (*end - *start)).to_f32 ().unwrap () * percentage).unwrap ()
    }
}
<commit_msg>Fixed failing test<commit_after>/*===============================================================================================*/
// Copyright 2016 Kyle Finlay
//
// 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.
/*===============================================================================================*/

// Crate imports
extern crate num_traits;

// Module imports
use self::num_traits::{Num, NumCast};

use ::util::Clamp;

/*===============================================================================================*/
/*------LERP TRAIT-------------------------------------------------------------------------------*/
/*===============================================================================================*/

/// The lerp trait.
pub trait Lerp {

    /// Linearly interpolates between two values.
    ///
    /// # Examples
    /// ```
    /// # use ion_math::util::Lerp;
    /// println! ("{}", f32::lerp (&1.0, &7.5, 0.5));
    /// ```
    fn lerp (start: &Self, end: &Self, percentage: f32) -> Self;

    /// Linearly interpolates between two values without clamping.
    ///
    /// # Examples
    /// ```
    /// # use ion_math::util::Lerp;
    /// println! ("{}", f32::lerp (&1.0, &7.5, 0.5));
    /// ```
    fn lerp_unclamped (start: &Self, end: &Self, percentage: f32) -> Self;
}

/*===============================================================================================*/
/*------LERP TRAIT IMPLEMENTATIONS---------------------------------------------------------------*/
/*===============================================================================================*/

impl<T> Lerp for T where
    T: Copy + Num + NumCast {

    fn lerp (start: &Self, end: &Self, percentage: f32) -> Self {
        T::from ((*start + (*end - *start)).to_f32 ().unwrap () * percentage.clamp (0.0, 1.0)).unwrap ()
    }

/*-----------------------------------------------------------------------------------------------*/

    fn lerp_unclamped (start: &Self, end: &Self, percentage: f32) -> Self {
        T::from ((*start + (*end - *start)).to_f32 ().unwrap () * percentage).unwrap ()
    }
}
<|endoftext|>


<commit_before>
extern crate curved_gear;
extern crate env_logger;
extern crate mioco;

use std::io;
use std::net::SocketAddr;

use curved_gear::*;
use mioco::udp::UdpSocket;

fn main() {
    env_logger::init().unwrap();

    let client_id = Identity::new(Extension::from_barr(b"client"));
    let server_id = Identity::new(Extension::from_barr(b"server"));
    let server_addr: SocketAddr = "0.0.0.0:7777".parse().unwrap();
    let remote_addr: SocketAddr = "127.0.0.1:7777".parse().unwrap();

    mioco::start(|| -> io::Result<()> {
        let listener = CCPListener::new(server_id.clone(), UdpSocket::bound(&server_addr).unwrap());

        mioco::spawn(|| -> io::Result<()> {
            let mut sock = CCPSocket::connect(client_id, server_id.create_remote(remote_addr));

            try!(conn.send("Hello world".as_bytes()));
            let buf = try!(conn.read());
            println!("{}", String::from_utf8_lossy(&buf));

            Ok(())
        });

        loop {
            let mut conn = try!(listener.accept_sock());

            mioco::spawn(move || -> io::Result<()> {
                loop {
                    let buf = try!(conn.read());
                    if buf.len() == 0 { break; }
                    try!(conn.write_all(&buf));
                }

                Ok(())
            });
        }
    }).unwrap();
}
<commit_msg>Reworking example<commit_after>
extern crate curved_gear;
extern crate env_logger;
extern crate mioco;

use std::net::SocketAddr;

use curved_gear::*;
use mioco::udp::UdpSocket;

fn main() {
    env_logger::init().unwrap();

    let client_id = Identity::new(Extension::from_barr(b"client"));
    let server_id = Identity::new(Extension::from_barr(b"server"));
    let server_addr: SocketAddr = "0.0.0.0:7777".parse().unwrap();
    let remote_addr: SocketAddr = "127.0.0.1:7777".parse().unwrap();

    mioco::start(move || -> Result<()> {
        let mut listener = CCPListener::new(server_id.clone(), UdpSocket::bound(&server_addr).unwrap());

        mioco::spawn(move || -> Result<()> {
            let mut sock = try!(CCPClientSocket::connect(UdpSocket::v4().unwrap(),
                client_id, server_id.create_remote(remote_addr)));

            try!(sock.send("Hello world".as_bytes()));
            let buf = try!(sock.recv());
            println!("{}", String::from_utf8_lossy(&buf));

            Ok(())
        });

        loop {
            let mut sock = try!(listener.accept_sock());

            mioco::spawn(move || -> Result<()> {
                loop {
                    let msg = try!(sock.recv());
                    if msg.len() == 0 { break; }
                    try!(sock.send(&msg));
                }

                Ok(())
            });
        }
    }).unwrap().unwrap();
}
<|endoftext|>


<commit_before>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use data::load_character;
use sdl2::pixels::Color;
use sdl2::render::Renderer;
use sdl2::rect::Point;
use conv::prelude::*;

const PLAYER_SPEED: f64 = 150.0;
const CHARACTER_W: f64 = 56.0;
const CHARACTER_H: f64 = 43.0;

const TERRAIN_W: f64 = 100.0;
const TERRAIN_H: f64 = 50.0;

const TILES_W: usize = 32;
const TILES_H: usize = 20;

#[derive(Clone)]
struct Background {
  pos: f64,
  sprite: Sprite,
}

impl Background {
  fn render(&mut self, renderer: &mut Renderer) {
    let size = self.sprite.size();

    let (_, window_h) = renderer.output_size().unwrap();
    renderer.copy_sprite(&self.sprite, Rectangle {
      x: 0.0,
      y: 0.0,
      w: size.0,
      h: window_h as f64,
    })
  }
}

#[derive(Clone, Copy)]
enum CharacterFrame {
  Still = 0,
  Right = 1,
  UpRight = 28,
  Up = 56,
  UpLeft = 84,
  Left = 108,
  DownLeft = 122,
  Down = 150,
  DownRight = 178
}

#[derive(Clone, Copy)]
enum TerrainFrame {
  Sand = 0,
  Grass = 1,
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
  anim_index: u32
}

struct TerrainTile {
  rect: Rectangle,
  terrain_sprites: Vec<Sprite>,
  current: TerrainFrame,
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  background: Background,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let character_datapoints = load_character();
    let terrain_spritesheet = Sprite::load(&mut game.renderer, "assets/terrain.png").unwrap();
    let mut sprites = Vec::with_capacity(256);
    let mut terrain_sprites = Vec::with_capacity(TILES_W);
    let mut tiles = Vec::with_capacity(TILES_W * TILES_H * 2);

    for x in 0..3 {
      terrain_sprites.push(terrain_spritesheet.region(Rectangle {
        w: TERRAIN_W,
        h: TERRAIN_H,
        x: TERRAIN_W * x as f64,
        y: 0.0 as f64,
      }).unwrap());
    }

    for x in 0..194 {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in 0..TILES_W {
      for y in 0..TILES_H {
        let x2: f64 = 100.0 * 1.5 as f64;
        let y2: f64 = 50.0 * 1.5 as f64;
        tiles.push(TerrainTile {
          rect: Rectangle {
            x: 100.0 * x as f64,
            y: 50.0 * y as f64,
            w: TERRAIN_W,
            h: TERRAIN_H,
          },
          terrain_sprites: terrain_sprites.clone(),
          current: TerrainFrame::Grass,
        });

        tiles.push(TerrainTile {
          rect: Rectangle {
            x: 100.0 * f64::value_from((x+1)).unwrap() - x2 as f64,
            y: 50.0 * f64::value_from((y+1)).unwrap() - y2 as f64,
            w: TERRAIN_W,
            h: TERRAIN_H,
          },
          terrain_sprites: terrain_sprites.clone(),
          current: TerrainFrame::Sand,
        });
      }
    }

    GameView {
      player: Character {
        rect: Rectangle {
          x: 64.0,
          y: 64.0,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites.clone(),
        current: CharacterFrame::Down,
        anim_index: 0
      },

      tiles: tiles,

      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      },
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx;
    self.player.rect.y += (dy / 2.0);

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0 * 0.70,
      h: game.output_size().1,
    };

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::Up }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::UpRight }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::UpLeft }
    else if dx == 0.0 && dy == 0.0 { CharacterFrame::Still }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::Right }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::Left }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::Down }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::DownRight }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownLeft }
    else { unreachable!() };

    game.renderer.set_draw_color(Color::RGBA(120, 120, 120, 0));
    self.background.render(&mut game.renderer);
    game.renderer.clear();

    for x in 0..TILES_W {
      for y in 0..TILES_H {
        let index = x * TILES_H + y;
        game.renderer.copy_sprite(&self.tiles[index].terrain_sprites[self.tiles[index].current as usize], self.tiles[index].rect);
      }
    }

    match game.events.mouse_click {
      Some(m) => {
        game.renderer.set_draw_color(Color::RGBA(255, 0, 0, 0));
        let _ = game.renderer.draw_line(Point::new(m.0, m.1 ), Point::new(m.0 - 10, m.1));
      },
      None => {},
    }

    // player
    game.renderer.set_draw_color(Color::RGBA(119,119,119,0));

    game.renderer.copy_sprite(&self.player.sprites[self.player.current as usize + self.player.anim_index as usize], self.player.rect);
    self.player.anim_index =
      if dx == 0.0 && dy == 0.0 { 0u32 }
      else if self.player.anim_index < 13u32 { self.player.anim_index + 1u32 }
      else { 0u32 };

    ViewAction::None
  }
}
<commit_msg>Remove artificial boundary 70%<commit_after>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use data::load_character;
use sdl2::pixels::Color;
use sdl2::render::Renderer;
use sdl2::rect::Point;
use conv::prelude::*;

const PLAYER_SPEED: f64 = 150.0;
const CHARACTER_W: f64 = 56.0;
const CHARACTER_H: f64 = 43.0;

const TERRAIN_W: f64 = 100.0;
const TERRAIN_H: f64 = 50.0;

const TILES_W: usize = 32;
const TILES_H: usize = 20;

#[derive(Clone)]
struct Background {
  pos: f64,
  sprite: Sprite,
}

impl Background {
  fn render(&mut self, renderer: &mut Renderer) {
    let size = self.sprite.size();

    let (_, window_h) = renderer.output_size().unwrap();
    renderer.copy_sprite(&self.sprite, Rectangle {
      x: 0.0,
      y: 0.0,
      w: size.0,
      h: window_h as f64,
    })
  }
}

#[derive(Clone, Copy)]
enum CharacterFrame {
  Still = 0,
  Right = 1,
  UpRight = 28,
  Up = 56,
  UpLeft = 84,
  Left = 108,
  DownLeft = 122,
  Down = 150,
  DownRight = 178
}

#[derive(Clone, Copy)]
enum TerrainFrame {
  Sand = 0,
  Grass = 1,
}

struct Character {
  rect: Rectangle,
  sprites: Vec<Sprite>,
  current: CharacterFrame,
  anim_index: u32
}

struct TerrainTile {
  rect: Rectangle,
  terrain_sprites: Vec<Sprite>,
  current: TerrainFrame,
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  background: Background,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let character_datapoints = load_character();
    let terrain_spritesheet = Sprite::load(&mut game.renderer, "assets/terrain.png").unwrap();
    let mut sprites = Vec::with_capacity(256);
    let mut terrain_sprites = Vec::with_capacity(TILES_W);
    let mut tiles = Vec::with_capacity(TILES_W * TILES_H * 2);

    for x in 0..3 {
      terrain_sprites.push(terrain_spritesheet.region(Rectangle {
        w: TERRAIN_W,
        h: TERRAIN_H,
        x: TERRAIN_W * x as f64,
        y: 0.0 as f64,
      }).unwrap());
    }

    for x in 0..194 {
      sprites.push(spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in 0..TILES_W {
      for y in 0..TILES_H {
        let x2: f64 = 100.0 * 1.5 as f64;
        let y2: f64 = 50.0 * 1.5 as f64;
        tiles.push(TerrainTile {
          rect: Rectangle {
            x: 100.0 * x as f64,
            y: 50.0 * y as f64,
            w: TERRAIN_W,
            h: TERRAIN_H,
          },
          terrain_sprites: terrain_sprites.clone(),
          current: TerrainFrame::Grass,
        });

        tiles.push(TerrainTile {
          rect: Rectangle {
            x: 100.0 * f64::value_from((x+1)).unwrap() - x2 as f64,
            y: 50.0 * f64::value_from((y+1)).unwrap() - y2 as f64,
            w: TERRAIN_W,
            h: TERRAIN_H,
          },
          terrain_sprites: terrain_sprites.clone(),
          current: TerrainFrame::Sand,
        });
      }
    }

    GameView {
      player: Character {
        rect: Rectangle {
          x: 64.0,
          y: 64.0,
          w: CHARACTER_W,
          h: CHARACTER_H,
        },
        sprites: sprites.clone(),
        current: CharacterFrame::Down,
        anim_index: 0
      },

      tiles: tiles,

      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      },
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved,
      (false, true) => moved,
    };

    self.player.rect.x += dx;
    self.player.rect.y += (dy / 2.0);

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0,
      h: game.output_size().1,
    };

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { CharacterFrame::Up }
    else if dx > 0.0 && dy < 0.0   { CharacterFrame::UpRight }
    else if dx < 0.0 && dy < 0.0   { CharacterFrame::UpLeft }
    else if dx == 0.0 && dy == 0.0 { CharacterFrame::Still }
    else if dx > 0.0 && dy == 0.0  { CharacterFrame::Right }
    else if dx < 0.0 && dy == 0.0  { CharacterFrame::Left }
    else if dx == 0.0 && dy > 0.0  { CharacterFrame::Down }
    else if dx > 0.0 && dy > 0.0   { CharacterFrame::DownRight }
    else if dx < 0.0 && dy > 0.0   { CharacterFrame::DownLeft }
    else { unreachable!() };

    game.renderer.set_draw_color(Color::RGBA(120, 120, 120, 0));
    self.background.render(&mut game.renderer);
    game.renderer.clear();

    for x in 0..TILES_W {
      for y in 0..TILES_H {
        let index = x * TILES_H + y;
        game.renderer.copy_sprite(&self.tiles[index].terrain_sprites[self.tiles[index].current as usize], self.tiles[index].rect);
      }
    }

    match game.events.mouse_click {
      Some(m) => {
        game.renderer.set_draw_color(Color::RGBA(255, 0, 0, 0));
        let _ = game.renderer.draw_line(Point::new(m.0, m.1 ), Point::new(m.0 - 10, m.1));
      },
      None => {},
    }

    // player
    game.renderer.set_draw_color(Color::RGBA(119,119,119,0));

    game.renderer.copy_sprite(&self.player.sprites[self.player.current as usize + self.player.anim_index as usize], self.player.rect);
    self.player.anim_index =
      if dx == 0.0 && dy == 0.0 { 0u32 }
      else if self.player.anim_index < 13u32 { self.player.anim_index + 1u32 }
      else { 0u32 };

    ViewAction::None
  }
}
<|endoftext|>


<commit_before>/// Internal metric representation
///
use std::fmt;

/// Enum of metric types
pub enum MetricKind {
    Counter(f64), // sample rate
    Gauge,
    Timer,
}

impl fmt::Debug for MetricKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MetricKind::Gauge      => write!(f, "Gauge"),
            MetricKind::Timer      => write!(f, "Timer"),
            MetricKind::Counter(s) => write!(f, "Counter(s={})", s)
        }
    }
}

/// Error types for parsing Metrics from strings.
///
#[derive(Debug)]
pub enum ParseError {
    // Error message, column
    SyntaxError(&'static str, usize)
}

/// Metric value objects.
///
#[derive(Debug)]
pub struct Metric {
    pub kind: MetricKind,
    pub name: String,
    pub value: f64
}

impl Metric {
    /// Create a new metric
    ///
    /// Uses the Into trait to allow both str and String types.
    pub fn new<S: Into<String>>(name: S, value: f64, kind: MetricKind) -> Metric {
        Metric{name: name.into(), value: value, kind: kind}
    }

    /// Valid message formats are:
    ///
    /// - `<str:metric_name>:<f64:value>|<str:type>`
    /// - `<str:metric_name>:<f64:value>|c|@<f64:sample_rate>`
    ///
    /// Multiple metrics can be sent in a single UDP packet
    /// separated by newlines.
    pub fn parse(source: &str) -> Result<Vec<Metric>, ParseError> {
        let mut results: Vec<Metric> = Vec::new();

        for line in source.lines() {
            match Metric::parse_line(line) {
                Ok(metric) => results.push(metric),
                Err(e) => return Err(e)
            }
        }
        if results.len() == 0 {
            return Err(ParseError::SyntaxError(
                    "No metrics found",
                    0))
        }
        Ok(results)
    }

    /// Parses a metric from each line in a packet.
    fn parse_line(line: &str) -> Result<Metric, ParseError> {
        // Get the metric name
        let name_parts: Vec<&str> = line.trim_right_matches('\n')
            .split(':')
            .collect();

        if name_parts.len() < 2 || name_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a name.",
                    0))
        }
        let name = name_parts[0].to_string();

        // Get the float val
        let val_parts: Vec<&str> = name_parts[1].split('|').collect();
        if val_parts.len() < 2 || val_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a value.",
                    name.len()))
        }
        let value = val_parts[0].parse::<f64>().ok().unwrap();

        // Get kind parts
        let kind = match val_parts[1] {
            "ms" => MetricKind::Timer,
            "g" => MetricKind::Gauge,
            "c" => {
                let mut rate:f64 = 1.0;
                if val_parts.len() == 3 {
                    rate = val_parts[2].trim_left_matches('@')
                        .parse::<f64>().ok().unwrap();
                }
                MetricKind::Counter(rate)
            },
            _ => return Err(ParseError::SyntaxError(
                    "Unknown metric type.",
                    name.len() + val_parts[0].len()))
        };
        Ok(Metric::new(name, value, kind))
    }
}

//
// Tests
//
#[cfg(test)]
mod tests {
    use metric::{Metric,MetricKind};
    use std::collections::HashMap;

    #[test]
    fn test_metric_kind_debug_fmt() {
        assert_eq!(
            "Gauge",
            format!("{:?}", MetricKind::Gauge)
        );
        assert_eq!(
            "Timer",
            format!("{:?}", MetricKind::Timer)
        );
        assert_eq!(
            "Counter(s=6)",
            format!("{:?}", MetricKind::Counter(6.0))
        );
    }

    #[test]
    fn test_metric_parse_invalid_no_name() {
        let res = Metric::parse("");
        assert!(res.is_err(), "Should have an error");
        assert!(!res.is_ok(), "Should have an error");
    }

    #[test]
    fn test_metric_parse_invalid_no_value() {
        let res = Metric::parse("foo:");
        assert!(res.is_err(), "Should have an error");
        assert!(!res.is_ok(), "Should have an error");
    }

    #[test]
    fn test_metric_multiple() {
        let res = Metric::parse("a.b:12.1|g\nb.c:13.2|c").unwrap();
        assert_eq!(2, res.len());

        assert_eq!("a.b", res[0].name);
        assert_eq!(12.1, res[0].value);

        assert_eq!("b.c", res[1].name);
        assert_eq!(13.2, res[1].value);
    }

    #[test]
    fn test_metric_valid() {
        let mut valid = HashMap::new();
        valid.insert(
            "foo.test:12.3|ms\n",
            Metric::new("foo.test", 12.3, MetricKind::Timer)
        );
        valid.insert(
            "foo.test:12.3|ms",
            Metric::new("foo.test", 12.3, MetricKind::Timer)
        );
        valid.insert(
            "test:18.123|g",
            Metric::new("test", 18.123, MetricKind::Gauge)
        );
        valid.insert(
            "test:18.123|g",
            Metric::new("test", 18.123, MetricKind::Gauge)
        );
        valid.insert(
            "thing.total:12|c",
            Metric::new("thing.total", 12.0, MetricKind::Counter(1.0))
        );
        valid.insert(
            "thing.total:5.6|c|@123",
            Metric::new("thing.total", 5.6, MetricKind::Counter(123.0))
        );

        for (input, expected) in valid.iter() {
            let result = Metric::parse(*input);
            assert!(result.is_ok());

            let actual = result.ok().unwrap();
            assert_eq!(expected.name, actual[0].name);
            assert_eq!(expected.value, actual[0].value);

            // TODO this is stupid, there must be a better way.
            assert_eq!(
                format!("{:?}", expected.kind),
                format!("{:?}", actual[0].kind)
            );
        }
    }

    #[test]
    fn test_metric_invalid() {
        let invalid = vec![
            "",
            "metric",
            "metric|11:",
            "metric|12",
            "metric:13|",
            "metric:14|c@1",
            ":|@",
            ":1.0|c"
        ];
        for input in invalid.iter() {
            let result = Metric::parse(*input);
            assert!(result.is_err());
        }
    }
}
<commit_msg>Parse metrics with fewer and smaller allocations.<commit_after>/// Internal metric representation
///
use std::fmt;

/// Enum of metric types
pub enum MetricKind {
    Counter(f64), // sample rate
    Gauge,
    Timer,
}

impl fmt::Debug for MetricKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MetricKind::Gauge      => write!(f, "Gauge"),
            MetricKind::Timer      => write!(f, "Timer"),
            MetricKind::Counter(s) => write!(f, "Counter(s={})", s)
        }
    }
}

/// Error types for parsing Metrics from strings.
///
#[derive(Debug)]
pub enum ParseError {
    // Error message, column
    SyntaxError(&'static str, usize)
}

/// Metric value objects.
///
#[derive(Debug)]
pub struct Metric {
    pub kind: MetricKind,
    pub name: String,
    pub value: f64
}

impl Metric {
    /// Create a new metric
    ///
    /// Uses the Into trait to allow both str and String types.
    pub fn new<S: Into<String>>(name: S, value: f64, kind: MetricKind) -> Metric {
        Metric{name: name.into(), value: value, kind: kind}
    }

    /// Valid message formats are:
    ///
    /// - `<str:metric_name>:<f64:value>|<str:type>`
    /// - `<str:metric_name>:<f64:value>|c|@<f64:sample_rate>`
    ///
    /// Multiple metrics can be sent in a single UDP packet
    /// separated by newlines.
    pub fn parse(source: &str) -> Result<Vec<Metric>, ParseError> {
        let mut results: Vec<Metric> = Vec::new();

        for line in source.lines() {
            match Metric::parse_line(line) {
                Ok(metric) => results.push(metric),
                Err(e) => return Err(e)
            }
        }
        if results.len() == 0 {
            return Err(ParseError::SyntaxError(
                    "No metrics found",
                    0))
        }
        Ok(results)
    }

    /// Parses a metric from each line in a packet.
    fn parse_line(line: &str) -> Result<Metric, ParseError> {
        // track position in string
        let mut idx = 0;

        // Get the metric name
        let name = match line.find(':') {
            Some(pos) => {
                idx = pos + 1;
                &line[0..pos]
            },
            _ => ""
        };
        if name.is_empty() {
            return Err(ParseError::SyntaxError(
                       "Metrics require a name.",
                       idx));
        }

        // Get the float val
        let value = match line[idx..].find('|') {
            Some(pos) => {
                let start = idx;
                idx += pos + 1;
                line[start..idx - 1].parse::<f64>().ok().unwrap()
            },
            _ => return Err(ParseError::SyntaxError(
                            "Metrics require a value.",
                            idx))
        };
        let kind_name = match line[idx..].find('|') {
            Some(pos) => {
                let start = idx;
                idx += pos;
                line[start..idx].to_string()
            },
            _ => line[idx..].to_string()
        };

        // Get kind parts, use deref/ref tricks
        // to get types to match
        let kind = match &*kind_name {
            "ms" => MetricKind::Timer,
            "g" => MetricKind::Gauge,
            "c" => {
                let rate: f64 = match line[idx..].find('@') {
                    Some(pos) => {
                        idx += pos + 1;
                        line[idx..].parse::<f64>().ok().unwrap()
                    },
                    _ => 1.0
                };
                MetricKind::Counter(rate)
            },
            _ => return Err(ParseError::SyntaxError(
                            "Unknown metric type.",
                            idx))
        };
        Ok(Metric::new(name, value, kind))
    }
}

//
// Tests
//
#[cfg(test)]
mod tests {
    use metric::{Metric,MetricKind};
    use std::collections::HashMap;

    #[test]
    fn test_metric_kind_debug_fmt() {
        assert_eq!(
            "Gauge",
            format!("{:?}", MetricKind::Gauge)
        );
        assert_eq!(
            "Timer",
            format!("{:?}", MetricKind::Timer)
        );
        assert_eq!(
            "Counter(s=6)",
            format!("{:?}", MetricKind::Counter(6.0))
        );
    }

    #[test]
    fn test_metric_parse_invalid_no_name() {
        let res = Metric::parse("");
        assert!(res.is_err(), "Should have an error");
        assert!(!res.is_ok(), "Should have an error");
    }

    #[test]
    fn test_metric_parse_invalid_no_value() {
        let res = Metric::parse("foo:");
        assert!(res.is_err(), "Should have an error");
        assert!(!res.is_ok(), "Should have an error");
    }

    #[test]
    fn test_metric_multiple() {
        let res = Metric::parse("a.b:12.1|g\nb.c:13.2|c").unwrap();
        assert_eq!(2, res.len());

        assert_eq!("a.b", res[0].name);
        assert_eq!(12.1, res[0].value);

        assert_eq!("b.c", res[1].name);
        assert_eq!(13.2, res[1].value);
    }

    #[test]
    fn test_metric_valid() {
        let mut valid = HashMap::new();
        valid.insert(
            "foo.test:12.3|ms\n",
            Metric::new("foo.test", 12.3, MetricKind::Timer)
        );
        valid.insert(
            "foo.test:12.3|ms",
            Metric::new("foo.test", 12.3, MetricKind::Timer)
        );
        valid.insert(
            "test:18.123|g",
            Metric::new("test", 18.123, MetricKind::Gauge)
        );
        valid.insert(
            "test:18.123|g",
            Metric::new("test", 18.123, MetricKind::Gauge)
        );
        valid.insert(
            "thing.total:12|c",
            Metric::new("thing.total", 12.0, MetricKind::Counter(1.0))
        );
        valid.insert(
            "thing.total:5.6|c|@123",
            Metric::new("thing.total", 5.6, MetricKind::Counter(123.0))
        );

        for (input, expected) in valid.iter() {
            let result = Metric::parse(*input);
            assert!(result.is_ok());

            let actual = result.ok().unwrap();
            assert_eq!(expected.name, actual[0].name);
            assert_eq!(expected.value, actual[0].value);

            // TODO this is stupid, there must be a better way.
            assert_eq!(
                format!("{:?}", expected.kind),
                format!("{:?}", actual[0].kind)
            );
        }
    }

    #[test]
    fn test_metric_invalid() {
        let invalid = vec![
            "",
            "metric",
            "metric|11:",
            "metric|12",
            "metric:13|",
            "metric:14|c@1",
            ":|@",
            ":1.0|c"
        ];
        for input in invalid.iter() {
            println!("{:?}", input);
            let result = Metric::parse(*input);
            assert!(result.is_err());
        }
    }
}
<|endoftext|>


<commit_before>//! Buffer and working directory management.

use buffer::Buffer;
use std::io::Error;
use std::path::PathBuf;

/// An owned collection of buffers and associated path,
/// representing a running editor environment.
pub struct Workspace {
    pub path: PathBuf,
    buffers: Vec<Buffer>,
    current_buffer_index: Option<usize>,
}

impl Workspace {
    /// Creates a new empty workspace for the specified path.
    pub fn new(path: PathBuf) -> Workspace {
        Workspace{ path: path, buffers: Vec::new(), current_buffer_index: None }
    }

    /// Adds a buffer to the workspace and selects it.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    /// ```
    pub fn add_buffer(&mut self, buf: Buffer) {
        self.buffers.push(buf);
        self.current_buffer_index = Some(self.buffers.len()-1);
    }

    /// Opens a buffer at the specified path and selects it.
    /// If a buffer with the specified path already exists,
    /// it is selected, rather than opening a duplicate buffer.
    /// Any errors encountered while opening the buffer are returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Open a buffer in the workspace.
    /// workspace.open_buffer(file_path.clone());
    /// ```
    pub fn open_buffer(&mut self, path: PathBuf) -> Option<Error> {
        if self.contains_buffer_with_path(&path) {
            // We already have this buffer in the workspace.
            // Loop through the buffers until it's selected.
            loop {
                match self.current_buffer() {
                    Some(buffer) => {
                        match buffer.path {
                            Some(ref current_path) => {
                                if *current_path == path {
                                    break;
                                }
                            },
                            None => (),
                        }
                    },
                    None => (),
                }

                self.next_buffer()
            }

            // Not going to run into IO errors if we're not opening a buffer.
            None
        } else {
            match Buffer::from_file(path) {
                Ok(buffer) => {
                    self.add_buffer(buffer);
                    None
                },
                Err(error) => Some(error),
            }
        }
    }

    /// Returns a mutable reference to the currently
    /// selected buffer, unless the workspace is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Get a reference to the current buffer.
    /// let buffer_reference = workspace.current_buffer().unwrap();
    /// ```
    pub fn current_buffer(&mut self) -> Option<&mut Buffer> {
        match self.current_buffer_index {
            Some(index) => Some(&mut self.buffers[index]),
            None => None,
        }
    }

    /// Removes the currently selected buffer from the collection.
    /// If the workspace is empty, this method does nothing.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Close the current buffer.
    /// workspace.close_current_buffer();
    /// ```
    pub fn close_current_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                self.buffers.remove(index);

                if self.buffers.is_empty() {
                    self.current_buffer_index = None;
                } else {
                    self.current_buffer_index = Some(self.buffers.len()-1);
                }
            }
            None => return,
        };
    }

    /// Selects the previous buffer in the workspace (buffers are ordered as
    /// they are added to the workspace). If the currently selected buffer is
    /// the first in the collection, this will wrap and select the last buffer.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Select the previous buffer.
    /// workspace.previous_buffer();
    /// ```
    pub fn previous_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                if index > 0 {
                    self.current_buffer_index = Some(index-1);
                } else {
                    self.current_buffer_index = Some(self.buffers.len()-1);
                }
            },
            None => return,
        }
    }

    /// Selects the next buffer in the workspace (buffers are ordered as
    /// they are added to the workspace). If the currently selected buffer is
    /// the last in the collection, this will wrap and select the first buffer.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Select the next buffer.
    /// workspace.next_buffer();
    /// ```
    pub fn next_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                if index == self.buffers.len()-1 {
                    self.current_buffer_index = Some(0);
                } else {
                    self.current_buffer_index = Some(index+1);
                }
            },
            None => return,
        }
    }

    /// Whether or not the workspace contains a buffer with the specified path.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path.clone()).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// assert!(workspace.contains_buffer_with_path(&file_path));
    /// ```
    pub fn contains_buffer_with_path(&self, path: &PathBuf) -> bool {
        self.buffers.iter().any(|buffer| {
            match buffer.path {
                Some(ref buffer_path) => path == buffer_path,
                None => false,
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::Workspace;
    use buffer::Buffer;
    use std::path::PathBuf;

    #[test]
    fn add_buffer_adds_and_selects_the_passed_buffer() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        let buf = Buffer::from_file(PathBuf::from("tests/sample/file")).unwrap();
        workspace.add_buffer(buf);

        assert_eq!(workspace.buffers.len(), 1);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn open_buffer_adds_and_selects_the_buffer_at_the_specified_path() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        assert_eq!(workspace.buffers.len(), 1);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn open_buffer_does_not_open_a_buffer_already_in_the_workspace() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        assert_eq!(workspace.buffers.len(), 1);
    }

    #[test]
    fn open_buffer_selects_buffer_if_it_already_exists_in_workspace() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        // Add and select another buffer.
        let mut buf = Buffer::new();
        buf.insert("scribe");
        workspace.add_buffer(buf);
        assert_eq!(workspace.current_buffer().unwrap().data(), "scribe");

        // Try to add the first buffer again.
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        // Ensure there are only two buffers, and that the
        // one requested via open_buffer is now selected.
        assert_eq!(workspace.buffers.len(), 2);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn current_buffer_returns_none_when_there_are_no_buffers() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn current_buffer_returns_one_when_there_are_buffers() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        let buf = Buffer::from_file(PathBuf::from("tests/sample/file")).unwrap();
        workspace.add_buffer(buf);
        assert!(workspace.current_buffer().is_some());
    }

    #[test]
    fn close_current_buffer_does_nothing_when_none_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.close_current_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn close_current_buffer_cleans_up_when_only_one_buffer_is_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.add_buffer(Buffer::new());
        workspace.close_current_buffer();
        assert!(workspace.current_buffer().is_none());
        assert!(workspace.current_buffer_index.is_none());
    }

    #[test]
    fn close_current_buffer_when_two_are_open_selects_the_other() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);

        // Ensure that the second buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        workspace.close_current_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");
    }

    #[test]
    fn previous_buffer_does_nothing_when_no_buffers_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.previous_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn previous_buffer_when_three_are_open_selects_previous_wrapping_to_last() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        let mut third_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        third_buffer.insert("third buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);
        workspace.add_buffer(third_buffer);

        // Ensure that the third buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");

        // Ensure that the second buffer is returned.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        // Ensure that the first buffer is returned.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");

        // Ensure that it wraps back to the third buffer.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");
    }

    #[test]
    fn next_buffer_does_nothing_when_no_buffers_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.next_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn next_buffer_when_three_are_open_selects_next_wrapping_to_first() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        let mut third_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        third_buffer.insert("third buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);
        workspace.add_buffer(third_buffer);

        // Ensure that the third buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");

        // Ensure that it wraps back to the first buffer.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");

        // Ensure that the second buffer is returned.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        // Ensure that the third buffer is returned.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");
    }
}
<commit_msg>Insert new buffers after the current buffer, not at the end.<commit_after>//! Buffer and working directory management.

use buffer::Buffer;
use std::io::Error;
use std::path::PathBuf;

/// An owned collection of buffers and associated path,
/// representing a running editor environment.
pub struct Workspace {
    pub path: PathBuf,
    buffers: Vec<Buffer>,
    current_buffer_index: Option<usize>,
}

impl Workspace {
    /// Creates a new empty workspace for the specified path.
    pub fn new(path: PathBuf) -> Workspace {
        Workspace{ path: path, buffers: Vec::new(), current_buffer_index: None }
    }

    /// Adds a buffer to the workspace, *inserting
    /// it after the current buffer*, and selects it.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    /// ```
    pub fn add_buffer(&mut self, buf: Buffer) {
        // The target index is directly after the current buffer's index.
        let target_index = self.current_buffer_index.map(|i| i + 1 ).unwrap_or(0);

        // Insert the buffer and select it.
        self.buffers.insert(target_index, buf);
        self.current_buffer_index = Some(target_index);
    }

    /// Opens a buffer at the specified path, *inserting
    /// it after the current buffer*, and selects it.
    /// If a buffer with the specified path already exists,
    /// it is selected, rather than opening a duplicate buffer.
    /// Any errors encountered while opening the buffer are returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Open a buffer in the workspace.
    /// workspace.open_buffer(file_path.clone());
    /// ```
    pub fn open_buffer(&mut self, path: PathBuf) -> Option<Error> {
        if self.contains_buffer_with_path(&path) {
            // We already have this buffer in the workspace.
            // Loop through the buffers until it's selected.
            loop {
                match self.current_buffer() {
                    Some(buffer) => {
                        match buffer.path {
                            Some(ref current_path) => {
                                if *current_path == path {
                                    break;
                                }
                            },
                            None => (),
                        }
                    },
                    None => (),
                }

                self.next_buffer()
            }

            // Not going to run into IO errors if we're not opening a buffer.
            None
        } else {
            match Buffer::from_file(path) {
                Ok(buffer) => {
                    self.add_buffer(buffer);
                    None
                },
                Err(error) => Some(error),
            }
        }
    }

    /// Returns a mutable reference to the currently
    /// selected buffer, unless the workspace is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Get a reference to the current buffer.
    /// let buffer_reference = workspace.current_buffer().unwrap();
    /// ```
    pub fn current_buffer(&mut self) -> Option<&mut Buffer> {
        match self.current_buffer_index {
            Some(index) => Some(&mut self.buffers[index]),
            None => None,
        }
    }

    /// Removes the currently selected buffer from the collection.
    /// If the workspace is empty, this method does nothing.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Close the current buffer.
    /// workspace.close_current_buffer();
    /// ```
    pub fn close_current_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                self.buffers.remove(index);

                if self.buffers.is_empty() {
                    self.current_buffer_index = None;
                } else {
                    self.current_buffer_index = Some(self.buffers.len()-1);
                }
            }
            None => return,
        };
    }

    /// Selects the previous buffer in the workspace (buffers are ordered as
    /// they are added to the workspace). If the currently selected buffer is
    /// the first in the collection, this will wrap and select the last buffer.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Select the previous buffer.
    /// workspace.previous_buffer();
    /// ```
    pub fn previous_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                if index > 0 {
                    self.current_buffer_index = Some(index-1);
                } else {
                    self.current_buffer_index = Some(self.buffers.len()-1);
                }
            },
            None => return,
        }
    }

    /// Selects the next buffer in the workspace (buffers are ordered as
    /// they are added to the workspace). If the currently selected buffer is
    /// the last in the collection, this will wrap and select the first buffer.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// // Select the next buffer.
    /// workspace.next_buffer();
    /// ```
    pub fn next_buffer(&mut self) {
        match self.current_buffer_index {
            Some(index) => {
                if index == self.buffers.len()-1 {
                    self.current_buffer_index = Some(0);
                } else {
                    self.current_buffer_index = Some(index+1);
                }
            },
            None => return,
        }
    }

    /// Whether or not the workspace contains a buffer with the specified path.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::Workspace;
    /// use std::path::PathBuf;
    ///
    /// // Set up the paths we'll use.
    /// let directory_path = PathBuf::from("tests/sample");
    /// let file_path = PathBuf::from("tests/sample/file");
    ///
    /// // Create a workspace.
    /// let mut workspace = Workspace::new(directory_path);
    ///
    /// // Add a buffer to the workspace.
    /// let buf = Buffer::from_file(file_path.clone()).unwrap();
    /// workspace.add_buffer(buf);
    ///
    /// assert!(workspace.contains_buffer_with_path(&file_path));
    /// ```
    pub fn contains_buffer_with_path(&self, path: &PathBuf) -> bool {
        self.buffers.iter().any(|buffer| {
            match buffer.path {
                Some(ref buffer_path) => path == buffer_path,
                None => false,
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::Workspace;
    use buffer::Buffer;
    use std::path::PathBuf;

    #[test]
    fn add_buffer_adds_and_selects_the_passed_buffer() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        let buf = Buffer::from_file(PathBuf::from("tests/sample/file")).unwrap();
        workspace.add_buffer(buf);

        assert_eq!(workspace.buffers.len(), 1);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn add_buffer_inserts_the_new_buffer_after_the_current_buffer() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        let mut buf1 = Buffer::new();
        let mut buf2 = Buffer::new();
        let mut buf3 = Buffer::new();
        buf1.insert("one");
        buf2.insert("two");
        buf3.insert("three");
        workspace.add_buffer(buf1);
        workspace.add_buffer(buf2);

        // Move to the first buffer.
        workspace.previous_buffer();

        // Insert the last buffer.
        workspace.add_buffer(buf3);

        // Make sure the newly inserted buffer was inserted after the current buffer.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "one");
    }

    #[test]
    fn open_buffer_adds_and_selects_the_buffer_at_the_specified_path() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        assert_eq!(workspace.buffers.len(), 1);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn open_buffer_does_not_open_a_buffer_already_in_the_workspace() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        assert_eq!(workspace.buffers.len(), 1);
    }

    #[test]
    fn open_buffer_selects_buffer_if_it_already_exists_in_workspace() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        // Add and select another buffer.
        let mut buf = Buffer::new();
        buf.insert("scribe");
        workspace.add_buffer(buf);
        assert_eq!(workspace.current_buffer().unwrap().data(), "scribe");

        // Try to add the first buffer again.
        workspace.open_buffer(PathBuf::from("tests/sample/file"));

        // Ensure there are only two buffers, and that the
        // one requested via open_buffer is now selected.
        assert_eq!(workspace.buffers.len(), 2);
        assert_eq!(workspace.current_buffer().unwrap().data(), "it works!\n");
    }

    #[test]
    fn current_buffer_returns_none_when_there_are_no_buffers() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn current_buffer_returns_one_when_there_are_buffers() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        let buf = Buffer::from_file(PathBuf::from("tests/sample/file")).unwrap();
        workspace.add_buffer(buf);
        assert!(workspace.current_buffer().is_some());
    }

    #[test]
    fn close_current_buffer_does_nothing_when_none_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.close_current_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn close_current_buffer_cleans_up_when_only_one_buffer_is_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.add_buffer(Buffer::new());
        workspace.close_current_buffer();
        assert!(workspace.current_buffer().is_none());
        assert!(workspace.current_buffer_index.is_none());
    }

    #[test]
    fn close_current_buffer_when_two_are_open_selects_the_other() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);

        // Ensure that the second buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        workspace.close_current_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");
    }

    #[test]
    fn previous_buffer_does_nothing_when_no_buffers_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.previous_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn previous_buffer_when_three_are_open_selects_previous_wrapping_to_last() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        let mut third_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        third_buffer.insert("third buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);
        workspace.add_buffer(third_buffer);

        // Ensure that the third buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");

        // Ensure that the second buffer is returned.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        // Ensure that the first buffer is returned.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");

        // Ensure that it wraps back to the third buffer.
        workspace.previous_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");
    }

    #[test]
    fn next_buffer_does_nothing_when_no_buffers_are_open() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));
        workspace.next_buffer();
        assert!(workspace.current_buffer().is_none());
    }

    #[test]
    fn next_buffer_when_three_are_open_selects_next_wrapping_to_first() {
        let mut workspace = Workspace::new(PathBuf::from("tests/sample"));

        // Create two buffers and add them to the workspace.
        let mut first_buffer = Buffer::new();
        let mut second_buffer = Buffer::new();
        let mut third_buffer = Buffer::new();
        first_buffer.insert("first buffer");
        second_buffer.insert("second buffer");
        third_buffer.insert("third buffer");
        workspace.add_buffer(first_buffer);
        workspace.add_buffer(second_buffer);
        workspace.add_buffer(third_buffer);

        // Ensure that the third buffer is currently selected.
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");

        // Ensure that it wraps back to the first buffer.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "first buffer");

        // Ensure that the second buffer is returned.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "second buffer");

        // Ensure that the third buffer is returned.
        workspace.next_buffer();
        assert_eq!(workspace.current_buffer().unwrap().data(), "third buffer");
    }
}
<|endoftext|>


<commit_before>use xen::hypercall::hypercall2;
use xen::hypercall::HyperCalls;

use ::arch::defs::Ulong;

pub type EvtchnPort = u32;

#[allow(dead_code)]
enum EventOp {
    BindInterdomain = 0,
    BindVirq = 1,
    BindPirq = 2,
    Close = 3,
    Send = 4,
    Status = 5,
    AllocUnbound = 6,
    BindIpi = 7,
    BindVcpu = 8,
    Unmask = 9,
    Reset = 10,
    InitControl = 11,
    ExpandArray = 12,
    SetPriority = 13,
}

#[allow(dead_code)]
fn event_channel_op(op: EventOp, event: Ulong) -> Ulong {
    hypercall2(HyperCalls::EventChannelOp, op as Ulong, event)
}
<commit_msg>event: Add send() function<commit_after>use xen::hypercall::hypercall2;
use xen::hypercall::HyperCalls;

use ::arch::defs::Ulong;

pub type EvtchnPort = u32;

#[allow(dead_code)]
enum EventOp {
    BindInterdomain = 0,
    BindVirq = 1,
    BindPirq = 2,
    Close = 3,
    Send = 4,
    Status = 5,
    AllocUnbound = 6,
    BindIpi = 7,
    BindVcpu = 8,
    Unmask = 9,
    Reset = 10,
    InitControl = 11,
    ExpandArray = 12,
    SetPriority = 13,
}

#[repr(C)]
struct EvtchnSend {
    port: EvtchnPort,
}

fn event_channel_op(op: EventOp, event: Ulong) -> Ulong {
    hypercall2(HyperCalls::EventChannelOp, op as Ulong, event)
}

pub fn send(port: EvtchnPort) -> i32 {
    let ev: EvtchnSend = EvtchnSend {
        port: port,
    };
    let ev_ptr = &ev as *const EvtchnSend;

    event_channel_op(EventOp::Send, ev_ptr as Ulong) as i32
}
<|endoftext|>