<commit_before>//!Types for interpolation between multiple colors.

use num::{Float, One, Zero, NumCast};
use std::cmp::max;

use Mix;

///A linear interpolation between colors.
///
///It's used to smoothly transition between a series of colors, that can be
///either evenly spaced or have customized positions. The gradient is
///continuous between the control points, but it's possible to iterate over a
///number of evenly spaced points using the `take` method. Any point outside
///the domain of the gradient will have the same color as the closest control
///point.
#[derive(Clone, Debug)]
pub struct Gradient<C: Mix + Clone>(Vec<(C::Scalar, C)>);

impl<C: Mix + Clone> Gradient<C> {
    ///Create a gradient of evenly spaced colors with the domain [0.0, 1.0].
    ///There must be at least one color.
    pub fn new<I: IntoIterator<Item = C>>(colors: I) -> Gradient<C> {
        let mut points: Vec<_> = colors.into_iter().map(|c| (C::Scalar::zero(), c)).collect();
        assert!(points.len() > 0);
        let step_size = C::Scalar::one() / <C::Scalar as NumCast>::from(max(points.len() - 1, 1) as f64).unwrap();

        for (i, &mut (ref mut p, _)) in points.iter_mut().enumerate() {
            *p = <C::Scalar as NumCast>::from(i).unwrap() * step_size;
        }

        Gradient(points)
    }

    ///Create a gradient of colors with custom spacing and domain. There must be
    ///at least one color and they are expected to be ordered by their
    ///position value.
    pub fn with_domain(colors: Vec<(C::Scalar, C)>) -> Gradient<C> {
        assert!(colors.len() > 0);

        //Maybe sort the colors?
        Gradient(colors)
    }

    ///Get a color from the gradient. The color of the closest control point
    ///will be returned if `i` is outside the domain.
    pub fn get(&self, i: C::Scalar) -> C {
        let &(mut min, ref min_color) = self.0.get(0).expect("a Gradient must contain at least one color");
        let mut min_color = min_color;
        let mut min_index = 0;

        if i <= min {
            return min_color.clone();
        }

        let &(mut max, ref max_color) = self.0.last().expect("a Gradient must contain at least one color");
        let mut max_color = max_color;
        let mut max_index = self.0.len() - 1;

        if i >= max {
            return max_color.clone();
        }

        while min_index < max_index - 1 {
            let index = min_index + (max_index - min_index) / 2;

            let (p, ref color) = self.0[index];

            if i <= p {
                max = p;
                max_color = color;
                max_index = index;
            } else {
                min = p;
                min_color = color;
                min_index = index;
            }
        }

        let factor = (i - min) / (max - min);

        min_color.mix(max_color, factor)
    }

    ///Take `n` evenly spaced colors from the gradient, as an iterator.
    pub fn take(&self, n: usize) -> Take<C> {
        let (min, max) = self.domain();

        Take {
            gradient: MaybeSlice::NotSlice(self),
            from: min,
            diff: max - min,
            len: n,
            current: 0,
        }
    }

    ///Slice this gradient to limit its domain.
    pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
        Slice {
            gradient: self,
            range: range.into(),
        }
    }

    ///Get the limits of this gradient's domain.
    pub fn domain(&self) -> (C::Scalar, C::Scalar) {
        let &(min, _) = self.0.get(0).expect("a Gradient must contain at least one color");
        let &(max, _) = self.0.last().expect("a Gradient must contain at least one color");
        (min, max)
    }
}

///An iterator over interpolated colors.
pub struct Take<'a, C: Mix + Clone + 'a> {
    gradient: MaybeSlice<'a, C>,
    from: C::Scalar,
    diff: C::Scalar,
    len: usize,
    current: usize,
}

impl<'a, C: Mix + Clone> Iterator for Take<'a, C> {
    type Item = C;

    fn next(&mut self) -> Option<C> {
        if self.current < self.len {
            let i = self.from + <C::Scalar as NumCast>::from(self.current).unwrap() * (self.diff / <C::Scalar as NumCast>::from(self.len).unwrap());
            self.current += 1;
            Some(self.gradient.get(i))
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len - self.current, Some(self.len - self.current))
    }
}

impl<'a, C: Mix + Clone> ExactSizeIterator for Take<'a, C> {}

///A slice of a Gradient that limits its domain.
#[derive(Clone, Debug)]
pub struct Slice<'a, C: Mix + Clone + 'a> {
    gradient: &'a Gradient<C>,
    range: Range<C::Scalar>,
}

impl<'a, C: Mix + Clone> Slice<'a, C> {
    ///Get a color from the gradient slice. The color of the closest domain
    ///limit will be returned if `i` is outside the domain.
    pub fn get(&self, i: C::Scalar) -> C {
        self.gradient.get(self.range.clamp(i))
    }

    ///Take `n` evenly spaced colors from the gradient slice, as an iterator.
    pub fn take(&self, n: usize) -> Take<C> {
        let (min, max) = self.domain();

        Take {
            gradient: MaybeSlice::Slice(self.clone()),
            from: min,
            diff: max - min,
            len: n,
            current: 0,
        }
    }

    ///Slice this gradient slice to further limit its domain. Ranges outside
    ///the domain will be clamped to the nearest domain limit.
    pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
        Slice {
            gradient: self.gradient,
            range: self.range.constrain(&range.into())
        }
    }

    ///Get the limits of this gradient slice's domain.
    pub fn domain(&self) -> (C::Scalar, C::Scalar) {
        if let Range { from: Some(from), to: Some(to) } = self.range {
            (from, to)
        } else {
            let (from, to) = self.gradient.domain();
            (self.range.from.unwrap_or(from), self.range.to.unwrap_or(to))
        }
    }
}

///A domain range for gradient slices.
#[derive(Clone, Debug, PartialEq)]
pub struct Range<T: Float> {
    from: Option<T>,
    to: Option<T>,
}

impl<T: Float> Range<T> {
    fn clamp(&self, mut x: T) -> T {
        x = self.from.unwrap_or(x).max(x);
        self.to.unwrap_or(x).min(x)
    }

    fn constrain(&self, other: &Range<T>) -> Range<T> {
        if let (Some(f), Some(t)) = (other.from, self.to) {
            if f >= t {
                return Range {
                    from: self.to,
                    to: self.to,
                };
            }
        }

        if let (Some(t), Some(f)) = (other.to, self.from) {
            if t <= f {
                return Range {
                    from: self.from,
                    to: self.from,
                };
            }
        }

        Range {
            from: match (self.from, other.from) {
                (Some(s), Some(o)) => Some(s.max(o)),
                (Some(s), None) => Some(s),
                (None, Some(o)) => Some(o),
                (None, None) => None,
            },
            to: match (self.to, other.to) {
                (Some(s), Some(o)) => Some(s.min(o)),
                (Some(s), None) => Some(s),
                (None, Some(o)) => Some(o),
                (None, None) => None,
            },
        }
    }
}

impl<T: Float> From<::std::ops::Range<T>> for Range<T> {
    fn from(range: ::std::ops::Range<T>) -> Range<T> {
        Range {
            from: Some(range.start),
            to: Some(range.end),
        }
    }
}

impl<T: Float> From<::std::ops::RangeFrom<T>> for Range<T> {
    fn from(range: ::std::ops::RangeFrom<T>) -> Range<T> {
        Range {
            from: Some(range.start),
            to: None,
        }
    }
}

impl<T: Float> From<::std::ops::RangeTo<T>> for Range<T> {
    fn from(range: ::std::ops::RangeTo<T>) -> Range<T> {
        Range {
            from: None,
            to: Some(range.end),
        }
    }
}

impl<T: Float> From<::std::ops::RangeFull> for Range<T> {
    fn from(_range: ::std::ops::RangeFull) -> Range<T> {
        Range {
            from: None,
            to: None,
        }
    }
}

enum MaybeSlice<'a, C: Mix + Clone + 'a> {
    NotSlice(&'a Gradient<C>),
    Slice(Slice<'a, C>),
}

impl<'a, C: Mix + Clone> MaybeSlice<'a, C> {
    fn get(&self, i: C::Scalar) -> C {
        match *self {
            MaybeSlice::NotSlice(g) => g.get(i),
            MaybeSlice::Slice(ref s) => s.get(i),
        }
    }
}

#[cfg(test)]
mod test {
    use super::{Range, Gradient};
    use Rgb;

    #[test]
    fn range_clamp() {
        let range: Range<f64> = (0.0..1.0).into();
        assert_eq!(range.clamp(-1.0), 0.0);
        assert_eq!(range.clamp(2.0), 1.0);
        assert_eq!(range.clamp(0.5), 0.5);
    }

    #[test]
    fn range_constrain() {
        let range: Range<f64> = (0.0..1.0).into();
        assert_eq!(range.constrain(&(-3.0..-5.0).into()), (0.0..0.0).into());
        assert_eq!(range.constrain(&(-3.0..0.8).into()), (0.0..0.8).into());

        assert_eq!(range.constrain(&(3.0..5.0).into()), (1.0..1.0).into());
        assert_eq!(range.constrain(&(0.2..5.0).into()), (0.2..1.0).into());

        assert_eq!(range.constrain(&(0.2..0.8).into()), (0.2..0.8).into());
    }

    #[test]
    fn simple_slice() {
        let g1 = Gradient::new(vec![Rgb::new(1.0, 0.0, 0.0), Rgb::new(0.0, 0.0, 1.0)]);
        let g2 = g1.slice(..0.5);

        let v1: Vec<_> = g1.take(10).take(5).collect();
        let v2: Vec<_> = g2.take(5).collect();

        assert_eq!(v1, v2);
    }
}
<commit_msg>Derive Clone for Take and MaybeSlice<commit_after>//!Types for interpolation between multiple colors.

use num::{Float, One, Zero, NumCast};
use std::cmp::max;

use Mix;

///A linear interpolation between colors.
///
///It's used to smoothly transition between a series of colors, that can be
///either evenly spaced or have customized positions. The gradient is
///continuous between the control points, but it's possible to iterate over a
///number of evenly spaced points using the `take` method. Any point outside
///the domain of the gradient will have the same color as the closest control
///point.
#[derive(Clone, Debug)]
pub struct Gradient<C: Mix + Clone>(Vec<(C::Scalar, C)>);

impl<C: Mix + Clone> Gradient<C> {
    ///Create a gradient of evenly spaced colors with the domain [0.0, 1.0].
    ///There must be at least one color.
    pub fn new<I: IntoIterator<Item = C>>(colors: I) -> Gradient<C> {
        let mut points: Vec<_> = colors.into_iter().map(|c| (C::Scalar::zero(), c)).collect();
        assert!(points.len() > 0);
        let step_size = C::Scalar::one() / <C::Scalar as NumCast>::from(max(points.len() - 1, 1) as f64).unwrap();

        for (i, &mut (ref mut p, _)) in points.iter_mut().enumerate() {
            *p = <C::Scalar as NumCast>::from(i).unwrap() * step_size;
        }

        Gradient(points)
    }

    ///Create a gradient of colors with custom spacing and domain. There must be
    ///at least one color and they are expected to be ordered by their
    ///position value.
    pub fn with_domain(colors: Vec<(C::Scalar, C)>) -> Gradient<C> {
        assert!(colors.len() > 0);

        //Maybe sort the colors?
        Gradient(colors)
    }

    ///Get a color from the gradient. The color of the closest control point
    ///will be returned if `i` is outside the domain.
    pub fn get(&self, i: C::Scalar) -> C {
        let &(mut min, ref min_color) = self.0.get(0).expect("a Gradient must contain at least one color");
        let mut min_color = min_color;
        let mut min_index = 0;

        if i <= min {
            return min_color.clone();
        }

        let &(mut max, ref max_color) = self.0.last().expect("a Gradient must contain at least one color");
        let mut max_color = max_color;
        let mut max_index = self.0.len() - 1;

        if i >= max {
            return max_color.clone();
        }

        while min_index < max_index - 1 {
            let index = min_index + (max_index - min_index) / 2;

            let (p, ref color) = self.0[index];

            if i <= p {
                max = p;
                max_color = color;
                max_index = index;
            } else {
                min = p;
                min_color = color;
                min_index = index;
            }
        }

        let factor = (i - min) / (max - min);

        min_color.mix(max_color, factor)
    }

    ///Take `n` evenly spaced colors from the gradient, as an iterator.
    pub fn take(&self, n: usize) -> Take<C> {
        let (min, max) = self.domain();

        Take {
            gradient: MaybeSlice::NotSlice(self),
            from: min,
            diff: max - min,
            len: n,
            current: 0,
        }
    }

    ///Slice this gradient to limit its domain.
    pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
        Slice {
            gradient: self,
            range: range.into(),
        }
    }

    ///Get the limits of this gradient's domain.
    pub fn domain(&self) -> (C::Scalar, C::Scalar) {
        let &(min, _) = self.0.get(0).expect("a Gradient must contain at least one color");
        let &(max, _) = self.0.last().expect("a Gradient must contain at least one color");
        (min, max)
    }
}

///An iterator over interpolated colors.
#[derive(Clone)]
pub struct Take<'a, C: Mix + Clone + 'a> {
    gradient: MaybeSlice<'a, C>,
    from: C::Scalar,
    diff: C::Scalar,
    len: usize,
    current: usize,
}

impl<'a, C: Mix + Clone> Iterator for Take<'a, C> {
    type Item = C;

    fn next(&mut self) -> Option<C> {
        if self.current < self.len {
            let i = self.from + <C::Scalar as NumCast>::from(self.current).unwrap() * (self.diff / <C::Scalar as NumCast>::from(self.len).unwrap());
            self.current += 1;
            Some(self.gradient.get(i))
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len - self.current, Some(self.len - self.current))
    }
}

impl<'a, C: Mix + Clone> ExactSizeIterator for Take<'a, C> {}

///A slice of a Gradient that limits its domain.
#[derive(Clone, Debug)]
pub struct Slice<'a, C: Mix + Clone + 'a> {
    gradient: &'a Gradient<C>,
    range: Range<C::Scalar>,
}

impl<'a, C: Mix + Clone> Slice<'a, C> {
    ///Get a color from the gradient slice. The color of the closest domain
    ///limit will be returned if `i` is outside the domain.
    pub fn get(&self, i: C::Scalar) -> C {
        self.gradient.get(self.range.clamp(i))
    }

    ///Take `n` evenly spaced colors from the gradient slice, as an iterator.
    pub fn take(&self, n: usize) -> Take<C> {
        let (min, max) = self.domain();

        Take {
            gradient: MaybeSlice::Slice(self.clone()),
            from: min,
            diff: max - min,
            len: n,
            current: 0,
        }
    }

    ///Slice this gradient slice to further limit its domain. Ranges outside
    ///the domain will be clamped to the nearest domain limit.
    pub fn slice<R: Into<Range<C::Scalar>>>(&self, range: R) -> Slice<C> {
        Slice {
            gradient: self.gradient,
            range: self.range.constrain(&range.into())
        }
    }

    ///Get the limits of this gradient slice's domain.
    pub fn domain(&self) -> (C::Scalar, C::Scalar) {
        if let Range { from: Some(from), to: Some(to) } = self.range {
            (from, to)
        } else {
            let (from, to) = self.gradient.domain();
            (self.range.from.unwrap_or(from), self.range.to.unwrap_or(to))
        }
    }
}

///A domain range for gradient slices.
#[derive(Clone, Debug, PartialEq)]
pub struct Range<T: Float> {
    from: Option<T>,
    to: Option<T>,
}

impl<T: Float> Range<T> {
    fn clamp(&self, mut x: T) -> T {
        x = self.from.unwrap_or(x).max(x);
        self.to.unwrap_or(x).min(x)
    }

    fn constrain(&self, other: &Range<T>) -> Range<T> {
        if let (Some(f), Some(t)) = (other.from, self.to) {
            if f >= t {
                return Range {
                    from: self.to,
                    to: self.to,
                };
            }
        }

        if let (Some(t), Some(f)) = (other.to, self.from) {
            if t <= f {
                return Range {
                    from: self.from,
                    to: self.from,
                };
            }
        }

        Range {
            from: match (self.from, other.from) {
                (Some(s), Some(o)) => Some(s.max(o)),
                (Some(s), None) => Some(s),
                (None, Some(o)) => Some(o),
                (None, None) => None,
            },
            to: match (self.to, other.to) {
                (Some(s), Some(o)) => Some(s.min(o)),
                (Some(s), None) => Some(s),
                (None, Some(o)) => Some(o),
                (None, None) => None,
            },
        }
    }
}

impl<T: Float> From<::std::ops::Range<T>> for Range<T> {
    fn from(range: ::std::ops::Range<T>) -> Range<T> {
        Range {
            from: Some(range.start),
            to: Some(range.end),
        }
    }
}

impl<T: Float> From<::std::ops::RangeFrom<T>> for Range<T> {
    fn from(range: ::std::ops::RangeFrom<T>) -> Range<T> {
        Range {
            from: Some(range.start),
            to: None,
        }
    }
}

impl<T: Float> From<::std::ops::RangeTo<T>> for Range<T> {
    fn from(range: ::std::ops::RangeTo<T>) -> Range<T> {
        Range {
            from: None,
            to: Some(range.end),
        }
    }
}

impl<T: Float> From<::std::ops::RangeFull> for Range<T> {
    fn from(_range: ::std::ops::RangeFull) -> Range<T> {
        Range {
            from: None,
            to: None,
        }
    }
}

#[derive(Clone)]
enum MaybeSlice<'a, C: Mix + Clone + 'a> {
    NotSlice(&'a Gradient<C>),
    Slice(Slice<'a, C>),
}

impl<'a, C: Mix + Clone> MaybeSlice<'a, C> {
    fn get(&self, i: C::Scalar) -> C {
        match *self {
            MaybeSlice::NotSlice(g) => g.get(i),
            MaybeSlice::Slice(ref s) => s.get(i),
        }
    }
}

#[cfg(test)]
mod test {
    use super::{Range, Gradient};
    use Rgb;

    #[test]
    fn range_clamp() {
        let range: Range<f64> = (0.0..1.0).into();
        assert_eq!(range.clamp(-1.0), 0.0);
        assert_eq!(range.clamp(2.0), 1.0);
        assert_eq!(range.clamp(0.5), 0.5);
    }

    #[test]
    fn range_constrain() {
        let range: Range<f64> = (0.0..1.0).into();
        assert_eq!(range.constrain(&(-3.0..-5.0).into()), (0.0..0.0).into());
        assert_eq!(range.constrain(&(-3.0..0.8).into()), (0.0..0.8).into());

        assert_eq!(range.constrain(&(3.0..5.0).into()), (1.0..1.0).into());
        assert_eq!(range.constrain(&(0.2..5.0).into()), (0.2..1.0).into());

        assert_eq!(range.constrain(&(0.2..0.8).into()), (0.2..0.8).into());
    }

    #[test]
    fn simple_slice() {
        let g1 = Gradient::new(vec![Rgb::new(1.0, 0.0, 0.0), Rgb::new(0.0, 0.0, 1.0)]);
        let g2 = g1.slice(..0.5);

        let v1: Vec<_> = g1.take(10).take(5).collect();
        let v2: Vec<_> = g2.take(5).collect();

        assert_eq!(v1, v2);
    }
}
<|endoftext|>


<commit_before>//!
//! A convenient structure to store and load settings.
//!
//! It is meant for items that are rarely read and stored, like command line flags or
//! application configuration.
//!
//! `knob::Settings` expects all values to implement `Show` + `FromStr` and stores them as
//! strings internally. This allows sideloading of settings through multiple means, e.g.
//! the command line or a simple config file. knob is not meant for structured data. If
//! you want to load such data, store the location of the data as a Knob setting and do
//! the loading yourself.
//!
//! `knob` is typesafe in a sense that it will return the expected type you wanted if possible and
//! fails otherwise.
//!
//! `knob` allows you to decorate the `Settings` structure yourself for convenience (see examples).
//!
//! # Storing and fetching values
//!
//! The following example shows you how to store an IP address in `knob`:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use std::io::net::ip::IpAddr;
//! use knob::Settings;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set("ip", "0.0.0.0");
//!   let socket: IpAddr = settings.fetch("ip").unwrap();
//!   assert_eq!(socket.to_str(), "0.0.0.0".to_string());
//! }
//! ~~~
//!
//! This works the same for IPv6 addresses:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::IpAddr;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set("ip", "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch("ip").unwrap();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! # Providing your own keys
//!
//! You can use enums as keys, as long as they implement `Show`:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::IpAddr;
//!
//! #[deriving(Show)]
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch(Ip).unwrap();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! # Registering command line options
//!
//! `knob` allows you to register command line options to read from the command line later.
//!
//! The options are `getopts` option groups.
//!
//! If the loading of the command line args fails, an error will be returned.
//!
//! ~~~{.rust}
//! extern crate getopts;
//! extern crate knob;
//!
//! use getopts::optopt;
//! use knob::Settings;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.opt(optopt("p", "port", "the port to bind to", "4000"));
//!   settings.opt(optopt("e", "environment", "the environment to run in", ""));
//!   let errors = settings.load_os_args();
//!   if errors.is_some() {
//!     println!("{}", settings.usage(String::from_str("Try one of these:")));
//!   }
//! }
//! ~~~
//!
//! # Decorating the settings struct
//!
//! To make matters more convenient, you can implement a decorator
//! to implement your own loading behaviour.
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::{IpAddr,Ipv4Addr,SocketAddr};
//!
//! #[deriving(Show)]
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! pub trait SocketSettings {
//!   fn socket(&self) -> SocketAddr;
//!   fn port(&self) -> u16;
//!   fn ip(&self) -> IpAddr;
//! }
//!
//! impl SocketSettings for Settings {
//!   fn socket(&self) -> SocketAddr {
//!     self.fetch_with(Addr, |addr| {
//!       match addr {
//!         Some(socket_addr) => { socket_addr },
//!         None => {
//!           let port: u16 = self.port();
//!           let ip: IpAddr = self.ip();
//!           SocketAddr { ip: ip, port: port }
//!         }
//!       }
//!     })
//!   }
//!
//!   fn port(&self) -> u16 {
//!     self.fetch(Port).unwrap_or(8080)
//!   }
//!
//!   fn ip(&self) -> IpAddr {
//!     self.fetch(Ip).unwrap_or(Ipv4Addr(127,0,0,1))
//!   }
//! }
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.ip();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! knob goes up to 11.

#![crate_id = "github.com/skade/knob#knob:1.1.3"]
#![crate_type = "lib"]
#![comment = "A convenient Rust settings system"]
#![license = "MIT"]

extern crate getopts;
extern crate collections;
extern crate debug;

use collections::hashmap::HashMap;
use std::os;
use std::from_str::FromStr;
use std::to_str::ToStr;

use getopts::{usage,getopts,OptGroup};
use getopts::Fail_;

/// The settings structure we save the options and settings in.
pub struct Settings {
  store: HashMap<String,String>,
  options: Box<Vec<OptGroup>>,
}

impl Settings {
  /// Create a new Settings struct.
  pub fn new() -> Settings {
    Settings { store: HashMap::new(), options: box Vec::new() }
  }

  /// Set a settings key to a value. The value will be serialized.
  pub fn set<A: ToStr, T: ToStr>(&mut self, setting: A, value: T) {
    self.store.swap(setting.to_str(), value.to_str());
  }

  /// Set a value using an Option struct. The value will only be set if the
  /// value is not None. This way, you can avoid unwrapping the result of a
  /// previous operation by yourself.
  pub fn set_opt<A: ToStr, T: ToStr>(&mut self, setting: A, value: Option<T>) {
    if value.is_some() {
      self.store.swap(setting.to_str(), value.unwrap().to_str());
    }
  }

  /// Fetch a setting for a key. Fails if the setting is present but could not be
  /// parsed.
  pub fn fetch<A: ToStr, T: FromStr>(&self, setting: A) -> Option<T> {
    match self.store.find(&setting.to_str()) {
      Some(string) => {
        let value = from_str(string.as_slice());
        if value.is_none() {
          fail!("setting could not be parsed: {:?}", setting.to_str())
        }
        value
      },
      None => { None }
    }
  }

  /// Fetch a setting for a key and pass it to the given function. The result of the function
  /// will be returned.
  pub fn fetch_with<A: ToStr, T: FromStr>(&self, setting: A, f: |Option<T>| -> T) -> T {
    let value = self.fetch(setting.to_str());
    f(value)
  }

  /// Register a commandline for later use with load_args.
  ///
  /// Currently, only optopt and reqopt are properly supported.
  pub fn opt(&mut self, opt: OptGroup) {
    self.options.push(opt);
  }

  /// Load the command line argument given by the OS.
  ///
  /// Optionally returns failures.
  pub fn load_os_args(&mut self) -> Option<Fail_> {
    self.load_args(os::args())
  }

  /// Load a list of command line arguments.
  ///
  /// Automatically sets "knob.progname" to the name of the program.
  ///
  /// Optionally returns failures.
  pub fn load_args(&mut self, args: Vec<String>) -> Option<Fail_> {
    let ref prog_name = args.get(0);

    self.set("knob.progname", prog_name.clone());

    let matches = match getopts(args.tail(), self.options.as_slice()) {
      Ok(m) => { m }
      Err(fail) => { return Some(fail) }
    };

    let given_options = self.options.clone();
    for opt in given_options.iter() {
      let opt_strings = &[opt.short_name.clone(), opt.long_name.clone()];
      self.set_opt(opt.long_name.clone(), matches.opts_str(opt_strings))
    };
    None
  }

  /// Returns the usage string for the stored OptGroups. Pass `brief`
  /// to have a brief message included before the usage strings.
  pub fn usage(&self, brief: String) -> String {
    usage(brief.as_slice(), self.options.as_slice())
  }
}

<commit_msg>Version 1.1.4<commit_after>//!
//! A convenient structure to store and load settings.
//!
//! It is meant for items that are rarely read and stored, like command line flags or
//! application configuration.
//!
//! `knob::Settings` expects all values to implement `Show` + `FromStr` and stores them as
//! strings internally. This allows sideloading of settings through multiple means, e.g.
//! the command line or a simple config file. knob is not meant for structured data. If
//! you want to load such data, store the location of the data as a Knob setting and do
//! the loading yourself.
//!
//! `knob` is typesafe in a sense that it will return the expected type you wanted if possible and
//! fails otherwise.
//!
//! `knob` allows you to decorate the `Settings` structure yourself for convenience (see examples).
//!
//! # Storing and fetching values
//!
//! The following example shows you how to store an IP address in `knob`:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use std::io::net::ip::IpAddr;
//! use knob::Settings;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set("ip", "0.0.0.0");
//!   let socket: IpAddr = settings.fetch("ip").unwrap();
//!   assert_eq!(socket.to_str(), "0.0.0.0".to_string());
//! }
//! ~~~
//!
//! This works the same for IPv6 addresses:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::IpAddr;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set("ip", "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch("ip").unwrap();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! # Providing your own keys
//!
//! You can use enums as keys, as long as they implement `Show`:
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::IpAddr;
//!
//! #[deriving(Show)]
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch(Ip).unwrap();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! # Registering command line options
//!
//! `knob` allows you to register command line options to read from the command line later.
//!
//! The options are `getopts` option groups.
//!
//! If the loading of the command line args fails, an error will be returned.
//!
//! ~~~{.rust}
//! extern crate getopts;
//! extern crate knob;
//!
//! use getopts::optopt;
//! use knob::Settings;
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.opt(optopt("p", "port", "the port to bind to", "4000"));
//!   settings.opt(optopt("e", "environment", "the environment to run in", ""));
//!   let errors = settings.load_os_args();
//!   if errors.is_some() {
//!     println!("{}", settings.usage(String::from_str("Try one of these:")));
//!   }
//! }
//! ~~~
//!
//! # Decorating the settings struct
//!
//! To make matters more convenient, you can implement a decorator
//! to implement your own loading behaviour.
//!
//! ~~~{.rust}
//! extern crate knob;
//!
//! use knob::Settings;
//! use std::io::net::ip::{IpAddr,Ipv4Addr,SocketAddr};
//!
//! #[deriving(Show)]
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! pub trait SocketSettings {
//!   fn socket(&self) -> SocketAddr;
//!   fn port(&self) -> u16;
//!   fn ip(&self) -> IpAddr;
//! }
//!
//! impl SocketSettings for Settings {
//!   fn socket(&self) -> SocketAddr {
//!     self.fetch_with(Addr, |addr| {
//!       match addr {
//!         Some(socket_addr) => { socket_addr },
//!         None => {
//!           let port: u16 = self.port();
//!           let ip: IpAddr = self.ip();
//!           SocketAddr { ip: ip, port: port }
//!         }
//!       }
//!     })
//!   }
//!
//!   fn port(&self) -> u16 {
//!     self.fetch(Port).unwrap_or(8080)
//!   }
//!
//!   fn ip(&self) -> IpAddr {
//!     self.fetch(Ip).unwrap_or(Ipv4Addr(127,0,0,1))
//!   }
//! }
//!
//! fn main() {
//!   let mut settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.ip();
//!   assert_eq!(socket.to_str(), "::0.0.0.1".to_string());
//! }
//! ~~~
//!
//! knob goes up to 11.

#![crate_id = "github.com/skade/knob#knob:1.1.4"]
#![crate_type = "lib"]
#![comment = "A convenient Rust settings system"]
#![license = "MIT"]

extern crate getopts;
extern crate collections;
extern crate debug;

use collections::hashmap::HashMap;
use std::os;
use std::from_str::FromStr;
use std::to_str::ToStr;

use getopts::{usage,getopts,OptGroup};
use getopts::Fail_;

/// The settings structure we save the options and settings in.
pub struct Settings {
  store: HashMap<String,String>,
  options: Box<Vec<OptGroup>>,
}

impl Settings {
  /// Create a new Settings struct.
  pub fn new() -> Settings {
    Settings { store: HashMap::new(), options: box Vec::new() }
  }

  /// Set a settings key to a value. The value will be serialized.
  pub fn set<A: ToStr, T: ToStr>(&mut self, setting: A, value: T) {
    self.store.swap(setting.to_str(), value.to_str());
  }

  /// Set a value using an Option struct. The value will only be set if the
  /// value is not None. This way, you can avoid unwrapping the result of a
  /// previous operation by yourself.
  pub fn set_opt<A: ToStr, T: ToStr>(&mut self, setting: A, value: Option<T>) {
    if value.is_some() {
      self.store.swap(setting.to_str(), value.unwrap().to_str());
    }
  }

  /// Fetch a setting for a key. Fails if the setting is present but could not be
  /// parsed.
  pub fn fetch<A: ToStr, T: FromStr>(&self, setting: A) -> Option<T> {
    match self.store.find(&setting.to_str()) {
      Some(string) => {
        let value = from_str(string.as_slice());
        if value.is_none() {
          fail!("setting could not be parsed: {:?}", setting.to_str())
        }
        value
      },
      None => { None }
    }
  }

  /// Fetch a setting for a key and pass it to the given function. The result of the function
  /// will be returned.
  pub fn fetch_with<A: ToStr, T: FromStr>(&self, setting: A, f: |Option<T>| -> T) -> T {
    let value = self.fetch(setting.to_str());
    f(value)
  }

  /// Register a commandline for later use with load_args.
  ///
  /// Currently, only optopt and reqopt are properly supported.
  pub fn opt(&mut self, opt: OptGroup) {
    self.options.push(opt);
  }

  /// Load the command line argument given by the OS.
  ///
  /// Optionally returns failures.
  pub fn load_os_args(&mut self) -> Option<Fail_> {
    self.load_args(os::args())
  }

  /// Load a list of command line arguments.
  ///
  /// Automatically sets "knob.progname" to the name of the program.
  ///
  /// Optionally returns failures.
  pub fn load_args(&mut self, args: Vec<String>) -> Option<Fail_> {
    let ref prog_name = args.get(0);

    self.set("knob.progname", prog_name.clone());

    let matches = match getopts(args.tail(), self.options.as_slice()) {
      Ok(m) => { m }
      Err(fail) => { return Some(fail) }
    };

    let given_options = self.options.clone();
    for opt in given_options.iter() {
      let opt_strings = &[opt.short_name.clone(), opt.long_name.clone()];
      self.set_opt(opt.long_name.clone(), matches.opts_str(opt_strings))
    };
    None
  }

  /// Returns the usage string for the stored OptGroups. Pass `brief`
  /// to have a brief message included before the usage strings.
  pub fn usage(&self, brief: String) -> String {
    usage(brief.as_slice(), self.options.as_slice())
  }
}

<|endoftext|>


<commit_before>//! JIT compiler for Brainfuck based on LLVM.

mod wrapper;
mod compiler;

pub use self::compiler::{LlvmCompilable, compile_and_run};
<commit_msg>Added LLVM explanation.<commit_after>//! JIT compiler for Brainfuck based on LLVM.
//!
//! Enabled with `--features=llvm`. This currently doesn't link on some platforms due to trouble
//! with libffi. And when it does link, it appears to use the LLVM bitcode interpreter rather
//! than MCJIT, and consequently is very slow.

mod wrapper;
mod compiler;

pub use self::compiler::{LlvmCompilable, compile_and_run};
<|endoftext|>


<commit_before>//! Logging utility macros

use std::cell::Cell;
use std::ops::Deref;
use std::fmt::Debug;
use std::thread::panicking;

/// Evaluates the given expression once (when first reached).
///
/// *NOTE*: This isn't particularly thread-safe, so 2 threads reaching the statement at the same
/// time may both run it.
#[macro_export]
macro_rules! once {
    ( $e:expr ) => {{
        static mut REACHED: bool = false;
        if unsafe { !REACHED } {
            unsafe {
                REACHED = true;
            }
            $e;
        }
    }}
}

/// Executes `trace!` if the formatted message wasn't logged by this macro before.
///
/// Note that each invocation is tracked seperately: If you use multiple `trace_unique!`
/// invocations, each might log the same message once.
#[macro_export]
macro_rules! trace_unique {
    ( $($args:expr),* ) => {
        if log_enabled!(::log::LogLevel::Trace) {
            use std::collections::HashSet;
            use std::cell::RefCell;
            thread_local!(
                static LOGGED: RefCell<HashSet<String>> = RefCell::new(HashSet::new())
            );

            let msg = format!($($args),*);
            LOGGED.with(|set| {
                let mut set = set.borrow_mut();
                if !set.contains(&msg) {
                    trace!("{}", msg);
                    set.insert(msg);
                }
            })
        }
    };
}

pub struct LogOnPanic<T: Copy + Debug>(Cell<T>);

impl<T: Copy + Debug> LogOnPanic<T> {
    pub fn new(t: T) -> Self { LogOnPanic(Cell::new(t)) }
}

impl<T: Copy + Debug> Deref for LogOnPanic<T> {
    type Target = Cell<T>;
    fn deref(&self) -> &Cell<T> { &self.0 }
}

impl<T: Copy + Debug> Drop for LogOnPanic<T> {
    fn drop(&mut self) {
        if panicking() {
            println!("LogOnPanic: {:?}", self.0.get())
        }
    }
}
<commit_msg>Remove unnecessary `#[macro_export]`<commit_after>//! Logging utility macros

use std::cell::Cell;
use std::ops::Deref;
use std::fmt::Debug;
use std::thread::panicking;

/// Evaluates the given expression once (when first reached).
///
/// *NOTE*: This isn't particularly thread-safe, so 2 threads reaching the statement at the same
/// time may both run it.
macro_rules! once {
    ( $e:expr ) => {{
        static mut REACHED: bool = false;
        if unsafe { !REACHED } {
            unsafe {
                REACHED = true;
            }
            $e;
        }
    }}
}

/// Executes `trace!` if the formatted message wasn't logged by this macro before.
///
/// Note that each invocation is tracked seperately: If you use multiple `trace_unique!`
/// invocations, each might log the same message once.
macro_rules! trace_unique {
    ( $($args:expr),* ) => {
        if log_enabled!(::log::LogLevel::Trace) {
            use std::collections::HashSet;
            use std::cell::RefCell;
            thread_local!(
                static LOGGED: RefCell<HashSet<String>> = RefCell::new(HashSet::new())
            );

            let msg = format!($($args),*);
            LOGGED.with(|set| {
                let mut set = set.borrow_mut();
                if !set.contains(&msg) {
                    trace!("{}", msg);
                    set.insert(msg);
                }
            })
        }
    };
}

pub struct LogOnPanic<T: Copy + Debug>(Cell<T>);

impl<T: Copy + Debug> LogOnPanic<T> {
    pub fn new(t: T) -> Self { LogOnPanic(Cell::new(t)) }
}

impl<T: Copy + Debug> Deref for LogOnPanic<T> {
    type Target = Cell<T>;
    fn deref(&self) -> &Cell<T> { &self.0 }
}

impl<T: Copy + Debug> Drop for LogOnPanic<T> {
    fn drop(&mut self) {
        if panicking() {
            println!("LogOnPanic: {:?}", self.0.get())
        }
    }
}
<|endoftext|>


<commit_before>use error::Error;
use json;
use std::default::Default;
use std::path::{Path, PathBuf};

#[derive(Debug, Default)]
pub struct Package {
    edition: Edition,
    targets: Vec<Target>,
}

#[derive(Debug, Default)]
pub struct Manifest {
    packages: Vec<Package>,
}

impl Manifest {
    pub fn from_str(src: &str) -> Result<Self, Error> {
        let mut j = json::parse(src).map_err(Error::InvalidManifestJson)?;

        let packages = j["packages"]
            .members_mut()
            .map(|package| {
                let edition: Edition = match package["edition"].as_str() {
                    Some("2015") => Edition::E2015,
                    Some("2018") => Edition::E2018,
                    Some(unknown) => panic!("Unrecognized value for edition \"{}\"", unknown),
                    None => Edition::default(),
                };

                let targets: Vec<Target> = package["targets"]
                    .members_mut()
                    .map(Target::from_json)
                    .collect();

                Package { edition, targets }
            })
            .collect();

        Result::Ok(Manifest { packages })
    }

    fn all_targets(&self) -> impl Iterator<Item = &Target> {
        self.packages
            .iter()
            .flat_map(|package| package.targets.iter())
    }

    pub fn custom_builds(&self) -> Vec<&Target> {
        self.all_targets().filter(|t| t.is_custom_build()).collect()
    }

    /// All valid targets that can be used to display modules
    pub fn targets(&self) -> Vec<&Target> {
        self.all_targets()
            .filter(|t| t.is_bin() || t.is_lib() || t.is_proc_macro())
            .collect()
    }

    pub fn lib(&self) -> Result<&Target, Error> {
        self.all_targets()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    }

    pub fn bin(&self, name: &str) -> Result<&Target, Error> {
        self.all_targets()
            .find(|t| t.is_bin() && t.name() == name)
            .ok_or_else(|| {
                let names = self.bin_names();
                Error::NoMatchingBinaryTargetFound(names)
            })
    }

    pub fn bin_names(&self) -> Vec<String> {
        self.all_targets()
            .filter(|t| t.is_bin())
            .map(|t| t.name().to_string())
            .collect()
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Edition {
    E2015,
    E2018,
}

impl Default for Edition {
    fn default() -> Self {
        Edition::E2015
    }
}

#[derive(Debug, PartialEq)]
pub struct Target {
    kind: Vec<String>,
    crate_types: Vec<String>,
    name: String,
    src_path: PathBuf,
    pub edition: Edition,
}

impl Target {
    const LIB_KINDS: [&'static str; 4] = ["lib", "rlib", "dylib", "staticlib"];

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn src_path(&self) -> &PathBuf {
        &self.src_path
    }

    pub fn is_bin(&self) -> bool {
        self.kind.contains(&String::from("bin"))
    }

    pub fn is_lib(&self) -> bool {
        self.kind.iter().any(|k| Self::LIB_KINDS.contains(&&k[..]))
    }

    pub fn is_proc_macro(&self) -> bool {
        self.kind.contains(&String::from("proc-macro"))
    }

    fn from_json(j: &mut json::JsonValue) -> Target {
        let kind: Vec<String> = {
            assert!(j["kind"].is_array());
            j["kind"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let crate_types: Vec<String> = {
            assert!(j["crate_types"].is_array());
            j["crate_types"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let name: String = j["name"].take_string().expect("name is missing");
        let src_path: PathBuf =
            Path::new(&j["src_path"].take_string().expect("src_path is missing")).to_path_buf();
        let edition: Edition = match j["edition"].as_str() {
            Some("2015") => Edition::E2015,
            Some("2018") => Edition::E2018,
            Some(unknown) => panic!("Unrecognized value for edition \"{}\"", unknown),
            None => Edition::default(),
        };
        Target {
            kind,
            crate_types,
            name,
            src_path,
            edition,
        }
    }

    fn is_custom_build(&self) -> bool {
        self.kind.contains(&String::from("custom-build"))
    }
}

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

    fn read_manifest(filename: &path::Path) -> Manifest {
        let manifest_str: String = fs::read_to_string(filename.with_extension("json"))
            .expect("manifest file cannot be read");
        Manifest::from_str(&manifest_str).expect("manifest cannot be read")
    }

    #[test]
    fn manifest_with_edition2018_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-edition-2018"));
        assert_eq!(Edition::E2018, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_with_edition2015_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-edition-2015"));
        assert_eq!(Edition::E2015, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_without_edition_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-no-edition"));
        assert_eq!(Edition::E2015, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_for_simple_lib() {
        let resource_path = path::Path::new("test-resources/example-lib-edition-2018");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib-edition-2018"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
    }

    #[test]
    fn manifest_for_simple_bin() {
        let resource_path = path::Path::new("test-resources/example-bin");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example2"),
                    src_path: resource_path.join("src/bin/example2.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example"),
                    src_path: resource_path.join("src/bin/example.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.targets()
        );
        assert!(manifest.targets()[0].is_bin());
        assert!(!manifest.targets()[0].is_lib());
    }

    #[test]
    fn manifest_with_custom_build() {
        let resource_path = path::Path::new("test-resources/example-lib-edition-2018");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-lib-edition-2018"),
                    src_path: resource_path.join("src/lib.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("custom-build")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("build-script-build"),
                    src_path: resource_path.join("build.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.all_targets().collect::<Vec<_>>()
        );
        assert_eq!(
            vec![&Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib-edition-2018"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },],
            manifest.targets()
        );
        assert_eq!(
            vec![&Target {
                kind: vec!(String::from("custom-build")),
                crate_types: vec!(String::from("bin")),
                name: String::from("build-script-build"),
                src_path: resource_path.join("build.rs"),
                edition: Edition::E2018
            }],
            manifest.custom_builds()
        );
    }

    #[test]
    fn manifest_for_plugin() {
        let resource_path = path::Path::new("test-resources/example-plugin");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("dylib")),
                crate_types: vec!(String::from("dylib")),
                name: String::from("example-plugin"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
    }

    #[test]
    fn manifest_for_proc_macro() {
        let resource_path = path::Path::new("test-resources/example-proc-macro");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("proc-macro")),
                crate_types: vec!(String::from("proc-macro")),
                name: String::from("example-proc-macro"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(!manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
        assert!(manifest.targets()[0].is_proc_macro());
    }

    #[test]
    fn manifest_for_bin_and_lib() {
        let resource_path = path::Path::new("test-resources/example-bin-and-lib");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-bin-and-lib"),
                    src_path: resource_path.join("src/lib.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example-bin-and-lib"),
                    src_path: resource_path.join("src/main.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.targets()
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
        assert!(!manifest.targets()[0].is_proc_macro());
        assert!(!manifest.targets()[1].is_lib());
        assert!(manifest.targets()[1].is_bin());
        assert!(!manifest.targets()[1].is_proc_macro());
    }
}
<commit_msg>deduplicate Edition match<commit_after>use error::Error;
use json;
use std::default::Default;
use std::path::{Path, PathBuf};

#[derive(Debug, Default)]
pub struct Package {
    edition: Edition,
    targets: Vec<Target>,
}

#[derive(Debug, Default)]
pub struct Manifest {
    packages: Vec<Package>,
}

impl Manifest {
    pub fn from_str(src: &str) -> Result<Self, Error> {
        let mut j = json::parse(src).map_err(Error::InvalidManifestJson)?;

        let packages = j["packages"]
            .members_mut()
            .map(|package| {
                let edition: Edition = package["edition"].as_str().into();

                let targets: Vec<Target> = package["targets"]
                    .members_mut()
                    .map(Target::from_json)
                    .collect();

                Package { edition, targets }
            })
            .collect();

        Result::Ok(Manifest { packages })
    }

    fn all_targets(&self) -> impl Iterator<Item = &Target> {
        self.packages
            .iter()
            .flat_map(|package| package.targets.iter())
    }

    pub fn custom_builds(&self) -> Vec<&Target> {
        self.all_targets().filter(|t| t.is_custom_build()).collect()
    }

    /// All valid targets that can be used to display modules
    pub fn targets(&self) -> Vec<&Target> {
        self.all_targets()
            .filter(|t| t.is_bin() || t.is_lib() || t.is_proc_macro())
            .collect()
    }

    pub fn lib(&self) -> Result<&Target, Error> {
        self.all_targets()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    }

    pub fn bin(&self, name: &str) -> Result<&Target, Error> {
        self.all_targets()
            .find(|t| t.is_bin() && t.name() == name)
            .ok_or_else(|| {
                let names = self.bin_names();
                Error::NoMatchingBinaryTargetFound(names)
            })
    }

    pub fn bin_names(&self) -> Vec<String> {
        self.all_targets()
            .filter(|t| t.is_bin())
            .map(|t| t.name().to_string())
            .collect()
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Edition {
    E2015,
    E2018,
}

impl Default for Edition {
    fn default() -> Self {
        Edition::E2015
    }
}

impl From<Option<&str>> for Edition {
    fn from(string: Option<&str>) -> Self {
        match string {
            Some("2015") => Edition::E2015,
            Some("2018") => Edition::E2018,
            Some(unknown) => panic!("Unrecognized value for edition \"{}\"", unknown),
            None => Edition::default(),
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct Target {
    kind: Vec<String>,
    crate_types: Vec<String>,
    name: String,
    src_path: PathBuf,
    pub edition: Edition,
}

impl Target {
    const LIB_KINDS: [&'static str; 4] = ["lib", "rlib", "dylib", "staticlib"];

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn src_path(&self) -> &PathBuf {
        &self.src_path
    }

    pub fn is_bin(&self) -> bool {
        self.kind.contains(&String::from("bin"))
    }

    pub fn is_lib(&self) -> bool {
        self.kind.iter().any(|k| Self::LIB_KINDS.contains(&&k[..]))
    }

    pub fn is_proc_macro(&self) -> bool {
        self.kind.contains(&String::from("proc-macro"))
    }

    fn from_json(j: &mut json::JsonValue) -> Target {
        let kind: Vec<String> = {
            assert!(j["kind"].is_array());
            j["kind"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let crate_types: Vec<String> = {
            assert!(j["crate_types"].is_array());
            j["crate_types"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let name: String = j["name"].take_string().expect("name is missing");
        let src_path: PathBuf =
            Path::new(&j["src_path"].take_string().expect("src_path is missing")).to_path_buf();
        let edition: Edition = j["edition"].as_str().into();
        Target {
            kind,
            crate_types,
            name,
            src_path,
            edition,
        }
    }

    fn is_custom_build(&self) -> bool {
        self.kind.contains(&String::from("custom-build"))
    }
}

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

    fn read_manifest(filename: &path::Path) -> Manifest {
        let manifest_str: String = fs::read_to_string(filename.with_extension("json"))
            .expect("manifest file cannot be read");
        Manifest::from_str(&manifest_str).expect("manifest cannot be read")
    }

    #[test]
    fn manifest_with_edition2018_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-edition-2018"));
        assert_eq!(Edition::E2018, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_with_edition2015_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-edition-2015"));
        assert_eq!(Edition::E2015, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_without_edition_can_be_parsed() {
        let manifest = read_manifest(path::Path::new("test-resources/example-lib-no-edition"));
        assert_eq!(Edition::E2015, manifest.targets()[0].edition);
    }

    #[test]
    fn manifest_for_simple_lib() {
        let resource_path = path::Path::new("test-resources/example-lib-edition-2018");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib-edition-2018"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
    }

    #[test]
    fn manifest_for_simple_bin() {
        let resource_path = path::Path::new("test-resources/example-bin");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example2"),
                    src_path: resource_path.join("src/bin/example2.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example"),
                    src_path: resource_path.join("src/bin/example.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.targets()
        );
        assert!(manifest.targets()[0].is_bin());
        assert!(!manifest.targets()[0].is_lib());
    }

    #[test]
    fn manifest_with_custom_build() {
        let resource_path = path::Path::new("test-resources/example-lib-edition-2018");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-lib-edition-2018"),
                    src_path: resource_path.join("src/lib.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("custom-build")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("build-script-build"),
                    src_path: resource_path.join("build.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.all_targets().collect::<Vec<_>>()
        );
        assert_eq!(
            vec![&Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib-edition-2018"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },],
            manifest.targets()
        );
        assert_eq!(
            vec![&Target {
                kind: vec!(String::from("custom-build")),
                crate_types: vec!(String::from("bin")),
                name: String::from("build-script-build"),
                src_path: resource_path.join("build.rs"),
                edition: Edition::E2018
            }],
            manifest.custom_builds()
        );
    }

    #[test]
    fn manifest_for_plugin() {
        let resource_path = path::Path::new("test-resources/example-plugin");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("dylib")),
                crate_types: vec!(String::from("dylib")),
                name: String::from("example-plugin"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
    }

    #[test]
    fn manifest_for_proc_macro() {
        let resource_path = path::Path::new("test-resources/example-proc-macro");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            &Target {
                kind: vec!(String::from("proc-macro")),
                crate_types: vec!(String::from("proc-macro")),
                name: String::from("example-proc-macro"),
                src_path: resource_path.join("src/lib.rs"),
                edition: Edition::E2018
            },
            manifest.targets()[0]
        );
        assert!(!manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
        assert!(manifest.targets()[0].is_proc_macro());
    }

    #[test]
    fn manifest_for_bin_and_lib() {
        let resource_path = path::Path::new("test-resources/example-bin-and-lib");
        let manifest = read_manifest(resource_path);
        assert_eq!(
            vec![
                &Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-bin-and-lib"),
                    src_path: resource_path.join("src/lib.rs"),
                    edition: Edition::E2018
                },
                &Target {
                    kind: vec!(String::from("bin")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("example-bin-and-lib"),
                    src_path: resource_path.join("src/main.rs"),
                    edition: Edition::E2018
                }
            ],
            manifest.targets()
        );
        assert!(manifest.targets()[0].is_lib());
        assert!(!manifest.targets()[0].is_bin());
        assert!(!manifest.targets()[0].is_proc_macro());
        assert!(!manifest.targets()[1].is_lib());
        assert!(manifest.targets()[1].is_bin());
        assert!(!manifest.targets()[1].is_proc_macro());
    }
}
<|endoftext|>


<commit_before>use serde;
use serde_json;
use openssl::ssl;
use mio::channel;
use std::io;
use std::net::SocketAddr;
use std::default::Default;
use std::convert::{From,Into};
use std::sync::mpsc;

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct HttpFront {
    pub app_id: String,
    pub hostname: String,
    pub path_begin: String,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TlsFront {
    pub app_id: String,
    pub hostname: String,
    pub path_begin: String,
    pub certificate: String,
    pub certificate_chain: Vec<String>,
    pub key:         String,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TcpFront {
    pub app_id: String,
    pub ip_address: String,
    pub port: u16
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct Instance {
    pub app_id: String,
    pub ip_address: String,
    pub port: u16
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct HttpProxyConfiguration {
    pub front:           SocketAddr,
    pub front_timeout:   u64,
    pub back_timeout:    u64,
    pub max_connections: usize,
    pub buffer_size:     usize,
    pub public_address:  Option<String>,
    pub answer_404:      String,
    pub answer_503:      String,
}

impl Default for HttpProxyConfiguration {
  fn default() -> HttpProxyConfiguration {
    HttpProxyConfiguration {
      front:           "127.0.0.1:8080".parse().unwrap(),
      front_timeout:   5000,
      back_timeout:    5000,
      max_connections: 1000,
      buffer_size:     12000,
      public_address:  None,
      answer_404:      String::from("HTTP/1.1 404 Not Found\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      answer_503:      String::from("HTTP/1.1 503 your application is in deployment\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
    }
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TlsProxyConfiguration {
    pub front:           SocketAddr,
    pub front_timeout:   u64,
    pub back_timeout:    u64,
    pub max_connections: usize,
    pub buffer_size:     usize,
    pub public_address:  Option<String>,
    pub answer_404:      String,
    pub answer_503:      String,
    pub options:         i64,
    pub cipher_list:     String,
}

impl Default for TlsProxyConfiguration {
  fn default() -> TlsProxyConfiguration {
    TlsProxyConfiguration {
      front:           "127.0.0.1:8443".parse().unwrap(),
      front_timeout:   5000,
      back_timeout:    5000,
      max_connections: 1000,
      buffer_size:     12000,
      public_address:  None,
      answer_404:      String::from("HTTP/1.1 404 Not Found\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      answer_503:      String::from("HTTP/1.1 503 your application is in deployment\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      cipher_list:     String::from("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"),
      options:         (ssl::SSL_OP_CIPHER_SERVER_PREFERENCE | ssl::SSL_OP_NO_COMPRESSION |
                         ssl::SSL_OP_NO_TICKET | ssl::SSL_OP_NO_SSLV2 |
                         ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1).bits(),
    }
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize)]
pub enum Command {
    AddHttpFront(HttpFront),
    RemoveHttpFront(HttpFront),

    AddTlsFront(TlsFront),
    RemoveTlsFront(TlsFront),

    AddTcpFront(TcpFront),
    RemoveTcpFront(TcpFront),

    AddInstance(Instance),
    RemoveInstance(Instance),

    HttpProxy(HttpProxyConfiguration),
    TlsProxy(TlsProxyConfiguration),
}

impl Command {
  pub fn get_topics(&self) -> Vec<Topic> {
    match *self {
      Command::AddHttpFront(_)    => vec![Topic::HttpProxyConfig                       ],
      Command::RemoveHttpFront(_) => vec![Topic::HttpProxyConfig                       ],
      Command::AddTlsFront(_)     => vec![Topic::TlsProxyConfig                        ],
      Command::RemoveTlsFront(_)  => vec![Topic::TlsProxyConfig                        ],
      Command::AddTcpFront(_)     => vec![Topic::TcpProxyConfig                        ],
      Command::RemoveTcpFront(_)  => vec![Topic::TcpProxyConfig                        ],
      Command::AddInstance(_)     => vec![Topic::HttpProxyConfig, Topic::TlsProxyConfig, Topic::TcpProxyConfig],
      Command::RemoveInstance(_)  => vec![Topic::HttpProxyConfig, Topic::TlsProxyConfig, Topic::TcpProxyConfig],
      Command::HttpProxy(_)       => vec![Topic::HttpProxyConfig],
      Command::TlsProxy(_)        => vec![Topic::TlsProxyConfig],
    }
  }
}

enum CommandField {
  Type,
  Data,
}

impl serde::Deserialize for CommandField {
  fn deserialize<D>(deserializer: &mut D) -> Result<CommandField, D::Error>
        where D: serde::de::Deserializer {
    struct CommandFieldVisitor;
    impl serde::de::Visitor for CommandFieldVisitor {
      type Value = CommandField;

      fn visit_str<E>(&mut self, value: &str) -> Result<CommandField, E>
        where E: serde::de::Error {
        match value {
          "type" => Ok(CommandField::Type),
          "data" => Ok(CommandField::Data),
          _ => Err(serde::de::Error::custom("expected type or data")),
        }
      }
    }

    deserializer.deserialize(CommandFieldVisitor)
  }
}

struct CommandVisitor;
impl serde::de::Visitor for CommandVisitor {
  type Value = Command;

  fn visit_map<V>(&mut self, mut visitor: V) -> Result<Command, V::Error>
        where V: serde::de::MapVisitor {
    let mut command_type:Option<String>    = None;
    let mut data:Option<serde_json::Value> = None;

    loop {
      match try!(visitor.visit_key()) {
        Some(CommandField::Type) => { command_type = Some(try!(visitor.visit_value())); }
        Some(CommandField::Data) => { data = Some(try!(visitor.visit_value())); }
        None => { break; }
      }
    }

    println!("decoded type = {:?}, value= {:?}", command_type, data);
    let command_type = match command_type {
      Some(command) => command,
      None => try!(visitor.missing_field("type")),
    };
    let data = match data {
      Some(data) => data,
      None => try!(visitor.missing_field("data")),
    };

    try!(visitor.end());

    if &command_type == "ADD_HTTP_FRONT" {
      let res = serde_json::from_value(data).or(Err(serde::de::Error::custom("add_http_front")));
      println!("ADD_HTTP_FRONT => {:?}", res);
      let acl = try!(res);
      Ok(Command::AddHttpFront(acl))
    } else if &command_type == "REMOVE_HTTP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_http_front"))));
      Ok(Command::RemoveHttpFront(acl))
    } else if &command_type == "ADD_TLS_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_tls_front"))));
      Ok(Command::AddTlsFront(acl))
    } else if &command_type == "REMOVE_TLS_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_tls_front"))));
      Ok(Command::RemoveTlsFront(acl))
    } else if &command_type == "ADD_TCP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_tcp_front"))));
      Ok(Command::AddTcpFront(acl))
    } else if &command_type == "REMOVE_TCP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_tcp_front"))));
      Ok(Command::RemoveTcpFront(acl))
    } else if &command_type == "ADD_INSTANCE" {
      let instance = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_instance"))));
      Ok(Command::AddInstance(instance))
    } else if &command_type == "REMOVE_INSTANCE" {
      let instance = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_instance"))));
      Ok(Command::RemoveInstance(instance))
    } else if &command_type == "CONFIGURE_HTTP_PROXY" {
      let conf = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("configure_http_proxy"))));
      Ok(Command::HttpProxy(conf))
    } else {
      Err(serde::de::Error::custom("unrecognized command"))
    }
  }
}

impl serde::Deserialize for Command {
  fn deserialize<D>(deserializer: &mut D) -> Result<Command, D::Error>
        where D: serde::de::Deserializer {
    static FIELDS: &'static [&'static str] = &["type", "data"];
    deserializer.deserialize_struct("Command", FIELDS, CommandVisitor)
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash)]
pub enum Topic {
    HttpProxyConfig,
    TlsProxyConfig,
    TcpProxyConfig
}

pub trait Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error>;
}

pub trait Receiver<T> {
  fn recv_message(&self) -> Result<T, io::Error>;
}

impl<T> Sender<T> for mpsc::Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error> {
    match self.send(t) {
      Ok(()) => Ok(()),
      Err(mpsc::SendError(_)) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

impl<T> Sender<T> for channel::Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error> {
    match self.send(t) {
      Ok(()) => Ok(()),
      Err(channel::SendError::Io(e)) => Err(e),
      Err(channel::SendError::Disconnected(_)) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

impl<T> Receiver<T> for channel::Receiver<T> {
  fn recv_message(&self) -> Result<T, io::Error> {
    match self.try_recv() {
      Ok(t) => Ok(t),
      Err(mpsc::TryRecvError::Empty) => Err(io::Error::new(io::ErrorKind::WouldBlock, "no data")),
      Err(mpsc::TryRecvError::Disconnected) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

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

  #[test]
  fn add_acl_test() {
    let raw_json = r#"{"type": "ADD_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
    }));
  }

  #[test]
  fn remove_acl_test() {
    let raw_json = r#"{"type": "REMOVE_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::RemoveHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
    }));
  }

  #[test]
  fn add_instance_test() {
    let raw_json = r#"{"type": "ADD_INSTANCE", "data": {"app_id": "xxx", "ip_address": "yyy", "port": 8080}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddInstance(Instance{
      app_id: String::from("xxx"),
      ip_address: String::from("yyy"),
      port: 8080
    }));
  }

  #[test]
  fn remove_instance_test() {
    let raw_json = r#"{"type": "REMOVE_INSTANCE", "data": {"app_id": "xxx", "ip_address": "yyy", "port": 8080}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::RemoveInstance(Instance{
      app_id: String::from("xxx"),
      ip_address: String::from("yyy"),
      port: 8080
    }));
  }

  #[test]
  fn http_front_crash_test() {
    let raw_json = r#"{"type": "ADD_HTTP_FRONT", "data": {"app_id": "aa", "hostname": "cltdl.fr", "path_begin": ""}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddHttpFront(HttpFront{
      app_id: String::from("aa"),
      hostname: String::from("cltdl.fr"),
      path_begin: String::from(""),
    }));
  }

  #[test]
  fn http_front_crash_test2() {
    let raw_json = r#"{"app_id": "aa", "hostname": "cltdl.fr", "path_begin": ""}"#;
    let front: HttpFront = serde_json::from_str(raw_json).unwrap();
    println!("{:?}",front);
    assert!(front == HttpFront{
      app_id: String::from("aa"),
      hostname: String::from("cltdl.fr"),
      path_begin: String::from(""),
    });
  }
}

<commit_msg>remove a warning<commit_after>use serde;
use serde_json;
use openssl::ssl;
use mio::channel;
use std::io;
use std::net::SocketAddr;
use std::default::Default;
use std::convert::From;
use std::sync::mpsc;

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct HttpFront {
    pub app_id: String,
    pub hostname: String,
    pub path_begin: String,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TlsFront {
    pub app_id: String,
    pub hostname: String,
    pub path_begin: String,
    pub certificate: String,
    pub certificate_chain: Vec<String>,
    pub key:         String,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TcpFront {
    pub app_id: String,
    pub ip_address: String,
    pub port: u16
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct Instance {
    pub app_id: String,
    pub ip_address: String,
    pub port: u16
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct HttpProxyConfiguration {
    pub front:           SocketAddr,
    pub front_timeout:   u64,
    pub back_timeout:    u64,
    pub max_connections: usize,
    pub buffer_size:     usize,
    pub public_address:  Option<String>,
    pub answer_404:      String,
    pub answer_503:      String,
}

impl Default for HttpProxyConfiguration {
  fn default() -> HttpProxyConfiguration {
    HttpProxyConfiguration {
      front:           "127.0.0.1:8080".parse().unwrap(),
      front_timeout:   5000,
      back_timeout:    5000,
      max_connections: 1000,
      buffer_size:     12000,
      public_address:  None,
      answer_404:      String::from("HTTP/1.1 404 Not Found\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      answer_503:      String::from("HTTP/1.1 503 your application is in deployment\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
    }
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize, Deserialize)]
pub struct TlsProxyConfiguration {
    pub front:           SocketAddr,
    pub front_timeout:   u64,
    pub back_timeout:    u64,
    pub max_connections: usize,
    pub buffer_size:     usize,
    pub public_address:  Option<String>,
    pub answer_404:      String,
    pub answer_503:      String,
    pub options:         i64,
    pub cipher_list:     String,
}

impl Default for TlsProxyConfiguration {
  fn default() -> TlsProxyConfiguration {
    TlsProxyConfiguration {
      front:           "127.0.0.1:8443".parse().unwrap(),
      front_timeout:   5000,
      back_timeout:    5000,
      max_connections: 1000,
      buffer_size:     12000,
      public_address:  None,
      answer_404:      String::from("HTTP/1.1 404 Not Found\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      answer_503:      String::from("HTTP/1.1 503 your application is in deployment\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"),
      cipher_list:     String::from("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"),
      options:         (ssl::SSL_OP_CIPHER_SERVER_PREFERENCE | ssl::SSL_OP_NO_COMPRESSION |
                         ssl::SSL_OP_NO_TICKET | ssl::SSL_OP_NO_SSLV2 |
                         ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1).bits(),
    }
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, Serialize)]
pub enum Command {
    AddHttpFront(HttpFront),
    RemoveHttpFront(HttpFront),

    AddTlsFront(TlsFront),
    RemoveTlsFront(TlsFront),

    AddTcpFront(TcpFront),
    RemoveTcpFront(TcpFront),

    AddInstance(Instance),
    RemoveInstance(Instance),

    HttpProxy(HttpProxyConfiguration),
    TlsProxy(TlsProxyConfiguration),
}

impl Command {
  pub fn get_topics(&self) -> Vec<Topic> {
    match *self {
      Command::AddHttpFront(_)    => vec![Topic::HttpProxyConfig                       ],
      Command::RemoveHttpFront(_) => vec![Topic::HttpProxyConfig                       ],
      Command::AddTlsFront(_)     => vec![Topic::TlsProxyConfig                        ],
      Command::RemoveTlsFront(_)  => vec![Topic::TlsProxyConfig                        ],
      Command::AddTcpFront(_)     => vec![Topic::TcpProxyConfig                        ],
      Command::RemoveTcpFront(_)  => vec![Topic::TcpProxyConfig                        ],
      Command::AddInstance(_)     => vec![Topic::HttpProxyConfig, Topic::TlsProxyConfig, Topic::TcpProxyConfig],
      Command::RemoveInstance(_)  => vec![Topic::HttpProxyConfig, Topic::TlsProxyConfig, Topic::TcpProxyConfig],
      Command::HttpProxy(_)       => vec![Topic::HttpProxyConfig],
      Command::TlsProxy(_)        => vec![Topic::TlsProxyConfig],
    }
  }
}

enum CommandField {
  Type,
  Data,
}

impl serde::Deserialize for CommandField {
  fn deserialize<D>(deserializer: &mut D) -> Result<CommandField, D::Error>
        where D: serde::de::Deserializer {
    struct CommandFieldVisitor;
    impl serde::de::Visitor for CommandFieldVisitor {
      type Value = CommandField;

      fn visit_str<E>(&mut self, value: &str) -> Result<CommandField, E>
        where E: serde::de::Error {
        match value {
          "type" => Ok(CommandField::Type),
          "data" => Ok(CommandField::Data),
          _ => Err(serde::de::Error::custom("expected type or data")),
        }
      }
    }

    deserializer.deserialize(CommandFieldVisitor)
  }
}

struct CommandVisitor;
impl serde::de::Visitor for CommandVisitor {
  type Value = Command;

  fn visit_map<V>(&mut self, mut visitor: V) -> Result<Command, V::Error>
        where V: serde::de::MapVisitor {
    let mut command_type:Option<String>    = None;
    let mut data:Option<serde_json::Value> = None;

    loop {
      match try!(visitor.visit_key()) {
        Some(CommandField::Type) => { command_type = Some(try!(visitor.visit_value())); }
        Some(CommandField::Data) => { data = Some(try!(visitor.visit_value())); }
        None => { break; }
      }
    }

    println!("decoded type = {:?}, value= {:?}", command_type, data);
    let command_type = match command_type {
      Some(command) => command,
      None => try!(visitor.missing_field("type")),
    };
    let data = match data {
      Some(data) => data,
      None => try!(visitor.missing_field("data")),
    };

    try!(visitor.end());

    if &command_type == "ADD_HTTP_FRONT" {
      let res = serde_json::from_value(data).or(Err(serde::de::Error::custom("add_http_front")));
      println!("ADD_HTTP_FRONT => {:?}", res);
      let acl = try!(res);
      Ok(Command::AddHttpFront(acl))
    } else if &command_type == "REMOVE_HTTP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_http_front"))));
      Ok(Command::RemoveHttpFront(acl))
    } else if &command_type == "ADD_TLS_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_tls_front"))));
      Ok(Command::AddTlsFront(acl))
    } else if &command_type == "REMOVE_TLS_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_tls_front"))));
      Ok(Command::RemoveTlsFront(acl))
    } else if &command_type == "ADD_TCP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_tcp_front"))));
      Ok(Command::AddTcpFront(acl))
    } else if &command_type == "REMOVE_TCP_FRONT" {
      let acl = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_tcp_front"))));
      Ok(Command::RemoveTcpFront(acl))
    } else if &command_type == "ADD_INSTANCE" {
      let instance = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("add_instance"))));
      Ok(Command::AddInstance(instance))
    } else if &command_type == "REMOVE_INSTANCE" {
      let instance = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("remove_instance"))));
      Ok(Command::RemoveInstance(instance))
    } else if &command_type == "CONFIGURE_HTTP_PROXY" {
      let conf = try!(serde_json::from_value(data).or(Err(serde::de::Error::custom("configure_http_proxy"))));
      Ok(Command::HttpProxy(conf))
    } else {
      Err(serde::de::Error::custom("unrecognized command"))
    }
  }
}

impl serde::Deserialize for Command {
  fn deserialize<D>(deserializer: &mut D) -> Result<Command, D::Error>
        where D: serde::de::Deserializer {
    static FIELDS: &'static [&'static str] = &["type", "data"];
    deserializer.deserialize_struct("Command", FIELDS, CommandVisitor)
  }
}

#[derive(Debug,Clone,PartialEq,Eq,Hash)]
pub enum Topic {
    HttpProxyConfig,
    TlsProxyConfig,
    TcpProxyConfig
}

pub trait Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error>;
}

pub trait Receiver<T> {
  fn recv_message(&self) -> Result<T, io::Error>;
}

impl<T> Sender<T> for mpsc::Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error> {
    match self.send(t) {
      Ok(()) => Ok(()),
      Err(mpsc::SendError(_)) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

impl<T> Sender<T> for channel::Sender<T> {
  fn send_message(&self, t:T) -> Result<(), io::Error> {
    match self.send(t) {
      Ok(()) => Ok(()),
      Err(channel::SendError::Io(e)) => Err(e),
      Err(channel::SendError::Disconnected(_)) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

impl<T> Receiver<T> for channel::Receiver<T> {
  fn recv_message(&self) -> Result<T, io::Error> {
    match self.try_recv() {
      Ok(t) => Ok(t),
      Err(mpsc::TryRecvError::Empty) => Err(io::Error::new(io::ErrorKind::WouldBlock, "no data")),
      Err(mpsc::TryRecvError::Disconnected) => Err(io::Error::new(io::ErrorKind::BrokenPipe, "receiver disconnected"))
    }
  }
}

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

  #[test]
  fn add_acl_test() {
    let raw_json = r#"{"type": "ADD_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
    }));
  }

  #[test]
  fn remove_acl_test() {
    let raw_json = r#"{"type": "REMOVE_HTTP_FRONT", "data": {"app_id": "xxx", "hostname": "yyy", "path_begin": "xxx", "port": 4242}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::RemoveHttpFront(HttpFront{
      app_id: String::from("xxx"),
      hostname: String::from("yyy"),
      path_begin: String::from("xxx"),
    }));
  }

  #[test]
  fn add_instance_test() {
    let raw_json = r#"{"type": "ADD_INSTANCE", "data": {"app_id": "xxx", "ip_address": "yyy", "port": 8080}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddInstance(Instance{
      app_id: String::from("xxx"),
      ip_address: String::from("yyy"),
      port: 8080
    }));
  }

  #[test]
  fn remove_instance_test() {
    let raw_json = r#"{"type": "REMOVE_INSTANCE", "data": {"app_id": "xxx", "ip_address": "yyy", "port": 8080}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::RemoveInstance(Instance{
      app_id: String::from("xxx"),
      ip_address: String::from("yyy"),
      port: 8080
    }));
  }

  #[test]
  fn http_front_crash_test() {
    let raw_json = r#"{"type": "ADD_HTTP_FRONT", "data": {"app_id": "aa", "hostname": "cltdl.fr", "path_begin": ""}}"#;
    let command: Command = serde_json::from_str(raw_json).unwrap();
    println!("{:?}", command);
    assert!(command == Command::AddHttpFront(HttpFront{
      app_id: String::from("aa"),
      hostname: String::from("cltdl.fr"),
      path_begin: String::from(""),
    }));
  }

  #[test]
  fn http_front_crash_test2() {
    let raw_json = r#"{"app_id": "aa", "hostname": "cltdl.fr", "path_begin": ""}"#;
    let front: HttpFront = serde_json::from_str(raw_json).unwrap();
    println!("{:?}",front);
    assert!(front == HttpFront{
      app_id: String::from("aa"),
      hostname: String::from("cltdl.fr"),
      path_begin: String::from(""),
    });
  }
}

<|endoftext|>