<commit_before>use std::marker::PhantomData;
use std::mem;
use std::fmt;
use std::slice;
use num;
use approx;
use channel::{FreeChannel, FreeChannelScalar, ChannelFormatCast, ChannelCast, ColorChannel};
use color::{Color, FromTuple, Bounded, HomogeneousColor, Lerp, Flatten};
use linalg::Matrix3;
use convert::FromColor;
use xyz::Xyz;

pub trait LmsModel<T>: Clone + PartialEq {
    fn forward_transform() -> Matrix3<T>;
    fn inverse_transform() -> Matrix3<T>;
}

pub struct LmsTag;

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Lms<T, Model> {
    l: FreeChannel<T>,
    m: FreeChannel<T>,
    s: FreeChannel<T>,
    model: PhantomData<Model>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CieCam2002;
#[derive(Clone, Debug, PartialEq)]
pub struct CieCam97s;
#[derive(Clone, Debug, PartialEq)]
pub struct Bradford;

type LmsCam2002<T> = Lms<T, CieCam2002>;

impl<T, Model> Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    pub fn from_channels(l: T, m: T, s: T) -> Self {
        Lms {
            l: FreeChannel::new(l),
            m: FreeChannel::new(m),
            s: FreeChannel::new(s),
            model: PhantomData,
        }
    }

    pub fn color_cast<TOut>(&self) -> Lms<TOut, Model>
        where T: ChannelFormatCast<TOut>,
              TOut: FreeChannelScalar
    {
        Lms {
            l: self.l.clone().channel_cast(),
            m: self.m.clone().channel_cast(),
            s: self.s.clone().channel_cast(),
            model: PhantomData,
        }
    }

    pub fn l(&self) -> T {
        self.l.0.clone()
    }
    pub fn m(&self) -> T {
        self.m.0.clone()
    }
    pub fn s(&self) -> T {
        self.s.0.clone()
    }
    pub fn l_mut(&mut self) -> &mut T {
        &mut self.l.0
    }
    pub fn m_mut(&mut self) -> &mut T {
        &mut self.m.0
    }
    pub fn s_mut(&mut self) -> &mut T {
        &mut self.s.0
    }
    pub fn set_l(&mut self, val: T) {
        self.l.0 = val;
    }
    pub fn set_m(&mut self, val: T) {
        self.m.0 = val;
    }
    pub fn set_s(&mut self, val: T) {
        self.s.0 = val;
    }
}

impl<T, Model> Color for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type Tag = LmsTag;
    type ChannelsTuple = (T, T, T);

    #[inline]
    fn num_channels() -> u32 {
        3
    }
    fn to_tuple(self) -> Self::ChannelsTuple {
        (self.l.0, self.m.0, self.s.0)
    }
}

impl<T, Model> FromTuple for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_tuple(values: (T, T, T)) -> Self {
        Lms::from_channels(values.0, values.1, values.2)
    }
}

impl<T, Model> HomogeneousColor for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type ChannelFormat = T;

    impl_color_homogeneous_color_square!(Lms<T> {l, m, s}, chan=FreeChannel, phantom={model});
}

impl<T, Model> Bounded for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    impl_color_bounded!(Lms {l, m, s}, phantom={model});
}

impl<T, Model> Lerp for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>,
          FreeChannel<T>: Lerp
{
    type Position = <FreeChannel<T> as Lerp>::Position;
    impl_color_lerp_square!(Lms {l, m, s}, phantom={model});
}

impl<T, Model> Flatten for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type ScalarFormat = T;

    impl_color_as_slice!(T);
    impl_color_from_slice_square!(Lms<T> {l:FreeChannel - 0, m:FreeChannel - 1,
        s:FreeChannel - 2});
}

impl<T, Model> approx::ApproxEq for Lms<T, Model>
    where T: FreeChannelScalar + approx::ApproxEq,
          T::Epsilon: Clone,
          Model: LmsModel<T>
{
    impl_approx_eq!({l, m, s});
}

impl<T, Model> Default for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    impl_color_default!(Lms {l:FreeChannel, m:FreeChannel, s:FreeChannel}, phantom={model});
}

impl<T, Model> fmt::Display for Lms<T, Model>
    where T: FreeChannelScalar + fmt::Display,
          Model: LmsModel<T>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "LMS({}, {}, {})", self.l, self.m, self.s)
    }
}

impl<T, Model> FromColor<Xyz<T>> for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_color(from: &Xyz<T>) -> Self {
        let transform = Model::forward_transform();
        let (l, m, s) = transform.transform_vector(from.clone().to_tuple());
        Lms::from_channels(l, m, s)
    }
}

impl<T, Model> FromColor<Lms<T, Model>> for Xyz<T>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_color(from: &Lms<T, Model>) -> Self {
        let transform = Model::inverse_transform();
        let (x, y, z) = transform.transform_vector(from.clone().to_tuple());
        Xyz::from_channels(x, y, z)
    }
}

impl<T> LmsModel<T> for CieCam2002
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.7328).unwrap(),
                           num::cast(0.4296).unwrap(),
                           num::cast(-0.1624).unwrap(),
                           num::cast(-0.7036).unwrap(),
                           num::cast(1.6975).unwrap(),
                           num::cast(0.0061).unwrap(),
                           num::cast(0.0030).unwrap(),
                           num::cast(0.0136).unwrap(),
                           num::cast(0.9834).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(1.09612).unwrap(),
                           num::cast(-0.27887).unwrap(),
                           num::cast(0.18275).unwrap(),
                           num::cast(0.45437).unwrap(),
                           num::cast(0.47353).unwrap(),
                           num::cast(0.07209).unwrap(),
                           num::cast(-0.009628).unwrap(),
                           num::cast(-0.005698).unwrap(),
                           num::cast(1.015326).unwrap()])
    }
}

impl<T> LmsModel<T> for CieCam97s
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.8562).unwrap(),
                           num::cast(0.3372).unwrap(),
                           num::cast(-0.1934).unwrap(),
                           num::cast(-0.8360).unwrap(),
                           num::cast(1.8327).unwrap(),
                           num::cast(0.0033).unwrap(),
                           num::cast(0.0357).unwrap(),
                           num::cast(-0.0469).unwrap(),
                           num::cast(1.0112).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.98740).unwrap(),
                           num::cast(-0.17683).unwrap(),
                           num::cast(0.18943).unwrap(),
                           num::cast(0.45044).unwrap(),
                           num::cast(0.46493).unwrap(),
                           num::cast(0.08463).unwrap(),
                           num::cast(-0.01397).unwrap(),
                           num::cast(0.027807).unwrap(),
                           num::cast(0.98616).unwrap()])
    }
}

impl<T> LmsModel<T> for Bradford
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.8951).unwrap(),
                           num::cast(0.2664).unwrap(),
                           num::cast(-0.1614).unwrap(),
                           num::cast(-0.7502).unwrap(),
                           num::cast(1.7135).unwrap(),
                           num::cast(0.0367).unwrap(),
                           num::cast(0.0389).unwrap(),
                           num::cast(-0.0685).unwrap(),
                           num::cast(1.0296).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.98699).unwrap(),
                           num::cast(-0.14705).unwrap(),
                           num::cast(0.15996).unwrap(),
                           num::cast(0.43231).unwrap(),
                           num::cast(0.51836).unwrap(),
                           num::cast(0.04929).unwrap(),
                           num::cast(-0.00853).unwrap(),
                           num::cast(0.040043).unwrap(),
                           num::cast(0.96849).unwrap()])
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use xyz::Xyz;
    use convert::*;

    #[test]
    fn test_from_xyz() {
        let c1 = Xyz::from_channels(0.5, 0.2, 0.0);
        let t1 = Lms::<_, CieCam2002>::from_color(&c1);
        assert_relative_eq!(t1, Lms::from_channels(0.45232, -0.01230, 0.00422), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t1), c1, epsilon=1e-4);

        let c2 = Xyz::from_channels(0.3, 0.3, 0.3);
        let t2 = Lms::<_, CieCam2002>::from_color(&c2);
        assert_relative_eq!(t2, Lms::from_channels(0.3, 0.3, 0.3), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t2), c2, epsilon=1e-4);

        let c3 = Xyz::from_channels(0.6, 0.4, 0.5);
        let t3 = Lms::<_, CieCam97s>::from_color(&c3);
        assert_relative_eq!(t3, Lms::from_channels(0.5519, 0.23313, 0.50826), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t3), c3, epsilon=1e-4);

        let c4 = Xyz::from_channels(0.2, 0.3, 0.6);
        let t4 = Lms::<_, Bradford>::from_color(&c4);
        assert_relative_eq!(t4, Lms::from_channels(0.1621, 0.38603, 0.6050), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t4), c4, epsilon=1e-4);
    }
}
<commit_msg>Implemented basic functionality tests for Lms.<commit_after>use std::marker::PhantomData;
use std::mem;
use std::fmt;
use std::slice;
use num;
use approx;
use channel::{FreeChannel, FreeChannelScalar, ChannelFormatCast, ChannelCast, ColorChannel};
use color::{Color, FromTuple, Bounded, HomogeneousColor, Lerp, Flatten};
use linalg::Matrix3;
use convert::FromColor;
use xyz::Xyz;

pub trait LmsModel<T>: Clone + PartialEq {
    fn forward_transform() -> Matrix3<T>;
    fn inverse_transform() -> Matrix3<T>;
}

pub struct LmsTag;

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Lms<T, Model> {
    l: FreeChannel<T>,
    m: FreeChannel<T>,
    s: FreeChannel<T>,
    model: PhantomData<Model>,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct CieCam2002;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct CieCam97s;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Bradford;

pub type LmsCam2002<T> = Lms<T, CieCam2002>;
pub type LmsCam97s<T> = Lms<T, CieCam97s>;
pub type LmsBradford<T> = Lms<T, Bradford>;

impl<T, Model> Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    pub fn from_channels(l: T, m: T, s: T) -> Self {
        Lms {
            l: FreeChannel::new(l),
            m: FreeChannel::new(m),
            s: FreeChannel::new(s),
            model: PhantomData,
        }
    }

    pub fn color_cast<TOut>(&self) -> Lms<TOut, Model>
        where T: ChannelFormatCast<TOut>,
              TOut: FreeChannelScalar
    {
        Lms {
            l: self.l.clone().channel_cast(),
            m: self.m.clone().channel_cast(),
            s: self.s.clone().channel_cast(),
            model: PhantomData,
        }
    }

    pub fn l(&self) -> T {
        self.l.0.clone()
    }
    pub fn m(&self) -> T {
        self.m.0.clone()
    }
    pub fn s(&self) -> T {
        self.s.0.clone()
    }
    pub fn l_mut(&mut self) -> &mut T {
        &mut self.l.0
    }
    pub fn m_mut(&mut self) -> &mut T {
        &mut self.m.0
    }
    pub fn s_mut(&mut self) -> &mut T {
        &mut self.s.0
    }
    pub fn set_l(&mut self, val: T) {
        self.l.0 = val;
    }
    pub fn set_m(&mut self, val: T) {
        self.m.0 = val;
    }
    pub fn set_s(&mut self, val: T) {
        self.s.0 = val;
    }
}

impl<T, Model> Color for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type Tag = LmsTag;
    type ChannelsTuple = (T, T, T);

    #[inline]
    fn num_channels() -> u32 {
        3
    }
    fn to_tuple(self) -> Self::ChannelsTuple {
        (self.l.0, self.m.0, self.s.0)
    }
}

impl<T, Model> FromTuple for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_tuple(values: (T, T, T)) -> Self {
        Lms::from_channels(values.0, values.1, values.2)
    }
}

impl<T, Model> HomogeneousColor for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type ChannelFormat = T;

    impl_color_homogeneous_color_square!(Lms<T> {l, m, s}, chan=FreeChannel, phantom={model});
}

impl<T, Model> Bounded for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    impl_color_bounded!(Lms {l, m, s}, phantom={model});
}

impl<T, Model> Lerp for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>,
          FreeChannel<T>: Lerp
{
    type Position = <FreeChannel<T> as Lerp>::Position;
    impl_color_lerp_square!(Lms {l, m, s}, phantom={model});
}

impl<T, Model> Flatten for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    type ScalarFormat = T;

    impl_color_as_slice!(T);
    impl_color_from_slice_square!(Lms<T> {l:FreeChannel - 0, m:FreeChannel - 1,
        s:FreeChannel - 2});
}

impl<T, Model> approx::ApproxEq for Lms<T, Model>
    where T: FreeChannelScalar + approx::ApproxEq,
          T::Epsilon: Clone,
          Model: LmsModel<T>
{
    impl_approx_eq!({l, m, s});
}

impl<T, Model> Default for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    impl_color_default!(Lms {l:FreeChannel, m:FreeChannel, s:FreeChannel}, phantom={model});
}

impl<T, Model> fmt::Display for Lms<T, Model>
    where T: FreeChannelScalar + fmt::Display,
          Model: LmsModel<T>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "LMS({}, {}, {})", self.l, self.m, self.s)
    }
}

impl<T, Model> FromColor<Xyz<T>> for Lms<T, Model>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_color(from: &Xyz<T>) -> Self {
        let transform = Model::forward_transform();
        let (l, m, s) = transform.transform_vector(from.clone().to_tuple());
        Lms::from_channels(l, m, s)
    }
}

impl<T, Model> FromColor<Lms<T, Model>> for Xyz<T>
    where T: FreeChannelScalar,
          Model: LmsModel<T>
{
    fn from_color(from: &Lms<T, Model>) -> Self {
        let transform = Model::inverse_transform();
        let (x, y, z) = transform.transform_vector(from.clone().to_tuple());
        Xyz::from_channels(x, y, z)
    }
}

impl<T> LmsModel<T> for CieCam2002
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.7328).unwrap(),
                           num::cast(0.4296).unwrap(),
                           num::cast(-0.1624).unwrap(),
                           num::cast(-0.7036).unwrap(),
                           num::cast(1.6975).unwrap(),
                           num::cast(0.0061).unwrap(),
                           num::cast(0.0030).unwrap(),
                           num::cast(0.0136).unwrap(),
                           num::cast(0.9834).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(1.09612).unwrap(),
                           num::cast(-0.27887).unwrap(),
                           num::cast(0.18275).unwrap(),
                           num::cast(0.45437).unwrap(),
                           num::cast(0.47353).unwrap(),
                           num::cast(0.07209).unwrap(),
                           num::cast(-0.009628).unwrap(),
                           num::cast(-0.005698).unwrap(),
                           num::cast(1.015326).unwrap()])
    }
}

impl<T> LmsModel<T> for CieCam97s
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.8562).unwrap(),
                           num::cast(0.3372).unwrap(),
                           num::cast(-0.1934).unwrap(),
                           num::cast(-0.8360).unwrap(),
                           num::cast(1.8327).unwrap(),
                           num::cast(0.0033).unwrap(),
                           num::cast(0.0357).unwrap(),
                           num::cast(-0.0469).unwrap(),
                           num::cast(1.0112).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.98740).unwrap(),
                           num::cast(-0.17683).unwrap(),
                           num::cast(0.18943).unwrap(),
                           num::cast(0.45044).unwrap(),
                           num::cast(0.46493).unwrap(),
                           num::cast(0.08463).unwrap(),
                           num::cast(-0.01397).unwrap(),
                           num::cast(0.027807).unwrap(),
                           num::cast(0.98616).unwrap()])
    }
}

impl<T> LmsModel<T> for Bradford
    where T: FreeChannelScalar
{
    fn forward_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.8951).unwrap(),
                           num::cast(0.2664).unwrap(),
                           num::cast(-0.1614).unwrap(),
                           num::cast(-0.7502).unwrap(),
                           num::cast(1.7135).unwrap(),
                           num::cast(0.0367).unwrap(),
                           num::cast(0.0389).unwrap(),
                           num::cast(-0.0685).unwrap(),
                           num::cast(1.0296).unwrap()])
    }

    fn inverse_transform() -> Matrix3<T> {
        Matrix3::<T>::new([num::cast(0.98699).unwrap(),
                           num::cast(-0.14705).unwrap(),
                           num::cast(0.15996).unwrap(),
                           num::cast(0.43231).unwrap(),
                           num::cast(0.51836).unwrap(),
                           num::cast(0.04929).unwrap(),
                           num::cast(-0.00853).unwrap(),
                           num::cast(0.040043).unwrap(),
                           num::cast(0.96849).unwrap()])
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use xyz::Xyz;
    use convert::*;
    use color::*;

    #[test]
    fn test_construct() {
        let c1 = LmsCam2002::from_channels(0.4, 0.6, 0.2);
        assert_relative_eq!(c1.l(), 0.4);
        assert_relative_eq!(c1.m(), 0.6);
        assert_relative_eq!(c1.s(), 0.2);
        assert_eq!(c1.to_tuple(), (0.4, 0.6, 0.2));
        assert_relative_eq!(LmsCam2002::from_tuple(c1.to_tuple()), c1);
    }

    #[test]
    fn test_lerp() {
        let c1 = LmsCam97s::from_channels(0.5, 0.9, 0.0);
        let c2 = LmsCam97s::from_channels(0.7, 0.0, 0.4);
        assert_relative_eq!(c1.lerp(&c2, 0.0), c1);
        assert_relative_eq!(c1.lerp(&c2, 1.0), c2);
        assert_relative_eq!(c1.lerp(&c2, 0.5), LmsCam97s::from_channels(0.6, 0.45, 0.2));
        assert_relative_eq!(c1.lerp(&c2, 0.75), LmsCam97s::from_channels(0.65, 0.225, 0.3));
    }

    #[test]
    fn test_normalize() {
        let c1 = LmsCam2002::from_channels(-50.0, 50.0, 1e7);
        assert!(c1.is_normalized());
        assert_relative_eq!(c1.normalize(), c1);
    }

    #[test]
    fn test_flatten() {
        let c1 = LmsBradford::from_channels(0.2, 0.5, 1.0);
        assert_eq!(c1.as_slice(), &[0.2, 0.5, 1.0]);
        assert_relative_eq!(LmsBradford::from_slice(c1.as_slice()), c1);
    }

    #[test]
    fn test_from_xyz() {
        let c1 = Xyz::from_channels(0.5, 0.2, 0.0);
        let t1 = Lms::<_, CieCam2002>::from_color(&c1);
        assert_relative_eq!(t1, Lms::from_channels(0.45232, -0.01230, 0.00422), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t1), c1, epsilon=1e-4);

        let c2 = Xyz::from_channels(0.3, 0.3, 0.3);
        let t2 = Lms::<_, CieCam2002>::from_color(&c2);
        assert_relative_eq!(t2, Lms::from_channels(0.3, 0.3, 0.3), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t2), c2, epsilon=1e-4);

        let c3 = Xyz::from_channels(0.6, 0.4, 0.5);
        let t3 = Lms::<_, CieCam97s>::from_color(&c3);
        assert_relative_eq!(t3, Lms::from_channels(0.5519, 0.23313, 0.50826), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t3), c3, epsilon=1e-4);

        let c4 = Xyz::from_channels(0.2, 0.3, 0.6);
        let t4 = Lms::<_, Bradford>::from_color(&c4);
        assert_relative_eq!(t4, Lms::from_channels(0.1621, 0.38603, 0.6050), epsilon=1e-4);
        assert_relative_eq!(Xyz::from_color(&t4), c4, epsilon=1e-4);
    }

    #[test]
    fn test_to_xyz() {
        let c1 = LmsCam2002::from_channels(0.25, 0.50, 0.75);
        let t1 = Xyz::from_color(&c1);
        assert_relative_eq!(t1, Xyz::from_channels(0.2716575, 0.404425, 0.75624), epsilon=1e-4);
        assert_relative_eq!(LmsCam2002::from_color(&t1), c1, epsilon=1e-4);
    }

    #[test]
    fn test_color_cast() {
        let c1 = LmsCam2002::from_channels(0.25, 0.50, 0.75);
        assert_relative_eq!(c1.color_cast(), c1);
        assert_relative_eq!(c1.color_cast(), LmsCam2002::from_channels(0.25f32, 0.50f32, 0.75f32));
        assert_relative_eq!(c1.color_cast::<f32>().color_cast(), c1);
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;

use str_error;
use Result;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

/*

Following the KiCad file formats specification chapter 4.4 - Identifiers and Strings:
A string may not contain an actual newline or carriage return, but it may use an escape sequence to encode a
newline, such as \n.
If a string has any of the following conditions, then it must be quoted with a leading and trailing double quote
character, otherwise it is acceptable to not quote the string:
1. has one or more of the following 4 separator bytes: ASCII space, tab, '(', or ')'.
2. has one or more of the following bytes: '%', '{', or '}'.
3. has a length of zero bytes, and you need a place holder for the field, then use "".
4. includes a byte of '-', and this byte is not in the first position of the string.
*/

pub fn encode_string(s:&str) -> String {
    fn rule_4(s:&str) -> bool {
        s.contains('-') && s.len() > 1 && s.as_bytes()[0] != 45
    }
    if s.contains('(') || s.contains(' ') || s.contains(')') || s.contains('\t') || s.contains('{') || s.contains('}') || s.contains('}') || s.contains('%') || s.is_empty() || rule_4(s) {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp::Empty
    }

    pub fn new_string(s:&str) -> Sexp {
        Sexp::String(s.to_string())
    }

    pub fn new_list(v:Vec<Sexp>) -> Sexp {
        Sexp::List(v)
    }

    pub fn new_named(name:&str, value:&str) -> Sexp {
        let v = vec![Sexp::new_string(name), Sexp::new_string(value)];
        Sexp::List(v)
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> Result<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => str_error(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> Result<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            _ => str_error(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> Result<f64> {
        let s = try!(self.string());
        match f64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> Result<i64> {
        let s = try!(self.string());
        match i64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> Result<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> Result<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return str_error(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> Result<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> Result<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> Result<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return str_error(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                write!(f, "{}", encode_string(s))
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                let l = v.len();
                for (i,x) in v.iter().enumerate() {
                    if i < l-1 {
                        try!(write!(f, "{} ", x));
                    } else {
                        try!(write!(f, "{}", x));
                    }
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}
<commit_msg>anything displayable can be a string :)<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;

use str_error;
use Result;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

/*

Following the KiCad file formats specification chapter 4.4 - Identifiers and Strings:
A string may not contain an actual newline or carriage return, but it may use an escape sequence to encode a
newline, such as \n.
If a string has any of the following conditions, then it must be quoted with a leading and trailing double quote
character, otherwise it is acceptable to not quote the string:
1. has one or more of the following 4 separator bytes: ASCII space, tab, '(', or ')'.
2. has one or more of the following bytes: '%', '{', or '}'.
3. has a length of zero bytes, and you need a place holder for the field, then use "".
4. includes a byte of '-', and this byte is not in the first position of the string.
*/

pub fn encode_string(s:&str) -> String {
    fn rule_4(s:&str) -> bool {
        s.contains('-') && s.len() > 1 && s.as_bytes()[0] != 45
    }
    if s.contains('(') || s.contains(' ') || s.contains(')') || s.contains('\t') || s.contains('{') || s.contains('}') || s.contains('}') || s.contains('%') || s.is_empty() || rule_4(s) {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp::Empty
    }

    pub fn new_string<T>(s:T) -> Sexp where T:fmt::Display {
        Sexp::String(format!("{}", s))
    }

    pub fn new_list(v:Vec<Sexp>) -> Sexp {
        Sexp::List(v)
    }

    pub fn new_named(name:&str, value:&str) -> Sexp {
        let v = vec![Sexp::new_string(name), Sexp::new_string(value)];
        Sexp::List(v)
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> Result<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => str_error(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> Result<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            _ => str_error(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> Result<f64> {
        let s = try!(self.string());
        match f64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> Result<i64> {
        let s = try!(self.string());
        match i64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> Result<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> Result<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return str_error(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> Result<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> Result<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> Result<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return str_error(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                write!(f, "{}", encode_string(s))
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                let l = v.len();
                for (i,x) in v.iter().enumerate() {
                    if i < l-1 {
                        try!(write!(f, "{} ", x));
                    } else {
                        try!(write!(f, "{}", x));
                    }
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017-2018 int08h LLC
//
// 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.

//! Ed25519 signing and verification
//!
//! `Ring` does not provide a multi-step (init-update-finish) interface
//! for Ed25519 signatures. `Verifier` and `Signer` provide this
//! missing multi-step api.

extern crate hex;
extern crate ring;
extern crate untrusted;

use self::ring::signature;
use self::ring::signature::Ed25519KeyPair;

use self::ring::rand;
use self::ring::rand::SecureRandom;
use self::untrusted::Input;

use std::fmt;
use std::fmt::Formatter;

/// A multi-step (init-update-finish) interface for verifying an
/// Ed25519 signature
#[derive(Debug)]
pub struct Verifier<'a> {
    pubkey: Input<'a>,
    buf: Vec<u8>,
}

impl<'a> Verifier<'a> {
    pub fn new(pubkey: &'a [u8]) -> Self {
        Verifier {
            pubkey: Input::from(pubkey),
            buf: Vec::with_capacity(256),
        }
    }

    pub fn update(&mut self, data: &[u8]) {
        self.buf.reserve(data.len());
        self.buf.extend_from_slice(data);
    }

    pub fn verify(&self, expected_sig: &[u8]) -> bool {
        let msg = Input::from(&self.buf);
        let sig = Input::from(expected_sig);

        match signature::verify(&signature::ED25519, self.pubkey, msg, sig) {
            Ok(_) => true,
            _ => false,
        }
    }
}

/// A multi-step (init-update-finish) interface for creating an
/// Ed25519 signature
pub struct Signer {
    key_pair: Ed25519KeyPair,
    buf: Vec<u8>,
}

impl Signer {
    pub fn new() -> Self {
        let rng = rand::SystemRandom::new();
        let mut seed = [0u8; 32];
        rng.fill(&mut seed).unwrap();

        Signer::from_seed(&seed)
    }

    pub fn from_seed(seed: &[u8]) -> Self {
        let seed_input = Input::from(seed);
        Signer {
            key_pair: Ed25519KeyPair::from_seed_unchecked(seed_input).unwrap(),
            buf: Vec::with_capacity(256),
        }
    }

    pub fn update(&mut self, data: &[u8]) {
        self.buf.reserve(data.len());
        self.buf.extend_from_slice(data);
    }

    pub fn sign(&mut self) -> Vec<u8> {
        let signature = self.key_pair.sign(&self.buf).as_ref().to_vec();
        self.buf.clear();

        signature
    }

    pub fn public_key_bytes(&self) -> &[u8] {
        self.key_pair.public_key_bytes()
    }
}

impl fmt::Display for Signer {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", hex::encode(self.public_key_bytes()))
    }
}

impl fmt::Debug for Signer {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(
            f,
            "Signer({}, {:?})",
            hex::encode(self.public_key_bytes()),
            self.buf
        )
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)] // rustfmt errors on the long signature strings
#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn verify_ed25519_sig_on_empty_message() {
        let pubkey = hex::decode(
            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
        ).unwrap();

        let signature = hex::decode(
            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
        ).unwrap();

        let v = Verifier::new(&pubkey);
        let result = v.verify(&signature);
        assert_eq!(result, true);
    }

    #[test]
    fn verify_ed25519_sig() {
        let pubkey = hex::decode(
            "c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7",
        ).unwrap();

        let message = hex::decode("5f4c8989").unwrap();

        let signature = hex::decode(
            "124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07"
        ).unwrap();

        let mut v = Verifier::new(&pubkey);
        v.update(&message);
        let result = v.verify(&signature);
        assert_eq!(result, true);
    }

    #[test]
    fn sign_ed25519_empty_message() {
        let seed = hex::decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
            .unwrap();

        let expected_sig = hex::decode(
            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
        ).unwrap();

        let mut s = Signer::from_seed(&seed);
        let sig = s.sign();
        assert_eq!(sig, expected_sig);
    }

    #[test]
    fn sign_ed25519_message() {
        let seed = hex::decode("0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9")
            .unwrap();

        let message = hex::decode("cbc77b").unwrap();

        let expected_sig = hex::decode(
            "d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c"
        ).unwrap();

        let mut s = Signer::from_seed(&seed);
        s.update(&message);
        let sig = s.sign();
        assert_eq!(sig, expected_sig);
    }

    #[test]
    fn sign_verify_round_trip() {
        let seed = hex::decode("334a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9")
            .unwrap();

        let message = "Hello world".as_bytes();

        let mut signer = Signer::from_seed(&seed);
        signer.update(&message);
        let signature = signer.sign();

        let mut v = Verifier::new(signer.public_key_bytes());
        v.update(&message);
        let result = v.verify(&signature);

        assert_eq!(result, true);
    }
}
<commit_msg>Minor Rustdoc updates<commit_after>// Copyright 2017-2018 int08h LLC
//
// 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.

//!
//! A multi-step (init-update-finish) interface for Ed25519 signing and verification
//!

extern crate hex;
extern crate ring;
extern crate untrusted;

use self::ring::signature;
use self::ring::signature::Ed25519KeyPair;
use self::ring::rand;
use self::ring::rand::SecureRandom;

use self::untrusted::Input;

use std::fmt;
use std::fmt::Formatter;

/// A multi-step (init-update-finish) interface for verifying an Ed25519 signature
#[derive(Debug)]
pub struct Verifier<'a> {
    pubkey: Input<'a>,
    buf: Vec<u8>,
}

impl<'a> Verifier<'a> {
    pub fn new(pubkey: &'a [u8]) -> Self {
        Verifier {
            pubkey: Input::from(pubkey),
            buf: Vec::with_capacity(256),
        }
    }

    pub fn update(&mut self, data: &[u8]) {
        self.buf.reserve(data.len());
        self.buf.extend_from_slice(data);
    }

    pub fn verify(&self, expected_sig: &[u8]) -> bool {
        let msg = Input::from(&self.buf);
        let sig = Input::from(expected_sig);

        match signature::verify(&signature::ED25519, self.pubkey, msg, sig) {
            Ok(_) => true,
            _ => false,
        }
    }
}

/// A multi-step (init-update-finish) interface for creating an Ed25519 signature
pub struct Signer {
    key_pair: Ed25519KeyPair,
    buf: Vec<u8>,
}

impl Signer {
    pub fn new() -> Self {
        let rng = rand::SystemRandom::new();
        let mut seed = [0u8; 32];
        rng.fill(&mut seed).unwrap();

        Signer::from_seed(&seed)
    }

    pub fn from_seed(seed: &[u8]) -> Self {
        let seed_input = Input::from(seed);
        Signer {
            key_pair: Ed25519KeyPair::from_seed_unchecked(seed_input).unwrap(),
            buf: Vec::with_capacity(256),
        }
    }

    pub fn update(&mut self, data: &[u8]) {
        self.buf.reserve(data.len());
        self.buf.extend_from_slice(data);
    }

    pub fn sign(&mut self) -> Vec<u8> {
        let signature = self.key_pair.sign(&self.buf).as_ref().to_vec();
        self.buf.clear();

        signature
    }

    pub fn public_key_bytes(&self) -> &[u8] {
        self.key_pair.public_key_bytes()
    }
}

impl fmt::Display for Signer {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", hex::encode(self.public_key_bytes()))
    }
}

impl fmt::Debug for Signer {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(
            f,
            "Signer({}, {:?})",
            hex::encode(self.public_key_bytes()),
            self.buf
        )
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)] // rustfmt errors on the long signature strings
#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn verify_ed25519_sig_on_empty_message() {
        let pubkey = hex::decode(
            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
        ).unwrap();

        let signature = hex::decode(
            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
        ).unwrap();

        let v = Verifier::new(&pubkey);
        let result = v.verify(&signature);
        assert_eq!(result, true);
    }

    #[test]
    fn verify_ed25519_sig() {
        let pubkey = hex::decode(
            "c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7",
        ).unwrap();

        let message = hex::decode("5f4c8989").unwrap();

        let signature = hex::decode(
            "124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07"
        ).unwrap();

        let mut v = Verifier::new(&pubkey);
        v.update(&message);
        let result = v.verify(&signature);
        assert_eq!(result, true);
    }

    #[test]
    fn sign_ed25519_empty_message() {
        let seed = hex::decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
            .unwrap();

        let expected_sig = hex::decode(
            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
        ).unwrap();

        let mut s = Signer::from_seed(&seed);
        let sig = s.sign();
        assert_eq!(sig, expected_sig);
    }

    #[test]
    fn sign_ed25519_message() {
        let seed = hex::decode("0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9")
            .unwrap();

        let message = hex::decode("cbc77b").unwrap();

        let expected_sig = hex::decode(
            "d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c"
        ).unwrap();

        let mut s = Signer::from_seed(&seed);
        s.update(&message);
        let sig = s.sign();
        assert_eq!(sig, expected_sig);
    }

    #[test]
    fn sign_verify_round_trip() {
        let seed = hex::decode("334a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9")
            .unwrap();

        let message = "Hello world".as_bytes();

        let mut signer = Signer::from_seed(&seed);
        signer.update(&message);
        let signature = signer.sign();

        let mut v = Verifier::new(signer.public_key_bytes());
        v.update(&message);
        let result = v.verify(&signature);

        assert_eq!(result, true);
    }
}
<|endoftext|>


<commit_before>struct Index {
    path: String,
    entries: Vec<String>,
}

impl Index {
    fn new(path: &str) -> Index {
        return Index { path: path.to_string(), entries: vec![] }
    }
}

#[test]
fn new_creates_index_with_passed_path() {
    let path = "my path";
    let index = Index::new(path);
    assert!(index.path == path);
}

#[test]
fn new_creates_index_with_empty_array() {
    let path = "my path";
    let index = Index::new(path);
    let empty_array: Vec<String> = vec![];
    assert!(index.entries == empty_array)
}
<commit_msg>Update Index struct to use Path objects instead of strings.<commit_after>struct Index {
    path: Path,
    entries: Vec<Path>,
}

impl Index {
    fn new(path: &str) -> Index {
        return Index { path: Path::new(path.to_string()), entries: vec![] }
    }
}

#[test]
fn new_creates_index_with_passed_path() {
    let path = "my path";
    let index = Index::new(path);

    // Get the index path as a string.
    let index_path = match index.path.as_str() {
        Some(value) => value,
        None => ""
    };

    assert_eq!(index_path, path);
}

#[test]
fn new_creates_index_with_empty_array() {
    let path = "my path";
    let index = Index::new(path);
    let empty_array: Vec<Path> = vec![];

    assert!(index.entries == empty_array)
}
<|endoftext|>


<commit_before>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.

//! Simple implementation of an LZW compressor.

use std::collections::HashMap;
use std::io::{Read, Write};
use error::Error;
use bitfile::{BitWriter, BitReader};

const EOF: u64 = 256;

struct State {
    max_code: u64,
    code_len: usize,
    next_code: u64,
    dict: HashMap<u64, Vec<u8>>,
}

impl State {
    fn new() -> State {
        let max_code_len = 16;
        let mut st = State {
            max_code: (1 << max_code_len) - 1,
            code_len: 9,
            next_code: 257,
            dict: HashMap::new(),
        };
        for c in 0..256 {
            let mut s = Vec::new();
            s.push(c as u8);
            st.dict.insert(c, s);
        }
        st
    }
}

pub fn compress<R, W>(mut input: R, output: W) -> Result<W, Error>
    where R: Read, W: Write {
    let max_code_len = 16;
    let max_code = (1 << max_code_len) - 1;
    let mut code_len = 9;
    let mut next_code = 257;
    let mut dict: HashMap<Vec<u8>, u64> = HashMap::new();
    for c in 0..256 {
        let mut s = Vec::new();
        s.push(c as u8);
        dict.insert(s, c as u64);
    }

    let mut current_string: Vec<u8> = Vec::new();

    let mut out = BitWriter::new(output);
    let mut buf = [0u8; 1];

    let mut nread = try!(input.read(&mut buf));
    while nread == 1 {
        let c = buf[0];

        current_string.push(c);
        if let None = dict.get(&current_string) {
            if next_code <= max_code {
                dict.insert(current_string.clone(), next_code);
                next_code += 1;
            }
            let _ = current_string.pop();
            if let Some(code) = dict.get(&current_string) {
                try!(out.write_bits(*code, code_len));
            } else {
                unreachable!();
            }
            current_string.truncate(0);
            current_string.push(c);
            if next_code < max_code && next_code >= (1 << code_len) {
                code_len += 1;
            }
        }

        nread = try!(input.read(&mut buf));
    }

    if current_string.len() > 0 {
        if let Some(code) = dict.get(&current_string) {
            try!(out.write_bits(*code, code_len));
        } else {
            unreachable!();
        }
    }

    try!(out.write_bits(EOF, code_len));
    out.flush()
}

pub fn decompress<R, W>(input: R, mut output: W) -> Result<W, Error>
    where R: Read, W: Write {
    let max_code_len = 16;
    let max_code = (1 << max_code_len) - 1;
    let mut code_len = 9;
    let mut next_code = 257;
    let mut dict: HashMap<u64, Vec<u8>> = HashMap::new();
    for c in 0..256 {
        let mut s = Vec::new();
        s.push(c as u8);
        dict.insert(c, s);
    }

    let mut previous_string: Vec<u8> = Vec::new();

    let mut inp = BitReader::new(input);

    let mut code = try!(inp.read_bits(code_len));
    while code != EOF {
        if let None = dict.get(&code) {
            let mut s = Vec::new();
            s.extend_from_slice(&previous_string[..]);
            s.extend_from_slice(&previous_string[0..1]);
            dict.insert(code, s);
        }

        let str_code = dict.get(&code).unwrap().clone();
        let _ = try!(output.write(&str_code[..]));

        if previous_string.len() > 0 && next_code <= max_code {
            let mut ns = Vec::new();
            ns.extend_from_slice(&previous_string[..]);
            ns.extend_from_slice(&str_code[0..1]);
            dict.insert(next_code, ns);
            next_code += 1;
        }
        previous_string = str_code;

        if next_code < max_code && next_code + 1 >= (1 << code_len) {
            code_len += 1;
        }
        code = try!(inp.read_bits(code_len));

    }

    Ok(output)
}

pub fn inspect<R>(input: R) -> Result<(), Error>
    where R: Read {

    let mut state = State::new();

    let mut previous_string: Vec<u8> = Vec::new();

    let mut inp = BitReader::new(input);

    let mut code = try!(inp.read_bits(state.code_len));
    while code != EOF {
        if let None = state.dict.get(&code) {
            let mut s = Vec::new();
            s.extend_from_slice(&previous_string[..]);
            s.extend_from_slice(&previous_string[0..1]);
            state.dict.insert(code, s);
        }

        let str_code = state.dict.get(&code).unwrap().clone();
        let as_string =
            match String::from_utf8(str_code.clone()) {
                Ok(s) => s,
                Err(_) => "<binary>".to_string(),
            };
        println!("{:4} {:?}", code, as_string);

        if previous_string.len() > 0 && state.next_code <= state.max_code {
            let mut ns = Vec::new();
            ns.extend_from_slice(&previous_string[..]);
            ns.extend_from_slice(&str_code[0..1]);
            state.dict.insert(state.next_code, ns);
            state.next_code += 1;
        }
        previous_string = str_code;

        if state.next_code < state.max_code && state.next_code + 1 >= (1 << state.code_len) {
            state.code_len += 1;
        }
        code = try!(inp.read_bits(state.code_len));

    }

    Ok(())
}

#[cfg(test)]
mod test {
    use ::std::io::Cursor;
    use super::{compress, decompress};

    #[test]
    fn compress_empty() {
        let input = b"";
        let expected = [128, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn compress_a() {
        let input = b"A";
        let expected = [32, 192, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn compress_aaa() {
        let input = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        let expected = [32, 192, 96, 80, 56, 36, 22, 13, 7, 130, 192, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn decompress_empty() {
        let input = [128, 0];
        let expected = b"";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn decompress_a() {
        let input = [32, 192, 0];
        let expected = b"A";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn decompress_aaa() {
        let input = [32, 192, 96, 80, 56, 36, 22, 13, 7, 130, 192, 0];
        let expected = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn compress_decompress() {
        use ::std::io::Cursor;
        let f = include_bytes!("lzw.rs");
        let original = &f[..];

        let compressed = compress(Cursor::new(&original[..]), vec![]).unwrap();

        let decompressed = decompress(Cursor::new(&compressed[..]), vec![]).unwrap();
        assert_eq!(original.len(), decompressed.len());
        assert_eq!(&original[..], &decompressed[..]);
    }
}
<commit_msg>Refactoring.<commit_after>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.

//! Simple implementation of an LZW compressor.

use std::collections::HashMap;
use std::io::{Read, Write};
use error::Error;
use bitfile::{BitWriter, BitReader};

const EOF: u64 = 256;

struct SharedState {
    max_code: u64,
    code_len: usize,
    next_code: u64,
}

impl SharedState {
    fn new() -> SharedState {
        let max_code_len = 16;
        let st = SharedState {
            max_code: (1 << max_code_len) - 1,
            code_len: 9,
            next_code: 257,
        };
        st
    }
}

struct CompressState {
    dict: HashMap<Vec<u8>, u64>,
}

impl CompressState {
    fn new() -> CompressState {
        let mut st = CompressState {
            dict: HashMap::new(),
        };
        for c in 0..256 {
            let mut s = Vec::new();
            s.push(c as u8);
            st.dict.insert(s, c);
        }
        st
    }
}

struct DecompressState {
    dict: HashMap<u64, Vec<u8>>,
}

impl DecompressState {
    fn new() -> DecompressState {
        let mut st = DecompressState {
            dict: HashMap::new(),
        };
        for c in 0..256 {
            let mut s = Vec::new();
            s.push(c as u8);
            st.dict.insert(c, s);
        }
        st
    }
}

pub fn compress<R, W>(mut input: R, output: W) -> Result<W, Error>
    where R: Read, W: Write {
    let mut state = SharedState::new();
    let mut cstate = CompressState::new();

    let mut current_string: Vec<u8> = Vec::new();

    let mut out = BitWriter::new(output);
    let mut buf = [0u8; 1];

    let mut nread = try!(input.read(&mut buf));
    while nread == 1 {
        let c = buf[0];

        current_string.push(c);
        if let None = cstate.dict.get(&current_string) {
            if state.next_code <= state.max_code {
                cstate.dict.insert(current_string.clone(), state.next_code);
                state.next_code += 1;
            }
            let _ = current_string.pop();
            if let Some(code) = cstate.dict.get(&current_string) {
                try!(out.write_bits(*code, state.code_len));
            } else {
                unreachable!();
            }
            current_string.truncate(0);
            current_string.push(c);
            if state.next_code < state.max_code && state.next_code >= (1 << state.code_len) {
                state.code_len += 1;
            }
        }

        nread = try!(input.read(&mut buf));
    }

    if current_string.len() > 0 {
        if let Some(code) = cstate.dict.get(&current_string) {
            try!(out.write_bits(*code, state.code_len));
        } else {
            unreachable!();
        }
    }

    try!(out.write_bits(EOF, state.code_len));
    out.flush()
}

pub fn decompress<R, W>(input: R, mut output: W) -> Result<W, Error>
    where R: Read, W: Write {
    let mut state = SharedState::new();
    let mut dstate = DecompressState::new();

    // let max_code_len = 16;
    // let max_code = (1 << max_code_len) - 1;
    // let mut code_len = 9;
    // let mut next_code = 257;
    // let mut dict: HashMap<u64, Vec<u8>> = HashMap::new();
    // for c in 0..256 {
    //     let mut s = Vec::new();
    //     s.push(c as u8);
    //     dict.insert(c, s);
    // }

    let mut previous_string: Vec<u8> = Vec::new();

    let mut inp = BitReader::new(input);

    let mut code = try!(inp.read_bits(state.code_len));
    while code != EOF {
        if let None = dstate.dict.get(&code) {
            let mut s = Vec::new();
            s.extend_from_slice(&previous_string[..]);
            s.extend_from_slice(&previous_string[0..1]);
            dstate.dict.insert(code, s);
        }

        let str_code = dstate.dict.get(&code).unwrap().clone();
        let _ = try!(output.write(&str_code[..]));

        if previous_string.len() > 0 && state.next_code <= state.max_code {
            let mut ns = Vec::new();
            ns.extend_from_slice(&previous_string[..]);
            ns.extend_from_slice(&str_code[0..1]);
            dstate.dict.insert(state.next_code, ns);
            state.next_code += 1;
        }
        previous_string = str_code;

        if state.next_code < state.max_code && state.next_code + 1 >= (1 << state.code_len) {
            state.code_len += 1;
        }
        code = try!(inp.read_bits(state.code_len));

    }

    Ok(output)
}

pub fn inspect<R>(input: R) -> Result<(), Error>
    where R: Read {

    let mut state = SharedState::new();
    let mut dstate = DecompressState::new();

    let mut previous_string: Vec<u8> = Vec::new();

    let mut inp = BitReader::new(input);

    let mut code = try!(inp.read_bits(state.code_len));
    while code != EOF {
        if let None = dstate.dict.get(&code) {
            let mut s = Vec::new();
            s.extend_from_slice(&previous_string[..]);
            s.extend_from_slice(&previous_string[0..1]);
            dstate.dict.insert(code, s);
        }

        let str_code = dstate.dict.get(&code).unwrap().clone();
        let as_string =
            match String::from_utf8(str_code.clone()) {
                Ok(s) => s,
                Err(_) => "<binary>".to_string(),
            };
        println!("{:4} {:?}", code, as_string);

        if previous_string.len() > 0 && state.next_code <= state.max_code {
            let mut ns = Vec::new();
            ns.extend_from_slice(&previous_string[..]);
            ns.extend_from_slice(&str_code[0..1]);
            dstate.dict.insert(state.next_code, ns);
            state.next_code += 1;
        }
        previous_string = str_code;

        if state.next_code < state.max_code && state.next_code + 1 >= (1 << state.code_len) {
            state.code_len += 1;
        }
        code = try!(inp.read_bits(state.code_len));

    }

    Ok(())
}

#[cfg(test)]
mod test {
    use ::std::io::Cursor;
    use super::{compress, decompress};

    #[test]
    fn compress_empty() {
        let input = b"";
        let expected = [128, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn compress_a() {
        let input = b"A";
        let expected = [32, 192, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn compress_aaa() {
        let input = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        let expected = [32, 192, 96, 80, 56, 36, 22, 13, 7, 130, 192, 0];
        let compressed = compress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &compressed[..]);
    }

    #[test]
    fn decompress_empty() {
        let input = [128, 0];
        let expected = b"";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn decompress_a() {
        let input = [32, 192, 0];
        let expected = b"A";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn decompress_aaa() {
        let input = [32, 192, 96, 80, 56, 36, 22, 13, 7, 130, 192, 0];
        let expected = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        let decompressed = decompress(Cursor::new(&input[..]), vec![]).unwrap();
        assert_eq!(&expected[..], &decompressed[..]);
    }

    #[test]
    fn compress_decompress() {
        use ::std::io::Cursor;
        let f = include_bytes!("lzw.rs");
        let original = &f[..];

        let compressed = compress(Cursor::new(&original[..]), vec![]).unwrap();

        let decompressed = decompress(Cursor::new(&compressed[..]), vec![]).unwrap();
        assert_eq!(original.len(), decompressed.len());
        assert_eq!(&original[..], &decompressed[..]);
    }
}
<|endoftext|>


<commit_before>use floating_duration::TimeFormat;
use regex::Regex;
use std::fs::OpenOptions;
use std::io::{Write, BufReader, BufRead, BufWriter};
use std::path;
use std::thread;
use std::process;
use term;
use time;

pub trait RunningTask {
    fn done(&mut self) -> bool;
    fn wait(self: Box<Self>);
    fn interrupt(self: Box<Self>);
}

pub trait Runnable {
    fn run(&self) -> Box<RunningTask>;
}

pub trait Task: Runnable {
    fn should_run(&self, _: &path::Path) -> bool;
    fn start_delay(&self) -> time::Duration;
}

pub struct ShellCommand {
    pub name: String,
    pub command: String,
    pub work_directory: Option<path::PathBuf>,
}

pub trait ShellTask: Task {
    // Will run the first command, on success the second..
    fn commands(&self) -> Vec<ShellCommand>;
    fn redirect_stdout(&self) -> Option<path::PathBuf>;
    fn redirect_stderr(&self) -> Option<path::PathBuf>;
    fn supress_stdout(&self) -> bool;
    fn supress_stderr(&self) -> bool;
}

fn handle_output<R: BufRead, W: Write>(reader: R, echo: bool, mut redirect: Option<BufWriter<W>>) {
    for line in reader.lines() {
        if !line.is_ok() {
            continue;
        }
        let line = line.unwrap();

        // TODO(sirver): My understanding is that \x1b[ should start every ANSI sequence, but I
        // also see \x1b( in my outputs - so I filtered that too. Figure out a more principled way
        // of removing color.
        // for now this is lifted from chalk/ansi-regex.
        lazy_static! {
            static ref REMOVE_ANSI: Regex = Regex::new("[\u{1b}\u{9b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]").unwrap();
            static ref REMOVE_SHIFT_INOUT: Regex = Regex::new("[\u{0e}\u{0f}]").unwrap();
        }
        let no_color = REMOVE_ANSI.replace_all(&line, "");
        let no_shift = REMOVE_SHIFT_INOUT.replace_all(&no_color, "");
        redirect
            .as_mut()
            .map(|w| writeln!(w, "{}", no_shift).unwrap());
        if echo {
            println!("{}", line);
        }
    }
}

struct RunningChildState {
    name: String,
    child: process::Child,
    start_time: time::PreciseTime,
    io_threads: Vec<thread::JoinHandle<()>>,
}

struct RunningShellTask {
    commands: Vec<ShellCommand>,
    echo_stdout: bool,
    redirect_stdout: Option<path::PathBuf>,
    echo_stderr: bool,
    redirect_stderr: Option<path::PathBuf>,
    running_child: Option<RunningChildState>,
}

impl RunningShellTask {
    pub fn spawn(commands: Vec<ShellCommand>,
                 echo_stdout: bool,
                 redirect_stdout: Option<path::PathBuf>,
                 echo_stderr: bool,
                 redirect_stderr: Option<path::PathBuf>)
                 -> Self {
        let mut this = RunningShellTask {
            commands: commands,
            echo_stdout,
            redirect_stdout,
            echo_stderr,
            redirect_stderr,
            running_child: None,
        };

        {
            let mut terminal = term::stdout().unwrap();
            write!(terminal, "\x1b[2J").unwrap(); // Clear the screen.
        }
        this.run_next_command(true);
        this
    }

    fn run_next_command(&mut self, is_first: bool) {
        assert!(self.running_child.is_none());
        if self.commands.is_empty() {
            return;
        }
        let command = self.commands.remove(0);

        let args = command.command.split_whitespace().collect::<Vec<&str>>();
        let mut terminal = term::stdout().unwrap();
        terminal.fg(term::color::CYAN).unwrap();
        writeln!(terminal, "==> {}", command.name).unwrap();
        terminal.reset().unwrap();
        terminal.flush().unwrap();

        let start_time = time::PreciseTime::now();
        let mut child = {
            let mut child = process::Command::new(args[0]);
            child
                .args(&args[1..])
                .stdin(process::Stdio::inherit())
                .stdout(process::Stdio::piped())
                .stderr(process::Stdio::piped());
            if let Some(path) = command.work_directory {
                child.current_dir(path);
            }
            child
                .spawn()
                .unwrap_or_else(|e| panic!("failed to execute: {}", e))
        };

        let mut io_threads = Vec::new();
        let creation_func = |p| {
            OpenOptions::new()
                .write(true)
                .create(is_first)
                .truncate(is_first)
                .append(!is_first)
                .open(p)
        };

        let stdout = BufReader::new(child.stdout.take().unwrap());
        let echo_stdout = self.echo_stdout;
        let redirect_stdout =
            self.redirect_stdout
                .as_ref()
                .map(|path| BufWriter::with_capacity(4096, creation_func(path).unwrap()));
        io_threads
            .push(thread::spawn(move || { handle_output(stdout, echo_stdout, redirect_stdout); }));
        let stderr = BufReader::new(child.stderr.take().unwrap());
        let echo_stderr = self.echo_stderr;
        let redirect_stderr =
            self.redirect_stderr
                .as_ref()
                .map(|path| BufWriter::with_capacity(4096, creation_func(path).unwrap()));
        io_threads
            .push(thread::spawn(move || { handle_output(stderr, echo_stderr, redirect_stderr); }));
        self.running_child = Some(RunningChildState {
                                      name: command.name,
                                      io_threads,
                                      child,
                                      start_time,
                                  });
    }

    fn current_command_finished(&mut self, success: bool) {
        assert!(self.running_child.is_some());
        let running_child = self.running_child.take().unwrap();

        let duration = running_child
            .start_time
            .to(time::PreciseTime::now())
            .to_std()
            .unwrap();
        let mut terminal = term::stdout().unwrap();
        terminal.fg(term::color::CYAN).unwrap();
        write!(terminal, "==> {}: ", running_child.name).unwrap();
        terminal.reset().unwrap();
        if success {
            terminal.fg(term::color::GREEN).unwrap();
            write!(terminal, "Success. ").unwrap();
        } else {
            terminal.fg(term::color::RED).unwrap();
            write!(terminal, "Failed. ").unwrap();
        }
        terminal.reset().unwrap();
        write!(terminal, "({})", TimeFormat(duration)).unwrap();
        writeln!(terminal, "").unwrap();
        if success {
            self.run_next_command(false);
        }
    }
}

impl Drop for RunningShellTask {
    fn drop(&mut self) {
        if let Some(mut running_child) = self.running_child.take() {
            for handle in running_child.io_threads.drain(..) {
                handle.join().unwrap();
            }
        }
    }
}

impl RunningTask for RunningShellTask {
    fn done(&mut self) -> bool {
        if self.running_child.is_none() {
            return true;
        }

        let success = match self.running_child
                  .as_mut()
                  .unwrap()
                  .child
                  .try_wait()
                  .expect("try_wait") {
            Some(status) => status.success(),
            None => return false,
        };
        self.current_command_finished(success);
        self.done()
    }

    fn wait(mut self: Box<Self>) {
        if self.done() {
            return;
        }
        self.running_child
            .take()
            .map(|mut r| r.child.wait().expect("wait"));
        self.wait()
    }

    fn interrupt(mut self: Box<Self>) {
        if self.done() {
            return;
        }
        self.running_child
            .take()
            .map(|mut r| r.child.kill().expect("kill"));
    }
}

impl<T: ShellTask> Runnable for T {
    /// Dispatches to 'program' with 'str'.
    fn run(&self) -> Box<RunningTask> {
        Box::new(RunningShellTask::spawn(self.commands(),
                                         !self.supress_stdout(),
                                         self.redirect_stdout(),
                                         !self.supress_stderr(),
                                         self.redirect_stderr()))
    }
}
<commit_msg>Make buffer smaller.<commit_after>use floating_duration::TimeFormat;
use regex::Regex;
use std::fs::OpenOptions;
use std::io::{Write, BufReader, BufRead, BufWriter};
use std::path;
use std::thread;
use std::process;
use term;
use time;

pub trait RunningTask {
    fn done(&mut self) -> bool;
    fn wait(self: Box<Self>);
    fn interrupt(self: Box<Self>);
}

pub trait Runnable {
    fn run(&self) -> Box<RunningTask>;
}

pub trait Task: Runnable {
    fn should_run(&self, _: &path::Path) -> bool;
    fn start_delay(&self) -> time::Duration;
}

pub struct ShellCommand {
    pub name: String,
    pub command: String,
    pub work_directory: Option<path::PathBuf>,
}

pub trait ShellTask: Task {
    // Will run the first command, on success the second..
    fn commands(&self) -> Vec<ShellCommand>;
    fn redirect_stdout(&self) -> Option<path::PathBuf>;
    fn redirect_stderr(&self) -> Option<path::PathBuf>;
    fn supress_stdout(&self) -> bool;
    fn supress_stderr(&self) -> bool;
}

fn handle_output<R: BufRead, W: Write>(reader: R, echo: bool, mut redirect: Option<BufWriter<W>>) {
    for line in reader.lines() {
        if !line.is_ok() {
            continue;
        }
        let line = line.unwrap();

        // TODO(sirver): My understanding is that \x1b[ should start every ANSI sequence, but I
        // also see \x1b( in my outputs - so I filtered that too. Figure out a more principled way
        // of removing color.
        // for now this is lifted from chalk/ansi-regex.
        lazy_static! {
            static ref REMOVE_ANSI: Regex = Regex::new("[\u{1b}\u{9b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]").unwrap();
            static ref REMOVE_SHIFT_INOUT: Regex = Regex::new("[\u{0e}\u{0f}]").unwrap();
        }
        let no_color = REMOVE_ANSI.replace_all(&line, "");
        let no_shift = REMOVE_SHIFT_INOUT.replace_all(&no_color, "");
        redirect
            .as_mut()
            .map(|w| writeln!(w, "{}", no_shift).unwrap());
        if echo {
            println!("{}", line);
        }
    }
}

struct RunningChildState {
    name: String,
    child: process::Child,
    start_time: time::PreciseTime,
    io_threads: Vec<thread::JoinHandle<()>>,
}

struct RunningShellTask {
    commands: Vec<ShellCommand>,
    echo_stdout: bool,
    redirect_stdout: Option<path::PathBuf>,
    echo_stderr: bool,
    redirect_stderr: Option<path::PathBuf>,
    running_child: Option<RunningChildState>,
}

impl RunningShellTask {
    pub fn spawn(commands: Vec<ShellCommand>,
                 echo_stdout: bool,
                 redirect_stdout: Option<path::PathBuf>,
                 echo_stderr: bool,
                 redirect_stderr: Option<path::PathBuf>)
                 -> Self {
        let mut this = RunningShellTask {
            commands: commands,
            echo_stdout,
            redirect_stdout,
            echo_stderr,
            redirect_stderr,
            running_child: None,
        };

        {
            let mut terminal = term::stdout().unwrap();
            write!(terminal, "\x1b[2J").unwrap(); // Clear the screen.
        }
        this.run_next_command(true);
        this
    }

    fn run_next_command(&mut self, is_first: bool) {
        assert!(self.running_child.is_none());
        if self.commands.is_empty() {
            return;
        }
        let command = self.commands.remove(0);

        let args = command.command.split_whitespace().collect::<Vec<&str>>();
        let mut terminal = term::stdout().unwrap();
        terminal.fg(term::color::CYAN).unwrap();
        writeln!(terminal, "==> {}", command.name).unwrap();
        terminal.reset().unwrap();
        terminal.flush().unwrap();

        let start_time = time::PreciseTime::now();
        let mut child = {
            let mut child = process::Command::new(args[0]);
            child
                .args(&args[1..])
                .stdin(process::Stdio::inherit())
                .stdout(process::Stdio::piped())
                .stderr(process::Stdio::piped());
            if let Some(path) = command.work_directory {
                child.current_dir(path);
            }
            child
                .spawn()
                .unwrap_or_else(|e| panic!("failed to execute: {}", e))
        };

        let mut io_threads = Vec::new();
        let creation_func = |p| {
            OpenOptions::new()
                .write(true)
                .create(is_first)
                .truncate(is_first)
                .append(!is_first)
                .open(p)
        };

        let stdout = BufReader::new(child.stdout.take().unwrap());
        let echo_stdout = self.echo_stdout;
        let redirect_stdout =
            self.redirect_stdout
                .as_ref()
                .map(|path| BufWriter::with_capacity(512, creation_func(path).unwrap()));
        io_threads
            .push(thread::spawn(move || { handle_output(stdout, echo_stdout, redirect_stdout); }));
        let stderr = BufReader::new(child.stderr.take().unwrap());
        let echo_stderr = self.echo_stderr;
        let redirect_stderr =
            self.redirect_stderr
                .as_ref()
                .map(|path| BufWriter::with_capacity(512, creation_func(path).unwrap()));
        io_threads
            .push(thread::spawn(move || { handle_output(stderr, echo_stderr, redirect_stderr); }));
        self.running_child = Some(RunningChildState {
                                      name: command.name,
                                      io_threads,
                                      child,
                                      start_time,
                                  });
    }

    fn current_command_finished(&mut self, success: bool) {
        assert!(self.running_child.is_some());
        let running_child = self.running_child.take().unwrap();

        let duration = running_child
            .start_time
            .to(time::PreciseTime::now())
            .to_std()
            .unwrap();
        let mut terminal = term::stdout().unwrap();
        terminal.fg(term::color::CYAN).unwrap();
        write!(terminal, "==> {}: ", running_child.name).unwrap();
        terminal.reset().unwrap();
        if success {
            terminal.fg(term::color::GREEN).unwrap();
            write!(terminal, "Success. ").unwrap();
        } else {
            terminal.fg(term::color::RED).unwrap();
            write!(terminal, "Failed. ").unwrap();
        }
        terminal.reset().unwrap();
        write!(terminal, "({})", TimeFormat(duration)).unwrap();
        writeln!(terminal, "").unwrap();
        if success {
            self.run_next_command(false);
        }
    }
}

impl Drop for RunningShellTask {
    fn drop(&mut self) {
        if let Some(mut running_child) = self.running_child.take() {
            for handle in running_child.io_threads.drain(..) {
                handle.join().unwrap();
            }
        }
    }
}

impl RunningTask for RunningShellTask {
    fn done(&mut self) -> bool {
        if self.running_child.is_none() {
            return true;
        }

        let success = match self.running_child
                  .as_mut()
                  .unwrap()
                  .child
                  .try_wait()
                  .expect("try_wait") {
            Some(status) => status.success(),
            None => return false,
        };
        self.current_command_finished(success);
        self.done()
    }

    fn wait(mut self: Box<Self>) {
        if self.done() {
            return;
        }
        self.running_child
            .take()
            .map(|mut r| r.child.wait().expect("wait"));
        self.wait()
    }

    fn interrupt(mut self: Box<Self>) {
        if self.done() {
            return;
        }
        self.running_child
            .take()
            .map(|mut r| r.child.kill().expect("kill"));
    }
}

impl<T: ShellTask> Runnable for T {
    /// Dispatches to 'program' with 'str'.
    fn run(&self) -> Box<RunningTask> {
        Box::new(RunningShellTask::spawn(self.commands(),
                                         !self.supress_stdout(),
                                         self.redirect_stdout(),
                                         !self.supress_stderr(),
                                         self.redirect_stderr()))
    }
}
<|endoftext|>


<commit_before>use super::*;
use semver;

#[test]
fn empty_file() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com""##);
    assert!(puppetfile.is_ok());
    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        vec![],
        parsed.modules
    );
}

#[test]
fn no_version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module { name: String::from_str("mayflower/php"), info: vec![] },
        parsed.modules[0]
    );
}

#[test]
fn git_version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php', '1.0.1'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module {
            name: String::from_str("mayflower/php"),
            info: vec![Version(semver::version::parse("1.0.1").unwrap())]
        },
        parsed.modules[0]
    );
}

#[test]
fn version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php',
:git => 'git://github.com/Mayflower/puppet-php.git'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module {
            name: String::from_str("mayflower/php"),
            info: vec![
                ModuleInfo(String::from_str("git"),
                           String::from_str("git://github.com/Mayflower/puppet-php.git"))
            ]
        },
        parsed.modules[0]
    );
}

#[test]
fn format() {
    let version = Version(semver::version::parse("1.0.0").unwrap());
    assert_eq!(String::from_str("1.0.0"), format!("{}", version));

    let mod_info = ModuleInfo(
        String::from_str("git"),
        String::from_str("git://github.com/Mayflower/puppet-php.git")
    );
    assert_eq!(
        String::from_str(":git => 'git://github.com/Mayflower/puppet-php.git'"),
        format!("{}", mod_info)
    );

    let module = Module {
        name: String::from_str("mayflower/php"),
        info: vec![version, mod_info]
    };
    assert_eq!(
        String::from_str("mod 'mayflower/php', '1.0.0',
  :git => 'git://github.com/Mayflower/puppet-php.git'"),
        format!("{}", module)
    );

    let puppetfile = Puppetfile {
        forge: String::from_str("https://forge.puppetlabs.com"),
        modules: vec![module]
    };
    assert_eq!(
        String::from_str("forge 'https://forge.puppetlabs.com'

mod 'mayflower/php', '1.0.0',
  :git => 'git://github.com/Mayflower/puppet-php.git'
"),
        format!("{}", puppetfile)
    );
}

#[test]
fn version_url() {
    let module = Module { name: String::from_str("mayflower/php"), info: vec![] };
    assert_eq!(
        "https://forge.puppetlabs.com/users/mayflower/modules/php/releases/find.json",
        format!(
            "{}",
            module.version_url(
                String::from_str("https://forge.puppetlabs.com/")
            )
        ).as_slice()
    )
}

#[test]
fn user_name_pair() {
    let module = Module { name: String::from_str("mayflower/php"), info: vec![] };
    assert_eq!(module.user_name_pair(), Some(("mayflower", "php")))
}

#[test]
fn forge_version() {
    let module = Module { name: String::from_str("puppetlabs/nginx"), info: vec![] };
    assert_eq!(
        module.forge_version(String::from_str("https://forge.puppetlabs.com/")),
        semver::version::parse("99.99.99")
    )
}
<commit_msg>fix fuckups with glob import<commit_after>use super::{Puppetfile, Module, ModuleInfo, Version};
use semver;

#[test]
fn empty_file() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com""##);
    assert!(puppetfile.is_ok());
    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        vec![],
        parsed.modules
    );
}

#[test]
fn no_version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module { name: String::from_str("mayflower/php"), info: vec![] },
        parsed.modules[0]
    );
}

#[test]
fn git_version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php', '1.0.1'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module {
            name: String::from_str("mayflower/php"),
            info: vec![Version(semver::version::parse("1.0.1").unwrap())]
        },
        parsed.modules[0]
    );
}

#[test]
fn version() {
    let puppetfile = Puppetfile::parse(r##"forge "https://forge.puppetlabs.com"

mod 'mayflower/php',
:git => 'git://github.com/Mayflower/puppet-php.git'
    "##);
    assert!(puppetfile.is_ok());

    let parsed = puppetfile.unwrap();
    assert_eq!(
        String::from_str("https://forge.puppetlabs.com"),
        parsed.forge
    );
    assert_eq!(
        Module {
            name: String::from_str("mayflower/php"),
            info: vec![
                ModuleInfo(String::from_str("git"),
                           String::from_str("git://github.com/Mayflower/puppet-php.git"))
            ]
        },
        parsed.modules[0]
    );
}

#[test]
fn format() {
    let version = Version(semver::version::parse("1.0.0").unwrap());
    assert_eq!(String::from_str("1.0.0"), format!("{}", version));

    let mod_info = ModuleInfo(
        String::from_str("git"),
        String::from_str("git://github.com/Mayflower/puppet-php.git")
    );
    assert_eq!(
        String::from_str(":git => 'git://github.com/Mayflower/puppet-php.git'"),
        format!("{}", mod_info)
    );

    let module = Module {
        name: String::from_str("mayflower/php"),
        info: vec![version, mod_info]
    };
    assert_eq!(
        String::from_str("mod 'mayflower/php', '1.0.0',
  :git => 'git://github.com/Mayflower/puppet-php.git'"),
        format!("{}", module)
    );

    let puppetfile = Puppetfile {
        forge: String::from_str("https://forge.puppetlabs.com"),
        modules: vec![module]
    };
    assert_eq!(
        String::from_str("forge 'https://forge.puppetlabs.com'

mod 'mayflower/php', '1.0.0',
  :git => 'git://github.com/Mayflower/puppet-php.git'
"),
        format!("{}", puppetfile)
    );
}

#[test]
fn version_url() {
    let module = Module { name: String::from_str("mayflower/php"), info: vec![] };
    assert_eq!(
        "https://forge.puppetlabs.com/users/mayflower/modules/php/releases/find.json",
        format!(
            "{}",
            module.version_url(
                String::from_str("https://forge.puppetlabs.com/")
            )
        ).as_slice()
    )
}

#[test]
fn user_name_pair() {
    let module = Module { name: String::from_str("mayflower/php"), info: vec![] };
    assert_eq!(module.user_name_pair(), Some(("mayflower", "php")))
}

#[test]
fn forge_version() {
    let module = Module { name: String::from_str("puppetlabs/nginx"), info: vec![] };
    assert_eq!(
        module.forge_version(String::from_str("https://forge.puppetlabs.com/")),
        semver::version::parse("99.99.99")
    )
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the `test` subcommand. Just a stub for now.

use subcommand::Subcommand;
use error::CliResult;
use term::Term;

struct Test;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "test" {
        Some(box Test as Box<Subcommand>)
    } else {
        None
    }
}

impl Subcommand for Test {
    fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(&mut self, _: &mut Term) {}
}
<commit_msg>Initial implementation of test<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the `test` subcommand. Just a stub for now.

use subcommand::Subcommand;
use error::CliResult;
use term::Term;
use book;
use std::io::{Command, File};
use std::os;

struct Test;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "test" {
        Some(box Test as Box<Subcommand>)
    } else {
        None
    }
}

impl Subcommand for Test {
    fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(&mut self, term: &mut Term) {
        let cwd = os::getcwd().unwrap();
        let src = cwd.clone();

        let summary = File::open(&src.join("SUMMARY.md"));
        match book::parse_summary(summary, &src) {
            Ok(book) => {
                for (_, item) in book.iter() {
                    let output_result = Command::new("rustdoc")
                        .arg(&item.path)
                        .arg("--test")
                        .output();
                    match output_result {
                        Ok(output) => {
                            if !output.output.is_empty() || !output.error.is_empty() {
                                term.err(format!("{}\n{}",
                                         String::from_utf8_lossy(output.output[]),
                                         String::from_utf8_lossy(output.error[]))[]);
                            }
                        }
                        Err(e) => {
                            term.err(format!("Could not execute `rustdoc`: {}", e)[]);
                        }
                    }
                }
            }
            Err(errors) => {
                for err in errors.into_iter() {
                    term.err(err[]);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use chrono::NaiveDateTime;
use conduit::{Request, Response};
use diesel::prelude::*;
use serde_json;

use db::RequestTransaction;
use schema::{crate_owner_invitations, crate_owners, crates, users};
use user::RequestUser;
use util::errors::{human, CargoResult};
use util::RequestUtils;
use owner::{CrateOwner, OwnerKind};

/// The model representing a row in the `crate_owner_invitations` database table.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Identifiable, Queryable)]
#[primary_key(invited_user_id, crate_id)]
pub struct CrateOwnerInvitation {
    pub invited_user_id: i32,
    pub invited_by_user_id: i32,
    pub crate_id: i32,
    pub created_at: NaiveDateTime,
}

#[derive(Insertable, Clone, Copy, Debug)]
#[table_name = "crate_owner_invitations"]
pub struct NewCrateOwnerInvitation {
    pub invited_user_id: i32,
    pub invited_by_user_id: i32,
    pub crate_id: i32,
}

impl CrateOwnerInvitation {
    pub fn invited_by_username(&self, conn: &PgConnection) -> String {
        users::table
            .find(self.invited_by_user_id)
            .select(users::gh_login)
            .first(&*conn)
            .unwrap_or_else(|_| String::from("(unknown username)"))
    }

    pub fn crate_name(&self, conn: &PgConnection) -> String {
        crates::table
            .find(self.crate_id)
            .select(crates::name)
            .first(&*conn)
            .unwrap_or_else(|_| String::from("(unknown crate name)"))
    }

    pub fn encodable(self, conn: &PgConnection) -> EncodableCrateOwnerInvitation {
        EncodableCrateOwnerInvitation {
            invited_by_username: self.invited_by_username(conn),
            crate_name: self.crate_name(conn),
            crate_id: self.crate_id,
            created_at: self.created_at,
        }
    }
}

/// The serialization format for the `CrateOwnerInvitation` model.
#[derive(Deserialize, Serialize, Debug)]
pub struct EncodableCrateOwnerInvitation {
    pub invited_by_username: String,
    pub crate_name: String,
    pub crate_id: i32,
    pub created_at: NaiveDateTime,
}

/// Handles the `GET /me/crate_owner_invitations` route.
pub fn list(req: &mut Request) -> CargoResult<Response> {
    let conn = &*req.db_conn()?;
    let user_id = req.user()?.id;

    let crate_owner_invitations = crate_owner_invitations::table
        .filter(crate_owner_invitations::invited_user_id.eq(user_id))
        .load::<CrateOwnerInvitation>(&*conn)?
        .into_iter()
        .map(|i| i.encodable(conn))
        .collect();

    #[derive(Serialize)]
    struct R {
        crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
    }
    Ok(req.json(&R {
        crate_owner_invitations,
    }))
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

#[derive(Deserialize, Serialize, Debug, Copy, Clone)]
pub struct InvitationResponse {
    pub crate_id: i32,
    pub accepted: bool,
}

/// Handles the `PUT /me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut Request) -> CargoResult<Response> {
    let conn = &*req.db_conn()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| human("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;

    if crate_invite.accepted {
        accept_invite(req, conn, crate_invite)
    } else {
        decline_invite(req, conn, crate_invite)
    }
}

fn accept_invite(
    req: &mut Request,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
) -> CargoResult<Response> {
    let user_id = req.user()?.id;
    use diesel::{delete, insert};
    let pending_crate_owner = crate_owner_invitations::table
        .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
        .filter(crate_owner_invitations::invited_user_id.eq(user_id))
        .first::<CrateOwnerInvitation>(&*conn)?;

    let owner = CrateOwner {
        crate_id: crate_invite.crate_id,
        owner_id: user_id,
        created_by: pending_crate_owner.invited_by_user_id,
        owner_kind: OwnerKind::User as i32,
    };

    conn.transaction(|| {
        insert(&owner).into(crate_owners::table).execute(conn)?;
        delete(
            crate_owner_invitations::table
                .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
                .filter(crate_owner_invitations::invited_user_id.eq(user_id)),
        ).execute(conn)?;

        #[derive(Serialize)]
        struct R {
            crate_owner_invitation: InvitationResponse,
        }
        Ok(req.json(&R {
            crate_owner_invitation: crate_invite,
        }))
    })
}

fn decline_invite(
    req: &mut Request,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
) -> CargoResult<Response> {
    use diesel::delete;
    let user_id = req.user()?.id;

    delete(
        crate_owner_invitations::table
            .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
            .filter(crate_owner_invitations::invited_user_id.eq(user_id)),
    ).execute(conn)?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }

    Ok(req.json(&R {
        crate_owner_invitation: crate_invite,
    }))
}
<commit_msg>change insert into crate_owner table<commit_after>use chrono::NaiveDateTime;
use conduit::{Request, Response};
use diesel::prelude::*;
use serde_json;

use db::RequestTransaction;
use schema::{crate_owner_invitations, crate_owners, crates, users};
use user::RequestUser;
use util::errors::{human, CargoResult};
use util::RequestUtils;
use owner::{CrateOwner, OwnerKind};

/// The model representing a row in the `crate_owner_invitations` database table.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Identifiable, Queryable)]
#[primary_key(invited_user_id, crate_id)]
pub struct CrateOwnerInvitation {
    pub invited_user_id: i32,
    pub invited_by_user_id: i32,
    pub crate_id: i32,
    pub created_at: NaiveDateTime,
}

#[derive(Insertable, Clone, Copy, Debug)]
#[table_name = "crate_owner_invitations"]
pub struct NewCrateOwnerInvitation {
    pub invited_user_id: i32,
    pub invited_by_user_id: i32,
    pub crate_id: i32,
}

impl CrateOwnerInvitation {
    pub fn invited_by_username(&self, conn: &PgConnection) -> String {
        users::table
            .find(self.invited_by_user_id)
            .select(users::gh_login)
            .first(&*conn)
            .unwrap_or_else(|_| String::from("(unknown username)"))
    }

    pub fn crate_name(&self, conn: &PgConnection) -> String {
        crates::table
            .find(self.crate_id)
            .select(crates::name)
            .first(&*conn)
            .unwrap_or_else(|_| String::from("(unknown crate name)"))
    }

    pub fn encodable(self, conn: &PgConnection) -> EncodableCrateOwnerInvitation {
        EncodableCrateOwnerInvitation {
            invited_by_username: self.invited_by_username(conn),
            crate_name: self.crate_name(conn),
            crate_id: self.crate_id,
            created_at: self.created_at,
        }
    }
}

/// The serialization format for the `CrateOwnerInvitation` model.
#[derive(Deserialize, Serialize, Debug)]
pub struct EncodableCrateOwnerInvitation {
    pub invited_by_username: String,
    pub crate_name: String,
    pub crate_id: i32,
    pub created_at: NaiveDateTime,
}

/// Handles the `GET /me/crate_owner_invitations` route.
pub fn list(req: &mut Request) -> CargoResult<Response> {
    let conn = &*req.db_conn()?;
    let user_id = req.user()?.id;

    let crate_owner_invitations = crate_owner_invitations::table
        .filter(crate_owner_invitations::invited_user_id.eq(user_id))
        .load::<CrateOwnerInvitation>(&*conn)?
        .into_iter()
        .map(|i| i.encodable(conn))
        .collect();

    #[derive(Serialize)]
    struct R {
        crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
    }
    Ok(req.json(&R {
        crate_owner_invitations,
    }))
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

#[derive(Deserialize, Serialize, Debug, Copy, Clone)]
pub struct InvitationResponse {
    pub crate_id: i32,
    pub accepted: bool,
}

/// Handles the `PUT /me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut Request) -> CargoResult<Response> {
    let conn = &*req.db_conn()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| human("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;

    if crate_invite.accepted {
        accept_invite(req, conn, crate_invite)
    } else {
        decline_invite(req, conn, crate_invite)
    }
}

fn accept_invite(
    req: &mut Request,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
) -> CargoResult<Response> {
    let user_id = req.user()?.id;
    use diesel::{insert, delete};
    use diesel::pg::upsert::do_update;
    use diesel::pg::upsert::OnConflictExtension;
    let pending_crate_owner = crate_owner_invitations::table
        .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
        .filter(crate_owner_invitations::invited_user_id.eq(user_id))
        .first::<CrateOwnerInvitation>(&*conn)?;

    let owner = CrateOwner {
        crate_id: crate_invite.crate_id,
        owner_id: user_id,
        created_by: pending_crate_owner.invited_by_user_id,
        owner_kind: OwnerKind::User as i32,
    };

    conn.transaction(|| {
        insert(&owner.on_conflict(
                crate_owners::table.primary_key(),
                do_update().set(crate_owners::deleted.eq(false)),
        )).into(crate_owners::table)
            .execute(conn)?;
        delete(
            crate_owner_invitations::table
                .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
                .filter(crate_owner_invitations::invited_user_id.eq(user_id)),
        ).execute(conn)?;

        #[derive(Serialize)]
        struct R {
            crate_owner_invitation: InvitationResponse,
        }
        Ok(req.json(&R {
            crate_owner_invitation: crate_invite,
        }))
    })
}

fn decline_invite(
    req: &mut Request,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
) -> CargoResult<Response> {
    use diesel::delete;
    let user_id = req.user()?.id;

    delete(
        crate_owner_invitations::table
            .filter(crate_owner_invitations::crate_id.eq(crate_invite.crate_id))
            .filter(crate_owner_invitations::invited_user_id.eq(user_id)),
    ).execute(conn)?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }

    Ok(req.json(&R {
        crate_owner_invitation: crate_invite,
    }))
}
<|endoftext|>