<commit_before>use num::bigint::{RandBigInt, BigInt};
use num::{Zero, One, pow};
use std::num::{FromPrimitive};
use num::integer::Integer;
use rand::thread_rng;

/// Cryptographically useful extensions to the provided BigInt functionality.
pub trait BigIntCrypto {
    /// Find the next prime from the current BigInt
    fn next_prime(&self) -> BigInt;

    /// use the extended euclidean algorithm to solve for (g,x,y) given (a,b) such that
    /// g = gcd(a,b) = a*x + b*y.
    fn gcdext(&self, other: &BigInt) -> (BigInt, BigInt, BigInt);

    /// Is this number a prime number. Uses a probablistic function to determine primality.
    fn is_prime(n: &BigInt) -> bool;
}

impl BigIntCrypto for BigInt {
    fn next_prime(&self) -> BigInt {
        let one: BigInt = One::one();
        let two = BigInt::from_isize(2).unwrap();
        let mut nextPrime = self.clone();
        if &nextPrime % &two == Zero::zero() {
            nextPrime = &nextPrime + &one;
        } else {
            nextPrime = &nextPrime + &two;
        }
        while !BigInt::is_prime(&nextPrime) {
            nextPrime = &nextPrime + &two;
        }
        nextPrime
    }

    fn gcdext(&self, other: &BigInt) -> (BigInt, BigInt, BigInt) {

        (Zero::zero(), Zero::zero(), Zero::zero())
    }

    fn is_prime(n: &BigInt) -> bool {
        let two = BigInt::from_isize(2).unwrap();
        let three = BigInt::from_isize(3).unwrap();
        if *n == three || *n == two {
            return true;
        }
        if *n < two || n % two == Zero::zero() {
            return false;
        }
        miller_rabin(n, 100)
    }

}

/// n must be greater than 3 and k indicates the number of rounds
fn miller_rabin(n: &BigInt, k: usize) -> bool{
    let one: BigInt = One::one();
    let two: BigInt = &one + &one;
    let mut d: BigInt = n - &One::one();
    let mut s: BigInt = Zero::zero();
    while d.is_even() {
        d = d >> 1;
        s = s + &One::one();
    }

    for j in 0..k {
        //println!("loop {} of {}", j, k);
        let a = thread_rng().gen_bigint_range(&two, &(n - &two));
        let mut x = mod_exp(&a, &d, n);
        //let mut x = two.clone();
        if (x == one) || (x == (n - &one)) {
            continue;
        }

        // Use a while loop instead of for here because range does not accept BigInt
        let mut i: BigInt = Zero::zero();
        loop  {
            x = mod_exp(&x, &two, n);
            if x == one || i == (&s - &one) {
                return false;
            }
            if x == (n - &one) {
                break;
            }
            i = i + &one;
        }
    }
    true
}

fn mod_exp(base: &BigInt, exponent: &BigInt, modulus: &BigInt) -> BigInt {
    let zero = Zero::zero();
    let one: BigInt = One::one();
    let two = &one + &one;
    let mut result: BigInt = One::one();
    let mut base_acc = base.clone();
    let mut exp_acc = exponent.clone();
    while exp_acc > zero {
        if (&exp_acc % &two) == one {
            result = (result * &base_acc) % modulus;
        }
        exp_acc = exp_acc >> 1;
        base_acc = (&base_acc * &base_acc) % modulus;
    }
    result
}

mod test_bigint_crypto {
    use super::{BigIntCrypto, mod_exp};
    use num::bigint::{RandBigInt, BigInt};
    use std::num::FromPrimitive;
    use num::One;
    use rand::thread_rng;

    #[test]
    fn next_prime_test() {
        let test_num = BigInt::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875258".as_bytes(), 10).unwrap();

        let expected_next = BigInt::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875297".as_bytes(), 10).unwrap();

        assert!(test_num.next_prime() == expected_next);
    }

    #[test]
    fn mod_exp_test() {
        let base = BigInt::from_isize(4).unwrap();
        let exponent = BigInt::from_isize(13).unwrap();
        let modulus = BigInt::from_isize(497).unwrap();
        let expected_result = BigInt::from_isize(445).unwrap();

        assert!(mod_exp(&base, &exponent, &modulus) == expected_result);

        // time test, this takes a long time to do!
        let large_num = BigInt::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875297".as_bytes(), 10).unwrap();
        let a = thread_rng().gen_bigint_range(&One::one(), &large_num);
        let b = thread_rng().gen_bigint_range(&One::one(), &large_num);
        let c = thread_rng().gen_bigint_range(&One::one(), &large_num);
        for _ in 0..100 {
            mod_exp(&a, &b, &c);
        }
    }

    #[test]
    fn is_prime_test() {
        let known_prime = BigInt::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875297".as_bytes(), 10).unwrap();

        assert!(BigInt::is_prime(&known_prime));
    }

}
<commit_msg>Code cleanup.<commit_after>use num::bigint::{ToBigUint, RandBigInt, BigUint};
use num::{Zero, One};
use num::integer::Integer;
use rand::thread_rng;

/// Cryptographically useful extensions to the provided BigUint functionality.
pub trait BigUintCrypto {
    /// Find the next prime from the current BigUint
    fn next_prime(&self) -> BigUint;

    /// use the extended euclidean algorithm to solve for (g,x,y) given (a,b) such that
    /// g = gcd(a,b) = a*x + b*y.
    fn gcdext(&self, other: &BigUint) -> (BigUint, BigUint, BigUint);

    /// Is this number a prime number. Uses a probablistic function to determine primality.
    fn is_prime(n: &BigUint) -> bool;

    /// perform the function (base^exponent) % modulus using exponentiation by sqauring
    fn mod_exp(base: &BigUint, exponent: &BigUint, modulus: &BigUint) -> BigUint;
}

impl BigUintCrypto for BigUint {
    fn next_prime(&self) -> BigUint {
        let one: BigUint = One::one();
        let two = 2.to_biguint().unwrap();
        let mut next_prime = self.clone();
        if &next_prime % &two == Zero::zero() {
            next_prime = &next_prime + &one;
        } else {
            next_prime = &next_prime + &two;
        }
        while !BigUint::is_prime(&next_prime) {
            next_prime = &next_prime + &two;
        }
        next_prime
    }

    fn gcdext(&self, other: &BigUint) -> (BigUint, BigUint, BigUint) {

        (Zero::zero(), Zero::zero(), Zero::zero())
    }

    fn is_prime(n: &BigUint) -> bool {
        let two = 2.to_biguint().unwrap();
        let three = 3.to_biguint().unwrap();
        if *n == three || *n == two {
            return true;
        }
        if *n < two || n % two == Zero::zero() {
            return false;
        }
        miller_rabin(n, 100)
    }

    fn mod_exp(base: &BigUint, exponent: &BigUint, modulus: &BigUint) -> BigUint {
        let zero = Zero::zero();
        let one: BigUint = One::one();
        let two = &one + &one;
        let mut result: BigUint = One::one();
        let mut base_acc = base.clone();
        let mut exp_acc = exponent.clone();
        while exp_acc > zero {
            if (&exp_acc % &two) == one {
                result = (result * &base_acc) % modulus;
            }
            exp_acc = exp_acc >> 1;
            base_acc = (&base_acc * &base_acc) % modulus;
        }
        result
    }
}

/// n must be greater than 3 and k indicates the number of rounds
fn miller_rabin(n: &BigUint, k: usize) -> bool{
    let one: BigUint = One::one();
    let two: BigUint = &one + &one;
    let mut d: BigUint = n - &One::one();
    let mut s: BigUint = Zero::zero();
    while d.is_even() {
        d = d >> 1;
        s = s + &One::one();
    }

    for _ in 0..k {
        //println!("loop {} of {}", j, k);
        let a = thread_rng().gen_biguint_range(&two, &(n - &two));
        let mut x = mod_exp(&a, &d, n);
        //let mut x = two.clone();
        if (x == one) || (x == (n - &one)) {
            continue;
        }

        // Use a while loop instead of for here because range does not accept BigUint
        let mut i: BigUint = Zero::zero();
        loop  {
            x = mod_exp(&x, &two, n);
            if x == one || i == (&s - &one) {
                return false;
            }
            if x == (n - &one) {
                break;
            }
            i = i + &one;
        }
    }
    true
}

fn mod_exp(base: &BigUint, exponent: &BigUint, modulus: &BigUint) -> BigUint {
    let zero = Zero::zero();
    let one: BigUint = One::one();
    let two = &one + &one;
    let mut result: BigUint = One::one();
    let mut base_acc = base.clone();
    let mut exp_acc = exponent.clone();
    while exp_acc > zero {
        if (&exp_acc % &two) == one {
            result = (result * &base_acc) % modulus;
        }
        exp_acc = exp_acc >> 1;
        base_acc = (&base_acc * &base_acc) % modulus;
    }
    result
}

#[cfg(test)]
mod test_BigUint_crypto {
    use super::{BigUintCrypto, mod_exp};
    use num::bigint::{RandBigInt, BigUint};
    use std::num::FromPrimitive;
    use num::One;
    use rand::thread_rng;

    #[test]
    fn next_prime_test() {
        let test_num = BigUint::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875258".as_bytes(), 10).unwrap();

        let expected_next = BigUint::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875297".as_bytes(), 10).unwrap();

        assert!(test_num.next_prime() == expected_next);
    }

    #[test]
    fn mod_exp_test() {
        let base = BigUint::from_isize(4).unwrap();
        let exponent = BigUint::from_isize(13).unwrap();
        let modulus = BigUint::from_isize(497).unwrap();
        let expected_result = BigUint::from_isize(445).unwrap();

        assert!(mod_exp(&base, &exponent, &modulus) == expected_result);
    }

    #[test]
    fn is_prime_test() {
        let known_prime = BigUint::
        parse_bytes("4829837983753984028472098472089547098728675098723407520875297".as_bytes(), 10).unwrap();

        assert!(BigUint::is_prime(&known_prime));
    }

    #[test]
    #[should_fail]
    fn is_prime_test_failuire() {
        let not_prime = BigUint::
        parse_bytes("359709793871987301975987296195681798740165298740176567105918720469720137416098423"
        .as_bytes(), 10).unwrap();

        assert!(BigUint::is_prime(&not_prime));
    }

}
<|endoftext|>


<commit_before>/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
///
/// # Example
///
/// ```
/// use std::f32;
///
/// let m = 10.0_f32;
/// let x = 4.0_f32;
/// let b = 60.0_f32;
///
/// // 100.0
/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
///
/// assert!(abs_difference <= f32::EPSILON);
/// ```
pub trait MulAdd<A = Self, B = Self> {
    /// The resulting type after applying the fused multiply-add.
    type Output;

    /// Performs the fused multiply-add operation.
    fn mul_add(self, a: A, b: B) -> Self::Output;
}

/// The fused multiply-add assignment operation.
pub trait MulAddAssign<A = Self, B = Self> {
    /// Performs the fused multiply-add operation.
    fn mul_add_assign(&mut self, a: A, b: B);
}

#[cfg(feature = "std")]
impl MulAdd<f32, f32> for f32 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        f32::mul_add(self, a, b)
    }
}

#[cfg(feature = "std")]
impl MulAdd<f64, f64> for f64 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        f64::mul_add(self, a, b)
    }
}

macro_rules! mul_add_impl {
    ($trait_name:ident for $($t:ty)*) => {$(
        impl $trait_name for $t {
            type Output = Self;

            #[inline]
            fn mul_add(self, a: Self, b: Self) -> Self::Output {
                (self * a) + b
            }
        }
    )*}
}

mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_impl!(MulAdd for i128 u128);

#[cfg(feature = "std")]
impl MulAddAssign<f32, f32> for f32 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = f32::mul_add(*self, a, b)
    }
}

#[cfg(feature = "std")]
impl MulAddAssign<f64, f64> for f64 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = f64::mul_add(*self, a, b)
    }
}

macro_rules! mul_add_assign_impl {
    ($trait_name:ident for $($t:ty)*) => {$(
        impl $trait_name for $t {
            #[inline]
            fn mul_add_assign(&mut self, a: Self, b: Self) {
                *self = (*self * a) + b
            }
        }
    )*}
}

mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_assign_impl!(MulAddAssign for i128 u128);

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

    #[test]
    fn mul_add_integer() {
        macro_rules! test_mul_add {
            ($($t:ident)+) => {
                $(
                    {
                        let m: $t = 2;
                        let x: $t = 3;
                        let b: $t = 4;

                        assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
                    }
                )+
            };
        }

        test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
    }

    #[test]
    #[cfg(feature = "std")]
    fn mul_add_float() {
        macro_rules! test_mul_add {
            ($($t:ident)+) => {
                $(
                    {
                        use core::$t;

                        let m: $t = 12.0;
                        let x: $t = 3.4;
                        let b: $t = 5.6;

                        let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();

                        assert!(abs_difference <= $t::EPSILON);
                    }
                )+
            };
        }

        test_mul_add!(f32 f64);
    }
}
<commit_msg>Impl MulAdd+MulAssign with libm fallback<commit_after>/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
///
/// # Example
///
/// ```
/// use std::f32;
///
/// let m = 10.0_f32;
/// let x = 4.0_f32;
/// let b = 60.0_f32;
///
/// // 100.0
/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
///
/// assert!(abs_difference <= f32::EPSILON);
/// ```
pub trait MulAdd<A = Self, B = Self> {
    /// The resulting type after applying the fused multiply-add.
    type Output;

    /// Performs the fused multiply-add operation.
    fn mul_add(self, a: A, b: B) -> Self::Output;
}

/// The fused multiply-add assignment operation.
pub trait MulAddAssign<A = Self, B = Self> {
    /// Performs the fused multiply-add operation.
    fn mul_add_assign(&mut self, a: A, b: B);
}

#[cfg(feature = "std")]
impl MulAdd<f32, f32> for f32 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        f32::mul_add(self, a, b)
    }
}

#[cfg(feature = "std")]
impl MulAdd<f64, f64> for f64 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        f64::mul_add(self, a, b)
    }
}

#[cfg(all(not(feature = "std"), feature = "libm"))]
impl MulAdd<f32, f32> for f32 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        <f32 as ::libm::F32Ext>::mul_add(self, a, b)
    }
}

#[cfg(all(not(feature = "std"), feature = "libm"))]
impl MulAdd<f64, f64> for f64 {
    type Output = Self;

    #[inline]
    fn mul_add(self, a: Self, b: Self) -> Self::Output {
        <f64 as ::libm::F64Ext>::mul_add(self, a, b)
    }
}

macro_rules! mul_add_impl {
    ($trait_name:ident for $($t:ty)*) => {$(
        impl $trait_name for $t {
            type Output = Self;

            #[inline]
            fn mul_add(self, a: Self, b: Self) -> Self::Output {
                (self * a) + b
            }
        }
    )*}
}

mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_impl!(MulAdd for i128 u128);

#[cfg(feature = "std")]
impl MulAddAssign<f32, f32> for f32 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = f32::mul_add(*self, a, b)
    }
}

#[cfg(feature = "std")]
impl MulAddAssign<f64, f64> for f64 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = f64::mul_add(*self, a, b)
    }
}

#[cfg(all(not(feature = "std"), feature = "libm"))]
impl MulAddAssign<f32, f32> for f32 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = <f32 as ::libm::F32Ext>::mul_add(*self, a, b)
    }
}

#[cfg(all(not(feature = "std"), feature = "libm"))]
impl MulAddAssign<f64, f64> for f64 {
    #[inline]
    fn mul_add_assign(&mut self, a: Self, b: Self) {
        *self = <f64 as ::libm::F64Ext>::mul_add(*self, a, b)
    }
}

macro_rules! mul_add_assign_impl {
    ($trait_name:ident for $($t:ty)*) => {$(
        impl $trait_name for $t {
            #[inline]
            fn mul_add_assign(&mut self, a: Self, b: Self) {
                *self = (*self * a) + b
            }
        }
    )*}
}

mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_assign_impl!(MulAddAssign for i128 u128);

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

    #[test]
    fn mul_add_integer() {
        macro_rules! test_mul_add {
            ($($t:ident)+) => {
                $(
                    {
                        let m: $t = 2;
                        let x: $t = 3;
                        let b: $t = 4;

                        assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
                    }
                )+
            };
        }

        test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
    }

    #[test]
    #[cfg(feature = "std")]
    fn mul_add_float() {
        macro_rules! test_mul_add {
            ($($t:ident)+) => {
                $(
                    {
                        use core::$t;

                        let m: $t = 12.0;
                        let x: $t = 3.4;
                        let b: $t = 5.6;

                        let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();

                        assert!(abs_difference <= $t::EPSILON);
                    }
                )+
            };
        }

        test_mul_add!(f32 f64);
    }
}
<|endoftext|>


<commit_before>use std::marker;
use std::ptr;
use std::slice;
use libc::{c_int, c_uint, c_void, size_t};

use {raw, panic, Repository, Error, Oid, Revwalk, Buf};
use util::Binding;

/// Stages that are reported by the PackBuilder progress callback.
pub enum PackBuilderStage {
    /// Adding objects to the pack
    AddingObjects,
    /// Deltafication of the pack
    Deltafication,
}

pub type ProgressCb<'a> = FnMut(PackBuilderStage, u32, u32) -> bool + 'a;
pub type ForEachCb<'a> = FnMut(&[u8]) -> bool + 'a;

/// A builder for creating a packfile
pub struct PackBuilder<'repo> {
    raw: *mut raw::git_packbuilder,
    progress: Option<Box<Box<ProgressCb<'repo>>>>,
    _marker: marker::PhantomData<&'repo Repository>,
}

impl<'repo> PackBuilder<'repo> {
    /// Insert a single object. For an optimal pack it's mandatory to insert
    /// objects in recency order, commits followed by trees and blobs.
    pub fn insert_object(&mut self,
                         id: Oid,
                         name: Option<&str>)
                         -> Result<(), Error> {
        let name = try!(::opt_cstr(name));
        unsafe {
            try_call!(raw::git_packbuilder_insert(self.raw, &*id.raw(), name));
        }
        Ok(())
    }

    /// Insert a root tree object. This will add the tree as well as all
    /// referenced trees and blobs.
    pub fn insert_tree(&mut self, id: Oid) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_tree(self.raw, &*id.raw()));
        }
        Ok(())
    }

    /// Insert a commit object. This will add a commit as well as the completed
    /// referenced tree.
    pub fn insert_commit(&mut self, id: Oid) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_commit(self.raw, &*id.raw()));
        }
        Ok(())
    }

    /// Insert objects as given by the walk. Those commits and all objects they
    /// reference will be inserted into the packbuilder.
    pub fn insert_walk(&mut self, walk: &mut Revwalk) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_walk(self.raw, walk.raw()));
        }
        Ok(())
    }

    /// Recursively insert an object and its referenced objects. Insert the
    /// object as well as any object it references.
    pub fn insert_recursive(&mut self,
                            id: Oid,
                            name: Option<&str>)
                            -> Result<(), Error> {
        let name = try!(::opt_cstr(name));
        unsafe {
            try_call!(raw::git_packbuilder_insert_recur(self.raw,
                                                        &*id.raw(),
                                                        name));
        }
        Ok(())
    }

    /// Write the contents of the packfile to an in-memory buffer. The contents
    /// of the buffer will become a valid packfile, even though there will be
    /// no attached index.
    pub fn write_buf(&mut self, buf: &mut Buf) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_write_buf(buf.raw(), self.raw));
        }
        Ok(())
    }

    /// Create the new pack and pass each object to the callback.
    pub fn foreach<F>(&mut self, mut cb: F) -> Result<(), Error>
        where F: FnMut(&[u8]) -> bool {
        let mut cb = &mut cb as &mut ForEachCb;
        let ptr = &mut cb as *mut _;
        unsafe {
            try_call!(raw::git_packbuilder_foreach(self.raw,
                                                   foreach_c,
                                                   ptr as *mut _));
        }
        Ok(())
    }

    /// `progress` will be called with progress information during pack
    /// building. Be aware that this is called inline with pack building
    /// operations, so performance may be affected.
    ///
    /// There can only be one progress callback attached, this will replace any
    /// existing one. See `unset_progress_callback` to remove the current
    /// progress callback without attaching a new one.
    pub fn set_progress_callback<F>(&mut self, progress: F) -> Result<(), Error>
        where F: FnMut(PackBuilderStage, u32, u32) -> bool + 'repo {
        let mut progress = Box::new(Box::new(progress) as Box<ProgressCb>);
        let ptr = &mut *progress as *mut _;
        let progress_c = Some(progress_c as raw::git_packbuilder_progress);
        unsafe {
            try_call!(raw::git_packbuilder_set_callbacks(self.raw,
                                                         progress_c,
                                                         ptr as *mut _));
        }
        self.progress = Some(progress);
        Ok(())
    }

    /// Remove the current progress callback.  See `set_progress_callback` to
    /// set the progress callback.
    pub fn unset_progress_callback(&mut self) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_set_callbacks(self.raw,
                                                         None,
                                                         ptr::null_mut()));
            self.progress = None;
        }
        Ok(())
    }

    /// Get the total number of objects the packbuilder will write out.
    pub fn object_count(&self) -> usize {
        unsafe { raw::git_packbuilder_object_count(self.raw) as usize }
    }

    /// Get the number of objects the packbuilder has already written out.
    pub fn written(&self) -> usize {
        unsafe { raw::git_packbuilder_written(self.raw) as usize }
    }

    /// Get the packfile's hash. A packfile's name is derived from the sorted
    /// hashing of all object names. This is only correct after the packfile
    /// has been written.
    pub fn hash(&self) -> Option<Oid> {
        if self.object_count() == 0 {
            unsafe {
                Some(Binding::from_raw(raw::git_packbuilder_hash(self.raw)))
            }
        } else {
            None
        }
    }
}

impl<'repo> Binding for PackBuilder<'repo> {
    type Raw = *mut raw::git_packbuilder;
    unsafe fn from_raw(ptr: *mut raw::git_packbuilder) -> PackBuilder<'repo> {
        PackBuilder {
            raw: ptr,
            progress: None,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_packbuilder {
        self.raw
    }
}

impl<'repo> Drop for PackBuilder<'repo> {
    fn drop(&mut self) {
        unsafe {
            raw::git_packbuilder_set_callbacks(self.raw, None, ptr::null_mut());
            raw::git_packbuilder_free(self.raw);
        }
    }
}

impl Binding for PackBuilderStage {
    type Raw = raw::git_packbuilder_stage_t;
    unsafe fn from_raw(raw: raw::git_packbuilder_stage_t) -> PackBuilderStage {
        match raw {
            raw::GIT_PACKBUILDER_ADDING_OBJECTS => PackBuilderStage::AddingObjects,
            raw::GIT_PACKBUILDER_DELTAFICATION => PackBuilderStage::Deltafication,
            _ => panic!("Unknown git diff binary kind"),
        }
    }
    fn raw(&self) -> raw::git_packbuilder_stage_t {
        match *self {
            PackBuilderStage::AddingObjects => raw::GIT_PACKBUILDER_ADDING_OBJECTS,
            PackBuilderStage::Deltafication => raw::GIT_PACKBUILDER_DELTAFICATION,
        }
    }
}

extern "C" fn foreach_c(buf: *const c_void,
                        size: size_t,
                        data: *mut c_void)
                        -> c_int {
    unsafe {
        let buf = slice::from_raw_parts(buf as *const u8, size as usize);

        let r = panic::wrap(|| {
            let data = data as *mut &mut ForEachCb;
            (*data)(buf)
        });
        if r == Some(true) {
            0
        } else {
            -1
        }
    }
}

extern "C" fn progress_c(stage: raw::git_packbuilder_stage_t,
                         current: c_uint,
                         total: c_uint,
                         data: *mut c_void)
                         -> c_int {
    unsafe {
        let stage = Binding::from_raw(stage);

        let r = panic::wrap(|| {
            let data = data as *mut Box<ProgressCb>;
            (*data)(stage, current, total)
        });
        if r == Some(true) {
            0
        } else {
            -1
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::path::Path;
    use {Buf, Repository, Oid};

    fn commit(repo: &Repository) -> (Oid, Oid) {
        let mut index = t!(repo.index());
        let root = repo.path().parent().unwrap();
        t!(File::create(&root.join("foo")));
        t!(index.add_path(Path::new("foo")));

        let tree_id = t!(index.write_tree());
        let tree = t!(repo.find_tree(tree_id));
        let sig = t!(repo.signature());
        let head_id = t!(repo.refname_to_id("HEAD"));
        let parent = t!(repo.find_commit(head_id));
        let commit = t!(repo.commit(Some("HEAD"),
                                    &sig,
                                    &sig,
                                    "commit",
                                    &tree,
                                    &[&parent]));
        (commit, tree_id)
    }

    fn pack_header(len: u8) -> Vec<u8> {
        [].into_iter()
          .chain(b"PACK") // signature
          .chain(&[0, 0, 0, 2]) // version number
          .chain(&[0, 0, 0, len]) // number of objects
          .cloned().collect::<Vec<u8>>()
    }

    fn empty_pack_header() -> Vec<u8> {
        pack_header(0).iter()
          .chain(&[0x02, 0x9d, 0x08, 0x82, 0x3b,  // ^
                   0xd8, 0xa8, 0xea, 0xb5, 0x10,  // | SHA-1 of the zero
                   0xad, 0x6a, 0xc7, 0x5c, 0x82,  // | object pack header
                   0x3c, 0xfd, 0x3e, 0xd3, 0x1e]) // v
          .cloned().collect::<Vec<u8>>()
    }

    #[test]
    fn smoke() {
        let (_td, repo) = ::test::repo_init();
        let _builder = t!(repo.packbuilder());
    }

    #[test]
    fn smoke_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        t!(builder.write_buf(&mut buf));
        assert!(builder.hash().unwrap().is_zero());
        assert_eq!(&*buf, &*empty_pack_header());
    }

    #[test]
    fn smoke_foreach() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Vec::<u8>::new();
        t!(builder.foreach(|bytes| {
            buf.extend(bytes);
            true
        }));
        assert_eq!(&*buf, &*empty_pack_header());
    }

    #[test]
    fn insert_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (commit, _tree) = commit(&repo);
        t!(builder.insert_object(commit, None));
        assert_eq!(builder.object_count(), 1);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(1));
    }

    #[test]
    fn insert_tree_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (_commit, tree) = commit(&repo);
        // will insert the tree itself and the blob, 2 objects
        t!(builder.insert_tree(tree));
        assert_eq!(builder.object_count(), 2);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(2));
    }

    #[test]
    fn insert_commit_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (commit, _tree) = commit(&repo);
        // will insert the commit, its tree and the blob, 3 objects
        t!(builder.insert_commit(commit));
        assert_eq!(builder.object_count(), 3);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(3));
    }

    #[test]
    fn progress_callback() {
        let mut progress_called = false;
        {
            let (_td, repo) = ::test::repo_init();
            let mut builder = t!(repo.packbuilder());
            let (commit, _tree) = commit(&repo);
            t!(builder.set_progress_callback(|_, _, _| {
                progress_called = true;
                true
            }));
            t!(builder.insert_commit(commit));
            t!(builder.write_buf(&mut Buf::new()));
        }
        assert_eq!(progress_called, true);
    }

    #[test]
    fn clear_progress_callback() {
        let mut progress_called = false;
        {
            let (_td, repo) = ::test::repo_init();
            let mut builder = t!(repo.packbuilder());
            let (commit, _tree) = commit(&repo);
            t!(builder.set_progress_callback(|_, _, _| {
                progress_called = true;
                true
            }));
            t!(builder.unset_progress_callback());
            t!(builder.insert_commit(commit));
            t!(builder.write_buf(&mut Buf::new()));
        }
        assert_eq!(progress_called, false);
    }
}
<commit_msg>Touch up a few bits of style<commit_after>use std::marker;
use std::ptr;
use std::slice;
use libc::{c_int, c_uint, c_void, size_t};

use {raw, panic, Repository, Error, Oid, Revwalk, Buf};
use util::Binding;

/// Stages that are reported by the PackBuilder progress callback.
pub enum PackBuilderStage {
    /// Adding objects to the pack
    AddingObjects,
    /// Deltafication of the pack
    Deltafication,
}

pub type ProgressCb<'a> = FnMut(PackBuilderStage, u32, u32) -> bool + 'a;
pub type ForEachCb<'a> = FnMut(&[u8]) -> bool + 'a;

/// A builder for creating a packfile
pub struct PackBuilder<'repo> {
    raw: *mut raw::git_packbuilder,
    progress: Option<Box<Box<ProgressCb<'repo>>>>,
    _marker: marker::PhantomData<&'repo Repository>,
}

impl<'repo> PackBuilder<'repo> {
    /// Insert a single object. For an optimal pack it's mandatory to insert
    /// objects in recency order, commits followed by trees and blobs.
    pub fn insert_object(&mut self, id: Oid, name: Option<&str>)
                         -> Result<(), Error> {
        let name = try!(::opt_cstr(name));
        unsafe {
            try_call!(raw::git_packbuilder_insert(self.raw, id.raw(), name));
        }
        Ok(())
    }

    /// Insert a root tree object. This will add the tree as well as all
    /// referenced trees and blobs.
    pub fn insert_tree(&mut self, id: Oid) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_tree(self.raw, id.raw()));
        }
        Ok(())
    }

    /// Insert a commit object. This will add a commit as well as the completed
    /// referenced tree.
    pub fn insert_commit(&mut self, id: Oid) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_commit(self.raw, id.raw()));
        }
        Ok(())
    }

    /// Insert objects as given by the walk. Those commits and all objects they
    /// reference will be inserted into the packbuilder.
    pub fn insert_walk(&mut self, walk: &mut Revwalk) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_insert_walk(self.raw, walk.raw()));
        }
        Ok(())
    }

    /// Recursively insert an object and its referenced objects. Insert the
    /// object as well as any object it references.
    pub fn insert_recursive(&mut self, id: Oid, name: Option<&str>)
                            -> Result<(), Error> {
        let name = try!(::opt_cstr(name));
        unsafe {
            try_call!(raw::git_packbuilder_insert_recur(self.raw,
                                                        id.raw(),
                                                        name));
        }
        Ok(())
    }

    /// Write the contents of the packfile to an in-memory buffer. The contents
    /// of the buffer will become a valid packfile, even though there will be
    /// no attached index.
    pub fn write_buf(&mut self, buf: &mut Buf) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_write_buf(buf.raw(), self.raw));
        }
        Ok(())
    }

    /// Create the new pack and pass each object to the callback.
    pub fn foreach<F>(&mut self, mut cb: F) -> Result<(), Error>
        where F: FnMut(&[u8]) -> bool
    {
        let mut cb = &mut cb as &mut ForEachCb;
        let ptr = &mut cb as *mut _;
        unsafe {
            try_call!(raw::git_packbuilder_foreach(self.raw,
                                                   foreach_c,
                                                   ptr as *mut _));
        }
        Ok(())
    }

    /// `progress` will be called with progress information during pack
    /// building. Be aware that this is called inline with pack building
    /// operations, so performance may be affected.
    ///
    /// There can only be one progress callback attached, this will replace any
    /// existing one. See `unset_progress_callback` to remove the current
    /// progress callback without attaching a new one.
    pub fn set_progress_callback<F>(&mut self, progress: F) -> Result<(), Error>
        where F: FnMut(PackBuilderStage, u32, u32) -> bool + 'repo
    {
        let mut progress = Box::new(Box::new(progress) as Box<ProgressCb>);
        let ptr = &mut *progress as *mut _;
        let progress_c = Some(progress_c as raw::git_packbuilder_progress);
        unsafe {
            try_call!(raw::git_packbuilder_set_callbacks(self.raw,
                                                         progress_c,
                                                         ptr as *mut _));
        }
        self.progress = Some(progress);
        Ok(())
    }

    /// Remove the current progress callback.  See `set_progress_callback` to
    /// set the progress callback.
    pub fn unset_progress_callback(&mut self) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_packbuilder_set_callbacks(self.raw,
                                                         None,
                                                         ptr::null_mut()));
            self.progress = None;
        }
        Ok(())
    }

    /// Get the total number of objects the packbuilder will write out.
    pub fn object_count(&self) -> usize {
        unsafe { raw::git_packbuilder_object_count(self.raw) as usize }
    }

    /// Get the number of objects the packbuilder has already written out.
    pub fn written(&self) -> usize {
        unsafe { raw::git_packbuilder_written(self.raw) as usize }
    }

    /// Get the packfile's hash. A packfile's name is derived from the sorted
    /// hashing of all object names. This is only correct after the packfile
    /// has been written.
    pub fn hash(&self) -> Option<Oid> {
        if self.object_count() == 0 {
            unsafe {
                Some(Binding::from_raw(raw::git_packbuilder_hash(self.raw)))
            }
        } else {
            None
        }
    }
}

impl<'repo> Binding for PackBuilder<'repo> {
    type Raw = *mut raw::git_packbuilder;
    unsafe fn from_raw(ptr: *mut raw::git_packbuilder) -> PackBuilder<'repo> {
        PackBuilder {
            raw: ptr,
            progress: None,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_packbuilder {
        self.raw
    }
}

impl<'repo> Drop for PackBuilder<'repo> {
    fn drop(&mut self) {
        unsafe {
            raw::git_packbuilder_set_callbacks(self.raw, None, ptr::null_mut());
            raw::git_packbuilder_free(self.raw);
        }
    }
}

impl Binding for PackBuilderStage {
    type Raw = raw::git_packbuilder_stage_t;
    unsafe fn from_raw(raw: raw::git_packbuilder_stage_t) -> PackBuilderStage {
        match raw {
            raw::GIT_PACKBUILDER_ADDING_OBJECTS => PackBuilderStage::AddingObjects,
            raw::GIT_PACKBUILDER_DELTAFICATION => PackBuilderStage::Deltafication,
            _ => panic!("Unknown git diff binary kind"),
        }
    }
    fn raw(&self) -> raw::git_packbuilder_stage_t {
        match *self {
            PackBuilderStage::AddingObjects => raw::GIT_PACKBUILDER_ADDING_OBJECTS,
            PackBuilderStage::Deltafication => raw::GIT_PACKBUILDER_DELTAFICATION,
        }
    }
}

extern fn foreach_c(buf: *const c_void,
                    size: size_t,
                    data: *mut c_void)
                    -> c_int {
    unsafe {
        let buf = slice::from_raw_parts(buf as *const u8, size as usize);

        let r = panic::wrap(|| {
            let data = data as *mut &mut ForEachCb;
            (*data)(buf)
        });
        if r == Some(true) {
            0
        } else {
            -1
        }
    }
}

extern fn progress_c(stage: raw::git_packbuilder_stage_t,
                     current: c_uint,
                     total: c_uint,
                     data: *mut c_void)
                     -> c_int {
    unsafe {
        let stage = Binding::from_raw(stage);

        let r = panic::wrap(|| {
            let data = data as *mut Box<ProgressCb>;
            (*data)(stage, current, total)
        });
        if r == Some(true) {
            0
        } else {
            -1
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::path::Path;
    use {Buf, Repository, Oid};

    fn commit(repo: &Repository) -> (Oid, Oid) {
        let mut index = t!(repo.index());
        let root = repo.path().parent().unwrap();
        t!(File::create(&root.join("foo")));
        t!(index.add_path(Path::new("foo")));

        let tree_id = t!(index.write_tree());
        let tree = t!(repo.find_tree(tree_id));
        let sig = t!(repo.signature());
        let head_id = t!(repo.refname_to_id("HEAD"));
        let parent = t!(repo.find_commit(head_id));
        let commit = t!(repo.commit(Some("HEAD"),
                                    &sig,
                                    &sig,
                                    "commit",
                                    &tree,
                                    &[&parent]));
        (commit, tree_id)
    }

    fn pack_header(len: u8) -> Vec<u8> {
        [].into_iter()
          .chain(b"PACK") // signature
          .chain(&[0, 0, 0, 2]) // version number
          .chain(&[0, 0, 0, len]) // number of objects
          .cloned().collect::<Vec<u8>>()
    }

    fn empty_pack_header() -> Vec<u8> {
        pack_header(0).iter()
          .chain(&[0x02, 0x9d, 0x08, 0x82, 0x3b,  // ^
                   0xd8, 0xa8, 0xea, 0xb5, 0x10,  // | SHA-1 of the zero
                   0xad, 0x6a, 0xc7, 0x5c, 0x82,  // | object pack header
                   0x3c, 0xfd, 0x3e, 0xd3, 0x1e]) // v
          .cloned().collect::<Vec<u8>>()
    }

    #[test]
    fn smoke() {
        let (_td, repo) = ::test::repo_init();
        let _builder = t!(repo.packbuilder());
    }

    #[test]
    fn smoke_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        t!(builder.write_buf(&mut buf));
        assert!(builder.hash().unwrap().is_zero());
        assert_eq!(&*buf, &*empty_pack_header());
    }

    #[test]
    fn smoke_foreach() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Vec::<u8>::new();
        t!(builder.foreach(|bytes| {
            buf.extend(bytes);
            true
        }));
        assert_eq!(&*buf, &*empty_pack_header());
    }

    #[test]
    fn insert_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (commit, _tree) = commit(&repo);
        t!(builder.insert_object(commit, None));
        assert_eq!(builder.object_count(), 1);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(1));
    }

    #[test]
    fn insert_tree_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (_commit, tree) = commit(&repo);
        // will insert the tree itself and the blob, 2 objects
        t!(builder.insert_tree(tree));
        assert_eq!(builder.object_count(), 2);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(2));
    }

    #[test]
    fn insert_commit_write_buf() {
        let (_td, repo) = ::test::repo_init();
        let mut builder = t!(repo.packbuilder());
        let mut buf = Buf::new();
        let (commit, _tree) = commit(&repo);
        // will insert the commit, its tree and the blob, 3 objects
        t!(builder.insert_commit(commit));
        assert_eq!(builder.object_count(), 3);
        t!(builder.write_buf(&mut buf));
        // Just check that the correct number of objects are written
        assert_eq!(&buf[0..12], &*pack_header(3));
    }

    #[test]
    fn progress_callback() {
        let mut progress_called = false;
        {
            let (_td, repo) = ::test::repo_init();
            let mut builder = t!(repo.packbuilder());
            let (commit, _tree) = commit(&repo);
            t!(builder.set_progress_callback(|_, _, _| {
                progress_called = true;
                true
            }));
            t!(builder.insert_commit(commit));
            t!(builder.write_buf(&mut Buf::new()));
        }
        assert_eq!(progress_called, true);
    }

    #[test]
    fn clear_progress_callback() {
        let mut progress_called = false;
        {
            let (_td, repo) = ::test::repo_init();
            let mut builder = t!(repo.packbuilder());
            let (commit, _tree) = commit(&repo);
            t!(builder.set_progress_callback(|_, _, _| {
                progress_called = true;
                true
            }));
            t!(builder.unset_progress_callback());
            t!(builder.insert_commit(commit));
            t!(builder.write_buf(&mut Buf::new()));
        }
        assert_eq!(progress_called, false);
    }
}
<|endoftext|>


<commit_before>use crate::series::Series;
use crate::tui::{CurrentAction, SeriesStatus, UIState};
use crate::util;
use chrono::Utc;
use smallvec::SmallVec;
use std::borrow::Cow;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Modifier, Style};
use tui::terminal::Frame;
use tui::widgets::{Block, Borders, Paragraph, Text};

pub struct InfoPanel;

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $value, $header.len()),
        )+]
    };

    (h $header:expr) => {
        Text::styled(concat!($header, "\n"), Style::default().modifier(Modifier::BOLD))
    };

    (v $value:expr, $len:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

impl InfoPanel {
    pub fn new() -> Self {
        Self {}
    }

    pub fn draw<B>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let info_block = Block::default().title("Info").borders(Borders::ALL);
        frame.render_widget(info_block, rect);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        match state.series.selected() {
            Some(SeriesStatus::Loaded(series)) => {
                Self::draw_series_info(state, series, &info_layout, frame)
            }
            Some(SeriesStatus::Unloaded(_)) => (),
            None => {
                let header = [Text::styled(
                    "No Series Found",
                    Style::default().modifier(Modifier::BOLD),
                )];

                let header_pg = Paragraph::new(header.iter()).alignment(Alignment::Center);
                frame.render_widget(header_pg, info_layout[0]);

                let body = [Text::raw(
                    "Add one by pressing ':' and using the 'add' command \

                    \n\nThe command requires a nickname for the series \
                    \nyou want to add. For automatic detection, the nickname \
                    \nshould be similar to the name of the folder the series \
                    \nis in on disk. Any tags in the series folder name \
                    \nshould not affect the detection algorithm. \

                    \n\nIf you don't care about automatic detection, \
                    \nyou can set the ID of the series by appending 'id=<value>' \
                    \nto the 'add' command and use any nickname you wish. \

                    \n\nBy default, the program will look for series in '~/anime/'. \
                    \nYou can change this in `~/.config/anup/config.toml`. \
                    \nYou can also manually specify the path to a series by appending \
                    \n'path=\"<value>\"' to the 'add' command when adding a new series, \
                    \nor the 'set' command when modifying an existing one.",
                )];

                let body_pg = Paragraph::new(body.iter())
                    .alignment(Alignment::Center)
                    .wrap(true);
                frame.render_widget(body_pg, info_layout[1]);
            }
        }
    }

    fn draw_series_info<B>(state: &UIState, series: &Series, layout: &[Rect], frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title_preferred,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            let widget = Paragraph::new(text_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => {
                    let watch_time_mins = info.episodes * info.episode_length_mins;
                    util::hm_from_mins(watch_time_mins as f32)
                },
                "Time Left" => {
                    let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
                    let time_left_mins = eps_left * info.episode_length_mins;

                    util::hm_from_mins(time_left_mins as f32)
                },
                "Episode Length" => format!("{}M", info.episode_length_mins)
            );

            let widget = Paragraph::new(left_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => format!("{}|{}", entry.watched_episodes(), info.episodes),
                "Score" => match entry.score() {
                    Some(score) => state.remote.score_to_str(score as u8),
                    None => "??".into(),
                },
                "Status" => entry.status()
            );

            let widget = Paragraph::new(center_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => match entry.start_date() {
                    Some(date) => format!("{}", date.format("%D")).into(),
                    None => Cow::Borrowed("??"),
                },
                "Finish Date" => match entry.end_date() {
                    Some(date) => format!("{}", date.format("%D")).into(),
                    None => Cow::Borrowed("??"),
                },
                "Rewatched" => entry.times_rewatched()
            );

            let widget = Paragraph::new(right_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        if let CurrentAction::WatchingEpisode(progress_time, _) = state.current_action {
            let watch_time = progress_time - Utc::now();
            let watch_secs = watch_time.num_seconds();

            if watch_secs > 0 {
                let remaining_mins = watch_secs as f32 / 60.0;

                let text_str = format!(
                    "{} Remaining Until Progression",
                    util::ms_from_mins(remaining_mins)
                );

                let text = [Text::styled(
                    text_str,
                    Style::default().modifier(Modifier::BOLD),
                )];

                let widget = Paragraph::new(text.iter()).alignment(Alignment::Center);
                frame.render_widget(widget, layout[2]);
            }
        }
    }
}
<commit_msg>anup: Update help message shown when no series are present<commit_after>use crate::series::Series;
use crate::tui::{CurrentAction, SeriesStatus, UIState};
use crate::util;
use chrono::Utc;
use smallvec::SmallVec;
use std::borrow::Cow;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Modifier, Style};
use tui::terminal::Frame;
use tui::widgets::{Block, Borders, Paragraph, Text};

pub struct InfoPanel;

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $value, $header.len()),
        )+]
    };

    (h $header:expr) => {
        Text::styled(concat!($header, "\n"), Style::default().modifier(Modifier::BOLD))
    };

    (v $value:expr, $len:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

impl InfoPanel {
    pub fn new() -> Self {
        Self {}
    }

    pub fn draw<B>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let info_block = Block::default().title("Info").borders(Borders::ALL);
        frame.render_widget(info_block, rect);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        match state.series.selected() {
            Some(SeriesStatus::Loaded(series)) => {
                Self::draw_series_info(state, series, &info_layout, frame)
            }
            Some(SeriesStatus::Unloaded(_)) => (),
            None => {
                let header = [Text::styled(
                    "No Series Found",
                    Style::default().modifier(Modifier::BOLD),
                )];

                let header_pg = Paragraph::new(header.iter()).alignment(Alignment::Center);
                frame.render_widget(header_pg, info_layout[0]);

                let body = [Text::raw(
                    "Add one by pressing the 'a' key\

                    \n\nThe opened panel will require you to specify\
                    \n a name for the series you want to add.\
                    \n\nFor automatic detection, the name should be\
                    \nsimilar to the name of the folder the series\
                    \nis in on disk.",
                )];

                let body_pg = Paragraph::new(body.iter())
                    .alignment(Alignment::Center)
                    .wrap(true);
                frame.render_widget(body_pg, info_layout[1]);
            }
        }
    }

    fn draw_series_info<B>(state: &UIState, series: &Series, layout: &[Rect], frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title_preferred,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            let widget = Paragraph::new(text_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => {
                    let watch_time_mins = info.episodes * info.episode_length_mins;
                    util::hm_from_mins(watch_time_mins as f32)
                },
                "Time Left" => {
                    let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
                    let time_left_mins = eps_left * info.episode_length_mins;

                    util::hm_from_mins(time_left_mins as f32)
                },
                "Episode Length" => format!("{}M", info.episode_length_mins)
            );

            let widget = Paragraph::new(left_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => format!("{}|{}", entry.watched_episodes(), info.episodes),
                "Score" => match entry.score() {
                    Some(score) => state.remote.score_to_str(score as u8),
                    None => "??".into(),
                },
                "Status" => entry.status()
            );

            let widget = Paragraph::new(center_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => match entry.start_date() {
                    Some(date) => format!("{}", date.format("%D")).into(),
                    None => Cow::Borrowed("??"),
                },
                "Finish Date" => match entry.end_date() {
                    Some(date) => format!("{}", date.format("%D")).into(),
                    None => Cow::Borrowed("??"),
                },
                "Rewatched" => entry.times_rewatched()
            );

            let widget = Paragraph::new(right_items.iter()).alignment(Alignment::Center);
            frame.render_widget(widget, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        if let CurrentAction::WatchingEpisode(progress_time, _) = state.current_action {
            let watch_time = progress_time - Utc::now();
            let watch_secs = watch_time.num_seconds();

            if watch_secs > 0 {
                let remaining_mins = watch_secs as f32 / 60.0;

                let text_str = format!(
                    "{} Remaining Until Progression",
                    util::ms_from_mins(remaining_mins)
                );

                let text = [Text::styled(
                    text_str,
                    Style::default().modifier(Modifier::BOLD),
                )];

                let widget = Paragraph::new(text.iter()).alignment(Alignment::Center);
                frame.render_widget(widget, layout[2]);
            }
        }
    }
}
<|endoftext|>


<commit_before>struct Chip8 {
    current_opcode: u16,
    // Memory of the emulator
    memory: [u8; 4096],

    // CPU registers (From 0 to E)
    v: [u8; 16],

    index_register: u16,
    program_counter: u16,

    // 64 x 32 screen of black-and-white pixels
    // Either on or off.
    graphics: [[u8; 64]; 32],

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    stack: [u16; 16],
    stack_pointer: u16,

    // Whichever keys are pressed
    keys: [u8; 16],
}

impl Chip8 {
    fn setup_graphics(&self) {

    }

    fn setup_input(&self) {

    }

    fn load_game(&self) {

    }

    fn emulate_cycle(&mut self) {
        let pc = self.program_counter as usize;

        let current_opcode: u16 = (self.memory[pc] as u16) << 8 | (self.memory[pc + 1] as u16);

        if self.delay_timer > 0 {
            self.delay_timer = self.delay_timer - 1;
        }

        if self.sound_timer > 0 {
            if self.sound_timer == 1 {
                println!("BEEPS")
            }

            self.sound_timer = self.sound_timer - 1;
        }
    }

    fn draw_graphics(&self) {

    }

    fn set_key_state(&self) {

    }
}

fn main() {
    let mut chip8: Chip8 = Chip8 {
        current_opcode:  0,

        memory: [0; 4096],

        v: [0; 16],

        index_register:  0x200,
        program_counter: 0x200,

        graphics: [[0; 64]; 32],

        delay_timer: 0,
        sound_timer: 0,

        stack: [0; 16],
        stack_pointer: 0,

        keys: [0; 16]
    };

    chip8.setup_graphics();

    chip8.setup_input();

    chip8.load_game();

    loop {
        chip8.emulate_cycle();

        chip8.draw_graphics();

        chip8.set_key_state();
    }
}
<commit_msg>Load font into memory, persist own opcode<commit_after>const FONT_SET: [u8; 80] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
];

struct Chip8 {
    current_opcode: u16,
    // Memory of the emulator
    memory: [u8; 4096],

    // CPU registers (From 0 to E)
    v: [u8; 16],

    index_register: u16,
    program_counter: u16,

    // 64 x 32 screen of black-and-white pixels
    // Either on or off.
    graphics: [[u8; 64]; 32],

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    stack: [u16; 16],
    stack_pointer: u16,

    // Whichever keys are pressed
    keys: [u8; 16],
}

impl Chip8 {
    fn load_font_set(&mut self) {
        for i in 0..80 {
            self.memory[i] = font_set[i];
        }
    }

    fn setup_graphics(&self) {

    }

    fn setup_input(&self) {

    }

    fn load_game(&self) {

    }

    fn emulate_cycle(&mut self) {
        let pc = self.program_counter as usize;

        self.current_opcode = (self.memory[pc] as u16) << 8 | (self.memory[pc + 1] as u16);

        // TODO
        match self.current_opcode & 0xf000 {
         _ => println!("Opcode not implemented: {:X}", self.current_opcode)
        }

        if self.delay_timer > 0 {
            self.delay_timer = self.delay_timer - 1;
        }

        if self.sound_timer > 0 {
            if self.sound_timer == 1 {
                println!("BEEPS")
            }

            self.sound_timer = self.sound_timer - 1;
        }
    }

    fn draw_graphics(&self) {

    }

    fn set_key_state(&self) {

    }
}

fn main() {
    let mut chip8: Chip8 = Chip8 {
        current_opcode:  0,

        memory: [0; 4096],

        v: [0; 16],

        index_register:  0x200,
        program_counter: 0x200,

        graphics: [[0; 64]; 32],

        delay_timer: 0,
        sound_timer: 0,

        stack: [0; 16],
        stack_pointer: 0,

        keys: [0; 16]
    };

    chip8.load_font_set();

    chip8.setup_graphics();

    chip8.setup_input();

    chip8.load_game();

    loop {
        chip8.emulate_cycle();

        chip8.draw_graphics();

        chip8.set_key_state();
    }
}
<|endoftext|>


<commit_before>use crate::err::Result;
use crate::file::SaveDir;
use diesel::connection::SimpleConnection;
use diesel::deserialize::{self, FromSql};
use diesel::prelude::*;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::{Nullable, Text};
use smallvec::SmallVec;
use std::io::Write;
use std::ops::Deref;
use std::path::PathBuf;

pub mod schema {
    table! {
        series_configs {
            id -> Integer,
            nickname -> Text,
            path -> Text,
            episode_matcher -> Nullable<Text>,
            player_args -> Nullable<Text>,
        }
    }

    table! {
        series_info {
            id -> Integer,
            title_preferred -> Text,
            title_romaji -> Text,
            episodes -> SmallInt,
            episode_length_mins -> SmallInt,
            sequel -> Nullable<Integer>,
        }
    }

    table! {
        series_entries {
            id -> Integer,
            watched_episodes -> SmallInt,
            score -> Nullable<SmallInt>,
            status -> SmallInt,
            times_rewatched -> SmallInt,
            start_date -> Nullable<Date>,
            end_date -> Nullable<Date>,
            needs_sync -> Bool,
        }
    }
}

pub struct Database(SqliteConnection);

impl Database {
    pub fn open() -> Result<Self> {
        let path = Self::validated_path()?;
        let conn = SqliteConnection::establish(&path.to_string_lossy())?;

        conn.batch_execute(include_str!("../sql/schema.sql"))?;

        Ok(Self(conn))
    }

    pub fn validated_path() -> Result<PathBuf> {
        let mut path = SaveDir::LocalData.validated_dir_path()?.to_path_buf();
        path.push("data.sqlite");
        Ok(path)
    }

    #[inline(always)]
    pub fn conn(&self) -> &SqliteConnection {
        &self.0
    }
}

impl Drop for Database {
    fn drop(&mut self) {
        self.conn().execute("PRAGMA optimize").ok();
    }
}

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct Path(PathBuf);

impl<DB> FromSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        let path = String::from_sql(bytes)?.into();
        Ok(Self(path))
    }
}

impl<DB> ToSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    str: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.to_string_lossy();
        value.to_sql(out)
    }
}

impl From<PathBuf> for Path {
    fn from(value: PathBuf) -> Self {
        Self(value)
    }
}

impl Deref for Path {
    type Target = PathBuf;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Clone, Debug, Default, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct PlayerArgs(SmallVec<[String; 3]>);

impl PlayerArgs {
    #[inline(always)]
    pub fn new() -> Self {
        Self(SmallVec::new())
    }
}

impl<DB> FromSql<Nullable<Text>, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        match bytes {
            Some(_) => {
                let args = String::from_sql(bytes)?
                    .split(";;")
                    .map(Into::into)
                    .collect();

                Ok(Self(args))
            }
            None => Ok(Self::new()),
        }
    }
}

impl<DB> ToSql<Text, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.join(";;");
        value.to_sql(out)
    }
}

impl AsRef<SmallVec<[String; 3]>> for PlayerArgs {
    fn as_ref(&self) -> &SmallVec<[String; 3]> {
        &self.0
    }
}

impl From<SmallVec<[String; 3]>> for PlayerArgs {
    fn from(value: SmallVec<[String; 3]>) -> Self {
        Self(value)
    }
}

impl Deref for PlayerArgs {
    type Target = SmallVec<[String; 3]>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<commit_msg>anup: derive AsRef<std::path::Path> for Path<commit_after>use crate::err::Result;
use crate::file::SaveDir;
use diesel::connection::SimpleConnection;
use diesel::deserialize::{self, FromSql};
use diesel::prelude::*;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::{Nullable, Text};
use smallvec::SmallVec;
use std::io::Write;
use std::ops::Deref;
use std::path::PathBuf;

pub mod schema {
    table! {
        series_configs {
            id -> Integer,
            nickname -> Text,
            path -> Text,
            episode_matcher -> Nullable<Text>,
            player_args -> Nullable<Text>,
        }
    }

    table! {
        series_info {
            id -> Integer,
            title_preferred -> Text,
            title_romaji -> Text,
            episodes -> SmallInt,
            episode_length_mins -> SmallInt,
            sequel -> Nullable<Integer>,
        }
    }

    table! {
        series_entries {
            id -> Integer,
            watched_episodes -> SmallInt,
            score -> Nullable<SmallInt>,
            status -> SmallInt,
            times_rewatched -> SmallInt,
            start_date -> Nullable<Date>,
            end_date -> Nullable<Date>,
            needs_sync -> Bool,
        }
    }
}

pub struct Database(SqliteConnection);

impl Database {
    pub fn open() -> Result<Self> {
        let path = Self::validated_path()?;
        let conn = SqliteConnection::establish(&path.to_string_lossy())?;

        conn.batch_execute(include_str!("../sql/schema.sql"))?;

        Ok(Self(conn))
    }

    pub fn validated_path() -> Result<PathBuf> {
        let mut path = SaveDir::LocalData.validated_dir_path()?.to_path_buf();
        path.push("data.sqlite");
        Ok(path)
    }

    #[inline(always)]
    pub fn conn(&self) -> &SqliteConnection {
        &self.0
    }
}

impl Drop for Database {
    fn drop(&mut self) {
        self.conn().execute("PRAGMA optimize").ok();
    }
}

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct Path(PathBuf);

impl<DB> FromSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        let path = String::from_sql(bytes)?.into();
        Ok(Self(path))
    }
}

impl<DB> ToSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    str: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.to_string_lossy();
        value.to_sql(out)
    }
}

impl AsRef<std::path::Path> for Path {
    fn as_ref(&self) -> &std::path::Path {
        &self.0
    }
}

impl From<PathBuf> for Path {
    fn from(value: PathBuf) -> Self {
        Self(value)
    }
}

impl Deref for Path {
    type Target = PathBuf;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Clone, Debug, Default, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct PlayerArgs(SmallVec<[String; 3]>);

impl PlayerArgs {
    #[inline(always)]
    pub fn new() -> Self {
        Self(SmallVec::new())
    }
}

impl<DB> FromSql<Nullable<Text>, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        match bytes {
            Some(_) => {
                let args = String::from_sql(bytes)?
                    .split(";;")
                    .map(Into::into)
                    .collect();

                Ok(Self(args))
            }
            None => Ok(Self::new()),
        }
    }
}

impl<DB> ToSql<Text, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.join(";;");
        value.to_sql(out)
    }
}

impl AsRef<SmallVec<[String; 3]>> for PlayerArgs {
    fn as_ref(&self) -> &SmallVec<[String; 3]> {
        &self.0
    }
}

impl From<SmallVec<[String; 3]>> for PlayerArgs {
    fn from(value: SmallVec<[String; 3]>) -> Self {
        Self(value)
    }
}

impl Deref for PlayerArgs {
    type Target = SmallVec<[String; 3]>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<|endoftext|>


<commit_before>/// This file contains commands to generally control and administer the bot.
use irc::Client;
use irc::IrcMessageEvent;
use std::ascii::AsciiExt;

fn log_message(event: &IrcMessageEvent) {
    let nick = event.mask.nick().unwrap_or_else(|| event.mask.mask().unwrap_or("*unknown*"));
    let message = match event.command.to_ascii_upper().as_slice() {
        "PRIVMSG" => match event.ctcp {
            Some((ctcp_command, ctcp_message)) => match ctcp_command {
                "ACTION" => format!("[{}] * {} {}", event.args[0], nick, ctcp_message),
                _ => format!("[{}] <{}> CTCP {} {}", event.args[0], nick, ctcp_command, ctcp_message),
            },
            None => format!("[{}] <{}> {}", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        },
        "NOTICE" => format!("[{}] -{}- {}", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        "JOIN" => format!("[{}] *** {} joined", event.args[0], nick),
        "PART" => format!("[{}] *** {} left ({})", event.args[0], nick, event.args[1]),
        "KICK" => {
            if event.args.len() > 2 {
                format!("[{}] *** {} kicked {} ({})", event.args[0], nick, event.args[1], event.args.slice_from(2).connect(" ").slice_from(1))
            } else {
                format!("[{}] *** {} kicked {}", event.args[0], nick, event.args[1])
            }
        },
        "TOPIC" => format!("[{}] *** {} changed the topic to \"{}\"", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        "PING" => return, // don't log pings
        _ => match event.mask.mask() {
            Some(mask) => format!("{} {} {}", mask, event.command, event.args.connect(" ")),
            None => format!("{} {}", event.command, event.args.connect(" ")),
        }
    };
    println!("{}", message);
}

pub fn register(client: &mut Client) {
    client.add_catch_all_listener(log_message);
}
<commit_msg>Add better support for telling if a message is actually a CTCP or not<commit_after>/// This file contains commands to generally control and administer the bot.
use irc::Client;
use irc::IrcMessageEvent;
use std::ascii::AsciiExt;

fn log_message(event: &IrcMessageEvent) {
    let nick = event.mask.nick().unwrap_or_else(|| event.mask.mask().unwrap_or("*unknown*"));
    let message = match event.command.to_ascii_upper().as_slice() {
        "PRIVMSG" => match event.ctcp {
            Some((ctcp_command, ctcp_message)) => match ctcp_command {
                "ACTION" => format!("[{}] * {} {}", event.args[0], nick, ctcp_message),
                _ => if ctcp_message.len() == 0 {
                    format!("[{}] CTCP {} from {}", event.args[0], ctcp_command, nick)
                } else {
                    format!("[{}] CTCP {} from {}: {}", event.args[0], ctcp_command, nick, ctcp_message)
                },
            },
            None => format!("[{}] <{}> {}", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        },
        "NOTICE" => format!("[{}] -{}- {}", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        "JOIN" => format!("[{}] *** {} joined", event.args[0], nick),
        "PART" => format!("[{}] *** {} left ({})", event.args[0], nick, event.args[1]),
        "KICK" => {
            if event.args.len() > 2 {
                format!("[{}] *** {} kicked {} ({})", event.args[0], nick, event.args[1], event.args.slice_from(2).connect(" ").slice_from(1))
            } else {
                format!("[{}] *** {} kicked {}", event.args[0], nick, event.args[1])
            }
        },
        "TOPIC" => format!("[{}] *** {} changed the topic to \"{}\"", event.args[0], nick, event.args.slice_from(1).connect(" ").slice_from(1)),
        "PING" => return, // don't log pings
        _ => match event.mask.mask() {
            Some(mask) => format!("{} {} {}", mask, event.command, event.args.connect(" ")),
            None => format!("{} {}", event.command, event.args.connect(" ")),
        }
    };
    println!("{}", message);
}

pub fn register(client: &mut Client) {
    client.add_catch_all_listener(log_message);
}
<|endoftext|>


<commit_before>use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use serialize::json;

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct DebianRepo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct AptKey {
    pub key_server: String,
    pub keys: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct PacmanRepo {
    pub name: String,
    pub url: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub path: Path,
    pub subdir: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInstallInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<Path>,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct FileInfo {
    pub name: Path,
    pub contents: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct UbuntuRepoInfo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Default, Clone, Decodable, Debug, Encodable)]
pub struct PipSettings {
    pub find_links: Vec<String>,
    pub index_urls: Vec<String>,
    pub dependencies: bool,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub struct SubConfigInfo {
    pub generator: Option<String>,
    pub path: Path,
    pub container: String,
}

#[derive(Encodable, Decodable, Clone)]
pub enum Builder {
    // -- Generic --
    Sh(String),
    Cmd(Vec<String>),
    Env(BTreeMap<String, String>),
    Depends(Path),
    Tar(TarInfo),
    TarInstall(TarInstallInfo),
    Text(BTreeMap<Path, String>),
    //AddFile(FileInfo),
    Remove(Path),
    EnsureDir(Path),
    EmptyDir(Path),
    CacheDirs(BTreeMap<Path, String>),
    //Busybox,

    // -- Generic --
    Install(Vec<String>),
    BuildDeps(Vec<String>),
    Container(String),
    SubConfig(SubConfigInfo),

    // -- Ubuntu --
    Ubuntu(String),
    UbuntuRepo(UbuntuRepoInfo),
    UbuntuUniverse,
    //AddUbuntuPPA(String),

    // -- Ubuntu/Debian --
    //AddDebianRepo(DebianRepo),
    //AddAptKey(AptKey),

    // -- Arch --
    //ArchBase,
    //PacmanInstall(Vec<String>),
    //PacmanRemove(Vec<String>),
    //PacmanBuild(Path),
    //AddPacmanRepo(PacmanRepo),

    // -- Alpine --
    Alpine(String),
    //AlpineRemove(Vec<String>),

    // -- Docker --
    //DockerImage(String),
    //DockerPrivate(String),
    //Dockerfile(Path),

    // -- Languages --
    NpmInstall(Vec<String>),
    //PipRequirement(Path),
    //GemInstall(Vec<String>),

    // -- Python --
    PipConfig(PipSettings),
    Py2Install(Vec<String>),
    Py2Requirements(Path),
    Py3Install(Vec<String>),
    Py3Requirements(Path),
}

pub fn builder_validator<'x>() -> Box<V::Validator + 'x> {
    return box V::Enum { options: vec!(
        ("Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("BuildDeps".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Container".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("SubConfig".to_string(), box V::Structure {
            members: vec!(
                ("generator".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Text".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

        ("Ubuntu".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuRepo".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("suite".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("components".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuUniverse".to_string(), box V::Nothing as Box<V::Validator>),
        ("Sh".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Cmd".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Remove".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EnsureDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EmptyDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("CacheDirs".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Env".to_string(), box V::Mapping {
            key_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Depends".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Tar".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    default: Some(Path::new("/")),
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new("")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("TarInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    optional: true,
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),

        ("Alpine".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),

        // Python
        ("PipConfig".to_string(), box V::Structure {
            members: vec!(
                ("dependencies".to_string(), box V::Scalar {
                    default: Some("false".to_string()),
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("find_links".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("index_urls".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Py2Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py2Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),
        ("Py3Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py3Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),

        // Node.js
        ("NpmInstall".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

    ), .. Default::default() } as Box<V::Validator>;
}

impl Debug for Builder {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
        write!(fmt, "{}", json::as_json(self))
    }
}

<commit_msg>Use builtin "Debug" of rust instead json for log messages<commit_after>use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use serialize::json;

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct DebianRepo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct AptKey {
    pub key_server: String,
    pub keys: Vec<String>,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct PacmanRepo {
    pub name: String,
    pub url: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub path: Path,
    pub subdir: Path,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct TarInstallInfo {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<Path>,
    pub script: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct FileInfo {
    pub name: Path,
    pub contents: String,
}

#[derive(Encodable, Decodable, Debug, Clone)]
pub struct UbuntuRepoInfo {
    pub url: String,
    pub suite: String,
    pub components: Vec<String>,
}

#[derive(Default, Clone, Decodable, Debug, Encodable)]
pub struct PipSettings {
    pub find_links: Vec<String>,
    pub index_urls: Vec<String>,
    pub dependencies: bool,
}

#[derive(Clone, Decodable, Encodable, Debug)]
pub struct SubConfigInfo {
    pub generator: Option<String>,
    pub path: Path,
    pub container: String,
}

#[derive(Encodable, Decodable, Clone, Debug)]
pub enum Builder {
    // -- Generic --
    Sh(String),
    Cmd(Vec<String>),
    Env(BTreeMap<String, String>),
    Depends(Path),
    Tar(TarInfo),
    TarInstall(TarInstallInfo),
    Text(BTreeMap<Path, String>),
    //AddFile(FileInfo),
    Remove(Path),
    EnsureDir(Path),
    EmptyDir(Path),
    CacheDirs(BTreeMap<Path, String>),
    //Busybox,

    // -- Generic --
    Install(Vec<String>),
    BuildDeps(Vec<String>),
    Container(String),
    SubConfig(SubConfigInfo),

    // -- Ubuntu --
    Ubuntu(String),
    UbuntuRepo(UbuntuRepoInfo),
    UbuntuUniverse,
    //AddUbuntuPPA(String),

    // -- Ubuntu/Debian --
    //AddDebianRepo(DebianRepo),
    //AddAptKey(AptKey),

    // -- Arch --
    //ArchBase,
    //PacmanInstall(Vec<String>),
    //PacmanRemove(Vec<String>),
    //PacmanBuild(Path),
    //AddPacmanRepo(PacmanRepo),

    // -- Alpine --
    Alpine(String),
    //AlpineRemove(Vec<String>),

    // -- Docker --
    //DockerImage(String),
    //DockerPrivate(String),
    //Dockerfile(Path),

    // -- Languages --
    NpmInstall(Vec<String>),
    //PipRequirement(Path),
    //GemInstall(Vec<String>),

    // -- Python --
    PipConfig(PipSettings),
    Py2Install(Vec<String>),
    Py2Requirements(Path),
    Py3Install(Vec<String>),
    Py3Requirements(Path),
}

pub fn builder_validator<'x>() -> Box<V::Validator + 'x> {
    return box V::Enum { options: vec!(
        ("Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("BuildDeps".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Container".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("SubConfig".to_string(), box V::Structure {
            members: vec!(
                ("generator".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("container".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Text".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

        ("Ubuntu".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuRepo".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("suite".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("components".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("UbuntuUniverse".to_string(), box V::Nothing as Box<V::Validator>),
        ("Sh".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Cmd".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Remove".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EnsureDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("EmptyDir".to_string(), box V::Directory {
            absolute: Some(true),
        .. Default::default() } as Box<V::Validator>),
        ("CacheDirs".to_string(), box V::Mapping {
            key_element: box V::Directory {
                absolute: Some(true),
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Env".to_string(), box V::Mapping {
            key_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
            value_element: box V::Scalar {
                .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Depends".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),
        ("Tar".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("path".to_string(), box V::Directory {
                    default: Some(Path::new("/")),
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    default: Some(Path::new("")),
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("TarInstall".to_string(), box V::Structure {
            members: vec!(
                ("url".to_string(), box V::Scalar {
                    .. Default::default() } as Box<V::Validator>),
                ("sha256".to_string(), box V::Scalar {
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("subdir".to_string(), box V::Directory {
                    optional: true,
                    absolute: Some(false),
                    .. Default::default() } as Box<V::Validator>),
                ("script".to_string(), box V::Scalar {
                    default: Some("./configure --prefix=/usr\n\
                                   make\n\
                                   make install\n\
                                   ".to_string()),
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),

        ("Alpine".to_string(), box V::Scalar {
        .. Default::default() } as Box<V::Validator>),

        // Python
        ("PipConfig".to_string(), box V::Structure {
            members: vec!(
                ("dependencies".to_string(), box V::Scalar {
                    default: Some("false".to_string()),
                    optional: true,
                    .. Default::default() } as Box<V::Validator>),
                ("find_links".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
                ("index_urls".to_string(), box V::Sequence {
                    element: box V::Scalar {
                        .. Default::default() } as Box<V::Validator>,
                    .. Default::default() } as Box<V::Validator>),
            ),
        .. Default::default() } as Box<V::Validator>),
        ("Py2Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py2Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),
        ("Py3Install".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),
        ("Py3Requirements".to_string(), box V::Scalar {
            default: Some("requirements.txt".to_string()),
        .. Default::default() } as Box<V::Validator>),

        // Node.js
        ("NpmInstall".to_string(), box V::Sequence {
            element: box V::Scalar {
            .. Default::default() } as Box<V::Validator>,
        .. Default::default() } as Box<V::Validator>),

    ), .. Default::default() } as Box<V::Validator>;
}
<|endoftext|>