<commit_before>use std::ops::{Index, IndexMut};
use std::default::Default;
use na::Vec2;

pub struct Grid<T> {
	data: Vec<T>,
	width: usize,
}

impl<T> Grid<T> {
	pub fn get_width(&self) -> usize {
		self.width
	}

	pub fn get_height(&self) -> usize {
		self.data.len() / self.width
	}
}

impl<T: Default+Clone> Grid<T> {
	pub fn new(width:usize, height:usize) -> Grid<T> {
		Grid{
			data: vec![Default::default(); width * height],
			width: width,
		}
	}
}

impl<T: Clone> Grid<T> {
	pub fn new_filled_with(thing: T, width: usize, height: usize) -> Grid<T> {
		Grid{
			data: vec![thing; width * height],
			width: width,
		}
	}
}

impl<T> Index<(usize, usize)> for Grid<T> {
	type Output= T;

    fn index(&self, (x, y): (usize, usize)) -> &T{
    	&self.data[x+y*self.width]
    }
}

impl<T> IndexMut<(usize, usize)> for Grid<T> {
	fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
		&mut self.data[x+y*self.width]
	}
}

pub enum Direction {
	Up,
	Down,
	Left,
	Right,
	Ur,
	Dr,
	Dl,
	Ul,
}

static ORTHOGONAL: [Direction; 4] = [Direction::Up, Direction::Down, Direction::Left, Direction::Right];
static ALL: [Direction; 8] = [Direction::Up, Direction::Down, Direction::Left, Direction::Right, Direction::Dl, Direction::Dr, Direction::Ul, Direction::Ur];

impl Direction {
	pub fn get_vec(&self) -> Vec2<isize> {
		use self::Direction::*;
		let vec = match *self {
			Up => Vec2::new(0,1),
			Down => Vec2::new(0,-1),
			Left => Vec2::new(-1,0),
			Right => Vec2::new(1,0),
			Ur => Vec2::new(1,1),
			Dr => Vec2::new(1,-1),
			Dl => Vec2::new(-1,-1),
			Ul => Vec2::new(-1,1),
		};
		vec
	}

    pub fn get_tuple(&self) -> (isize, isize) {
        use self::Direction::*;
        match *self {
            Up => (0,1),
			Down => (0,-1),
			Left => (-1,0),
			Right => (1,0),
			Ur => (1,1),
			Dr => (1,-1),
			Dl => (-1,-1),
			Ul => (-1,1),
        }
    }

	pub fn get_orthogonal_dirs() -> &'static[Direction] {
		&ORTHOGONAL
	}
    pub fn get_dirs() -> &'static[Direction] {
        &ALL
    }
}

pub enum Error {
    IndexOutOfBounds,
}
<commit_msg>tabs to spaces. I have no idea why tabs keep appearing even though my editor is set to use soft tabs.<commit_after>use std::ops::{Index, IndexMut};
use std::default::Default;
use na::Vec2;

pub struct Grid<T> {
    data: Vec<T>,
    width: usize,
}

impl<T> Grid<T> {
    pub fn get_width(&self) -> usize {
        self.width
    }

    pub fn get_height(&self) -> usize {
        self.data.len() / self.width
    }
}

impl<T: Default+Clone> Grid<T> {
    pub fn new(width:usize, height:usize) -> Grid<T> {
        Grid{
            data: vec![Default::default(); width * height],
            width: width,
        }
    }
}

impl<T: Clone> Grid<T> {
    pub fn new_filled_with(thing: T, width: usize, height: usize) -> Grid<T> {
        Grid{
            data: vec![thing; width * height],
            width: width,
        }
    }
}

impl<T> Index<(usize, usize)> for Grid<T> {
    type Output= T;

    fn index(&self, (x, y): (usize, usize)) -> &T{
        &self.data[x+y*self.width]
    }
}

impl<T> IndexMut<(usize, usize)> for Grid<T> {
    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
        &mut self.data[x+y*self.width]
    }
}

pub enum Direction {
    Up,
    Down,
    Left,
    Right,
    Ur,
    Dr,
    Dl,
    Ul,
}

static ORTHOGONAL: [Direction; 4] = [Direction::Up, Direction::Down, Direction::Left, Direction::Right];
static ALL: [Direction; 8] = [Direction::Up, Direction::Down, Direction::Left, Direction::Right, Direction::Dl, Direction::Dr, Direction::Ul, Direction::Ur];

impl Direction {
    pub fn get_vec(&self) -> Vec2<isize> {
        use self::Direction::*;
        let vec = match *self {
            Up => Vec2::new(0,1),
            Down => Vec2::new(0,-1),
            Left => Vec2::new(-1,0),
            Right => Vec2::new(1,0),
            Ur => Vec2::new(1,1),
            Dr => Vec2::new(1,-1),
            Dl => Vec2::new(-1,-1),
            Ul => Vec2::new(-1,1),
        };
        vec
    }

    pub fn get_tuple(&self) -> (isize, isize) {
        use self::Direction::*;
        match *self {
            Up => (0,1),
            Down => (0,-1),
            Left => (-1,0),
            Right => (1,0),
            Ur => (1,1),
            Dr => (1,-1),
            Dl => (-1,-1),
            Ul => (-1,1),
        }
    }

    pub fn get_orthogonal_dirs() -> &'static[Direction] {
        &ORTHOGONAL
    }
    pub fn get_dirs() -> &'static[Direction] {
        &ALL
    }
}

pub enum Error {
    IndexOutOfBounds,
}
<|endoftext|>


<commit_before>use mime::{Mime, TopLevel, SubLevel};
use image;

// XXX: Move into Piston?
pub trait AsImageFormat {
    fn parse_image_format(&self) -> Option<(Mime, image::ImageFormat)>;
}

impl AsImageFormat for Mime {
    fn parse_image_format(&self) -> Option<(Mime, image::ImageFormat)> {
        Some(match *self {
            Mime(TopLevel::Image, SubLevel::Png, _) => (self.clone(), image::ImageFormat::PNG),
            Mime(TopLevel::Image, SubLevel::Jpeg, _) => (self.clone(), image::ImageFormat::JPEG),
            Mime(TopLevel::Image, SubLevel::Gif, _) => (self.clone(), image::ImageFormat::GIF),
            Mime(_, SubLevel::Ext(ref i), _) if i == "x-icon" => (
                Mime(TopLevel::Image, SubLevel::Ext("x-icon".into()), vec![]),
                image::ImageFormat::ICO
            ),
            _ => return None
        })
    }
}
<commit_msg>More favicon mimetypes<commit_after>use mime::{Mime, TopLevel, SubLevel};
use image;

// XXX: Move into Piston?
pub trait AsImageFormat {
    fn parse_image_format(&self) -> Option<(Mime, image::ImageFormat)>;
}

impl AsImageFormat for Mime {
    fn parse_image_format(&self) -> Option<(Mime, image::ImageFormat)> {
        Some(match *self {
            Mime(TopLevel::Image, SubLevel::Png, _) => (self.clone(), image::ImageFormat::PNG),
            Mime(TopLevel::Image, SubLevel::Jpeg, _) => (self.clone(), image::ImageFormat::JPEG),
            Mime(TopLevel::Image, SubLevel::Gif, _) => (self.clone(), image::ImageFormat::GIF),
            Mime(_, SubLevel::Ext(ref i), _) if i == "x-icon" || i == "vnd.microsoft.icon" => (
                Mime(TopLevel::Image, SubLevel::Ext("x-icon".into()), vec![]),
                image::ImageFormat::ICO
            ),
            _ => return None
        })
    }
}
<|endoftext|>


<commit_before>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Applies `self` to another term with substitution and variable update, consuming them
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, rhs)
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update, consuming them in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1); // update free variables' indices from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1) // safe
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs.clone(), depth); // safe
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth) // safe
        }
    }
}

fn update_free_variables(term: &mut Term, depth: usize) {
    match *term {
        Var(i) => if i > depth {
            *term = Var(i + depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), depth) // safe
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), depth); // safe
            update_free_variables(term.rhs_ref_mut().unwrap(), depth) // safe
        }
    }
}

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};
    use combinators::i;
    use parser::parse;
    use super::{apply, abs};
    use super::Term::Var;

    #[test]
    fn applying() {
        let lhs    = parse(&"λλ42(λ13)").unwrap();
        let rhs    = parse(&"λ51").unwrap();
        let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
        assert_eq!(apply(lhs, rhs), Ok(result));

        assert_eq!(i().app(zero()).eval().unwrap(), abs(abs(Var(1))));
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<commit_msg>looks like beta-reduction is finally fixed for good<commit_after>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Applies `self` to another term with substitution and variable update, consuming them
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, rhs)
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update, consuming them in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update free variables' indices from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1) // safe
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs.clone(), depth); // safe
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth) // safe
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1) // safe
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth); // safe
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth) // safe
        }
    }
}

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};
    use combinators::i;
    use parser::parse;
    use super::{apply, abs};
    use super::Term::Var;

    #[test]
    fn applying() {
        let lhs    = parse(&"λλ42(λ13)").unwrap();
        let rhs    = parse(&"λ51").unwrap();
        let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
        assert_eq!(apply(lhs, rhs), Ok(result));

        assert_eq!(i().app(zero()).eval().unwrap(), abs(abs(Var(1))));
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<|endoftext|>


<commit_before>use std::time::{SystemTime, UNIX_EPOCH};
use chrono::{Local, TimeZone};
use chrono::Duration;

use nom::IResult::Done;
use nom;

/* For running git and html-tidy */
use std::process::Command;

use std::env;

/* For from::utf8 */
use std::str;

pub fn get_seconds() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

pub fn ts_to_date(timestamp: u64) -> String {
    Local
        .timestamp(timestamp as i64, 0)
        .format("%Y-%m-%d, %H:%M")
        .to_string()
}

pub fn sec_to_hms_string(seconds: u64) -> String {
    let hours = seconds / 3600;
    let minutes = (seconds - hours * 3600) / 60;
    let seconds = seconds - minutes * 60 - hours * 3600;
    match (hours, minutes, seconds) {
        (0, 0, 1) => String::from("1 second"),
        (0, 0, s) => format!("{} seconds", s),
        (0, 1, _) => String::from("1 minute"),
        (0, m, _) => format!("{} minutes", m),
        /* Range matching: slightly dubious feature here */
        (1, 0...4, _)   => String::from("1 hour"),
        (h, 0...4, _)   => format!("{} hours", h),
        (h, 56...59, _) => format!("{} hours", h + 1),
        (h, m, _)       => format!("{} hours and {} minutes", h, m),
    }
}

/* For parsing time in HH:MM format. */
named!(duration_hhmm(&[u8]) -> Duration,
    do_parse!(
        hour: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        tag!(":") >>
        min:  map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        /*tag!(":") >>
        sec: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>*/
        (Duration::minutes(hour * 60 + min))
    )
);

pub fn parse_hhmm_to_seconds(timestr: &str) -> Option<u64> {
    match duration_hhmm(timestr.as_bytes()) {
        Done(_, out) => Some(out.num_seconds() as u64),
        _ => None,
    }
}

pub fn set_to_trk_dir() -> bool {
    let mut path = env::current_dir().unwrap();
    loop {
        path.push(".trk");
        if path.exists() {
            path.pop();
            env::set_current_dir(&path).is_ok();
            return true;
        } else {
            path.pop();
            if !path.pop() {
                println!("Fatal: not a .trk directory (or subdirectory of one).");
                return false;
            }
        }
    }
}

pub fn git_init_trk() -> bool {
    if !set_to_trk_dir() {
        println!("Could not initialise trk internal git repo!\
                 (couldn't find upper level .trk dir).");
        return false;
    }

    let mut path = env::current_dir().unwrap();
    path.push(".trk");
    if path.exists() {
        env::set_current_dir(&path).is_ok();
    } else {
        println!("Couldn't access .trk sub directory to initialise trk internal git repo.");
        return false;
    }
    let output = Command::new("git")
           .arg("init")
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git init!");
            return false;
        }
    }
    let output = Command::new("git")
           .arg("add")
           .arg("timesheet.json")
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git init!");
            return false;
        }
    }

    /* Reset current_dir to previous value */
    path.pop();
    env::set_current_dir(&path).is_ok();
    true
}

pub fn git_commit_trk(message: &str) -> bool {
    if !set_to_trk_dir() {
        println!("Could not commit to trk internal git repo!\
                 (couldn't find upper level .trk dir).");
        return false;
    }

    let mut p = env::current_dir().unwrap();
    p.push(".trk");
    if p.exists() {
        env::set_current_dir(&p).is_ok();
    } else {
        println!("Couldn't access .trk sub directory to initialise trk internal git repo.");
        return false;
    }
    let output = Command::new("git")
           .arg("commit")
           .arg("timesheet.json")
           .arg("-m")
           .arg(message)
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git commit!");
            return false;
        }
    }

    /* Reset current_dir to previous value */
    p.pop();
    env::set_current_dir(&p).is_ok();
    true
}
pub fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git")
           .arg("config")
           .arg("user.name")
           .output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* Remove trailing newline character */
            let mut output = output.to_string();
            output
                .pop()
                .expect("Empty name in git config? Not even a newline?!?");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed. {}", output);
            None
        }
    } else {
        None
    }
}

pub fn git_commit_message(hash: &str) -> Option<String> {
    if let Ok(output) = Command::new("git")
           .arg("log")
           .arg("--format=%B")
           .arg("-n")
           .arg("1")
           .arg(hash)
           .output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            Some(output.to_string())
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git log --format=%B -n 1 <hash> failed. {}", output);
            None
        }
    } else {
        None
    }
}

pub fn format_file(filename: &str) {
    if let Ok(_) = Command::new("tidy")
           .arg("--tidy-mark")
           .arg("no")
           .arg("-i")
           .arg("-m")
           .arg(filename)
           .output() {
    } else {
        println!("tidy-html not found!");
    }
}
<commit_msg>simplify pattern matching<commit_after>use std::time::{SystemTime, UNIX_EPOCH};
use chrono::{Local, TimeZone};
use chrono::Duration;

use nom::IResult::Done;
use nom;

/* For running git and html-tidy */
use std::process::Command;

use std::env;

/* For from::utf8 */
use std::str;

pub fn get_seconds() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

pub fn ts_to_date(timestamp: u64) -> String {
    Local
        .timestamp(timestamp as i64, 0)
        .format("%Y-%m-%d, %H:%M")
        .to_string()
}

pub fn sec_to_hms_string(seconds: u64) -> String {
    let hours = seconds / 3600;
    let minutes = (seconds - hours * 3600) / 60;
    let seconds = seconds - minutes * 60 - hours * 3600;
    match (hours, minutes, seconds) {
        (0, 0, 1) => String::from("1 second"),
        (0, 0, s) => format!("{} seconds", s),
        (0, 1, _) => String::from("1 minute"),
        (0, m, _) => format!("{} minutes", m),
        /* Range matching: slightly dubious feature here */
        (1, 0...4, _)   => String::from("1 hour"),
        (h, 0...4, _)   => format!("{} hours", h),
        (h, 56...59, _) => format!("{} hours", h + 1),
        (h, m, _)       => format!("{} hours and {} minutes", h, m),
    }
}

/* For parsing time in HH:MM format. */
named!(duration_hhmm(&[u8]) -> Duration,
    do_parse!(
        hour: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        tag!(":") >>
        min:  map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        /*tag!(":") >>
        sec: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>*/
        (Duration::minutes(hour * 60 + min))
    )
);

pub fn parse_hhmm_to_seconds(timestr: &str) -> Option<u64> {
    match duration_hhmm(timestr.as_bytes()) {
        Done(_, out) => Some(out.num_seconds() as u64),
        _ => None,
    }
}

pub fn set_to_trk_dir() -> bool {
    let mut path = env::current_dir().unwrap();
    loop {
        path.push(".trk");
        if path.exists() {
            path.pop();
            env::set_current_dir(&path).is_ok();
            return true;
        } else {
            path.pop();
            if !path.pop() {
                println!("Fatal: not a .trk directory (or subdirectory of one).");
                return false;
            }
        }
    }
}

pub fn git_init_trk() -> bool {
    if !set_to_trk_dir() {
        println!("Could not initialise trk internal git repo!\
                 (couldn't find upper level .trk dir).");
        return false;
    }

    let mut path = env::current_dir().unwrap();
    path.push(".trk");
    if path.exists() {
        env::set_current_dir(&path).is_ok();
    } else {
        println!("Couldn't access .trk sub directory to initialise trk internal git repo.");
        return false;
    }
    let output = Command::new("git")
           .arg("init")
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git init!");
            return false;
        }
    }
    let output = Command::new("git")
           .arg("add")
           .arg("timesheet.json")
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git init!");
            return false;
        }
    }

    /* Reset current_dir to previous value */
    path.pop();
    env::set_current_dir(&path).is_ok();
    true
}

pub fn git_commit_trk(message: &str) -> bool {
    if !set_to_trk_dir() {
        println!("Could not commit to trk internal git repo!\
                 (couldn't find upper level .trk dir).");
        return false;
    }

    let mut p = env::current_dir().unwrap();
    p.push(".trk");
    if p.exists() {
        env::set_current_dir(&p).is_ok();
    } else {
        println!("Couldn't access .trk sub directory to initialise trk internal git repo.");
        return false;
    }
    let output = Command::new("git")
           .arg("commit")
           .arg("timesheet.json")
           .arg("-m")
           .arg(message)
           .output();
    match output {
        Ok(_) => {},
        Err(_) => {
            println!("Could not run git commit!");
            return false;
        }
    }

    /* Reset current_dir to previous value */
    p.pop();
    env::set_current_dir(&p).is_ok();
    true
}
pub fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git")
           .arg("config")
           .arg("user.name")
           .output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* Remove trailing newline character */
            let mut output = output.to_string();
            output
                .pop()
                .expect("Empty name in git config? Not even a newline?!?");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed. {}", output);
            None
        }
    } else {
        None
    }
}

pub fn git_commit_message(hash: &str) -> Option<String> {
    if let Ok(output) = Command::new("git")
           .arg("log")
           .arg("--format=%B")
           .arg("-n")
           .arg("1")
           .arg(hash)
           .output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            Some(output.to_string())
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git log --format=%B -n 1 <hash> failed. {}", output);
            None
        }
    } else {
        None
    }
}

pub fn format_file(filename: &str) {
    if Command::new("tidy")
           .arg("--tidy-mark")
           .arg("no")
           .arg("-i")
           .arg("-m")
           .arg(filename)
           .output().is_ok() {
    } else {
        println!("tidy-html not found!");
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::str::Chars;
use unicode_normalization::char::{canonical_combining_class, decompose_compatible};
use unicode_normalization::UnicodeNormalization;
use unidecode::unidecode_char;

const HYPHENS: &str = "-\u{2010}‑‒–—―−－﹘﹣";
const ASCII_UNUSUAL_WHITESPACE: &[char] = &['\t', '\r', '\n'];

pub fn is_mixed_case(s: &str) -> bool {
    let mut has_lowercase = false;
    let mut has_uppercase = false;
    let mut iter = s.chars();

    loop {
        match iter.next() {
            Some(c) => {
                if c.is_uppercase() {
                    has_uppercase = true;
                    break;
                } else if c.is_lowercase() {
                    has_lowercase = true;
                    break;
                }
            }
            None => {
                return false;
            }
        }
    }

    if has_lowercase {
        iter.any(|c| c.is_uppercase())
    } else {
        debug_assert!(has_uppercase);
        iter.any(|c| c.is_lowercase())
    }
}

#[inline]
pub fn is_combining(c: char) -> bool {
    canonical_combining_class(c) > 0
}

#[inline]
pub fn is_ascii_alphabetic(c: char) -> bool {
    match c {
        'a'...'z' => true,
        'A'...'Z' => true,
        _ => false,
    }
}

// Sadly necessary because string split gives "type of this value must be known"
// compilation error when passed a closure in some contexts
#[inline]
pub fn is_nonalphanumeric(c: char) -> bool {
    !c.is_alphanumeric()
}

#[inline]
pub fn lowercase_if_alpha(c: char) -> Option<char> {
    if c.is_uppercase() {
        c.to_lowercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn uppercase_if_alpha(c: char) -> Option<char> {
    if c.is_lowercase() {
        c.to_uppercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn transliterate(c: char) -> Chars<'static> {
    unidecode_char(c).chars()
}

#[inline]
pub fn to_ascii_letter(c: char) -> Option<char> {
    debug_assert!(c.is_uppercase(), c.to_string());
    match c {
        'A'...'Z' => Some(c),
        _ => transliterate(c)
            .next()
            .and_then(|c| c.to_uppercase().next()),
    }
}

pub fn to_ascii(s: &str) -> Cow<str> {
    if s.is_ascii() {
        Cow::Borrowed(s)
    } else {
        let mut capitalized_any = false;

        // When transliterating, allow the first character to be either
        // capitalized or lowercase, but otherwise enforce lowercase.
        // Also drop any non-alphabetic characters.
        Cow::Owned(
            s.chars()
                .flat_map(transliterate)
                .filter_map(|c| {
                    if c.is_uppercase() {
                        if !capitalized_any {
                            capitalized_any = true;
                            Some(c)
                        } else {
                            c.to_lowercase().next()
                        }
                    } else if c.is_alphabetic() {
                        Some(c)
                    } else {
                        None
                    }
                })
                .collect(),
        )
    }
}

pub fn capitalize_word(word: &str, simple: bool) -> String {
    debug_assert!(simple == word.chars().all(is_ascii_alphabetic));

    if simple {
        let bytes = word.as_bytes();
        let mut result = String::with_capacity(word.len());
        result.push(bytes[0].to_ascii_uppercase() as char);
        result.extend(bytes[1..].iter().map(|c| c.to_ascii_lowercase() as char));
        result
    } else {
        let mut capitalize_next = true;

        word.chars()
            .map(|c| {
                let result = if capitalize_next {
                    c.to_uppercase().next()
                } else {
                    c.to_lowercase().next()
                }
                .unwrap();

                // If the character doesn't have both uppercase and lowercase versions,
                // it'll be unchanged. That's a prerequisite for it being a separator.
                capitalize_next = result == c && !c.is_alphanumeric() && !is_combining(c);

                result
            })
            .collect()
    }
}

// Ideally we'd use the unicode standard `quick_check` algorithm, but Rust
// doesn't expose that for compatibility forms, and this is simpler to implement,
// though it will have more false negatives.
#[inline]
fn stable_nfkd(c: char) -> bool {
    if is_combining(c) {
        false
    } else {
        let mut stable = true;
        decompose_compatible(c, |d| stable = stable && d == c);
        stable
    }
}

#[allow(clippy::if_same_then_else)]
pub fn normalize_nfkd_hyphens_spaces(string: &str) -> Cow<str> {
    if string.is_ascii() && !string.contains(ASCII_UNUSUAL_WHITESPACE) {
        Cow::Borrowed(string)
    } else if string
        .chars()
        .all(|c| stable_nfkd(c) && (c == ' ' || !c.is_whitespace()))
    {
        Cow::Borrowed(string)
    } else {
        let string = string
            .nfkd()
            .map(|c| {
                if HYPHENS.contains(c) {
                    '-'
                } else if c.is_whitespace() {
                    ' '
                } else {
                    c
                }
            })
            .collect();

        Cow::Owned(string)
    }
}

#[derive(Debug, Clone)]
pub struct CharacterCounts {
    pub chars: u8,
    pub alpha: u8,
    pub upper: u8,
    pub ascii_alpha: u8,
    pub ascii_vowels: u8,
}

pub fn categorize_chars(word: &str) -> CharacterCounts {
    debug_assert!(word.len() <= u8::max_value() as usize);

    let mut chars = 0;
    let mut alpha = 0;
    let mut upper = 0;
    let mut ascii_alpha = 0;
    let mut ascii_vowels = 0;

    for c in word.chars() {
        match c {
            'a'...'z' => {
                if "aeiouy".contains(c) {
                    ascii_vowels += 1;
                } else {
                    ascii_alpha += 1;
                }
            }
            'A'...'Z' => {
                if "AEIOUY".contains(c) {
                    ascii_vowels += 1;
                } else {
                    ascii_alpha += 1;
                }
                upper += 1;
            }
            _ if c.is_uppercase() => {
                alpha += 1;
                upper += 1;
            }
            _ if c.is_alphabetic() => {
                alpha += 1;
            }
            _ => {
                chars += 1;
            }
        }
    }

    // Maybe skipping individual increments and doing this instead is
    // premature optimization, but why not
    ascii_alpha += ascii_vowels;
    alpha += ascii_alpha;
    chars += alpha;

    CharacterCounts {
        chars,
        alpha,
        upper,
        ascii_alpha,
        ascii_vowels,
    }
}

pub fn starts_with_consonant(word: &str) -> bool {
    word.chars()
        .nth(0)
        .filter(|c| is_ascii_alphabetic(*c) && !"aeiouAEIOU".contains(*c))
        .is_some()
}

pub fn starts_with_uppercase(word: &str) -> bool {
    word.chars().nth(0).filter(|c| c.is_uppercase()).is_some()
}

pub fn combining_chars(word: &str) -> usize {
    word.chars().filter(|c| is_combining(*c)).count()
}

pub fn has_sequential_alphas(word: &str) -> bool {
    let mut prev_alpha = false;
    for c in word.chars() {
        let alpha = c.is_alphabetic();
        if prev_alpha && alpha {
            return true;
        } else {
            prev_alpha = alpha;
        }
    }

    false
}

#[macro_export]
macro_rules! eq_or_starts_with {
    ($a:expr, $b:expr) => {{
        let mut chars_a = $a.chars().filter_map(lowercase_if_alpha);
        let mut chars_b = $b.chars().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let a = chars_a.next();
            let b = chars_b.next();

            if a.is_none() || b.is_none() {
                result = true;
                break;
            } else if a != b {
                result = false;
                break;
            }
        }

        result
    }};
}

#[macro_export]
macro_rules! eq_or_ends_with {
    ($needle:expr, $haystack:expr) => {{
        let mut n_chars = $needle.chars().rev().filter_map(lowercase_if_alpha);
        let mut h_chars = $haystack.chars().rev().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let n = n_chars.next();
            let h = h_chars.next();

            if n.is_none() {
                result = true;
                break;
            } else if n != h {
                result = false;
                break;
            }
        }

        result
    }};
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::{black_box, Bencher};

    #[test]
    fn sequential_alphas() {
        assert!(has_sequential_alphas("ab"));
        assert!(has_sequential_alphas("abc"));
        assert!(has_sequential_alphas("a.bc"));
        assert!(has_sequential_alphas("鄭a"));
        assert!(!has_sequential_alphas(""));
        assert!(!has_sequential_alphas("a"));
        assert!(!has_sequential_alphas("a.b"));
        assert!(!has_sequential_alphas("鄭.a"));
        assert!(!has_sequential_alphas("ﾟ."));
    }

    #[test]
    fn capitalization() {
        assert_eq!("A", capitalize_word("a", true));
        assert_eq!("Aa", capitalize_word("aa", true));
        assert_eq!("Aa", capitalize_word("AA", true));
    }

    #[bench]
    fn is_mixed_case_false(b: &mut Bencher) {
        b.iter(|| black_box(is_mixed_case("JOHN MACDONALD")))
    }

    #[bench]
    fn is_mixed_case_true(b: &mut Bencher) {
        b.iter(|| black_box(is_mixed_case("J. MacDonald")))
    }

    #[bench]
    fn capitalize_uppercase_word(b: &mut Bencher) {
        b.iter(|| black_box(capitalize_word("JONATHAN", true)))
    }

    #[bench]
    fn capitalize_complex_word(b: &mut Bencher) {
        b.iter(|| black_box(capitalize_word("föö-bar", false)))
    }

    #[bench]
    fn normalize_ascii(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James 'J' S. Brown MD").len()))
    }

    #[bench]
    fn normalize_nfkd_stable(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James «J» S. Brown MD").len()))
    }

    #[bench]
    fn normalize_needs_fix(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James 'J' S. Bröwn MD").len()))
    }
}
<commit_msg>Use is_nfkd_quick & move hyphen check to namecasing<commit_after>use std::borrow::Cow;
use std::str::Chars;
use unicode_normalization::char::canonical_combining_class;
use unicode_normalization::{is_nfkd_quick, IsNormalized, UnicodeNormalization};
use unidecode::unidecode_char;

const NONASCII_HYPHENS: &str = "\u{2010}‑‒–—―−－﹘﹣";

pub fn is_mixed_case(s: &str) -> bool {
    let mut has_lowercase = false;
    let mut has_uppercase = false;
    let mut iter = s.chars();

    loop {
        match iter.next() {
            Some(c) => {
                if c.is_uppercase() {
                    has_uppercase = true;
                    break;
                } else if c.is_lowercase() {
                    has_lowercase = true;
                    break;
                }
            }
            None => {
                return false;
            }
        }
    }

    if has_lowercase {
        iter.any(|c| c.is_uppercase())
    } else {
        debug_assert!(has_uppercase);
        iter.any(|c| c.is_lowercase())
    }
}

#[inline]
pub fn is_combining(c: char) -> bool {
    canonical_combining_class(c) > 0
}

#[inline]
pub fn is_ascii_alphabetic(c: char) -> bool {
    match c {
        'a'...'z' => true,
        'A'...'Z' => true,
        _ => false,
    }
}

// Sadly necessary because string split gives "type of this value must be known"
// compilation error when passed a closure in some contexts
#[inline]
pub fn is_nonalphanumeric(c: char) -> bool {
    !c.is_alphanumeric()
}

#[inline]
pub fn lowercase_if_alpha(c: char) -> Option<char> {
    if c.is_uppercase() {
        c.to_lowercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn uppercase_if_alpha(c: char) -> Option<char> {
    if c.is_lowercase() {
        c.to_uppercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn transliterate(c: char) -> Chars<'static> {
    unidecode_char(c).chars()
}

#[inline]
pub fn to_ascii_letter(c: char) -> Option<char> {
    debug_assert!(c.is_uppercase(), c.to_string());
    match c {
        'A'...'Z' => Some(c),
        _ => transliterate(c)
            .next()
            .and_then(|c| c.to_uppercase().next()),
    }
}

pub fn to_ascii(s: &str) -> Cow<str> {
    if s.is_ascii() {
        Cow::Borrowed(s)
    } else {
        let mut capitalized_any = false;

        // When transliterating, allow the first character to be either
        // capitalized or lowercase, but otherwise enforce lowercase.
        // Also drop any non-alphabetic characters.
        Cow::Owned(
            s.chars()
                .flat_map(transliterate)
                .filter_map(|c| {
                    if c.is_uppercase() {
                        if !capitalized_any {
                            capitalized_any = true;
                            Some(c)
                        } else {
                            c.to_lowercase().next()
                        }
                    } else if c.is_alphabetic() {
                        Some(c)
                    } else {
                        None
                    }
                })
                .collect(),
        )
    }
}

// Specialized for name-casing
pub fn capitalize_word(word: &str, simple: bool) -> String {
    debug_assert!(simple == word.chars().all(is_ascii_alphabetic));

    if simple {
        let bytes = word.as_bytes();
        let mut result = String::with_capacity(word.len());
        result.push(bytes[0].to_ascii_uppercase() as char);
        result.extend(bytes[1..].iter().map(|c| c.to_ascii_lowercase() as char));
        result
    } else {
        let mut capitalize_next = true;

        word.chars()
            .map(|c| {
                let result = if capitalize_next {
                    c.to_uppercase().next()
                } else {
                    c.to_lowercase().next()
                }
                .unwrap();

                // If the character doesn't have both uppercase and lowercase versions,
                // it'll be unchanged. That's a prerequisite for it being a separator.
                capitalize_next = result == c && !c.is_alphanumeric() && !is_combining(c);
                if capitalize_next && NONASCII_HYPHENS.contains(c) {
                    '-'
                } else {
                    result
                }
            })
            .collect()
    }
}

#[inline]
fn already_normalized(string: &str) -> bool {
    let mut banned_char = false;
    let normalized = is_nfkd_quick(string.chars().take_while(|&c| {
        banned_char = c.is_whitespace() && c != ' ';
        !banned_char
    }));
    normalized == IsNormalized::Yes && !banned_char
}

pub fn normalize_nfkd_hyphens_spaces(string: &str) -> Cow<str> {
    if already_normalized(string) {
        Cow::Borrowed(string)
    } else {
        let string = string
            .chars()
            .map(|c| if c.is_whitespace() { ' ' } else { c })
            .nfkd()
            .collect();

        Cow::Owned(string)
    }
}

#[derive(Debug, Clone)]
pub struct CharacterCounts {
    pub chars: u8,
    pub alpha: u8,
    pub upper: u8,
    pub ascii_alpha: u8,
    pub ascii_vowels: u8,
}

pub fn categorize_chars(word: &str) -> CharacterCounts {
    debug_assert!(word.len() <= u8::max_value() as usize);

    let mut chars = 0;
    let mut alpha = 0;
    let mut upper = 0;
    let mut ascii_alpha = 0;
    let mut ascii_vowels = 0;

    for c in word.chars() {
        match c {
            'a'...'z' => {
                if "aeiouy".contains(c) {
                    ascii_vowels += 1;
                } else {
                    ascii_alpha += 1;
                }
            }
            'A'...'Z' => {
                if "AEIOUY".contains(c) {
                    ascii_vowels += 1;
                } else {
                    ascii_alpha += 1;
                }
                upper += 1;
            }
            _ if c.is_uppercase() => {
                alpha += 1;
                upper += 1;
            }
            _ if c.is_alphabetic() => {
                alpha += 1;
            }
            _ => {
                chars += 1;
            }
        }
    }

    // Maybe skipping individual increments and doing this instead is
    // premature optimization, but why not
    ascii_alpha += ascii_vowels;
    alpha += ascii_alpha;
    chars += alpha;

    CharacterCounts {
        chars,
        alpha,
        upper,
        ascii_alpha,
        ascii_vowels,
    }
}

pub fn starts_with_consonant(word: &str) -> bool {
    word.chars()
        .nth(0)
        .filter(|c| is_ascii_alphabetic(*c) && !"aeiouAEIOU".contains(*c))
        .is_some()
}

pub fn starts_with_uppercase(word: &str) -> bool {
    word.chars().nth(0).filter(|c| c.is_uppercase()).is_some()
}

pub fn combining_chars(word: &str) -> usize {
    word.chars().filter(|c| is_combining(*c)).count()
}

pub fn has_sequential_alphas(word: &str) -> bool {
    let mut prev_alpha = false;
    for c in word.chars() {
        let alpha = c.is_alphabetic();
        if prev_alpha && alpha {
            return true;
        } else {
            prev_alpha = alpha;
        }
    }

    false
}

#[macro_export]
macro_rules! eq_or_starts_with {
    ($a:expr, $b:expr) => {{
        let mut chars_a = $a.chars().filter_map(lowercase_if_alpha);
        let mut chars_b = $b.chars().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let a = chars_a.next();
            let b = chars_b.next();

            if a.is_none() || b.is_none() {
                result = true;
                break;
            } else if a != b {
                result = false;
                break;
            }
        }

        result
    }};
}

#[macro_export]
macro_rules! eq_or_ends_with {
    ($needle:expr, $haystack:expr) => {{
        let mut n_chars = $needle.chars().rev().filter_map(lowercase_if_alpha);
        let mut h_chars = $haystack.chars().rev().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let n = n_chars.next();
            let h = h_chars.next();

            if n.is_none() {
                result = true;
                break;
            } else if n != h {
                result = false;
                break;
            }
        }

        result
    }};
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::{black_box, Bencher};

    #[test]
    fn sequential_alphas() {
        assert!(has_sequential_alphas("ab"));
        assert!(has_sequential_alphas("abc"));
        assert!(has_sequential_alphas("a.bc"));
        assert!(has_sequential_alphas("鄭a"));
        assert!(!has_sequential_alphas(""));
        assert!(!has_sequential_alphas("a"));
        assert!(!has_sequential_alphas("a.b"));
        assert!(!has_sequential_alphas("鄭.a"));
        assert!(!has_sequential_alphas("ﾟ."));
    }

    #[test]
    fn capitalization() {
        assert_eq!("A", capitalize_word("a", true));
        assert_eq!("Aa", capitalize_word("aa", true));
        assert_eq!("Aa", capitalize_word("AA", true));
    }

    #[bench]
    fn is_mixed_case_false(b: &mut Bencher) {
        b.iter(|| black_box(is_mixed_case("JOHN MACDONALD")))
    }

    #[bench]
    fn is_mixed_case_true(b: &mut Bencher) {
        b.iter(|| black_box(is_mixed_case("J. MacDonald")))
    }

    #[bench]
    fn capitalize_uppercase_word(b: &mut Bencher) {
        b.iter(|| black_box(capitalize_word("JONATHAN", true)))
    }

    #[bench]
    fn capitalize_complex_word(b: &mut Bencher) {
        b.iter(|| black_box(capitalize_word("föö-bar", false)))
    }

    #[bench]
    fn normalize_ascii(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James 'J' S. Brown MD").len()))
    }

    #[bench]
    fn normalize_nfkd_stable(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James «J» S. Brown MD").len()))
    }

    #[bench]
    fn normalize_needs_fix(b: &mut Bencher) {
        b.iter(|| black_box(normalize_nfkd_hyphens_spaces("James 'J' S. Bröwn MD").len()))
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "shim")]
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::path::Path;
use std::process::Command;
use std::str;

fn parse_config_output(output: &str, include_dirs: &mut Vec<OsString>) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| {
        if p.len() > 2 {
            Some(p.split_at(2))
        } else {
            None
        }
    });

    for (flag, val) in parts {
        match flag {
            "-I" => include_dirs.push(val.into()),
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            },
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            },
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            },
            _ => ()
        }
    }
}

#[cfg(feature = "shim")]
fn build_shim<P: AsRef<Path>>(include_dirs: &[P]) {
    let mut config = gcc::Config::new();
    for path in include_dirs.iter() {
        config.include(path);
    }
    config.flag("-Wno-deprecated-declarations").file("shim.c").compile("libgcrypt_shim.a");
}

#[cfg(not(feature = "shim"))]
fn build_shim<P: AsRef<Path>>(_include_dirs: &[P]) { }

fn main() {
    let mut include_dirs = Vec::new();

    if let Ok(lib) = env::var("LIBGCRYPT_LIB") {
        if let Some(include) = env::var_os("LIBGCRYPT_INCLUDE_DIR") {
            include_dirs.push(include);
        }

        let mode = match env::var_os("LIBGCRYPT_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-flags=-l {0}={1}", mode, lib);
    } else {
        let mut command = Command::new(env::var_os("LIBGCRYPT_CONFIG")
                .unwrap_or("libgcrypt-config".into()));
        command.arg("--cflags").arg("--libs");

        let output = command.output().unwrap();
        if !output.status.success() {
            panic!("`{:?}` did not exit successfully: {}", command, output.status);
        }
        parse_config_output(&str::from_utf8(&output.stdout).unwrap(), &mut include_dirs);
    }

    build_shim(&include_dirs);
}

<commit_msg>Tidy up build script<commit_after>#[cfg(feature = "shim")]
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::path::Path;
use std::process::Command;
use std::str;

fn parse_config_output(output: &str, include_dirs: &mut Vec<OsString>) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| {
        if p.len() > 2 {
            Some(p.split_at(2))
        } else {
            None
        }
    });

    for (flag, val) in parts {
        match flag {
            "-I" => include_dirs.push(val.into()),
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            },
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            },
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            },
            _ => ()
        }
    }
}

#[cfg(feature = "shim")]
fn build_shim<P: AsRef<Path>>(include_dirs: &[P]) {
    let mut config = gcc::Config::new();
    for path in include_dirs.iter() {
        config.include(path);
    }
    config.flag("-Wno-deprecated-declarations").file("shim.c").compile("libgcrypt_shim.a");
}

#[cfg(not(feature = "shim"))]
fn build_shim<P: AsRef<Path>>(_include_dirs: &[P]) { }

fn main() {
    let mut include_dirs = Vec::new();

    if let Ok(lib) = env::var("LIBGCRYPT_LIB") {
        if let Some(include) = env::var_os("LIBGCRYPT_INCLUDE_DIR") {
            include_dirs.push(include);
        }

        let mode = match env::var_os("LIBGCRYPT_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-flags=-l {0}={1}", mode, lib);
    } else {
        let mut command = Command::new(env::var_os("LIBGCRYPT_CONFIG")
                .unwrap_or("libgcrypt-config".into()));
        command.args(&["--cflags", "--libs"]);

        let output = command.output().unwrap();
        if !output.status.success() {
            panic!("`{:?}` did not exit successfully: {}", command, output.status);
        }
        parse_config_output(&str::from_utf8(&output.stdout).unwrap(), &mut include_dirs);
    }

    build_shim(&include_dirs);
}

<|endoftext|>


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

use regex::Regex;
use url::Url;

/// Get the base URL (domain and tld) of an URL.
/// Returns an empty string in case there are no hosts.
pub fn get_base_url(url: &str) -> String {
    let parsed_url = Url::parse(url).unwrap();
    let mut base_url = parsed_url.host_str().unwrap_or("").to_string();
    // Remove all sub-domains.
    let mut period_count = base_url.chars().filter(|&c| c == '.').count();
    while period_count > 1 {
        base_url = base_url.chars().skip_while(|&c| c != '.').skip(1).collect();
        period_count = base_url.chars().filter(|&c| c == '.').count();
    }
    base_url
}

/// Get the filename from the URL.
pub fn get_filename(url: &str) -> Option<String> {
    let parsed_url = Url::parse(url).unwrap();
    parsed_url.path_segments()
        .and_then(|segments| segments.last())
        .map(|filename| filename.to_string())
}

/// Check if the input string looks like a URL.
pub fn is_url(input: &str) -> bool {
    let regex = Regex::new(r"[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)").unwrap();
    regex.is_match(input)
}
<commit_msg>Fixed download view by decoding the filename.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use regex::Regex;
use url::Url;
use url::percent_encoding::percent_decode;

/// Get the base URL (domain and tld) of an URL.
/// Returns an empty string in case there are no hosts.
pub fn get_base_url(url: &str) -> String {
    let parsed_url = Url::parse(url).unwrap();
    let mut base_url = parsed_url.host_str().unwrap_or("").to_string();
    // Remove all sub-domains.
    let mut period_count = base_url.chars().filter(|&c| c == '.').count();
    while period_count > 1 {
        base_url = base_url.chars().skip_while(|&c| c != '.').skip(1).collect();
        period_count = base_url.chars().filter(|&c| c == '.').count();
    }
    base_url
}

/// Get the filename from the URL.
pub fn get_filename(url: &str) -> Option<String> {
    let parsed_url = Url::parse(url).unwrap();
    parsed_url.path_segments()
        .and_then(|segments| segments.last())
        .and_then(|filename| percent_decode(filename.as_bytes()).decode_utf8().ok())
        .map(|string| string.into_owned())
}

/// Check if the input string looks like a URL.
pub fn is_url(input: &str) -> bool {
    let regex = Regex::new(r"[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)").unwrap();
    regex.is_match(input)
}
<|endoftext|>


<commit_before>pub trait RangeSearch1D<K, V> {
    /// Insert key-value pair
    fn new();
    /// Search for key k
    fn insert(&mut self, key: K, val: V);
    /// Delete key k
    fn delete(&mut self, key: &K);
    /// Range search: find all keys between k1 and k2
    fn search(&self, key1: &K, key2: &K) -> Option<Vec<&K>>;
    /// Range count: number of keys between k1 and k2
    fn count(&self, key1: &K, key2: &K) -> usize;
}
<commit_msg>geometric_search: partical implement RangeSearch1D::insert/count for BST<commit_after>use super::symbol_tables::{ST, OrderedST};
use super::symbol_tables::binary_search_tree::{BST, Node};

pub trait RangeSearch1D<K, V>: OrderedST<K, V> {
    /// Insert key-value pair
    fn insert(&mut self, key: K, val: V);
    /// Range search: find all keys between k1 and k2
    fn search(&self, key1: &K, key2: &K) -> Option<Vec<&K>>;
    /// Range count: number of keys between k1 and k2
    fn count(&self, key1: &K, key2: &K) -> usize;
}

pub trait IntervalST<K: Ord, V> {
    type Iter: Iterator;
    /// create interval search tree
    fn new() -> Self;
    /// put interval-value pair into ST
    fn put(&mut self, lo: K, hi: K, val: V);
    /// value paired with given interval
    fn get(&self, lo: &K, hi: &K) -> Option<&V>;
    /// delete the given interval
    fn delete(&mut self, lo: &K, hi: &K);
    /// all intervals that intersect the given interval
    fn intersects(&self, lo: &K, hi: &K) -> Self::Iter;
}

impl<K: Ord, V> RangeSearch1D<K, V> for BST<K, V> {

    fn insert(&mut self, key: K, val: V) {
        self.put(key, val);
    }

    fn search(&self, lo: &K, hi: &K) -> Option<Vec<&K>> {
        unimplemented!()
    }

    fn count(&self, lo: &K, hi: &K) -> usize {
        if self.contains(hi) {
            self.rank(hi) - self.rank(lo) + 1
        } else {
            // number of keys < hi
            self.rank(hi) - self.rank(lo)
        }
    }
}

#[test]
fn test_range_search_1d() {
    let mut ost = BST::<char,()>::new();
    ost.insert('B', ());
    ost.insert('D', ());
    ost.insert('A', ());
    ost.insert('I', ());
    ost.insert('H', ());
    ost.insert('F', ());
    ost.insert('P', ());

    assert_eq!(ost.count(&'G', &'K'), 2);
}
<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use types::{gchar,gsize};
use types::{gpointer};

use libc;
use libc::c_char;
use std::c_str::{CString,ToCStr};
use std::kinds::marker;
use std::mem::transmute;
use std::ptr::copy_nonoverlapping_memory;
use std::slice;
use std::str;
use std::string;

pub struct UTF8Chars<'a> {
    data: *const gchar,
    lifetime: marker::ContravariantLifetime<'a>
}

impl<'a> UTF8Chars<'a> {
    pub unsafe fn wrap(ptr: *const gchar) -> UTF8Chars<'a> {
        UTF8Chars { data: ptr, lifetime: marker::ContravariantLifetime }
    }

    pub fn to_string(&self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const u8)
        }
    }
}

#[inline]
unsafe fn utf8_cont_bits(p: *const gchar) -> u32 {
    let byte = *(p as *const u8);
    debug_assert!((byte & 0xC0) == 0x80);
    (byte & 0x3F) as u32
}

impl<'a> Iterator<char> for UTF8Chars<'a> {
    fn next(&mut self) -> Option<char> {
        let p = self.data;
        let first_byte = unsafe { *(p as *const u8) };
        if first_byte == 0 {
            return None;
        }
        unsafe {
            let mut wc: u32;
            if first_byte < 0xC0 {
                debug_assert!(first_byte < 0x80);
                wc = first_byte as u32;
                self.data = p.offset(1);
            } else {
                let c1 = utf8_cont_bits(p.offset(1));
                if first_byte < 0xE0 {
                    wc = ((first_byte & 0x1F) as u32 << 6) | c1;
                    debug_assert!(wc >= 0x80);
                    self.data = p.offset(2);
                } else {
                    let c2 = utf8_cont_bits(p.offset(2));
                    if first_byte < 0xF0 {
                        wc = ((first_byte & 0x0F) as u32 << 12)
                             | (c1 << 6) | c2;
                        debug_assert!(wc >= 0x0800);
                        debug_assert!((wc & 0xF800) != 0xD800);
                        self.data = p.offset(3);
                    } else {
                        debug_assert!(first_byte <= 0xF4);
                        let c3 = utf8_cont_bits(p.offset(3));
                        wc = ((first_byte & 0x07) as u32 << 18)
                             | (c1 << 12) | (c2 << 6) | c3;
                        debug_assert!(wc >= 0x010000 && wc <= 0x10FFFF);
                        self.data = p.offset(4);
                    }
                }
            }
            Some(transmute(wc))
        }
    }
}

unsafe fn dup_to_c_str(source: *const c_char, len: uint) -> CString {
    let copy = libc::malloc(len as libc::size_t + 1) as *mut c_char;
    copy_nonoverlapping_memory(copy, source, len + 1);
    CString::new(copy as *const c_char, true)
}

impl<'a> ToCStr for UTF8Chars<'a> {

    fn to_c_str(&self) -> CString {
        unsafe { self.to_c_str_unchecked() }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        let src = self.data as *const c_char;
        let len = libc::strlen(src) as uint;
        dup_to_c_str(src, len)
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }
}

pub struct UTF8Buf {
    data: *mut gchar,
}

impl UTF8Buf {

    pub unsafe fn wrap(data: *mut gchar) -> UTF8Buf {
        UTF8Buf { data: data }
    }

    pub fn chars<'a>(&'a self) -> UTF8Chars<'a> {
        unsafe { UTF8Chars::wrap(self.data as *const gchar) }
    }

    pub fn into_collection(self) -> UTF8Str {
        unsafe {
            let len = libc::strlen(self.data as *const c_char);
            UTF8Str { buf: transmute(self), len: len as uint }
        }
    }

    #[inline]
    pub fn to_string(&self) -> String { self.chars().to_string() }

    #[inline]
    pub fn to_owned(&self) -> String { self.to_string() }

    #[inline]
    pub fn into_string(self) -> String { self.to_string() }

    #[inline]
    pub fn into_owned(self) -> String { self.to_string() }

    pub fn is_empty(&self) -> bool { unsafe { *self.data == 0 } }
}

impl Drop for UTF8Buf {
    fn drop(&mut self) {
        unsafe { ffi::g_free(self.data as gpointer) }
    }
}

impl Clone for UTF8Buf {
    fn clone(&self) -> UTF8Buf {
        unsafe {
            UTF8Buf::wrap(ffi::g_strdup(self.data as *const gchar))
        }
    }
}

impl ToCStr for UTF8Buf {

    fn to_c_str(&self) -> CString {
        self.chars().to_c_str()
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        self.chars().to_c_str_unchecked()
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }
}

pub struct UTF8Str {
    buf: UTF8Buf,
    len: uint
}

static NUL: gchar = 0;

impl UTF8Str {

    pub unsafe fn wrap(data: *mut gchar, len: uint) -> UTF8Str {
        UTF8Str { buf: UTF8Buf::wrap(data), len: len }
    }

    pub fn chars<'a>(&'a self) -> UTF8Chars<'a> { self.buf.chars() }

    pub fn to_string(&self) -> String {
        unsafe {
            string::raw::from_buf_len(self.buf.data as *const u8, self.len)
        }
    }
}

impl Clone for UTF8Str {
    fn clone(&self) -> UTF8Str {
        unsafe {
            let copy = ffi::g_malloc((self.len + 1) as gsize) as *mut gchar;
            copy_nonoverlapping_memory(copy,
                    self.buf.data as *const gchar, self.len);
            *copy.offset(self.len as int) = NUL;
            UTF8Str::wrap(copy, self.len)
        }
    }
}

impl Str for UTF8Str {
    fn as_slice<'a>(&'a self) -> &'a str {
        unsafe {
            slice::raw::buf_as_slice(
                self.buf.data as *const u8,
                self.len,
                |bytes| {
                    let s = str::raw::from_utf8(bytes);
                    transmute(s)
                })
        }
    }
}

impl StrAllocating for UTF8Str {
    fn into_string(self) -> String { self.to_string() }
}

impl Collection for UTF8Str {

    // Common sense says we should return the UTF-8 length,
    // but &str returns length in bytes, so...
    fn len(&self) -> uint { return self.len }

    fn is_empty(&self) -> bool { return self.len == 0 }
}

impl ToCStr for UTF8Str {

    fn to_c_str(&self) -> CString {
        unsafe { self.to_c_str_unchecked() }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        dup_to_c_str(self.buf.data as *const c_char, self.len)
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        self.buf.with_c_str(f)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        self.buf.with_c_str_unchecked(f)
    }
}

impl PartialEq for UTF8Str {
    fn eq(&self, other: &UTF8Str) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl Eq for UTF8Str { }

impl<S: Str> Equiv<S> for UTF8Str {
    fn equiv(&self, other: &S) -> bool {
        self.as_slice() == other.as_slice()
    }
}

pub trait WithUTF8 {
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T;
}

impl<'a> WithUTF8 for UTF8Chars<'a> {
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        f(self.data)
    }
}

impl WithUTF8 for UTF8Buf {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        f(self.data as *const gchar)
    }
}

impl WithUTF8 for UTF8Str {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.buf.with_utf8_c_str(f)
    }
}

impl<'a> WithUTF8 for &'a str {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.with_c_str(f)
    }
}

impl WithUTF8 for String {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.with_c_str(f)
    }
}
<commit_msg>utf8: Inline public methods implemented through other public API<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use types::{gchar,gsize};
use types::{gpointer};

use libc;
use libc::c_char;
use std::c_str::{CString,ToCStr};
use std::kinds::marker;
use std::mem::transmute;
use std::ptr::copy_nonoverlapping_memory;
use std::slice;
use std::str;
use std::string;

pub struct UTF8Chars<'a> {
    data: *const gchar,
    lifetime: marker::ContravariantLifetime<'a>
}

impl<'a> UTF8Chars<'a> {
    pub unsafe fn wrap(ptr: *const gchar) -> UTF8Chars<'a> {
        UTF8Chars { data: ptr, lifetime: marker::ContravariantLifetime }
    }

    pub fn to_string(&self) -> String {
        unsafe {
            string::raw::from_buf(self.data as *const u8)
        }
    }
}

#[inline]
unsafe fn utf8_cont_bits(p: *const gchar) -> u32 {
    let byte = *(p as *const u8);
    debug_assert!((byte & 0xC0) == 0x80);
    (byte & 0x3F) as u32
}

impl<'a> Iterator<char> for UTF8Chars<'a> {
    fn next(&mut self) -> Option<char> {
        let p = self.data;
        let first_byte = unsafe { *(p as *const u8) };
        if first_byte == 0 {
            return None;
        }
        unsafe {
            let mut wc: u32;
            if first_byte < 0xC0 {
                debug_assert!(first_byte < 0x80);
                wc = first_byte as u32;
                self.data = p.offset(1);
            } else {
                let c1 = utf8_cont_bits(p.offset(1));
                if first_byte < 0xE0 {
                    wc = ((first_byte & 0x1F) as u32 << 6) | c1;
                    debug_assert!(wc >= 0x80);
                    self.data = p.offset(2);
                } else {
                    let c2 = utf8_cont_bits(p.offset(2));
                    if first_byte < 0xF0 {
                        wc = ((first_byte & 0x0F) as u32 << 12)
                             | (c1 << 6) | c2;
                        debug_assert!(wc >= 0x0800);
                        debug_assert!((wc & 0xF800) != 0xD800);
                        self.data = p.offset(3);
                    } else {
                        debug_assert!(first_byte <= 0xF4);
                        let c3 = utf8_cont_bits(p.offset(3));
                        wc = ((first_byte & 0x07) as u32 << 18)
                             | (c1 << 12) | (c2 << 6) | c3;
                        debug_assert!(wc >= 0x010000 && wc <= 0x10FFFF);
                        self.data = p.offset(4);
                    }
                }
            }
            Some(transmute(wc))
        }
    }
}

unsafe fn dup_to_c_str(source: *const c_char, len: uint) -> CString {
    let copy = libc::malloc(len as libc::size_t + 1) as *mut c_char;
    copy_nonoverlapping_memory(copy, source, len + 1);
    CString::new(copy as *const c_char, true)
}

impl<'a> ToCStr for UTF8Chars<'a> {

    #[inline]
    fn to_c_str(&self) -> CString {
        unsafe { self.to_c_str_unchecked() }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        let src = self.data as *const c_char;
        let len = libc::strlen(src) as uint;
        dup_to_c_str(src, len)
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }
}

pub struct UTF8Buf {
    data: *mut gchar,
}

impl UTF8Buf {

    pub unsafe fn wrap(data: *mut gchar) -> UTF8Buf {
        UTF8Buf { data: data }
    }

    pub fn chars<'a>(&'a self) -> UTF8Chars<'a> {
        unsafe { UTF8Chars::wrap(self.data as *const gchar) }
    }

    pub fn into_collection(self) -> UTF8Str {
        unsafe {
            let len = libc::strlen(self.data as *const c_char);
            UTF8Str { buf: transmute(self), len: len as uint }
        }
    }

    #[inline]
    pub fn to_string(&self) -> String { self.chars().to_string() }

    #[inline]
    pub fn to_owned(&self) -> String { self.to_string() }

    #[inline]
    pub fn into_string(self) -> String { self.to_string() }

    #[inline]
    pub fn into_owned(self) -> String { self.to_string() }

    pub fn is_empty(&self) -> bool { unsafe { *self.data == 0 } }
}

impl Drop for UTF8Buf {
    fn drop(&mut self) {
        unsafe { ffi::g_free(self.data as gpointer) }
    }
}

impl Clone for UTF8Buf {
    fn clone(&self) -> UTF8Buf {
        unsafe {
            UTF8Buf::wrap(ffi::g_strdup(self.data as *const gchar))
        }
    }
}

impl ToCStr for UTF8Buf {

    #[inline]
    fn to_c_str(&self) -> CString {
        self.chars().to_c_str()
    }

    #[inline]
    unsafe fn to_c_str_unchecked(&self) -> CString {
        self.chars().to_c_str_unchecked()
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        f(self.data as *const i8)
    }
}

pub struct UTF8Str {
    buf: UTF8Buf,
    len: uint
}

static NUL: gchar = 0;

impl UTF8Str {

    pub unsafe fn wrap(data: *mut gchar, len: uint) -> UTF8Str {
        UTF8Str { buf: UTF8Buf::wrap(data), len: len }
    }

    pub fn chars<'a>(&'a self) -> UTF8Chars<'a> { self.buf.chars() }

    pub fn to_string(&self) -> String {
        unsafe {
            string::raw::from_buf_len(self.buf.data as *const u8, self.len)
        }
    }
}

impl Clone for UTF8Str {
    fn clone(&self) -> UTF8Str {
        unsafe {
            let copy = ffi::g_malloc((self.len + 1) as gsize) as *mut gchar;
            copy_nonoverlapping_memory(copy,
                    self.buf.data as *const gchar, self.len);
            *copy.offset(self.len as int) = NUL;
            UTF8Str::wrap(copy, self.len)
        }
    }
}

impl Str for UTF8Str {
    fn as_slice<'a>(&'a self) -> &'a str {
        unsafe {
            slice::raw::buf_as_slice(
                self.buf.data as *const u8,
                self.len,
                |bytes| {
                    let s = str::raw::from_utf8(bytes);
                    transmute(s)
                })
        }
    }
}

impl StrAllocating for UTF8Str {
    fn into_string(self) -> String { self.to_string() }
}

impl Collection for UTF8Str {

    // Common sense says we should return the UTF-8 length,
    // but &str returns length in bytes, so...
    fn len(&self) -> uint { return self.len }

    fn is_empty(&self) -> bool { return self.len == 0 }
}

impl ToCStr for UTF8Str {

    fn to_c_str(&self) -> CString {
        unsafe { self.to_c_str_unchecked() }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString {
        dup_to_c_str(self.buf.data as *const c_char, self.len)
    }

    fn with_c_str<T>(&self, f: |*const i8| -> T) -> T {
        self.buf.with_c_str(f)
    }

    unsafe fn with_c_str_unchecked<T>(&self, f: |*const i8| -> T) -> T {
        self.buf.with_c_str_unchecked(f)
    }
}

impl PartialEq for UTF8Str {
    fn eq(&self, other: &UTF8Str) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl Eq for UTF8Str { }

impl<S: Str> Equiv<S> for UTF8Str {
    fn equiv(&self, other: &S) -> bool {
        self.as_slice() == other.as_slice()
    }
}

pub trait WithUTF8 {
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T;
}

impl<'a> WithUTF8 for UTF8Chars<'a> {
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        f(self.data)
    }
}

impl WithUTF8 for UTF8Buf {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        f(self.data as *const gchar)
    }
}

impl WithUTF8 for UTF8Str {

    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.buf.with_utf8_c_str(f)
    }
}

impl<'a> WithUTF8 for &'a str {

    #[inline]
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.with_c_str(f)
    }
}

impl WithUTF8 for String {

    #[inline]
    fn with_utf8_c_str<T>(&self, f: |*const gchar| -> T) -> T {
        self.with_c_str(f)
    }
}
<|endoftext|>