<commit_before>#[derive(Clone, Copy, Debug)]
enum Const {
  Data,
  Codata,
}

impl ToString for Const {
  fn to_string(&self) -> String {
    use Const::*;
    match *self {
      Data => "data".to_string(),
      Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    self.name.clone()
     // + "@" + &self.idx.to_string()
  }
}

#[derive(Clone, Debug)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match self {
      &Const(ref constant) => constant.to_string(),
      &Var(ref var) => var.to_string(),
      &Lam(ref var, ref ty, ref body) => {
        var.to_string() + ":" + &ty.to_string() + " -> " + &body.to_string()
      }
      &Pi(ref var, ref ty, ref body) => {
        "forall (".to_string() + &var.to_string() + ":" + &ty.to_string() +
        ") -> " + &body.to_string()
      }
      &App(ref f, ref arg) => f.to_string() + " " + &arg.to_string(),
    }
  }
}

fn main() {
  println!("{}", Const::Data.to_string());
  println!("{:?}", Expr::constant(Const::Data));
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = Expr::var(&a);
  let exprx = Expr::var(&x);
  println!("{}", x.to_string());
  let id = Expr::pi(a.clone(),
                    Expr::constant(Const::Data),
                    Expr::lam(x, expra, exprx));
  println!("{}", id.to_string());
}
<commit_msg>Parenthesize applications. Print id on int.<commit_after>#[derive(Clone, Copy, Debug)]
enum Const {
  Data,
  Codata,
}

impl ToString for Const {
  fn to_string(&self) -> String {
    use Const::*;
    match *self {
      Data => "data".to_string(),
      Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    self.name.clone()
    // + "@" + &self.idx.to_string()
  }
}

#[derive(Clone, Debug)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match self {
      &Const(ref constant) => constant.to_string(),
      &Var(ref var) => var.to_string(),
      &Lam(ref var, ref ty, ref body) => {
        var.to_string() + ":" + &ty.to_string() + " -> " + &body.to_string()
      }
      &Pi(ref var, ref ty, ref body) => {
        "forall (".to_string() + &var.to_string() + ":" + &ty.to_string() +
        ") -> " + &body.to_string()
      }
      &App(ref f, ref arg) => {
        "(".to_string() + &f.to_string() + ") " + &arg.to_string()
      }
    }
  }
}

fn main() {
  println!("{}", Const::Data.to_string());
  println!("{:?}", Expr::constant(Const::Data));
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = Expr::var(&a);
  let exprx = Expr::var(&x);
  println!("{}", x.to_string());
  let id = Expr::pi(a.clone(),
                    Expr::constant(Const::Data),
                    Expr::lam(x, expra, exprx));
  println!("{}", id.to_string());
  let apply_id = Expr::app(Expr::app(id, Expr::var(&Var::new("int", 0))),
                           Expr::var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
}
<|endoftext|>


<commit_before>pub use self::core::MoveCell;

#[allow(unsafe_code)]
mod core {
    use std::cell::UnsafeCell;
    use std::mem;

    /// A mutable memory location that clones its contents on retrieval.
    pub struct MoveCell<T>(UnsafeCell<T>);

    impl<T> MoveCell<T> {
        /// Creates a new `MoveCell` containing the given value.
        ///
        /// # Example
        ///
        /// ```
        /// use mitochondria::MoveCell;
        ///
        /// let c = MoveCell::new("Hello cytosol!".to_owned());
        /// ```
        #[inline]
        pub fn new(value: T) -> Self {
            MoveCell(UnsafeCell::new(value))
        }

        /// Replaces the value of this cell, returning the old one.
        ///
        /// # Example
        ///
        /// ```
        /// use mitochondria::MoveCell;
        ///
        /// let c = MoveCell::new("Hello lysosome!".to_owned());
        ///
        /// let greeting = c.replace("Goodbye!".to_owned());
        /// ```
        #[inline]
        pub fn replace(&self, value: T) -> T {
            unsafe {
                mem::replace(&mut *self.0.get(), value)
            }
        }
    }
}

impl<T> MoveCell<T> {
    /// Sets the value of this cell, dropping the old one.
    ///
    /// # Example
    ///
    /// ```
    /// use mitochondria::MoveCell;
    ///
    /// let c = MoveCell::new("Hello vacuole!".to_owned());
    ///
    /// c.set("Hello cytoskeleton!".to_owned());
    /// ```
    #[inline]
    pub fn set(&self, value: T) {
        drop(self.replace(value));
    }
}

impl<T: Default> MoveCell<T> {
    /// Returns the value in this cell, replacing it by `T::default`.
    ///
    /// # Example
    ///
    /// ```
    /// use mitochondria::MoveCell;
    ///
    /// let c = MoveCell::new(vec!["vacuole", "cytoskeleton"]);
    ///
    /// assert_eq!(c.take(), &["vacuole", "cytoskeleton"]);
    /// assert_eq!(c.take(), &[] as &[&str]);
    /// ```
    #[inline]
    pub fn take(&self) -> T {
        self.replace(T::default())
    }
}
<commit_msg>Implement Send and Default for MoveCell<commit_after>pub use self::core::MoveCell;

#[allow(unsafe_code)]
mod core {
    use std::cell::UnsafeCell;
    use std::mem;

    /// A mutable memory location that clones its contents on retrieval.
    pub struct MoveCell<T>(UnsafeCell<T>);

    unsafe impl<T> Send for MoveCell<T> where T: Send {}

    impl<T> MoveCell<T> {
        /// Creates a new `MoveCell` containing the given value.
        ///
        /// # Example
        ///
        /// ```
        /// use mitochondria::MoveCell;
        ///
        /// let c = MoveCell::new("Hello cytosol!".to_owned());
        /// ```
        #[inline]
        pub fn new(value: T) -> Self {
            MoveCell(UnsafeCell::new(value))
        }

        /// Replaces the value of this cell, returning the old one.
        ///
        /// # Example
        ///
        /// ```
        /// use mitochondria::MoveCell;
        ///
        /// let c = MoveCell::new("Hello lysosome!".to_owned());
        ///
        /// let greeting = c.replace("Goodbye!".to_owned());
        /// ```
        #[inline]
        pub fn replace(&self, value: T) -> T {
            unsafe {
                mem::replace(&mut *self.0.get(), value)
            }
        }
    }
}

impl<T> MoveCell<T> {
    /// Sets the value of this cell, dropping the old one.
    ///
    /// # Example
    ///
    /// ```
    /// use mitochondria::MoveCell;
    ///
    /// let c = MoveCell::new("Hello vacuole!".to_owned());
    ///
    /// c.set("Hello cytoskeleton!".to_owned());
    /// ```
    #[inline]
    pub fn set(&self, value: T) {
        drop(self.replace(value));
    }
}

impl<T: Default> Default for MoveCell<T> {
    #[inline]
    fn default() -> Self {
        MoveCell::new(Default::default())
    }
}

impl<T: Default> MoveCell<T> {
    /// Returns the value in this cell, replacing it by `T::default`.
    ///
    /// # Example
    ///
    /// ```
    /// use mitochondria::MoveCell;
    ///
    /// let c = MoveCell::new(vec!["vacuole", "cytoskeleton"]);
    ///
    /// assert_eq!(c.take(), &["vacuole", "cytoskeleton"]);
    /// assert_eq!(c.take(), &[] as &[&str]);
    /// ```
    #[inline]
    pub fn take(&self) -> T {
        self.replace(T::default())
    }
}
<|endoftext|>


<commit_before>use std::convert::{AsRef, AsMut, From};
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::fmt::{Debug, Formatter};
use std::ops::Range;

use super::{BezCurve, Point2d, Vector2d, Float, Point, Vector, lerp};

/// A struct that contains range information for slicing, used for slicing into the global factor
/// vector. The reason this is used instead of stdlib's `Range` struct is that `Range` does not
/// implement Copy, which means we have to use `RefCell`s instead of `Cell`s for interior mutability.
#[derive(Copy, Clone)]
struct RangeSlice {
    start: usize,
    end: usize
}

impl RangeSlice {
    #[inline]
    fn new(start: usize, end: usize) -> RangeSlice {
        RangeSlice {
            start: start,
            end: end
        }
    }

    fn as_range(&self) -> Range<usize> {
        self.start..self.end
    }

    fn len(&self) -> usize {
        self.end - self.start
    }
}

fn combination(n: u64, k: u64) -> u64 {
    factorial(n) / (factorial(k) * factorial(n - k))
}

fn factorial(mut n: u64) -> u64 {
    let mut accumulator: u64 = 1;
    while n > 0 {
        accumulator = accumulator.checked_mul(n).expect("Attempted to create Bézier curve with combination that overflow u64; decrease curve order");
        n -= 1;
    }
    accumulator
}

/// Given the `order` and references to the `factors`, `dfactors`, and `vec` cells, update the
/// cells to contain accurate information about the factors of the order.
fn update_factors(order: usize, factors: &Cell<RangeSlice>, dfactors: &Cell<RangeSlice>, vec: &RefCell<Vec<u64>>) {
    if factors.get().len() != order + 1 {
        let mut vec = vec.borrow_mut();
        // Remove everything from the vector without freeing memory
        unsafe{ vec.set_len(0) };

        // The vector stores both the factors of the order and the order's derivative, and this is the
        // length necessary to contain those factors.
        let new_len = (order + 1) * 2 - 1;
        if vec.capacity() < new_len {
            let reserve_amount = new_len - vec.capacity();
            vec.reserve(reserve_amount);
        }

        {
            let order = order as u64;

            for k in 0..order + 1 {
                vec.push(combination(order, k));
            }

            for k in 0..order {
                vec.push(combination(order - 1, k));
            }
        }

        factors.set(RangeSlice::new(0, order + 1));
        dfactors.set(RangeSlice::new(order + 1, vec.len()));
    }
}

/// An n-order bezier curve
#[derive(Clone)]
pub struct NBez<F, P = Point2d<F>, V = Vector2d<F>, C = Vec<P>>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    points: C,
    factor_vec: RefCell<Vec<u64>>,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,
    phantom: PhantomData<(F, P, V)>
}

impl<F, P, V, C> From<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    fn from(container: C) -> NBez<F, P, V, C> {
        NBez::from_container(container)
    }
}

impl<F, P, V, C> NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    #[inline]
    pub fn from_container(points: C) -> NBez<F, P, V, C> {
        if points.as_ref().len() >= 22 {
            panic!("Cannot create Bézier polynomials with an order >= 21")
        }

        NBez {
            points: points,
            factor_vec: RefCell::new(Vec::new()),
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),
            phantom: PhantomData
        }
    }

    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<F, P, V, C> BezCurve<F> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    type Point = P;
    type Vector = V;
    type Elevated = NBez<F, P, V, Vec<P>>;

    /// Currently non-functional; returns `None`
    fn from_slice(_: &[P]) -> Option<NBez<F, P, V, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> P {
        let points = self.points.as_ref();
        update_factors(self.order(), &self.factors, &self.dfactors, &self.factor_vec);
        let factors = &self.factor_vec.borrow()[self.factors.get().as_range()];

        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = factors.len() - 1;
        let mut acc = P::zero();
        let mut factor = 0;

        for point in points.iter() {
            acc = acc + *point *
                        t.powi(factor as i32) *
                        t1.powi((order - factor) as i32) *
                        F::from_u64(factors[factor]).unwrap();
            factor += 1;
        }
        acc
    }

    fn slope_unbounded(&self, t: F) -> V {
        let points = self.points.as_ref();
        update_factors(self.order(), &self.factors, &self.dfactors, &self.factor_vec);
        let dfactors = &self.factor_vec.borrow()[self.dfactors.get().as_range()];

        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = dfactors.len() - 1;
        let mut acc = P::zero();
        let mut factor = 0;
        let mut point_last = points[0].clone();

        for point in points[1..].iter().map(|p| *p) {
            acc = acc + (point - point_last) *
                        t.powi(factor as i32) *
                        t1.powi((order-factor) as i32) *
                        F::from_u64(dfactors[factor] * (order + 1) as u64).unwrap();
            point_last = point;
            factor += 1;
        }
        acc.into()
    }

    fn elevate(&self) -> NBez<F, P, V, Vec<P>> {
        let points = self.points.as_ref();
        let order = self.order() + 1;
        let order_f = F::from_usize(order).unwrap();

        // Elevated points
        let mut el_points = Vec::with_capacity(order + 1);
        el_points.push(points[0]);

        let mut prev_p = points[0];
        for (i, p) in points.iter().map(|p| *p).enumerate().skip(1) {
            el_points.push(lerp(p, prev_p, F::from_usize(i).unwrap()/order_f));

            prev_p = p;
        }

        el_points.push(points[self.order()]);
        NBez::from_container(el_points)
    }

    /// Currently non-functional; returns `None`
    fn split(&self, _: F) -> Option<(NBez<F, P, V, C>, NBez<F, P, V, C>)> {
        None
    }

    /// Currently non-functional; panics with unimplemented
    fn split_unbounded(&self, _: F) -> (NBez<F, P, V, C>, NBez<F, P, V, C>) {
        unimplemented!()
    }

    fn order(&self) -> usize {
        self.points.as_ref().len()-1
    }
}

impl<F, P, V, C> AsRef<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, P, V, C> AsMut<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

impl<F, P, V, C> Debug for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + Debug {
    fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
        f.debug_tuple("NBez")
            .field(&self.points)
            .finish()
    }
}
<commit_msg>Added AsRef/AsMut<[P]> to NBez<commit_after>use std::convert::{AsRef, AsMut, From};
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::fmt::{Debug, Formatter};
use std::ops::Range;

use super::{BezCurve, Point2d, Vector2d, Float, Point, Vector, lerp};

/// A struct that contains range information for slicing, used for slicing into the global factor
/// vector. The reason this is used instead of stdlib's `Range` struct is that `Range` does not
/// implement Copy, which means we have to use `RefCell`s instead of `Cell`s for interior mutability.
#[derive(Copy, Clone)]
struct RangeSlice {
    start: usize,
    end: usize
}

impl RangeSlice {
    #[inline]
    fn new(start: usize, end: usize) -> RangeSlice {
        RangeSlice {
            start: start,
            end: end
        }
    }

    fn as_range(&self) -> Range<usize> {
        self.start..self.end
    }

    fn len(&self) -> usize {
        self.end - self.start
    }
}

fn combination(n: u64, k: u64) -> u64 {
    factorial(n) / (factorial(k) * factorial(n - k))
}

fn factorial(mut n: u64) -> u64 {
    let mut accumulator: u64 = 1;
    while n > 0 {
        accumulator = accumulator.checked_mul(n).expect("Attempted to create Bézier curve with combination that overflow u64; decrease curve order");
        n -= 1;
    }
    accumulator
}

/// Given the `order` and references to the `factors`, `dfactors`, and `vec` cells, update the
/// cells to contain accurate information about the factors of the order.
fn update_factors(order: usize, factors: &Cell<RangeSlice>, dfactors: &Cell<RangeSlice>, vec: &RefCell<Vec<u64>>) {
    if factors.get().len() != order + 1 {
        let mut vec = vec.borrow_mut();
        // Remove everything from the vector without freeing memory
        unsafe{ vec.set_len(0) };

        // The vector stores both the factors of the order and the order's derivative, and this is the
        // length necessary to contain those factors.
        let new_len = (order + 1) * 2 - 1;
        if vec.capacity() < new_len {
            let reserve_amount = new_len - vec.capacity();
            vec.reserve(reserve_amount);
        }

        {
            let order = order as u64;

            for k in 0..order + 1 {
                vec.push(combination(order, k));
            }

            for k in 0..order {
                vec.push(combination(order - 1, k));
            }
        }

        factors.set(RangeSlice::new(0, order + 1));
        dfactors.set(RangeSlice::new(order + 1, vec.len()));
    }
}

/// An n-order bezier curve
#[derive(Clone)]
pub struct NBez<F, P = Point2d<F>, V = Vector2d<F>, C = Vec<P>>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    points: C,
    factor_vec: RefCell<Vec<u64>>,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,
    phantom: PhantomData<(F, P, V)>
}

impl<F, P, V, C> From<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    fn from(container: C) -> NBez<F, P, V, C> {
        NBez::from_container(container)
    }
}

impl<F, P, V, C> NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    #[inline]
    pub fn from_container(points: C) -> NBez<F, P, V, C> {
        if points.as_ref().len() >= 22 {
            panic!("Cannot create Bézier polynomials with an order >= 21")
        }

        NBez {
            points: points,
            factor_vec: RefCell::new(Vec::new()),
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),
            phantom: PhantomData
        }
    }

    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<F, P, V, C> BezCurve<F> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    type Point = P;
    type Vector = V;
    type Elevated = NBez<F, P, V, Vec<P>>;

    /// Currently non-functional; returns `None`
    fn from_slice(_: &[P]) -> Option<NBez<F, P, V, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> P {
        let points = self.points.as_ref();
        update_factors(self.order(), &self.factors, &self.dfactors, &self.factor_vec);
        let factors = &self.factor_vec.borrow()[self.factors.get().as_range()];

        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = factors.len() - 1;
        let mut acc = P::zero();
        let mut factor = 0;

        for point in points.iter() {
            acc = acc + *point *
                        t.powi(factor as i32) *
                        t1.powi((order - factor) as i32) *
                        F::from_u64(factors[factor]).unwrap();
            factor += 1;
        }
        acc
    }

    fn slope_unbounded(&self, t: F) -> V {
        let points = self.points.as_ref();
        update_factors(self.order(), &self.factors, &self.dfactors, &self.factor_vec);
        let dfactors = &self.factor_vec.borrow()[self.dfactors.get().as_range()];

        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = dfactors.len() - 1;
        let mut acc = P::zero();
        let mut factor = 0;
        let mut point_last = points[0].clone();

        for point in points[1..].iter().map(|p| *p) {
            acc = acc + (point - point_last) *
                        t.powi(factor as i32) *
                        t1.powi((order-factor) as i32) *
                        F::from_u64(dfactors[factor] * (order + 1) as u64).unwrap();
            point_last = point;
            factor += 1;
        }
        acc.into()
    }

    fn elevate(&self) -> NBez<F, P, V, Vec<P>> {
        let points = self.points.as_ref();
        let order = self.order() + 1;
        let order_f = F::from_usize(order).unwrap();

        // Elevated points
        let mut el_points = Vec::with_capacity(order + 1);
        el_points.push(points[0]);

        let mut prev_p = points[0];
        for (i, p) in points.iter().map(|p| *p).enumerate().skip(1) {
            el_points.push(lerp(p, prev_p, F::from_usize(i).unwrap()/order_f));

            prev_p = p;
        }

        el_points.push(points[self.order()]);
        NBez::from_container(el_points)
    }

    /// Currently non-functional; returns `None`
    fn split(&self, _: F) -> Option<(NBez<F, P, V, C>, NBez<F, P, V, C>)> {
        None
    }

    /// Currently non-functional; panics with unimplemented
    fn split_unbounded(&self, _: F) -> (NBez<F, P, V, C>, NBez<F, P, V, C>) {
        unimplemented!()
    }

    fn order(&self) -> usize {
        self.points.as_ref().len()-1
    }
}

impl<F, P, V, C> AsRef<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, P, V, C> AsMut<C> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

impl<F, P, V, C> AsRef<[P]> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    fn as_ref(&self) -> &[P] {
        self.points.as_ref()
    }
}

impl<F, P, V, C> AsMut<[P]> for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> {
    fn as_mut(&mut self) -> &mut [P] {
        self.points.as_mut()
    }
}

impl<F, P, V, C> Debug for NBez<F, P, V, C>
        where F: Float,
              P: Point<F, V>,
              V: Vector<F>,
              C: AsRef<[P]> + AsMut<[P]> + Debug {
    fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
        f.debug_tuple("NBez")
            .field(&self.points)
            .finish()
    }
}
<|endoftext|>


<commit_before>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::{from_reader, from_str};
use serde_json::value::Value;
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};
use urlencoded::QueryMap;

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, params: &QueryMap) -> Url {

    let email_addr = EmailAddress::new(&params.get("login_hint").unwrap()[0]).unwrap();
    let client_id = &params.get("client_id").unwrap()[0];
    let nonce = &params.get("nonce").unwrap()[0];
    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    let _ = app.store.store_session(&session, &[
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &params.get("redirect_uri").unwrap()[0]),
    ]).unwrap();

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    // TODO: cache other data for use in the callback handler, so that we
    // don't have to request the Discovery document twice. We could even store
    // per-provider data in Redis so we can amortize the cost of discovery.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        ).unwrap();
        from_str(&data).unwrap()
    };
    let config = val.as_object().unwrap();
    let authz_base = config["authorization_endpoint"].as_str().unwrap();

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).unwrap()

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> Result<(String, String), String> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    let email_addr = EmailAddress::new(stored.get("email").unwrap()).unwrap();
    let origin = stored.get("client_id").unwrap();
    let nonce = stored.get("nonce").unwrap();

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        ).unwrap();
        from_str(&data).unwrap()
    };
    let config = val.as_object().unwrap();
    let token_url = config["token_endpoint"].as_str().unwrap();

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let id_token = token_obj.find("id_token").unwrap().as_str().unwrap();

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let url = config["jwks_uri"].as_str().unwrap();
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::KeySet { domain: &email_addr.domain },
            &client,
            &url
        ).unwrap();
        let doc: Value = from_str(&data).unwrap();
        verify_jws(id_token, &doc).unwrap()
    };

    // Verify that the issuer matches the configured value.
    let iss = jwt_payload.find("iss").unwrap().as_str().unwrap();
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);

    // Verify the audience, subject, and expiry.
    let aud = jwt_payload.find("aud").unwrap().as_str().unwrap();
    assert!(aud == provider.client_id);
    let token_addr = jwt_payload.find("email").unwrap().as_str().unwrap();
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    let exp = jwt_payload.find("exp").unwrap().as_i64().unwrap();
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = stored.get("redirect").unwrap();
    Ok((id_token, redirect.to_string()))

}
<commit_msg>Add tests for email canonicalization (fixes #11)<commit_after>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::{from_reader, from_str};
use serde_json::value::Value;
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};
use urlencoded::QueryMap;

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, params: &QueryMap) -> Url {

    let email_addr = EmailAddress::new(&params.get("login_hint").unwrap()[0]).unwrap();
    let client_id = &params.get("client_id").unwrap()[0];
    let nonce = &params.get("nonce").unwrap()[0];
    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    let _ = app.store.store_session(&session, &[
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &params.get("redirect_uri").unwrap()[0]),
    ]).unwrap();

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    // TODO: cache other data for use in the callback handler, so that we
    // don't have to request the Discovery document twice. We could even store
    // per-provider data in Redis so we can amortize the cost of discovery.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        ).unwrap();
        from_str(&data).unwrap()
    };
    let config = val.as_object().unwrap();
    let authz_base = config["authorization_endpoint"].as_str().unwrap();

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).unwrap()

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> Result<(String, String), String> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    let email_addr = EmailAddress::new(stored.get("email").unwrap()).unwrap();
    let origin = stored.get("client_id").unwrap();
    let nonce = stored.get("nonce").unwrap();

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let provider = &app.providers[&email_addr.domain];
    let val: Value = {
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        ).unwrap();
        from_str(&data).unwrap()
    };
    let config = val.as_object().unwrap();
    let token_url = config["token_endpoint"].as_str().unwrap();

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let id_token = token_obj.find("id_token").unwrap().as_str().unwrap();

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let url = config["jwks_uri"].as_str().unwrap();
        let data = app.store.cache.fetch_url(
            &app.store,
            CacheKey::KeySet { domain: &email_addr.domain },
            &client,
            &url
        ).unwrap();
        let doc: Value = from_str(&data).unwrap();
        verify_jws(id_token, &doc).unwrap()
    };

    // Verify that the issuer matches the configured value.
    let iss = jwt_payload.find("iss").unwrap().as_str().unwrap();
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);

    // Verify the audience, subject, and expiry.
    let aud = jwt_payload.find("aud").unwrap().as_str().unwrap();
    assert!(aud == provider.client_id);
    let token_addr = jwt_payload.find("email").unwrap().as_str().unwrap();
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    let exp = jwt_payload.find("exp").unwrap().as_i64().unwrap();
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = stored.get("redirect").unwrap();
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Syn Developers
//
// 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.

use punctuated::Punctuated;
use super::*;

ast_struct! {
    /// A "Path" is essentially Rust's notion of a name.
    ///
    /// It's represented as a sequence of identifiers,
    /// along with a bunch of supporting information.
    ///
    /// E.g. `std::cmp::PartialEq`
    pub struct Path {
        /// A `::foo` path, is relative to the crate root rather than current
        /// module (like paths in an import).
        pub leading_colon: Option<Token![::]>,
        /// The segments in the path: the things separated by `::`.
        pub segments: Punctuated<PathSegment, Token![::]>,
    }
}

impl Path {
    pub fn global(&self) -> bool {
        self.leading_colon.is_some()
    }
}

#[cfg(feature = "printing")]
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);

impl<T> From<T> for Path
where
    T: Into<PathSegment>,
{
    fn from(segment: T) -> Self {
        let mut path = Path {
            leading_colon: None,
            segments: Punctuated::new(),
        };
        path.segments.push_value(segment.into());
        path
    }
}

ast_struct! {
    /// A segment of a path: an identifier, an optional lifetime, and a set of types.
    ///
    /// E.g. `std`, `String` or `Box<T>`
    pub struct PathSegment {
        /// The identifier portion of this path segment.
        pub ident: Ident,
        /// Type/lifetime arguments attached to this path. They come in
        /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
        /// this is more than just simple syntactic sugar; the use of
        /// parens affects the region binding rules, so we preserve the
        /// distinction.
        pub arguments: PathArguments,
    }
}

impl<T> From<T> for PathSegment
where
    T: Into<Ident>,
{
    fn from(ident: T) -> Self {
        PathSegment {
            ident: ident.into(),
            arguments: PathArguments::None,
        }
    }
}

ast_enum! {
    /// Arguments of a path segment.
    ///
    /// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
    pub enum PathArguments {
        None,
        /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`
        AngleBracketed(AngleBracketedGenericArguments),
        /// The `(A, B)` and `C` in `Foo(A, B) -> C`
        Parenthesized(ParenthesizedGenericArguments),
    }
}

impl Default for PathArguments {
    fn default() -> Self {
        PathArguments::None
    }
}

impl PathArguments {
    pub fn is_empty(&self) -> bool {
        match *self {
            PathArguments::None => true,
            PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
            PathArguments::Parenthesized(_) => false,
        }
    }
}

ast_enum! {
    /// A individual generic argument, like `'a`, `T`, or `Item=T`.
    pub enum GenericArgument {
        /// The lifetime parameters for this path segment.
        Lifetime(Lifetime),
        /// The type parameters for this path segment, if present.
        Type(Type),
        /// Bindings (equality constraints) on associated types, if present.
        ///
        /// E.g., `Foo<A=Bar>`.
        Binding(Binding),
        /// Const expression. Must be inside of a block.
        ///
        /// NOTE: Identity expressions are represented as Type arguments, as
        /// they are indistinguishable syntactically.
        Const(Expr),
    }
}

ast_struct! {
    /// A path like `Foo<'a, T>`
    pub struct AngleBracketedGenericArguments {
        pub colon2_token: Option<Token![::]>,
        pub lt_token: Token![<],
        pub args: Punctuated<GenericArgument, Token![,]>,
        pub gt_token: Token![>],
    }
}

ast_struct! {
    /// Bind a type to an associated type: `A=Foo`.
    pub struct Binding {
        pub ident: Ident,
        pub eq_token: Token![=],
        pub ty: Type,
    }
}

ast_struct! {
    /// A path like `Foo(A,B) -> C`
    pub struct ParenthesizedGenericArguments {
        pub paren_token: token::Paren,
        /// `(A, B)`
        pub inputs: Punctuated<Type, Token![,]>,
        /// `C`
        pub output: ReturnType,
    }
}

ast_struct! {
    /// The explicit Self type in a "qualified path". The actual
    /// path, including the trait and the associated item, is stored
    /// separately. `position` represents the index of the associated
    /// item qualified with this Self type.
    ///
    /// ```text
    /// <Vec<T> as a::b::Trait>::AssociatedItem
    ///  ^~~~~~    ~~~~~~~~~~~~~~^
    ///  ty        position = 3
    ///
    /// <Vec<T>>::AssociatedItem
    ///  ^~~~~~   ^
    ///  ty       position = 0
    /// ```
    pub struct QSelf {
        pub lt_token: Token![<],
        pub ty: Box<Type>,
        pub position: usize,
        pub as_token: Option<Token![as]>,
        pub gt_token: Token![>],
    }
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use synom::Synom;

    impl Synom for Path {
        named!(parse -> Self, do_parse!(
            colon: option!(punct!(::)) >>
            segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
            cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn"), epsilon!()) >>
            (Path {
                leading_colon: colon,
                segments: segments,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("path")
        }
    }

    #[cfg(not(feature = "full"))]
    impl Synom for GenericArgument {
        named!(parse -> Self, alt!(
            call!(ty_no_eq_after) => { GenericArgument::Type }
            |
            syn!(Lifetime) => { GenericArgument::Lifetime }
            |
            syn!(Binding) => { GenericArgument::Binding }
        ));
    }

    #[cfg(feature = "full")]
    impl Synom for GenericArgument {
        named!(parse -> Self, alt!(
            call!(ty_no_eq_after) => { GenericArgument::Type }
            |
            syn!(Lifetime) => { GenericArgument::Lifetime }
            |
            syn!(Binding) => { GenericArgument::Binding }
            |
            syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
            |
            syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
        ));

        fn description() -> Option<&'static str> {
            Some("generic argument")
        }
    }

    impl Synom for AngleBracketedGenericArguments {
        named!(parse -> Self, do_parse!(
            colon2: option!(punct!(::)) >>
            lt: punct!(<) >>
            args: call!(Punctuated::parse_terminated) >>
            gt: punct!(>) >>
            (AngleBracketedGenericArguments {
                colon2_token: colon2,
                lt_token: lt,
                args: args,
                gt_token: gt,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("angle bracketed generic arguments")
        }
    }

    impl Synom for ParenthesizedGenericArguments {
        named!(parse -> Self, do_parse!(
            data: parens!(Punctuated::parse_terminated) >>
            output: syn!(ReturnType) >>
            (ParenthesizedGenericArguments {
                paren_token: data.0,
                inputs: data.1,
                output: output,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
        }
    }

    impl Synom for PathSegment {
        named!(parse -> Self, alt!(
            do_parse!(
                ident: syn!(Ident) >>
                arguments: syn!(AngleBracketedGenericArguments) >>
                (PathSegment {
                    ident: ident,
                    arguments: PathArguments::AngleBracketed(arguments),
                })
            )
            |
            mod_style_path_segment
        ));

        fn description() -> Option<&'static str> {
            Some("path segment")
        }
    }

    impl Synom for Binding {
        named!(parse -> Self, do_parse!(
            id: syn!(Ident) >>
            eq: punct!(=) >>
            ty: syn!(Type) >>
            (Binding {
                ident: id,
                eq_token: eq,
                ty: ty,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("associated type binding")
        }
    }

    impl Path {
        named!(pub parse_mod_style -> Self, do_parse!(
            colon: option!(punct!(::)) >>
            segments: call!(Punctuated::parse_separated_nonempty_with,
                            mod_style_path_segment) >>
            (Path {
                leading_colon: colon,
                segments: segments,
            })
        ));
    }

    named!(mod_style_path_segment -> PathSegment, alt!(
        syn!(Ident) => { Into::into }
        |
        keyword!(super) => { Into::into }
        |
        keyword!(self) => { Into::into }
        |
        keyword!(Self) => { Into::into }
        |
        keyword!(crate) => { Into::into }
    ));

    named!(pub qpath -> (Option<QSelf>, Path), alt!(
        map!(syn!(Path), |p| (None, p))
        |
        do_parse!(
            lt: punct!(<) >>
            this: syn!(Type) >>
            path: option!(tuple!(keyword!(as), syn!(Path))) >>
            gt: punct!(>) >>
            colon2: punct!(::) >>
            rest: call!(Punctuated::parse_separated_nonempty) >>
            ({
                let (pos, as_, path) = match path {
                    Some((as_, mut path)) => {
                        let pos = path.segments.len();
                        path.segments.push_punct(colon2);
                        path.segments.extend(rest.into_pairs());
                        (pos, Some(as_), path)
                    }
                    None => {
                        (0, None, Path {
                            leading_colon: Some(colon2),
                            segments: rest,
                        })
                    }
                };
                (Some(QSelf {
                    lt_token: lt,
                    ty: Box::new(this),
                    position: pos,
                    as_token: as_,
                    gt_token: gt,
                }), path)
            })
        )
        |
        map!(keyword!(self), |s| (None, s.into()))
    ));

    named!(pub ty_no_eq_after -> Type, do_parse!(
        ty: syn!(Type) >>
        not!(punct!(=)) >>
        (ty)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{ToTokens, Tokens};

    impl ToTokens for Path {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.leading_colon.to_tokens(tokens);
            self.segments.to_tokens(tokens);
        }
    }

    impl ToTokens for PathSegment {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
            self.arguments.to_tokens(tokens);
        }
    }

    impl ToTokens for PathArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                PathArguments::None => {}
                PathArguments::AngleBracketed(ref arguments) => {
                    arguments.to_tokens(tokens);
                }
                PathArguments::Parenthesized(ref arguments) => {
                    arguments.to_tokens(tokens);
                }
            }
        }
    }

    impl ToTokens for GenericArgument {
        #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
                GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
                GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
                GenericArgument::Const(ref e) => match *e {
                    Expr::Lit(_) => e.to_tokens(tokens),

                    // NOTE: We should probably support parsing blocks with only
                    // expressions in them without the full feature for const
                    // generics.
                    #[cfg(feature = "full")]
                    Expr::Block(_) => e.to_tokens(tokens),

                    // ERROR CORRECTION: Add braces to make sure that the
                    // generated code is valid.
                    _ => token::Brace::default().surround(tokens, |tokens| {
                        e.to_tokens(tokens);
                    }),
                },
            }
        }
    }

    impl ToTokens for AngleBracketedGenericArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.colon2_token.to_tokens(tokens);
            self.lt_token.to_tokens(tokens);
            self.args.to_tokens(tokens);
            self.gt_token.to_tokens(tokens);
        }
    }

    impl ToTokens for Binding {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
            self.eq_token.to_tokens(tokens);
            self.ty.to_tokens(tokens);
        }
    }

    impl ToTokens for ParenthesizedGenericArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.paren_token.surround(tokens, |tokens| {
                self.inputs.to_tokens(tokens);
            });
            self.output.to_tokens(tokens);
        }
    }
}
<commit_msg>Documentation of path generics<commit_after>// Copyright 2018 Syn Developers
//
// 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.

use punctuated::Punctuated;
use super::*;

ast_struct! {
    /// A "Path" is essentially Rust's notion of a name.
    ///
    /// It's represented as a sequence of identifiers,
    /// along with a bunch of supporting information.
    ///
    /// E.g. `std::cmp::PartialEq`
    pub struct Path {
        /// A `::foo` path, is relative to the crate root rather than current
        /// module (like paths in an import).
        pub leading_colon: Option<Token![::]>,
        /// The segments in the path: the things separated by `::`.
        pub segments: Punctuated<PathSegment, Token![::]>,
    }
}

impl Path {
    pub fn global(&self) -> bool {
        self.leading_colon.is_some()
    }
}

#[cfg(feature = "printing")]
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);

impl<T> From<T> for Path
where
    T: Into<PathSegment>,
{
    fn from(segment: T) -> Self {
        let mut path = Path {
            leading_colon: None,
            segments: Punctuated::new(),
        };
        path.segments.push_value(segment.into());
        path
    }
}

ast_struct! {
    /// A segment of a path: an identifier, an optional lifetime, and a set of types.
    ///
    /// E.g. `std`, `String` or `Box<T>`
    pub struct PathSegment {
        /// The identifier portion of this path segment.
        pub ident: Ident,
        /// Type/lifetime arguments attached to this path. They come in
        /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
        /// this is more than just simple syntactic sugar; the use of
        /// parens affects the region binding rules, so we preserve the
        /// distinction.
        pub arguments: PathArguments,
    }
}

impl<T> From<T> for PathSegment
where
    T: Into<Ident>,
{
    fn from(ident: T) -> Self {
        PathSegment {
            ident: ident.into(),
            arguments: PathArguments::None,
        }
    }
}

ast_enum! {
    /// Bracketed or parenthesized arguments of a path segment.
    ///
    /// E.g. `<K, V>` as in `HashMap<K, V>` or `(A, B) -> C` as in `Fn(A, B) -> C`
    pub enum PathArguments {
        None,
        /// The `<'a, T>` in `std::slice::iter<'a, T>`.
        AngleBracketed(AngleBracketedGenericArguments),
        /// The `(A, B)` and `C` in `Fn(A, B) -> C`.
        Parenthesized(ParenthesizedGenericArguments),
    }
}

impl Default for PathArguments {
    fn default() -> Self {
        PathArguments::None
    }
}

impl PathArguments {
    pub fn is_empty(&self) -> bool {
        match *self {
            PathArguments::None => true,
            PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
            PathArguments::Parenthesized(_) => false,
        }
    }
}

ast_enum! {
    /// A individual generic argument, like `'a`, `T`, or `Item = T`.
    pub enum GenericArgument {
        /// A lifetime argument.
        Lifetime(Lifetime),
        /// A type argument.
        Type(Type),
        /// A binding (equality constraint) on an associated type.
        ///
        /// E.g. in `Iterator<Item = u8>`.
        Binding(Binding),
        /// A const expression. Must be inside of a block.
        ///
        /// NOTE: Identity expressions are represented as Type arguments, as
        /// they are indistinguishable syntactically.
        Const(Expr),
    }
}

ast_struct! {
    /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
    /// V>`.
    pub struct AngleBracketedGenericArguments {
        pub colon2_token: Option<Token![::]>,
        pub lt_token: Token![<],
        pub args: Punctuated<GenericArgument, Token![,]>,
        pub gt_token: Token![>],
    }
}

ast_struct! {
    /// A binding (equality constraint) on an associated type: `Item = u8`.
    pub struct Binding {
        pub ident: Ident,
        pub eq_token: Token![=],
        pub ty: Type,
    }
}

ast_struct! {
    /// Arguments of a function path segment: the `(A, B)` and `C` in `Fn(A,B)
    /// -> C`.
    pub struct ParenthesizedGenericArguments {
        pub paren_token: token::Paren,
        /// `(A, B)`
        pub inputs: Punctuated<Type, Token![,]>,
        /// `C`
        pub output: ReturnType,
    }
}

ast_struct! {
    /// The explicit Self type in a qualified path: the `T` in `<T as
    /// Display>::fmt`
    ///
    /// The actual path, including the trait and the associated item, is stored
    /// separately. The `position` field represents the index of the associated
    /// item qualified with this Self type.
    ///
    /// ```text
    /// <Vec<T> as a::b::Trait>::AssociatedItem
    ///  ^~~~~~    ~~~~~~~~~~~~~~^
    ///  ty        position = 3
    ///
    /// <Vec<T>>::AssociatedItem
    ///  ^~~~~~   ^
    ///  ty       position = 0
    /// ```
    pub struct QSelf {
        pub lt_token: Token![<],
        pub ty: Box<Type>,
        pub position: usize,
        pub as_token: Option<Token![as]>,
        pub gt_token: Token![>],
    }
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use synom::Synom;

    impl Synom for Path {
        named!(parse -> Self, do_parse!(
            colon: option!(punct!(::)) >>
            segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
            cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn"), epsilon!()) >>
            (Path {
                leading_colon: colon,
                segments: segments,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("path")
        }
    }

    #[cfg(not(feature = "full"))]
    impl Synom for GenericArgument {
        named!(parse -> Self, alt!(
            call!(ty_no_eq_after) => { GenericArgument::Type }
            |
            syn!(Lifetime) => { GenericArgument::Lifetime }
            |
            syn!(Binding) => { GenericArgument::Binding }
        ));
    }

    #[cfg(feature = "full")]
    impl Synom for GenericArgument {
        named!(parse -> Self, alt!(
            call!(ty_no_eq_after) => { GenericArgument::Type }
            |
            syn!(Lifetime) => { GenericArgument::Lifetime }
            |
            syn!(Binding) => { GenericArgument::Binding }
            |
            syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
            |
            syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
        ));

        fn description() -> Option<&'static str> {
            Some("generic argument")
        }
    }

    impl Synom for AngleBracketedGenericArguments {
        named!(parse -> Self, do_parse!(
            colon2: option!(punct!(::)) >>
            lt: punct!(<) >>
            args: call!(Punctuated::parse_terminated) >>
            gt: punct!(>) >>
            (AngleBracketedGenericArguments {
                colon2_token: colon2,
                lt_token: lt,
                args: args,
                gt_token: gt,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("angle bracketed generic arguments")
        }
    }

    impl Synom for ParenthesizedGenericArguments {
        named!(parse -> Self, do_parse!(
            data: parens!(Punctuated::parse_terminated) >>
            output: syn!(ReturnType) >>
            (ParenthesizedGenericArguments {
                paren_token: data.0,
                inputs: data.1,
                output: output,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
        }
    }

    impl Synom for PathSegment {
        named!(parse -> Self, alt!(
            do_parse!(
                ident: syn!(Ident) >>
                arguments: syn!(AngleBracketedGenericArguments) >>
                (PathSegment {
                    ident: ident,
                    arguments: PathArguments::AngleBracketed(arguments),
                })
            )
            |
            mod_style_path_segment
        ));

        fn description() -> Option<&'static str> {
            Some("path segment")
        }
    }

    impl Synom for Binding {
        named!(parse -> Self, do_parse!(
            id: syn!(Ident) >>
            eq: punct!(=) >>
            ty: syn!(Type) >>
            (Binding {
                ident: id,
                eq_token: eq,
                ty: ty,
            })
        ));

        fn description() -> Option<&'static str> {
            Some("associated type binding")
        }
    }

    impl Path {
        named!(pub parse_mod_style -> Self, do_parse!(
            colon: option!(punct!(::)) >>
            segments: call!(Punctuated::parse_separated_nonempty_with,
                            mod_style_path_segment) >>
            (Path {
                leading_colon: colon,
                segments: segments,
            })
        ));
    }

    named!(mod_style_path_segment -> PathSegment, alt!(
        syn!(Ident) => { Into::into }
        |
        keyword!(super) => { Into::into }
        |
        keyword!(self) => { Into::into }
        |
        keyword!(Self) => { Into::into }
        |
        keyword!(crate) => { Into::into }
    ));

    named!(pub qpath -> (Option<QSelf>, Path), alt!(
        map!(syn!(Path), |p| (None, p))
        |
        do_parse!(
            lt: punct!(<) >>
            this: syn!(Type) >>
            path: option!(tuple!(keyword!(as), syn!(Path))) >>
            gt: punct!(>) >>
            colon2: punct!(::) >>
            rest: call!(Punctuated::parse_separated_nonempty) >>
            ({
                let (pos, as_, path) = match path {
                    Some((as_, mut path)) => {
                        let pos = path.segments.len();
                        path.segments.push_punct(colon2);
                        path.segments.extend(rest.into_pairs());
                        (pos, Some(as_), path)
                    }
                    None => {
                        (0, None, Path {
                            leading_colon: Some(colon2),
                            segments: rest,
                        })
                    }
                };
                (Some(QSelf {
                    lt_token: lt,
                    ty: Box::new(this),
                    position: pos,
                    as_token: as_,
                    gt_token: gt,
                }), path)
            })
        )
        |
        map!(keyword!(self), |s| (None, s.into()))
    ));

    named!(pub ty_no_eq_after -> Type, do_parse!(
        ty: syn!(Type) >>
        not!(punct!(=)) >>
        (ty)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{ToTokens, Tokens};

    impl ToTokens for Path {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.leading_colon.to_tokens(tokens);
            self.segments.to_tokens(tokens);
        }
    }

    impl ToTokens for PathSegment {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
            self.arguments.to_tokens(tokens);
        }
    }

    impl ToTokens for PathArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                PathArguments::None => {}
                PathArguments::AngleBracketed(ref arguments) => {
                    arguments.to_tokens(tokens);
                }
                PathArguments::Parenthesized(ref arguments) => {
                    arguments.to_tokens(tokens);
                }
            }
        }
    }

    impl ToTokens for GenericArgument {
        #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
                GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
                GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
                GenericArgument::Const(ref e) => match *e {
                    Expr::Lit(_) => e.to_tokens(tokens),

                    // NOTE: We should probably support parsing blocks with only
                    // expressions in them without the full feature for const
                    // generics.
                    #[cfg(feature = "full")]
                    Expr::Block(_) => e.to_tokens(tokens),

                    // ERROR CORRECTION: Add braces to make sure that the
                    // generated code is valid.
                    _ => token::Brace::default().surround(tokens, |tokens| {
                        e.to_tokens(tokens);
                    }),
                },
            }
        }
    }

    impl ToTokens for AngleBracketedGenericArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.colon2_token.to_tokens(tokens);
            self.lt_token.to_tokens(tokens);
            self.args.to_tokens(tokens);
            self.gt_token.to_tokens(tokens);
        }
    }

    impl ToTokens for Binding {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
            self.eq_token.to_tokens(tokens);
            self.ty.to_tokens(tokens);
        }
    }

    impl ToTokens for ParenthesizedGenericArguments {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.paren_token.surround(tokens, |tokens| {
                self.inputs.to_tokens(tokens);
            });
            self.output.to_tokens(tokens);
        }
    }
}
<|endoftext|>


<commit_before>use chrono::{DateTime, Datelike, Local};
use conf::Conf;
use error::{Error, Result, ResultContext};
use files;
use markdown;
use std::fs::File;
use std::io::{Read, BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;
use std::str;
use std::string::String;
use tera::{Tera, Context};
use toml;

const POST_META_MARK: &str = "%%%\n";

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum PostKind{
    Article,
    Page,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PostMeta {
    pub title: String,
    pub link: String,
    pub url: String,
    pub ts: DateTime<Local>,
    pub kind: PostKind,
}

const DEFAULT_TITLE: &str = "Default Title";
const DEFAULT_LINK: &str = "default-link";
const DEFAULT_ARTICLE_URL: &str = "/{{ year }}/{{ month }}/{{ day }}/{{ link }}.html";
const DEFAULT_PAGE_URL: &str = "/{{ link }}.html";

impl Default for PostMeta {
    fn default() -> Self {
        PostMeta {
            title: DEFAULT_TITLE.to_string(),
            link: DEFAULT_LINK.to_string(),
            url: DEFAULT_ARTICLE_URL.to_string(),
            ts: Local::now(),
            kind: PostKind::Article,
        }
    }
}

#[derive(Serialize, Debug)]
pub struct Post {
    pub meta: PostMeta,
    pub content: Vec<u8>,
}

impl Default for Post {
    fn default() -> Self {
        Post {
            meta: PostMeta::default(),
            content: vec![],
        }
    }
}

impl Deref for Post {
    type Target = PostMeta;
    fn deref(&self) -> &PostMeta {
        &self.meta
    }
}

impl Post {
    pub fn new() -> Self {
        Post::default()
    }

    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Option<Self>> {
        let mut reader = File::open(&path).map(|f| BufReader::new(f))
            .context(format!("error opening {:?}", path.as_ref()))?;

        let mut meta = "".to_string();
        let mut line = "".to_string();
        loop {
            match reader.read_line(&mut line) {
                Ok(n) if n > 0 && &line != POST_META_MARK => {
                    meta += &line;
                    line.clear();
                },
                Ok(n) if n > 0 => break,
                _ => return Ok(None),
            }
        }

        let meta: PostMeta = toml::from_str(&meta)
            .context(format!("error parsing metadata of {:?}", path.as_ref()))?;
        // XXX maybe add more metadata sanity check here
        // as later we'll lose the corresponding file path
        // when generating it
        if meta.url.len() == 0 {
            return Err(Error::new(format!("output URL of post {:?} is 0", path.as_ref())));
        }

        let mut content = vec![];
        reader.read_to_end(&mut content)
              .context(format!("error reading content from {:?}", path.as_ref()))?;

        let content = match path.as_ref().extension().and_then(|s| s.to_str()) {
            Some("md") | Some("markdown") =>
                markdown::markdown_to_html(str::from_utf8(&content)?)?.into_bytes(),
            _ =>
                content,
        };

        Ok(Some(Post { meta, content }))
    }

    pub fn url(&self) -> Result<String> {
        let mut ctx = Context::new();
        ctx.add("year", &self.ts.year());
        ctx.add("month", &self.ts.month());
        ctx.add("day", &self.ts.day());
        ctx.add("link", &self.link);
        Tera::one_off(&self.url, &ctx, false)
            .map_err(|e| Error::from(e))
    }
}

pub fn create_post<P: AsRef<Path>>(path: P, conf: Conf, kind: PostKind) -> Result<()> {
    let link = match path.as_ref().file_stem().and_then(|s| s.to_str()) {
        None => return Err(Error::new("need specify link of post".to_string())),
        Some(stem) => stem.to_string(),
    };

    let mut post = Post::new();
    post.meta.url = match &kind {
        &PostKind::Article => DEFAULT_ARTICLE_URL.to_string(),
        &PostKind::Page => DEFAULT_PAGE_URL.to_string(),
    };
    post.meta.kind = kind;
    post.meta.link = link.to_string();

    let content = toml::to_string(&post.meta)? + POST_META_MARK;
    files::fwrite(&path, content.as_bytes(), conf.force.unwrap_or(false))?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::std::{env, fs};
    use ::std::fs::{OpenOptions, File, remove_file};
    use ::std::io::Write;
    use ::std::path::PathBuf;

    fn assert_create(kind: PostKind) {
        let mut c = Conf::default();
        c.force = Some(true);
        let just_now = Local::now();

        let path = env::temp_dir().join("x.md");
        create_post(&path, c.clone(), kind.clone()).unwrap();

        fs::OpenOptions::new().append(true)
            .open(&path).unwrap()
            .write(b"XXX").unwrap();

        let post = Post::from_file(&path).unwrap().unwrap();

        assert!(just_now < post.ts && post.ts < Local::now());
        assert!(&post.link == "x");
        assert!(post.kind == kind);
        assert!(post.content == markdown::markdown_to_html("XXX").unwrap().into_bytes());

        fs::remove_file(path).unwrap();
    }

    #[test]
    fn test_create_post() {
        assert_create(PostKind::Article);
        assert_create(PostKind::Page);
    }

    fn temp_src() -> (PathBuf, File) {
        let path = env::temp_dir().join("y.md");
        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .open(&path).unwrap();
        (path, file)
    }

    #[test]
    fn test_post_with_bad_meta() {
        // no meta at all
        let (path, _) = temp_src();
        let post = Post::from_file(&path);
        assert!(post.is_ok());
        assert!(post.unwrap().is_none());
        remove_file(path).unwrap();

        // only a meta ending mark
        let (path, mut file) = temp_src();
        file.write(POST_META_MARK.as_bytes()).unwrap();
        let post = Post::from_file(&path);
        assert!(post.is_err());
        remove_file(path).unwrap();

        // zero-length URL
        let (path, mut file) = temp_src();
        file.write(b"title = \"xxx\"\n\
                     link = \"yyy\"\n\
                     url = \"\"\n\
                     ts = \"2017-12-04T20:23:37.463860-05:00\"\n\
                     kind = \"Page\"\n")
            .unwrap();
        file.write(POST_META_MARK.as_bytes()).unwrap();

        let post = Post::from_file(&path);
        assert!(post.is_err());

        remove_file(path).unwrap();
    }
}
<commit_msg>Force leading zero of day and month in output URL<commit_after>use chrono::{DateTime, Datelike, Local};
use conf::Conf;
use error::{Error, Result, ResultContext};
use files;
use markdown;
use std::fs::File;
use std::io::{Read, BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;
use std::str;
use std::string::String;
use tera::{Tera, Context};
use toml;

const POST_META_MARK: &str = "%%%\n";

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum PostKind{
    Article,
    Page,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PostMeta {
    pub title: String,
    pub link: String,
    pub url: String,
    pub ts: DateTime<Local>,
    pub kind: PostKind,
}

const DEFAULT_TITLE: &str = "Default Title";
const DEFAULT_LINK: &str = "default-link";
const DEFAULT_ARTICLE_URL: &str = "/{{ year }}/{{ month }}/{{ day }}/{{ link }}.html";
const DEFAULT_PAGE_URL: &str = "/{{ link }}.html";

impl Default for PostMeta {
    fn default() -> Self {
        PostMeta {
            title: DEFAULT_TITLE.to_string(),
            link: DEFAULT_LINK.to_string(),
            url: DEFAULT_ARTICLE_URL.to_string(),
            ts: Local::now(),
            kind: PostKind::Article,
        }
    }
}

#[derive(Serialize, Debug)]
pub struct Post {
    pub meta: PostMeta,
    pub content: Vec<u8>,
}

impl Default for Post {
    fn default() -> Self {
        Post {
            meta: PostMeta::default(),
            content: vec![],
        }
    }
}

impl Deref for Post {
    type Target = PostMeta;
    fn deref(&self) -> &PostMeta {
        &self.meta
    }
}

impl Post {
    pub fn new() -> Self {
        Post::default()
    }

    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Option<Self>> {
        let mut reader = File::open(&path).map(|f| BufReader::new(f))
            .context(format!("error opening {:?}", path.as_ref()))?;

        let mut meta = "".to_string();
        let mut line = "".to_string();
        loop {
            match reader.read_line(&mut line) {
                Ok(n) if n > 0 && &line != POST_META_MARK => {
                    meta += &line;
                    line.clear();
                },
                Ok(n) if n > 0 => break,
                _ => return Ok(None),
            }
        }

        let meta: PostMeta = toml::from_str(&meta)
            .context(format!("error parsing metadata of {:?}", path.as_ref()))?;
        // XXX maybe add more metadata sanity check here
        // as later we'll lose the corresponding file path
        // when generating it
        if meta.url.len() == 0 {
            return Err(Error::new(format!("output URL of post {:?} is 0", path.as_ref())));
        }

        let mut content = vec![];
        reader.read_to_end(&mut content)
              .context(format!("error reading content from {:?}", path.as_ref()))?;

        let content = match path.as_ref().extension().and_then(|s| s.to_str()) {
            Some("md") | Some("markdown") =>
                markdown::markdown_to_html(str::from_utf8(&content)?)?.into_bytes(),
            _ =>
                content,
        };

        Ok(Some(Post { meta, content }))
    }

    pub fn url(&self) -> Result<String> {
        let mut ctx = Context::new();
        // call format to make sure the leading zero exists
        ctx.add("year", &self.ts.year());
        ctx.add("month", &self.ts.format("%m").to_string());
        ctx.add("day", &self.ts.format("%d").to_string());
        ctx.add("link", &self.link);
        Tera::one_off(&self.url, &ctx, false)
            .map_err(|e| Error::from(e))
    }
}

pub fn create_post<P: AsRef<Path>>(path: P, conf: Conf, kind: PostKind) -> Result<()> {
    let link = match path.as_ref().file_stem().and_then(|s| s.to_str()) {
        None => return Err(Error::new("need specify link of post".to_string())),
        Some(stem) => stem.to_string(),
    };

    let mut post = Post::new();
    post.meta.url = match &kind {
        &PostKind::Article => DEFAULT_ARTICLE_URL.to_string(),
        &PostKind::Page => DEFAULT_PAGE_URL.to_string(),
    };
    post.meta.kind = kind;
    post.meta.link = link.to_string();

    let content = toml::to_string(&post.meta)? + POST_META_MARK;
    files::fwrite(&path, content.as_bytes(), conf.force.unwrap_or(false))?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::std::{env, fs};
    use ::std::fs::{OpenOptions, File, remove_file};
    use ::std::io::Write;
    use ::std::path::PathBuf;

    fn assert_create(kind: PostKind) {
        let mut c = Conf::default();
        c.force = Some(true);
        let just_now = Local::now();

        let path = env::temp_dir().join("x.md");
        create_post(&path, c.clone(), kind.clone()).unwrap();

        fs::OpenOptions::new().append(true)
            .open(&path).unwrap()
            .write(b"XXX").unwrap();

        let post = Post::from_file(&path).unwrap().unwrap();

        assert!(just_now < post.ts && post.ts < Local::now());
        assert!(&post.link == "x");
        assert!(post.kind == kind);
        assert!(post.content == markdown::markdown_to_html("XXX").unwrap().into_bytes());

        fs::remove_file(path).unwrap();
    }

    #[test]
    fn test_create_post() {
        assert_create(PostKind::Article);
        assert_create(PostKind::Page);
    }

    fn temp_src() -> (PathBuf, File) {
        let path = env::temp_dir().join("y.md");
        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .open(&path).unwrap();
        (path, file)
    }

    #[test]
    fn test_post_with_bad_meta() {
        // no meta at all
        let (path, _) = temp_src();
        let post = Post::from_file(&path);
        assert!(post.is_ok());
        assert!(post.unwrap().is_none());
        remove_file(path).unwrap();

        // only a meta ending mark
        let (path, mut file) = temp_src();
        file.write(POST_META_MARK.as_bytes()).unwrap();
        let post = Post::from_file(&path);
        assert!(post.is_err());
        remove_file(path).unwrap();

        // zero-length URL
        let (path, mut file) = temp_src();
        file.write(b"title = \"xxx\"\n\
                     link = \"yyy\"\n\
                     url = \"\"\n\
                     ts = \"2017-12-04T20:23:37.463860-05:00\"\n\
                     kind = \"Page\"\n")
            .unwrap();
        file.write(POST_META_MARK.as_bytes()).unwrap();

        let post = Post::from_file(&path);
        assert!(post.is_err());

        remove_file(path).unwrap();
    }
}
<|endoftext|>


<commit_before>use super::base::{BaseRand, RAND_MAX};
use super::flag::Flag;
use std::f64::consts::PI;

pub enum Style {
    Normal,
    Pythagoras,
    Gauss(u32),
    BMgauss(f64, f64, bool),
    Marsaglia(f64, f64, f64, bool),
}

pub enum JumpStyle {
    Static,
    Next,
    Jump,
    DoubleJump,
}

/// Rand is random number solver
/// Please don't assume that the fields are any type
pub struct Rand {
    base: Vec<BaseRand>,
    handle: Option<usize>,
    style: Style,
    jump: JumpStyle,
}

impl Rand {
    /// a lazy way to get a random solver
    pub fn new() -> Self {
        Rand {
            base: vec![BaseRand::new()],
            handle: Some(0),
            style: Style::Normal,
            jump: JumpStyle::Static,
        }
    }

    pub fn new_multibase(mul: usize) -> Self {
        let mut foo = Rand::new();
        let mut bar: Vec<usize> = Vec::new();
        for _ in 0..mul {
            bar.push(foo.base());
        }
        for _ in 1..mul {
            foo.new_base();
        }
        foo.srand(bar);
        foo.jump_style(JumpStyle::DoubleJump);
        foo
    }

    pub fn set_style(&mut self, style: Style) {
        self.style = style;
    }

    pub fn srand(&mut self, seed: Vec<usize>) {
        for i in 0..self.base.len() {
            self._srand(i, seed[i]);
        }
    }

    pub fn _srand(&mut self, handle: usize, seed: usize) {
        self.base[handle].srand(seed);
    }

    pub fn lazy_srand(&mut self) {
        for i in 0..self.base.len() {
            self.base[i].lazy_srand();
        }
    }

    pub fn set_function(&mut self, handle: usize, style: &str) {
        self.base[handle].set_function(style);
    }

    /// get a random number
    pub fn rand(&mut self) -> f64 {
        match self.style {
            Style::Normal => self.base() as f64,
            Style::Pythagoras => pythagoras(self),
            Style::Gauss(nsum) => gauss(self, nsum),
            Style::BMgauss(u, v, phase) => {
                let mut _u = u;
                let mut _v = v;
                let mut _phase = phase;
                let _return = bmgauss(self, &mut _u, &mut _v, &mut _phase);
                self.style = Style::BMgauss(_u, _v, _phase);
                _return
            }
            Style::Marsaglia(v1, v2, s, phase) => {
                let mut _v1 = v1;
                let mut _v2 = v2;
                let mut _s = s;
                let mut _phase = phase;
                let _return = marsaglia(self, &mut _v1, &mut _v2, &mut _s, &mut _phase);
                self.style = Style::Marsaglia(_v1, _v2, _s, _phase);
                _return
            }
        }
    }

    pub fn base(&mut self) -> usize {
        match self.handle {
            Some(e) => self.base[e].rand(),
            _ => {
                let mut _base = 0;
                for i in 0..self.base.len() {
                    _base += self.base[i].rand() / self.base.len();
                }
                _base
            }
        }
    }

    pub fn new_base(&mut self) {
        self.base.push(BaseRand::new());
    }

    pub fn del_base(&mut self) -> Option<BaseRand> {
        self.base.pop()
    }

    pub fn jump_style(&mut self, style: JumpStyle) {
        self.jump = style;
    }

    pub fn jump(&mut self) {
        match self.handle {
            Some(e) => {
                match self.jump {
                    JumpStyle::Next => self.handle = Some((e + 1) % self.base.len()),
                    JumpStyle::Jump => {
                        let _jump = self.base();
                        self.handle = Some(_jump % self.base.len());
                    }
                    JumpStyle::DoubleJump => {
                        let mut _gap = Flag::new();
                        _gap._on(self.base());
                        while _gap.is_on() {
                            let _jump = self.base();
                            self.handle = Some(_jump % self.base.len());
                            _gap.down();
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    /// get a random number for multibase
    pub fn multirand(&mut self) -> f64 {
        let _return = self.rand();
        self.jump();
        _return
    }
}

fn base64(base: &mut Rand, base_offset: usize, max_offset: usize) -> f64 {
    (base.base() + base_offset) as f64 / (RAND_MAX + base_offset + max_offset) as f64
}

fn gauss(base: &mut Rand, nsum: u32) -> f64 {
    let mut x = 0.0;
    for _ in 0..nsum {
        x += base64(base, 0, 0);
    }
    x -= nsum as f64 / 2.0;
    x /= (nsum as f64 / 12.0).sqrt();
    x
}

fn bmgauss(base: &mut Rand, u: &mut f64, v: &mut f64, phase: &mut bool) -> f64 {
    if *phase {
        *phase = false;
        *u = base64(base, 1, 1);
        *v = base64(base, 0, 1);
        (-2.0 * (*u).log10()).sqrt() * (2.0 * PI * (*v)).sin()
    } else {
        *phase = true;
        (-2.0 * (*u).log10()).sqrt() * (2.0 * PI * (*v)).cos()
    }
}

fn marsaglia(base: &mut Rand, v1: &mut f64, v2: &mut f64, s: &mut f64, phase: &mut bool) -> f64 {
    if *phase {
        *phase = false;
        let u1 = base64(base, 0, 0);
        let u2 = base64(base, 0, 0);
        *v1 = 2.0 * u1 - 1.0;
        *v2 = 2.0 * u2 - 1.0;
        *v1 * (-2.0 * (*s).log10() / (*s)).sqrt()
    } else {
        *phase = true;
        *v2 * (-2.0 * (*s).log10() / (*s)).sqrt()
    }
}

fn pythagoras(base: &mut Rand) -> f64 {
    let a = base64(base, 0, 1);
    let b = base64(base, 0, 1);
    let c_sqr = a * a + b * b;
    c_sqr.sqrt()
}
<commit_msg>Publish<commit_after>use super::base::{BaseRand, RAND_MAX};
use super::flag::Flag;
use std::f64::consts::PI;

pub enum Style {
    Normal,
    Pythagoras,
    Gauss(u32),
    BMgauss(f64, f64, bool),
    Marsaglia(f64, f64, f64, bool),
}

pub enum JumpStyle {
    Static,
    Next,
    Jump,
    DoubleJump,
}

/// Rand is random number solver
/// Please don't assume that the fields are any type
pub struct Rand {
    base: Vec<BaseRand>,
    handle: Option<usize>,
    style: Style,
    jump: JumpStyle,
}

impl Rand {
    /// a lazy way to get a random solver
    pub fn new() -> Self {
        Rand {
            base: vec![BaseRand::new()],
            handle: Some(0),
            style: Style::Normal,
            jump: JumpStyle::Static,
        }
    }

    pub fn new_multibase(mul: usize) -> Self {
        let mut foo = Rand::new();
        let mut bar: Vec<usize> = Vec::new();
        for _ in 0..mul {
            bar.push(foo.base());
        }
        for _ in 1..mul {
            foo.new_base();
        }
        foo.multisrand(bar);
        foo.jump_style(JumpStyle::DoubleJump);
        foo
    }

    pub fn set_style(&mut self, style: Style) {
        self.style = style;
    }

    pub fn srand(&mut self, handle: usize, seed: usize) {
        self.base[handle].srand(seed);
    }

    pub fn multisrand(&mut self, seed: Vec<usize>) {
        for i in 0..self.base.len() {
            self.srand(i, seed[i]);
        }
    }

    pub fn lazy_srand(&mut self) {
        for i in 0..self.base.len() {
            self.base[i].lazy_srand();
        }
    }

    pub fn set_function(&mut self, handle: usize, style: &str) {
        self.base[handle].set_function(style);
    }

    /// get a random number
    pub fn rand(&mut self) -> f64 {
        match self.style {
            Style::Normal => self.base() as f64,
            Style::Pythagoras => pythagoras(self),
            Style::Gauss(nsum) => gauss(self, nsum),
            Style::BMgauss(u, v, phase) => {
                let mut _u = u;
                let mut _v = v;
                let mut _phase = phase;
                let _return = bmgauss(self, &mut _u, &mut _v, &mut _phase);
                self.style = Style::BMgauss(_u, _v, _phase);
                _return
            }
            Style::Marsaglia(v1, v2, s, phase) => {
                let mut _v1 = v1;
                let mut _v2 = v2;
                let mut _s = s;
                let mut _phase = phase;
                let _return = marsaglia(self, &mut _v1, &mut _v2, &mut _s, &mut _phase);
                self.style = Style::Marsaglia(_v1, _v2, _s, _phase);
                _return
            }
        }
    }

    /// get a random number for multibase
    pub fn multirand(&mut self) -> f64 {
        let _return = self.rand();
        self.jump();
        _return
    }

    pub fn base(&mut self) -> usize {
        match self.handle {
            Some(e) => self.base[e].rand(),
            _ => {
                let mut _base = 0;
                for i in 0..self.base.len() {
                    _base += self.base[i].rand() / self.base.len();
                }
                _base
            }
        }
    }

    pub fn new_base(&mut self) {
        self.base.push(BaseRand::new());
    }

    pub fn del_base(&mut self) -> Option<BaseRand> {
        self.base.pop()
    }

    pub fn add_base(&mut self, base: BaseRand) {
        self.base.push(base);
    }

    pub fn jump_style(&mut self, style: JumpStyle) {
        self.jump = style;
    }

    pub fn jump(&mut self) {
        match self.handle {
            Some(e) => {
                match self.jump {
                    JumpStyle::Next => self.handle = Some((e + 1) % self.base.len()),
                    JumpStyle::Jump => {
                        let _jump = self.base();
                        self.handle = Some(_jump % self.base.len());
                    }
                    JumpStyle::DoubleJump => {
                        let mut _gap = Flag::new();
                        _gap._on(self.base());
                        while _gap.is_on() {
                            let _jump = self.base();
                            self.handle = Some(_jump % self.base.len());
                            _gap.down();
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn base64(base: &mut Rand, base_offset: usize, max_offset: usize) -> f64 {
    (base.base() + base_offset) as f64 / (RAND_MAX + base_offset + max_offset) as f64
}

fn gauss(base: &mut Rand, nsum: u32) -> f64 {
    let mut x = 0.0;
    for _ in 0..nsum {
        x += base64(base, 0, 0);
    }
    x -= nsum as f64 / 2.0;
    x /= (nsum as f64 / 12.0).sqrt();
    x
}

fn bmgauss(base: &mut Rand, u: &mut f64, v: &mut f64, phase: &mut bool) -> f64 {
    if *phase {
        *phase = false;
        *u = base64(base, 1, 1);
        *v = base64(base, 0, 1);
        (-2.0 * (*u).log10()).sqrt() * (2.0 * PI * (*v)).sin()
    } else {
        *phase = true;
        (-2.0 * (*u).log10()).sqrt() * (2.0 * PI * (*v)).cos()
    }
}

fn marsaglia(base: &mut Rand, v1: &mut f64, v2: &mut f64, s: &mut f64, phase: &mut bool) -> f64 {
    if *phase {
        *phase = false;
        let u1 = base64(base, 0, 0);
        let u2 = base64(base, 0, 0);
        *v1 = 2.0 * u1 - 1.0;
        *v2 = 2.0 * u2 - 1.0;
        *v1 * (-2.0 * (*s).log10() / (*s)).sqrt()
    } else {
        *phase = true;
        *v2 * (-2.0 * (*s).log10() / (*s)).sqrt()
    }
}

fn pythagoras(base: &mut Rand) -> f64 {
    let a = base64(base, 0, 1);
    let b = base64(base, 0, 1);
    let c_sqr = a * a + b * b;
    c_sqr.sqrt()
}
<|endoftext|>