<commit_before>//! [Church single-pair list](https://en.wikipedia.org/wiki/Church_encoding#One_pair_as_a_list_node)

use term::*;
use term::Term::*;
use term::Error::*;
use booleans::*;
use pair::*;

/// Equivalent to fls(); produces a Church-encoded nil, the last link of a Church-encoded list.
///
/// NIL := FALSE
///
/// # Example
/// ```
/// use lambda_calculus::list::nil;
/// use lambda_calculus::booleans::fls;
///
/// assert_eq!(nil(), fls());
/// ```
pub fn nil() -> Term { fls() }

/// Applied to a Church-encoded list it determines if it is empty.
///
/// IS_NIL := λl.l (λhtd.FALSE) TRUE = λ 1 (λ λ λ FALSE) TRUE
///
/// # Example
/// ```
/// use lambda_calculus::list::{nil, is_nil};
/// use lambda_calculus::booleans::tru;
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(is_nil().app(nil())), tru());
/// ```
pub fn is_nil() -> Term {
    abs(
        Var(1)
        .app(abs(abs(abs(fls()))))
        .app(tru())
    )
}

/// Equivalent to pair(); applied to two terms it returns them encoded as a list.
///
/// CONS := PAIR
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::list::{nil, cons};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110_consed = normalize(cons().app(one()).app(cons().app(one()).app(cons().app(zero()).app(nil()))));
/// let list_110_from_vec = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(list_110_consed, list_110_from_vec);
/// ```
pub fn cons() -> Term { pair() }

/// Equivalent to first(); applied to a Church-encoded list it returns its first element.
///
/// HEAD := FIRST
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::list::head;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110 = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(normalize(head().app(list_110)), one());
/// ```
pub fn head() -> Term { first() }

/// Equivalent to second(); applied to a Church-encoded list it returns a new list with all its elements but the first one.
///
/// TAIL := SECOND
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::list::tail;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110 = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(normalize(tail().app(list_110)), Term::from(vec![one(), zero()]));
/// ```
pub fn tail() -> Term { second() }

impl Term {
    /// Checks whether self is a Church-encoded empty list, i.e. nil().
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::list::nil;
    ///
    /// assert!(nil().is_empty());
    /// ```
    pub fn is_empty(&self) -> bool {
        *self == nil()
    }

    // Returns a reference to the last term of a Church-encoded list.
    fn last_ref(&self) -> Result<&Term, Error> {
        let mut last_candidate = try!(self.snd_ref());

        while let Ok(ref second) = last_candidate.snd_ref() {
            last_candidate = *second;
        }

        Ok(last_candidate)
    }

    /// Checks whether self is a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert!(list_110.is_list());
    /// ```
    pub fn is_list(&self) -> bool {
        self.last_ref() == Ok(&nil())
    }

    /// Splits a Church-encoded list into a pair containing its first term and a list of all the other terms, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons(), Ok((one(), Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons(self) -> Result<(Term, Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair()
        }
    }

    /// Splits a Church-encoded list into a pair containing references to its first term and a to list of all the other terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons_ref(), Ok((&one(), &Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons_ref(&self) -> Result<(&Term, &Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair_ref()
        }
    }

    /// Splits a Church-encoded list into a pair containing mutable references to its first term and a to list of all the other terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons_ref_mut(), Ok((&mut one(), &mut Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair_ref_mut()
        }
    }

    /// Returns the first term from a Church-encoded list, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head(), Ok(one()));
    /// ```
    pub fn head(self) -> Result<Term, Error> {
        Ok(try!(self.uncons()).0)
    }

    /// Returns a reference to the first term of a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head_ref(), Ok(&one()));
    /// ```
    pub fn head_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.uncons_ref()).0)
    }

    /// Returns a mutable reference to the first term of a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head_ref_mut(), Ok(&mut one()));
    /// ```
    pub fn head_ref_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.uncons_ref_mut()).0)
    }

    /// Returns a list of all the terms of a Church-encoded list but the first one, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail(), Ok(Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail(self) -> Result<Term, Error> {
        Ok(try!(self.uncons()).1)
    }

    /// Returns a reference to a list of all the terms of a Church-encoded list but the first one.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail_ref(), Ok(&Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.uncons_ref()).1)
    }

    /// Returns a mutable reference to a list of all the terms of a Church-encoded list but the first one.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail_ref_mut(), Ok(&mut Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail_ref_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.uncons_ref_mut()).1)
    }

    /// Returns the length of a Church-encoded list
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.len(), Ok(3));
    /// ```
    pub fn len(&self) -> Result<usize, Error> {
        let mut inner = self;
        let mut n = 0;

        while *inner != nil() {
            n += 1;
            inner = try!(inner.tail_ref());
        }

        Ok(n)
    }

    /// Adds a term to the beginning of a Church-encoded list and returns the new list. Consumes self and the argument.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.push(zero()), Term::from(vec![zero(), one(), one(), zero()]));
    /// ```
    pub fn push(self, term: Term) -> Term {
        abs(Var(1).app(term).app(self))
    }

    /// Removes the first element from a Church-encoded list and returns it.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.pop(), Ok(one()));
    /// assert_eq!(list_110, Term::from(vec![one(), zero()]));
    /// ```
    pub fn pop(&mut self) -> Result<Term, Error> {
        let (head, tail) = try!(self.clone().uncons()); // TODO: drop clone()
        *self = tail;

        Ok(head)
    }
}

impl From<Vec<Term>> for Term {
    fn from(terms: Vec<Term>) -> Self {
        let mut output = nil();

        for term in terms.into_iter().rev() {
            output = output.push(term);
        }

        output
    }
}

impl Iterator for Term {
    type Item = Term;

    fn next(&mut self) -> Option<Term> {
        if self.is_empty() {
            None
        } else {
            Some(self.pop().unwrap())
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use arithmetic::{zero, one, to_cnum};
    use reduction::normalize;

    #[test]
    fn empty_list() {
        let nil = nil();

        assert!(!nil.is_list());
        assert!(nil.is_empty());
    }

    #[test]
    fn list_push() {
        let list_pushed = nil().push(zero()).push(one()).push(one());
        let list_consed = normalize(cons().app(one()).app(cons().app(one()).app(cons().app(zero()).app(nil()))));

        assert_eq!(list_pushed, list_consed);
    }

    #[test]
    fn list_from_vector() {
        let list_from_vec = Term::from(vec![one(), one(), zero()]);
        let list_pushed = nil().push(zero()).push(one()).push(one());

        assert_eq!(list_from_vec, list_pushed);
    }

    #[test]
    fn list_length() {
        let list0 = nil();
        assert_eq!(list0.len(), Ok(0));
        let list1 = list0.push(one());
        assert_eq!(list1.len(), Ok(1));
        let list2 = list1.push(one());
        assert_eq!(list2.len(), Ok(2));
        let list3 = list2.push(one());
        assert_eq!(list3.len(), Ok(3));
    }

    #[test]
    fn list_operations() {
        let list_three_five_four = Term::from(vec![to_cnum(3), to_cnum(5), to_cnum(4)]);

        assert!(list_three_five_four.is_list());

        assert_eq!(list_three_five_four.head_ref(), Ok(&to_cnum(3)));
        assert_eq!(list_three_five_four.tail_ref(), Ok(&Term::from(vec![to_cnum(5), to_cnum(4)])));

        assert_eq!(list_three_five_four.tail_ref().and_then(|t| t.head_ref()), Ok(&to_cnum(5)));
        assert_eq!(list_three_five_four.tail_ref().and_then(|t| t.tail_ref()).and_then(|t| t.head_ref()), Ok(&to_cnum(4)));

        let unconsed = list_three_five_four.uncons();
        assert_eq!(unconsed, Ok((to_cnum(3), Term::from(vec![to_cnum(5), to_cnum(4)]))));
    }

    #[test]
    fn list_pop() {
        let mut list_poppable = Term::from(vec![one(), zero(), zero()]);

        assert_eq!(list_poppable.pop(), Ok(one()));
        assert_eq!(list_poppable.pop(), Ok(zero()));
        assert_eq!(list_poppable.pop(), Ok(zero()));
        assert_eq!(list_poppable.pop(), Err(NotAList));
    }

    #[test]
    fn iterating_list() {
        let list010 = Term::from(vec![zero(), one(), zero()]);
        let mut iter = list010.into_iter();

        assert_eq!(iter.next(), Some(zero()));
        assert_eq!(iter.next(), Some(one()));
        assert_eq!(iter.next(), Some(zero()));
        assert_eq!(iter.next(), None);
    }
}
<commit_msg>in_nil() > null()<commit_after>//! [Church single-pair list](https://en.wikipedia.org/wiki/Church_encoding#One_pair_as_a_list_node)

use term::*;
use term::Term::*;
use term::Error::*;
use booleans::*;
use pair::*;

/// Equivalent to fls(); produces a Church-encoded nil, the last link of a Church-encoded list.
///
/// NIL := FALSE
///
/// # Example
/// ```
/// use lambda_calculus::list::nil;
/// use lambda_calculus::booleans::fls;
///
/// assert_eq!(nil(), fls());
/// ```
pub fn nil() -> Term { fls() }

/// Applied to a Church-encoded list it determines if it is empty.
///
/// NULL := λl.l (λhtd.FALSE) TRUE = λ 1 (λ λ λ FALSE) TRUE
///
/// # Example
/// ```
/// use lambda_calculus::list::{nil, null};
/// use lambda_calculus::booleans::tru;
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(null().app(nil())), tru());
/// ```
pub fn null() -> Term {
    abs(
        Var(1)
        .app(abs(abs(abs(fls()))))
        .app(tru())
    )
}

/// Equivalent to pair(); applied to two terms it returns them encoded as a list.
///
/// CONS := PAIR
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::list::{nil, cons};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110_consed = normalize(cons().app(one()).app(cons().app(one()).app(cons().app(zero()).app(nil()))));
/// let list_110_from_vec = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(list_110_consed, list_110_from_vec);
/// ```
pub fn cons() -> Term { pair() }

/// Equivalent to first(); applied to a Church-encoded list it returns its first element.
///
/// HEAD := FIRST
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::list::head;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110 = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(normalize(head().app(list_110)), one());
/// ```
pub fn head() -> Term { first() }

/// Equivalent to second(); applied to a Church-encoded list it returns a new list with all its elements but the first one.
///
/// TAIL := SECOND
///
/// # Example
/// ```
/// use lambda_calculus::term::Term;
/// use lambda_calculus::list::tail;
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::normalize;
///
/// let list_110 = Term::from(vec![one(), one(), zero()]);
///
/// assert_eq!(normalize(tail().app(list_110)), Term::from(vec![one(), zero()]));
/// ```
pub fn tail() -> Term { second() }

impl Term {
    /// Checks whether self is a Church-encoded empty list, i.e. nil().
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::list::nil;
    ///
    /// assert!(nil().is_empty());
    /// ```
    pub fn is_empty(&self) -> bool {
        *self == nil()
    }

    // Returns a reference to the last term of a Church-encoded list.
    fn last_ref(&self) -> Result<&Term, Error> {
        let mut last_candidate = try!(self.snd_ref());

        while let Ok(ref second) = last_candidate.snd_ref() {
            last_candidate = *second;
        }

        Ok(last_candidate)
    }

    /// Checks whether self is a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert!(list_110.is_list());
    /// ```
    pub fn is_list(&self) -> bool {
        self.last_ref() == Ok(&nil())
    }

    /// Splits a Church-encoded list into a pair containing its first term and a list of all the other terms, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons(), Ok((one(), Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons(self) -> Result<(Term, Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair()
        }
    }

    /// Splits a Church-encoded list into a pair containing references to its first term and a to list of all the other terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons_ref(), Ok((&one(), &Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons_ref(&self) -> Result<(&Term, &Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair_ref()
        }
    }

    /// Splits a Church-encoded list into a pair containing mutable references to its first term and a to list of all the other terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.uncons_ref_mut(), Ok((&mut one(), &mut Term::from(vec![one(), zero()]))));
    /// ```
    pub fn uncons_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        if !self.is_list() {
            Err(NotAList)
        } else {
            self.unpair_ref_mut()
        }
    }

    /// Returns the first term from a Church-encoded list, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head(), Ok(one()));
    /// ```
    pub fn head(self) -> Result<Term, Error> {
        Ok(try!(self.uncons()).0)
    }

    /// Returns a reference to the first term of a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head_ref(), Ok(&one()));
    /// ```
    pub fn head_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.uncons_ref()).0)
    }

    /// Returns a mutable reference to the first term of a Church-encoded list.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.head_ref_mut(), Ok(&mut one()));
    /// ```
    pub fn head_ref_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.uncons_ref_mut()).0)
    }

    /// Returns a list of all the terms of a Church-encoded list but the first one, consuming self.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail(), Ok(Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail(self) -> Result<Term, Error> {
        Ok(try!(self.uncons()).1)
    }

    /// Returns a reference to a list of all the terms of a Church-encoded list but the first one.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail_ref(), Ok(&Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.uncons_ref()).1)
    }

    /// Returns a mutable reference to a list of all the terms of a Church-encoded list but the first one.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.tail_ref_mut(), Ok(&mut Term::from(vec![one(), zero()])));
    /// ```
    pub fn tail_ref_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.uncons_ref_mut()).1)
    }

    /// Returns the length of a Church-encoded list
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.len(), Ok(3));
    /// ```
    pub fn len(&self) -> Result<usize, Error> {
        let mut inner = self;
        let mut n = 0;

        while *inner != nil() {
            n += 1;
            inner = try!(inner.tail_ref());
        }

        Ok(n)
    }

    /// Adds a term to the beginning of a Church-encoded list and returns the new list. Consumes self and the argument.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.push(zero()), Term::from(vec![zero(), one(), one(), zero()]));
    /// ```
    pub fn push(self, term: Term) -> Term {
        abs(Var(1).app(term).app(self))
    }

    /// Removes the first element from a Church-encoded list and returns it.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term;
    /// use lambda_calculus::arithmetic::{zero, one};
    ///
    /// let mut list_110 = Term::from(vec![one(), one(), zero()]);
    ///
    /// assert_eq!(list_110.pop(), Ok(one()));
    /// assert_eq!(list_110, Term::from(vec![one(), zero()]));
    /// ```
    pub fn pop(&mut self) -> Result<Term, Error> {
        let (head, tail) = try!(self.clone().uncons()); // TODO: drop clone()
        *self = tail;

        Ok(head)
    }
}

impl From<Vec<Term>> for Term {
    fn from(terms: Vec<Term>) -> Self {
        let mut output = nil();

        for term in terms.into_iter().rev() {
            output = output.push(term);
        }

        output
    }
}

impl Iterator for Term {
    type Item = Term;

    fn next(&mut self) -> Option<Term> {
        if self.is_empty() {
            None
        } else {
            Some(self.pop().unwrap())
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use arithmetic::{zero, one, to_cnum};
    use reduction::normalize;

    #[test]
    fn empty_list() {
        let nil = nil();

        assert!(!nil.is_list());
        assert!(nil.is_empty());
    }

    #[test]
    fn list_push() {
        let list_pushed = nil().push(zero()).push(one()).push(one());
        let list_consed = normalize(cons().app(one()).app(cons().app(one()).app(cons().app(zero()).app(nil()))));

        assert_eq!(list_pushed, list_consed);
    }

    #[test]
    fn list_from_vector() {
        let list_from_vec = Term::from(vec![one(), one(), zero()]);
        let list_pushed = nil().push(zero()).push(one()).push(one());

        assert_eq!(list_from_vec, list_pushed);
    }

    #[test]
    fn list_length() {
        let list0 = nil();
        assert_eq!(list0.len(), Ok(0));
        let list1 = list0.push(one());
        assert_eq!(list1.len(), Ok(1));
        let list2 = list1.push(one());
        assert_eq!(list2.len(), Ok(2));
        let list3 = list2.push(one());
        assert_eq!(list3.len(), Ok(3));
    }

    #[test]
    fn list_operations() {
        let list_three_five_four = Term::from(vec![to_cnum(3), to_cnum(5), to_cnum(4)]);

        assert!(list_three_five_four.is_list());

        assert_eq!(list_three_five_four.head_ref(), Ok(&to_cnum(3)));
        assert_eq!(list_three_five_four.tail_ref(), Ok(&Term::from(vec![to_cnum(5), to_cnum(4)])));

        assert_eq!(list_three_five_four.tail_ref().and_then(|t| t.head_ref()), Ok(&to_cnum(5)));
        assert_eq!(list_three_five_four.tail_ref().and_then(|t| t.tail_ref()).and_then(|t| t.head_ref()), Ok(&to_cnum(4)));

        let unconsed = list_three_five_four.uncons();
        assert_eq!(unconsed, Ok((to_cnum(3), Term::from(vec![to_cnum(5), to_cnum(4)]))));
    }

    #[test]
    fn list_pop() {
        let mut list_poppable = Term::from(vec![one(), zero(), zero()]);

        assert_eq!(list_poppable.pop(), Ok(one()));
        assert_eq!(list_poppable.pop(), Ok(zero()));
        assert_eq!(list_poppable.pop(), Ok(zero()));
        assert_eq!(list_poppable.pop(), Err(NotAList));
    }

    #[test]
    fn iterating_list() {
        let list010 = Term::from(vec![zero(), one(), zero()]);
        let mut iter = list010.into_iter();

        assert_eq!(iter.next(), Some(zero()));
        assert_eq!(iter.next(), Some(one()));
        assert_eq!(iter.next(), Some(zero()));
        assert_eq!(iter.next(), None);
    }
}
<|endoftext|>


<commit_before>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "Apache-2.0 OR MIT",
    "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "rdrand",             // ISC, mdbook, rustfmt
    "fuchsia-cprng",      // BSD-3-Clause, mdbook, rustfmt
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
    // FIXME: this dependency violates the documentation comment above:
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("annotate-snippets"),
    Crate("ansi_term"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("autocfg"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("c2-chacha"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-queue"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("dlmalloc"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fortanix-sgx-abi"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("getrandom"),
    Crate("hashbrown"),
    Crate("humantime"),
    Crate("indexmap"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("ppv-lite86"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("term_size"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("wasi"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
    Crate("hermit-abi"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = !check_license(&toml) || *bad;
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<commit_msg>Fix mdbook-linkcheck license checks.<commit_after>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "Apache-2.0 OR MIT",
    "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "rdrand",             // ISC, mdbook, rustfmt
    "fuchsia-cprng",      // BSD-3-Clause, mdbook, rustfmt
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
    // FIXME: this dependency violates the documentation comment above:
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
    "dunce",              // CC0-1.0 mdbook-linkcheck
    "codespan-reporting", // Apache-2.0 mdbook-linkcheck
    "codespan",           // Apache-2.0 mdbook-linkcheck
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("annotate-snippets"),
    Crate("ansi_term"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("autocfg"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("c2-chacha"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-queue"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("dlmalloc"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fortanix-sgx-abi"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("getrandom"),
    Crate("hashbrown"),
    Crate("humantime"),
    Crate("indexmap"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("ppv-lite86"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("term_size"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("wasi"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
    Crate("hermit-abi"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = !check_license(&toml) || *bad;
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<|endoftext|>


<commit_before>use rand::{SeedableRng, FromEntropy, Rng, rngs::SmallRng};

#[test]
fn simple() {
    assert_eq!(4, 4);
}

// Having more than 1 test does seem to make a difference
// (i.e., this calls ptr::swap which having just one test does not).
#[test]
fn fixed_rng() {
    let mut rng = rand::rngs::StdRng::seed_from_u64(0xdeadcafe);
    let x: u32 = rng.gen();
    let y: u32 = rng.gen();
    assert_ne!(x, y);
}

#[test]
fn entropy_rng() {
    // Use this opportunity to test querying the RNG (needs an external crate, hence tested here and not in the compiletest suite)
    let mut rng = SmallRng::from_entropy();
    let _val = rng.gen::<i32>();

    // Also try per-thread RNG.
    let mut rng = rand::thread_rng();
    let _val = rng.gen::<i32>();
}

// A test that won't work on miri
#[cfg(not(miri))]
#[test]
fn does_not_work_on_miri() {
    let x = 0u8;
    assert!(&x as *const _ as usize % 4 < 4);
}
<commit_msg>deduplicate tests<commit_after>use rand::{SeedableRng, FromEntropy, Rng, rngs::SmallRng};

// Having more than 1 test does seem to make a difference
// (i.e., this calls ptr::swap which having just one test does not).
#[test]
fn simple() {
    assert_eq!(4, 4);
}

// Having more than 1 test does seem to make a difference
// (i.e., this calls ptr::swap which having just one test does not).
#[test]
fn entropy_rng() {
    // Use this opportunity to test querying the RNG (needs an external crate, hence tested here and not in the compiletest suite)
    let mut rng = SmallRng::from_entropy();
    let _val = rng.gen::<i32>();

    // Also try per-thread RNG.
    let mut rng = rand::thread_rng();
    let _val = rng.gen::<i32>();
}

// A test that won't work on miri
#[cfg(not(miri))]
#[test]
fn does_not_work_on_miri() {
    let x = 0u8;
    assert!(&x as *const _ as usize % 4 < 4);
}
<|endoftext|>


<commit_before>use ::anyhow::Result;
use ::comfy_table::presets::UTF8_HORIZONTAL_BORDERS_ONLY;
use ::comfy_table::*;
use ::crossterm::style::style;
use ::snap::read::FrameDecoder;
use ::std::collections::BTreeMap;
use ::std::io;
use ::std::string::ToString;

use ::pueue::log::{get_log_file_handles, get_log_paths};
use ::pueue::message::TaskLogMessage;
use ::pueue::settings::Settings;
use ::pueue::state::State;
use ::pueue::task::{Task, TaskResult, TaskStatus};

use crate::cli::SubCommand;
use crate::output_helper::*;

pub fn print_success(message: String) {
    println!("{}", message);
}

pub fn print_error(message: String) {
    let styled = style(message).with(Color::Red);
    println!("{}", styled);
}

/// Print the current state of the daemon in a nicely formatted table.
pub fn print_state(state: State, cli_command: &SubCommand) {
    let (json, group_only) = match cli_command {
        SubCommand::Status { json, group } => (*json, group.clone()),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_state. This shouldn't happen",
            cli_command
        ),
    };

    // If the json flag is specified, print the state as json and exit.
    if json {
        println!("{}", serde_json::to_string(&state).unwrap());
        return;
    }

    // Don't show default queue headline if a single group is requested
    if group_only.is_none() {
        println!("{}", get_default_headline(&state));
    }

    // Early exit and hint if there are no tasks in the queue
    if state.tasks.is_empty() {
        println!("\nTask list is empty. Add tasks with `pueue add -- [cmd]`");
        return;
    }

    // Skip default queue, if a single group is requested
    if group_only.is_none() {
        let default_tasks = get_default_tasks(&state.tasks);
        if !default_tasks.is_empty() {
            print_table(&default_tasks);
        }
    }

    // Print new table for each group
    for (group, tasks) in sort_tasks_by_group(&state.tasks) {
        // Skip unwanted groups, if a single group is requested
        if let Some(group_only) = &group_only {
            if group_only != &group {
                continue;
            }
        }
        println!("{}", get_group_headline(&group, &state));
        print_table(&tasks);
    }
}

/// Print some tasks into a nicely formatted table
fn print_table(tasks: &BTreeMap<usize, Task>) {
    let (has_delayed_tasks, has_dependencies) = has_special_columns(tasks);

    // Create table header row
    let mut headers = vec![Cell::new("Index"), Cell::new("Status")];
    if has_delayed_tasks {
        headers.push(Cell::new("Enqueue At"));
    }
    if has_dependencies {
        headers.push(Cell::new("Deps"));
    }
    headers.append(&mut vec![
        Cell::new("Exitcode"),
        Cell::new("Command"),
        Cell::new("Path"),
        Cell::new("Start"),
        Cell::new("End"),
    ]);

    // Initialize comfy table.
    let mut table = Table::new();
    table
        .set_content_arrangement(ContentArrangement::Dynamic)
        .load_preset(UTF8_HORIZONTAL_BORDERS_ONLY)
        .set_header(headers);

    // Add rows one by one.
    for (id, task) in tasks {
        let mut row = Row::new();
        row.add_cell(Cell::new(&id.to_string()));

        // Determine the human readable task status representation and the respective color.
        let status_string = task.status.to_string();
        let (status_text, color) = match task.status {
            TaskStatus::Running => (status_string, Color::Green),
            TaskStatus::Paused | TaskStatus::Locked => (status_string, Color::White),
            TaskStatus::Done => match &task.result {
                Some(TaskResult::Success) => (TaskResult::Success.to_string(), Color::Green),
                Some(TaskResult::DependencyFailed) => ("Dependency failed".to_string(), Color::Red),
                Some(TaskResult::FailedToSpawn(_)) => ("Failed to spawn".to_string(), Color::Red),
                Some(result) => (result.to_string(), Color::Red),
                None => panic!("Got a 'Done' task without a task result. Please report this bug."),
            },
            _ => (status_string, Color::Yellow),
        };
        row.add_cell(Cell::new(status_text).fg(color));

        if has_delayed_tasks {
            if let Some(enqueue_at) = task.enqueue_at {
                row.add_cell(Cell::new(enqueue_at.format("%Y-%m-%d\n%H:%M:%S")));
            } else {
                row.add_cell(Cell::new(""));
            }
        }

        if has_dependencies {
            let text = task
                .dependencies
                .iter()
                .map(|id| id.to_string())
                .collect::<Vec<String>>()
                .join(", ");
            row.add_cell(Cell::new(text));
        }

        // Match the color of the exit code.
        // If the exit_code is none, it has been killed by the task handler.
        let exit_code_cell = match task.result {
            Some(TaskResult::Success) => Cell::new("0").fg(Color::Green),
            Some(TaskResult::Failed(code)) => Cell::new(&code.to_string()).fg(Color::Red),
            _ => Cell::new(""),
        };
        row.add_cell(exit_code_cell);

        // Add command and path.
        row.add_cell(Cell::new(&task.command));
        row.add_cell(Cell::new(&task.path));

        // Add start time, if already set.
        if let Some(start) = task.start {
            let formatted = start.format("%H:%M").to_string();
            row.add_cell(Cell::new(&formatted));
        } else {
            row.add_cell(Cell::new(""));
        }

        // Add finish time, if already set.
        if let Some(end) = task.end {
            let formatted = end.format("%H:%M").to_string();
            row.add_cell(Cell::new(&formatted));
        } else {
            row.add_cell(Cell::new(""));
        }

        table.add_row(row);
    }

    // Print the table.
    println!("{}", table);
}

/// Print the log ouput of finished tasks.
/// Either print the logs of every task
/// or only print the logs of the specified tasks.
pub fn print_logs(
    mut task_logs: BTreeMap<usize, TaskLogMessage>,
    cli_command: &SubCommand,
    settings: &Settings,
) {
    let (json, task_ids) = match cli_command {
        SubCommand::Log { json, task_ids } => (*json, task_ids.clone()),
        // TODO: Check that SubCommand::Follow is also unreachable.
        // SubCommand::Follow { task_id, .. } => (false, vec![*task_id]),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_log. This shouldn't happen",
            cli_command
        ),
    };
    if json {
        println!("{}", serde_json::to_string(&task_logs).unwrap());
        return;
    }

    if task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks");
        return;
    }

    if !task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks for your specified ids");
        return;
    }

    let mut task_iter = task_logs.iter_mut().peekable();
    while let Some((_, mut task_log)) = task_iter.next() {
        print_log(&mut task_log, settings);

        // Add a newline if there is another task that's going to be printed.
        if let Some((_, task_log)) = task_iter.peek() {
            if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused]
                .contains(&task_log.task.status)
            {
                println!();
            }
        }
    }
}

/// Print the log of a single task.
pub fn print_log(task_log: &mut TaskLogMessage, settings: &Settings) {
    let task = &task_log.task;
    // We only show logs of finished or running tasks.
    if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused].contains(&task.status) {
        return;
    }

    // Print task id and exit code.
    let task_text = style(format!("Task {}", task.id)).attribute(Attribute::Bold);
    let exit_status = match &task.result {
        Some(TaskResult::Success) => style("completed successfully".into()).with(Color::Green),
        Some(TaskResult::Failed(exit_code)) => {
            style(format!("failed with exit code {}", exit_code)).with(Color::Red)
        }
        Some(TaskResult::FailedToSpawn(err)) => {
            style(format!("failed to spawn: {}", err)).with(Color::Red)
        }
        Some(TaskResult::Killed) => style("killed by system or user".to_string()).with(Color::Red),
        Some(TaskResult::DependencyFailed) => {
            style("dependency failed".to_string()).with(Color::Red)
        }
        None => style("running".to_string()),
    };
    println!("{} {}", task_text, exit_status);

    // Print command and path.
    println!("Command: {}", task.command);
    println!("Path: {}", task.path);

    if let Some(start) = task.start {
        println!("Start: {}", start.to_rfc2822());
    }
    if let Some(end) = task.end {
        println!("End: {}", end.to_rfc2822());
    }

    if settings.client.read_local_logs {
        print_local_log_output(task_log.task.id, settings);
    } else if task_log.stdout.is_some() && task_log.stderr.is_some() {
        print_task_output_from_daemon(task_log);
    } else {
        println!("Logs requested from pueue daemon, but none received. Please report this bug.");
    }
}

/// The daemon didn't send any log output, thereby we didn't request any.
/// If that's the case, read the log files from the local pueue directory
pub fn print_local_log_output(task_id: usize, settings: &Settings) {
    let (mut stdout_log, mut stderr_log) =
        match get_log_file_handles(task_id, &settings.daemon.pueue_directory) {
            Ok((stdout, stderr)) => (stdout, stderr),
            Err(err) => {
                println!("Failed to get log file handles: {}", err);
                return;
            }
        };
    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();

    if let Ok(metadata) = stdout_log.metadata() {
        if metadata.len() != 0 {
            println!(
                "{}",
                style("stdout:")
                    .with(Color::Green)
                    .attribute(Attribute::Bold)
            );

            if let Err(err) = io::copy(&mut stdout_log, &mut stdout) {
                println!("Failed reading local stdout log file: {}", err);
            };
        }
    }

    if let Ok(metadata) = stderr_log.metadata() {
        println!(
            "{}",
            style("stderr:")
                .with(Color::Green)
                .attribute(Attribute::Bold)
        );

        if metadata.len() != 0 {
            if let Err(err) = io::copy(&mut stderr_log, &mut stdout) {
                println!("Failed reading local stderr log file: {}", err);
            };
        }
    }
}

/// Prints log output received from the daemon.
/// We can safely call .unwrap() on stdout and stderr in here, since this
/// branch is always called after ensuring that both are `Some`.
pub fn print_task_output_from_daemon(task_log: &TaskLogMessage) {
    if !task_log.stdout.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_output(&task_log, true) {
            println!("Error while parsing stdout: {}", err);
        }
    }

    if !task_log.stderr.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_output(&task_log, false) {
            println!("Error while parsing stderr: {}", err);
        };
    }
}

/// Print log output of a finished process.
pub fn print_remote_task_output(task_log: &TaskLogMessage, stdout: bool) -> Result<()> {
    let (pre_text, bytes) = if stdout {
        ("stdout: ", task_log.stdout.as_ref().unwrap())
    } else {
        ("stderr: ", task_log.stderr.as_ref().unwrap())
    };

    println!(
        "{}",
        style(pre_text)
            .with(Color::Green)
            .attribute(Attribute::Bold)
    );

    let mut decompressor = FrameDecoder::new(bytes.as_slice());

    let stdout = io::stdout();
    let mut write = stdout.lock();
    io::copy(&mut decompressor, &mut write)?;

    Ok(())
}

/// Follow the log ouput of running task.
///
/// If no task is specified, this will check for the following cases:
///
/// - No running task: Print an error that there are no running tasks
/// - Single running task: Follow the output of that task
/// - Multiple running tasks: Print out the list of possible tasks to follow.
pub fn follow_task_logs(pueue_directory: String, task_id: usize, stderr: bool) {
    let (stdout_handle, stderr_handle) = match get_log_file_handles(task_id, &pueue_directory) {
        Ok((stdout, stderr)) => (stdout, stderr),
        Err(err) => {
            println!("Failed to get log file handles: {}", err);
            return;
        }
    };
    let mut handle = if stderr { stderr_handle } else { stdout_handle };

    let (out_path, err_path) = get_log_paths(task_id, &pueue_directory);
    let handle_path = if stderr { err_path } else { out_path };

    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();
    loop {
        // Check whether the file still exists. Exit if it doesn't.
        if !handle_path.exists() {
            println!("File has gone away. Did somebody remove the task?");
            return;
        }
        // Read the next chunk of text from the last position.
        if let Err(err) = io::copy(&mut handle, &mut stdout) {
            println!("Error while reading file: {}", err);
            return;
        };
    }
}
<commit_msg>Remove TODO comment<commit_after>use ::anyhow::Result;
use ::comfy_table::presets::UTF8_HORIZONTAL_BORDERS_ONLY;
use ::comfy_table::*;
use ::crossterm::style::style;
use ::snap::read::FrameDecoder;
use ::std::collections::BTreeMap;
use ::std::io;
use ::std::string::ToString;

use ::pueue::log::{get_log_file_handles, get_log_paths};
use ::pueue::message::TaskLogMessage;
use ::pueue::settings::Settings;
use ::pueue::state::State;
use ::pueue::task::{Task, TaskResult, TaskStatus};

use crate::cli::SubCommand;
use crate::output_helper::*;

pub fn print_success(message: String) {
    println!("{}", message);
}

pub fn print_error(message: String) {
    let styled = style(message).with(Color::Red);
    println!("{}", styled);
}

/// Print the current state of the daemon in a nicely formatted table.
pub fn print_state(state: State, cli_command: &SubCommand) {
    let (json, group_only) = match cli_command {
        SubCommand::Status { json, group } => (*json, group.clone()),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_state. This shouldn't happen",
            cli_command
        ),
    };

    // If the json flag is specified, print the state as json and exit.
    if json {
        println!("{}", serde_json::to_string(&state).unwrap());
        return;
    }

    // Don't show default queue headline if a single group is requested
    if group_only.is_none() {
        println!("{}", get_default_headline(&state));
    }

    // Early exit and hint if there are no tasks in the queue
    if state.tasks.is_empty() {
        println!("\nTask list is empty. Add tasks with `pueue add -- [cmd]`");
        return;
    }

    // Skip default queue, if a single group is requested
    if group_only.is_none() {
        let default_tasks = get_default_tasks(&state.tasks);
        if !default_tasks.is_empty() {
            print_table(&default_tasks);
        }
    }

    // Print new table for each group
    for (group, tasks) in sort_tasks_by_group(&state.tasks) {
        // Skip unwanted groups, if a single group is requested
        if let Some(group_only) = &group_only {
            if group_only != &group {
                continue;
            }
        }
        println!("{}", get_group_headline(&group, &state));
        print_table(&tasks);
    }
}

/// Print some tasks into a nicely formatted table
fn print_table(tasks: &BTreeMap<usize, Task>) {
    let (has_delayed_tasks, has_dependencies) = has_special_columns(tasks);

    // Create table header row
    let mut headers = vec![Cell::new("Index"), Cell::new("Status")];
    if has_delayed_tasks {
        headers.push(Cell::new("Enqueue At"));
    }
    if has_dependencies {
        headers.push(Cell::new("Deps"));
    }
    headers.append(&mut vec![
        Cell::new("Exitcode"),
        Cell::new("Command"),
        Cell::new("Path"),
        Cell::new("Start"),
        Cell::new("End"),
    ]);

    // Initialize comfy table.
    let mut table = Table::new();
    table
        .set_content_arrangement(ContentArrangement::Dynamic)
        .load_preset(UTF8_HORIZONTAL_BORDERS_ONLY)
        .set_header(headers);

    // Add rows one by one.
    for (id, task) in tasks {
        let mut row = Row::new();
        row.add_cell(Cell::new(&id.to_string()));

        // Determine the human readable task status representation and the respective color.
        let status_string = task.status.to_string();
        let (status_text, color) = match task.status {
            TaskStatus::Running => (status_string, Color::Green),
            TaskStatus::Paused | TaskStatus::Locked => (status_string, Color::White),
            TaskStatus::Done => match &task.result {
                Some(TaskResult::Success) => (TaskResult::Success.to_string(), Color::Green),
                Some(TaskResult::DependencyFailed) => ("Dependency failed".to_string(), Color::Red),
                Some(TaskResult::FailedToSpawn(_)) => ("Failed to spawn".to_string(), Color::Red),
                Some(result) => (result.to_string(), Color::Red),
                None => panic!("Got a 'Done' task without a task result. Please report this bug."),
            },
            _ => (status_string, Color::Yellow),
        };
        row.add_cell(Cell::new(status_text).fg(color));

        if has_delayed_tasks {
            if let Some(enqueue_at) = task.enqueue_at {
                row.add_cell(Cell::new(enqueue_at.format("%Y-%m-%d\n%H:%M:%S")));
            } else {
                row.add_cell(Cell::new(""));
            }
        }

        if has_dependencies {
            let text = task
                .dependencies
                .iter()
                .map(|id| id.to_string())
                .collect::<Vec<String>>()
                .join(", ");
            row.add_cell(Cell::new(text));
        }

        // Match the color of the exit code.
        // If the exit_code is none, it has been killed by the task handler.
        let exit_code_cell = match task.result {
            Some(TaskResult::Success) => Cell::new("0").fg(Color::Green),
            Some(TaskResult::Failed(code)) => Cell::new(&code.to_string()).fg(Color::Red),
            _ => Cell::new(""),
        };
        row.add_cell(exit_code_cell);

        // Add command and path.
        row.add_cell(Cell::new(&task.command));
        row.add_cell(Cell::new(&task.path));

        // Add start time, if already set.
        if let Some(start) = task.start {
            let formatted = start.format("%H:%M").to_string();
            row.add_cell(Cell::new(&formatted));
        } else {
            row.add_cell(Cell::new(""));
        }

        // Add finish time, if already set.
        if let Some(end) = task.end {
            let formatted = end.format("%H:%M").to_string();
            row.add_cell(Cell::new(&formatted));
        } else {
            row.add_cell(Cell::new(""));
        }

        table.add_row(row);
    }

    // Print the table.
    println!("{}", table);
}

/// Print the log ouput of finished tasks.
/// Either print the logs of every task
/// or only print the logs of the specified tasks.
pub fn print_logs(
    mut task_logs: BTreeMap<usize, TaskLogMessage>,
    cli_command: &SubCommand,
    settings: &Settings,
) {
    let (json, task_ids) = match cli_command {
        SubCommand::Log { json, task_ids } => (*json, task_ids.clone()),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_log. This shouldn't happen",
            cli_command
        ),
    };
    if json {
        println!("{}", serde_json::to_string(&task_logs).unwrap());
        return;
    }

    if task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks");
        return;
    }

    if !task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks for your specified ids");
        return;
    }

    let mut task_iter = task_logs.iter_mut().peekable();
    while let Some((_, mut task_log)) = task_iter.next() {
        print_log(&mut task_log, settings);

        // Add a newline if there is another task that's going to be printed.
        if let Some((_, task_log)) = task_iter.peek() {
            if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused]
                .contains(&task_log.task.status)
            {
                println!();
            }
        }
    }
}

/// Print the log of a single task.
pub fn print_log(task_log: &mut TaskLogMessage, settings: &Settings) {
    let task = &task_log.task;
    // We only show logs of finished or running tasks.
    if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused].contains(&task.status) {
        return;
    }

    // Print task id and exit code.
    let task_text = style(format!("Task {}", task.id)).attribute(Attribute::Bold);
    let exit_status = match &task.result {
        Some(TaskResult::Success) => style("completed successfully".into()).with(Color::Green),
        Some(TaskResult::Failed(exit_code)) => {
            style(format!("failed with exit code {}", exit_code)).with(Color::Red)
        }
        Some(TaskResult::FailedToSpawn(err)) => {
            style(format!("failed to spawn: {}", err)).with(Color::Red)
        }
        Some(TaskResult::Killed) => style("killed by system or user".to_string()).with(Color::Red),
        Some(TaskResult::DependencyFailed) => {
            style("dependency failed".to_string()).with(Color::Red)
        }
        None => style("running".to_string()),
    };
    println!("{} {}", task_text, exit_status);

    // Print command and path.
    println!("Command: {}", task.command);
    println!("Path: {}", task.path);

    if let Some(start) = task.start {
        println!("Start: {}", start.to_rfc2822());
    }
    if let Some(end) = task.end {
        println!("End: {}", end.to_rfc2822());
    }

    if settings.client.read_local_logs {
        print_local_log_output(task_log.task.id, settings);
    } else if task_log.stdout.is_some() && task_log.stderr.is_some() {
        print_task_output_from_daemon(task_log);
    } else {
        println!("Logs requested from pueue daemon, but none received. Please report this bug.");
    }
}

/// The daemon didn't send any log output, thereby we didn't request any.
/// If that's the case, read the log files from the local pueue directory
pub fn print_local_log_output(task_id: usize, settings: &Settings) {
    let (mut stdout_log, mut stderr_log) =
        match get_log_file_handles(task_id, &settings.daemon.pueue_directory) {
            Ok((stdout, stderr)) => (stdout, stderr),
            Err(err) => {
                println!("Failed to get log file handles: {}", err);
                return;
            }
        };
    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();

    if let Ok(metadata) = stdout_log.metadata() {
        if metadata.len() != 0 {
            println!(
                "{}",
                style("stdout:")
                    .with(Color::Green)
                    .attribute(Attribute::Bold)
            );

            if let Err(err) = io::copy(&mut stdout_log, &mut stdout) {
                println!("Failed reading local stdout log file: {}", err);
            };
        }
    }

    if let Ok(metadata) = stderr_log.metadata() {
        println!(
            "{}",
            style("stderr:")
                .with(Color::Green)
                .attribute(Attribute::Bold)
        );

        if metadata.len() != 0 {
            if let Err(err) = io::copy(&mut stderr_log, &mut stdout) {
                println!("Failed reading local stderr log file: {}", err);
            };
        }
    }
}

/// Prints log output received from the daemon.
/// We can safely call .unwrap() on stdout and stderr in here, since this
/// branch is always called after ensuring that both are `Some`.
pub fn print_task_output_from_daemon(task_log: &TaskLogMessage) {
    if !task_log.stdout.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_output(&task_log, true) {
            println!("Error while parsing stdout: {}", err);
        }
    }

    if !task_log.stderr.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_output(&task_log, false) {
            println!("Error while parsing stderr: {}", err);
        };
    }
}

/// Print log output of a finished process.
pub fn print_remote_task_output(task_log: &TaskLogMessage, stdout: bool) -> Result<()> {
    let (pre_text, bytes) = if stdout {
        ("stdout: ", task_log.stdout.as_ref().unwrap())
    } else {
        ("stderr: ", task_log.stderr.as_ref().unwrap())
    };

    println!(
        "{}",
        style(pre_text)
            .with(Color::Green)
            .attribute(Attribute::Bold)
    );

    let mut decompressor = FrameDecoder::new(bytes.as_slice());

    let stdout = io::stdout();
    let mut write = stdout.lock();
    io::copy(&mut decompressor, &mut write)?;

    Ok(())
}

/// Follow the log ouput of running task.
///
/// If no task is specified, this will check for the following cases:
///
/// - No running task: Print an error that there are no running tasks
/// - Single running task: Follow the output of that task
/// - Multiple running tasks: Print out the list of possible tasks to follow.
pub fn follow_task_logs(pueue_directory: String, task_id: usize, stderr: bool) {
    let (stdout_handle, stderr_handle) = match get_log_file_handles(task_id, &pueue_directory) {
        Ok((stdout, stderr)) => (stdout, stderr),
        Err(err) => {
            println!("Failed to get log file handles: {}", err);
            return;
        }
    };
    let mut handle = if stderr { stderr_handle } else { stdout_handle };

    let (out_path, err_path) = get_log_paths(task_id, &pueue_directory);
    let handle_path = if stderr { err_path } else { out_path };

    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();
    loop {
        // Check whether the file still exists. Exit if it doesn't.
        if !handle_path.exists() {
            println!("File has gone away. Did somebody remove the task?");
            return;
        }
        // Read the next chunk of text from the last position.
        if let Err(err) = io::copy(&mut handle, &mut stdout) {
            println!("Error while reading file: {}", err);
            return;
        };
    }
}
<|endoftext|>


<commit_before>pure fn isqrt(n: u64) -> u64 {
    let mut (min, max) = (0, n);
    while min < max {
        let mid = (min + max + 1) / 2;
        if (mid * mid) == n {
            return mid;
        }

        if (mid * mid) >= n {
            max = mid - 1;
        } else {
            min = mid;
        }
    }
    return min;
}

pure fn div_rem(n: uint, d: uint) -> (uint, uint) {
    (n / d, n % d)
}

pure fn add_carry(n1: uint, n2: uint) -> (uint, uint) {
    add_carry2((n1, 0), n2)
}

pure fn add_carry2(car: (uint, uint), n2: uint) -> (uint, uint) {
    let (n1, prev_carry) = car;
    let carry = if n1 > uint::max_value - n2 { 1 } else { 0 };
    if carry > uint::max_value - prev_carry { fail ~"carry overflow" }
    (n1 + n2, carry + prev_carry)
}

pure fn add_carry_multi(ns: &[uint]) -> (uint, uint) {
    ns.foldl((0, 0), |accum, n| add_carry2(accum, n))
}

pure fn sub_borrow(n1: uint, n2: uint) -> (uint, uint) {
    sub_borrow2((n1, 0), n2)
}

pure fn sub_borrow2(bor: (uint, uint), n2: uint) -> (uint, uint) {
    let (n1, prev_borrow) = bor;
    let borrow = if n1 - uint::min_value < n2 { 1u } else { 0u };
    (n1 - n2, prev_borrow + borrow)
}

pure fn sub_borrow_multi(n0: uint, ns: &[uint]) -> (uint, uint) {
    ns.foldl((n0, 0), |accum, n| sub_borrow2(accum, n))
}

pure fn mul_carry(n1: uint, n2: uint) -> (uint, uint) {
    let halfbits = sys::size_of::<uint>() * 8u / 2u;
    let div      = (1u << halfbits);

    let (hi1, lo1) = div_rem(n1, div);
    let (hi2, lo2) = div_rem(n2, div);
    let (mid12_hi, mid12_lo) = div_rem(lo1 * hi2, div);
    let (mid21_hi, mid21_lo) = div_rem(hi1 * lo2, div);
    let (lo_sum, lo_carry) = add_carry_multi([lo1 * lo2, mid12_lo * div, mid21_lo * div]);
    let (hi_sum, hi_carry) = add_carry_multi([hi1 * hi2, mid12_hi, mid21_hi, lo_carry]);
    assert hi_carry == 0u;
    return (lo_sum, hi_sum);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_div_rem() {
        let (d, m) = div_rem(1234u, 56u);
        assert d * 56u + m == 1234u;
    }

    #[test]
    fn test_add_carry() {
        fn check_add(n1: uint, n2: uint, sum: (uint, uint)) {
            assert add_carry(n1, n2) == sum;
            assert add_carry(n2, n1) == sum;
        }
        check_add(0u, 0u, (0u, 0u));
        check_add(0u, uint::max_value, (uint::max_value, 0u));
        check_add(1u, 3u, (1u + 3u, 0u));
        check_add(uint::max_value / 2u, uint::max_value / 2u, (uint::max_value - 1u, 0u));
        check_add(uint::max_value / 2u + 1u, uint::max_value / 2u, (uint::max_value, 0u));
        check_add(uint::max_value / 2u + 1u, uint::max_value / 2u + 1u, (0u, 1u));
        check_add(1000u, uint::max_value, (999u, 1u));
    }

    #[test]
    fn test_add_carry_multi() {
        assert add_carry_multi([0u, 0u, 0u]) == (0u, 0u);
        assert add_carry_multi([uint::max_value, uint::max_value, uint::max_value]) == (uint::max_value - 2u, 2u);
        assert add_carry_multi([uint::max_value, uint::max_value, uint::max_value, 2u]) == (uint::max_value, 2u);
        assert add_carry_multi([uint::max_value, uint::max_value, uint::max_value, 3u]) == (0u, 3u);
    }

    #[test]
    fn test_sub_borrow() {
        assert sub_borrow(0u, 0u) == (0u, 0u);
        assert sub_borrow(0u, 100u) == (uint::max_value - 99u, 1u);
        assert sub_borrow(100u, 100u) == (0u, 0u);
        assert sub_borrow(100u, 50u) == (50u, 0u);
        assert sub_borrow(100u, 200u) == (uint::max_value - 99u, 1u);
    }

    #[test]
    fn test_sub_borrow_multi() {
        assert sub_borrow_multi(0u, [0u, 0u, 0u, 0u]) == (0u, 0u);
        assert sub_borrow_multi(5u, [1u, 1u, 1u, 1u]) == (1u, 0u);
        assert sub_borrow_multi(0u, [1u, 1u, 1u, 1u]) == (uint::max_value - 3u, 1u);
        assert sub_borrow_multi(5u, [2u, 2u, 2u, 2u]) == (uint::max_value - 2u, 1u);
        assert sub_borrow_multi(0u, [uint::max_value, uint::max_value, uint::max_value, uint::max_value]) == (4u, 4u);
    }

    #[test]
    fn test_mul_carry() {
        fn check_mul(n1: uint, n2: uint, mul: (uint, uint)) {
            assert mul_carry(n1, n2) == mul;
            assert mul_carry(n2, n1) == mul;
        }

        check_mul(2u, 6u, (12u, 0u));

        fn check_mul_half(n: uint) {
            if n % 2u == 0u {
                // (2^(n-1) - 1) (2a)     = 2^n (a - 1) + (2^n - 1) - 2a + 1
                check_mul(uint::max_value / 2u, n, (uint::max_value - n + 1u, n / 2u - 1u));
            } else {
                // (2^(n-1) - 1) (2a + 1) = 2^n a + (2^(n-1) - 1) - 2a
                check_mul(uint::max_value / 2u, n, (uint::max_value / 2u - (n - 1u), n / 2u));
            }
        }
        check_mul_half(1u);
        check_mul_half(2u);
        check_mul_half(3u);
        check_mul_half(100u);

        // (2^n-1) a = 2^n(a - 1) + (2^n-1) - a + 1
        fn check_mul_full(n: uint) {
            check_mul(uint::max_value, n, (uint::max_value - n + 1u, n - 1u));
        }
        check_mul_full(1u);
        check_mul_full(2u);
        check_mul_full(3u);
        check_mul_full(0xFFFFFFFFu);
        check_mul_full(uint::max_value / 2u);
        check_mul_full(uint::max_value);
    }
}
<commit_msg>Remove unused functions of euler::arith<commit_after>pure fn isqrt(n: u64) -> u64 {
    let mut (min, max) = (0, n);
    while min < max {
        let mid = (min + max + 1) / 2;
        if (mid * mid) == n {
            return mid;
        }

        if (mid * mid) >= n {
            max = mid - 1;
        } else {
            min = mid;
        }
    }
    return min;
}
<|endoftext|>