<commit_before>use std::cmp;
use std::fmt;
use std::net::Ipv6Addr;
use std::str::FromStr;

use common::{IpNetworkError, cidr_parts, parse_prefix};

const IPV6_BITS: u8 = 128;
const IPV6_SEGMENT_BITS: u8 = 16;

/// Represents a network range where the IP addresses are of v6
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq,PartialOrd,Ord)]
pub struct Ipv6Network {
    addr: Ipv6Addr,
    prefix: u8,
}

impl Ipv6Network {
    /// Constructs a new `Ipv6Network` from any `Ipv6Addr` and a prefix denoting the network size.
    /// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`.
    pub fn new(addr: Ipv6Addr, prefix: u8) -> Result<Ipv6Network, IpNetworkError> {
        if prefix > IPV6_BITS {
            Err(IpNetworkError::InvalidPrefix)
        } else {
            Ok(Ipv6Network {
                addr: addr,
                prefix: prefix,
            })
        }
    }

    /// Returns an iterator over `Ipv6Network`. Each call to `next` will return the next
    /// `Ipv6Addr` in the given network. `None` will be returned when there are no more
    /// addresses.
    #[cfg(feature = "ipv6-iterator")]
    pub fn iter(&self) -> Ipv6NetworkIterator {

        let dec = u128::from(self.addr);
        let max = u128::max_value();
        let prefix = self.prefix;

        let mask = max.checked_shl((IPV6_BITS - prefix) as u32).unwrap_or(0);
        let start: u128 = dec & mask;

        let mask = max.checked_shr(prefix as u32).unwrap_or(0);
        let end: u128 = dec | mask;

        Ipv6NetworkIterator{
            next: start,
            end: end,
        }

    }

    /// Returns the address of the network denoted by this `Ipv6Network`.
    /// This means the lowest possible IPv6 address inside of the network.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
    /// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
    /// ```
    #[cfg(feature = "ipv6-methods")]
    pub fn network(&self) -> Ipv6Addr {
        let mask = u128::from(self.mask());
        let ip = u128::from(self.addr) & mask;
        Ipv6Addr::from(ip)
    }

    /// Returns the broadcast address of this `Ipv6Network`.
    /// This means the highest possible IPv4 address inside of the network.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
    /// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff));
    /// ```
    #[cfg(feature = "ipv6-methods")]
    pub fn broadcast(&self) -> Ipv6Addr {
        let mask = u128::from(self.mask());
        let broadcast = u128::from(self.addr) | !mask;
        Ipv6Addr::from(broadcast)
    }

    pub fn ip(&self) -> Ipv6Addr {
        self.addr
    }

    pub fn prefix(&self) -> u8 {
        self.prefix
    }

    /// Returns the mask for this `Ipv6Network`.
    /// That means the `prefix` most significant bits will be 1 and the rest 0
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
    /// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
    /// ```
    pub fn mask(&self) -> Ipv6Addr {
        // Ipv6Addr::from is only implemented for [u8; 16]
        let mut segments = [0; 16];
        for (i, segment) in segments.iter_mut().enumerate() {
            let bits_remaining = self.prefix.saturating_sub(i as u8 * 8);
            let set_bits = cmp::min(bits_remaining, 8);
            *segment = !(0xff as u16 >> set_bits) as u8;
        }
        Ipv6Addr::from(segments)
    }

    /// Checks if a given `Ipv6Addr` is in this `Ipv6Network`
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
    /// assert!(net.contains(Ipv6Addr::new(0xff01, 0, 0, 0, 0, 0, 0, 0x1)));
    /// assert!(!net.contains(Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0x1)));
    /// ```
    pub fn contains(&self, ip: Ipv6Addr) -> bool {
        let a = self.addr.segments();
        let b = ip.segments();
        let addrs = Iterator::zip(a.iter(), b.iter());
        self.mask().segments().iter().zip(addrs).all(|(mask, (a, b))| a & mask == b & mask)
    }
}

impl FromStr for Ipv6Network {
    type Err = IpNetworkError;
    fn from_str(s: &str) -> Result<Ipv6Network, IpNetworkError> {
        let (addr_str, prefix_str) = cidr_parts(s)?;
        let addr = Ipv6Addr::from_str(addr_str).map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
        let prefix = parse_prefix(prefix_str, IPV6_BITS)?;
        Ipv6Network::new(addr, prefix)
    }
}

#[cfg(feature = "ipv6-iterator")]
pub struct Ipv6NetworkIterator {
    next: u128,
    end: u128,
}

#[cfg(feature = "ipv6-iterator")]
impl Iterator for Ipv6NetworkIterator {
    type Item = Ipv6Addr;

    fn next(&mut self) -> Option<Ipv6Addr> {

        if self.next <= self.end {

            let next = Ipv6Addr::from(self.next);
            self.next += 1;
            Some(next)

        } else {

            None

        }

    }
}

impl fmt::Display for Ipv6Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

/// Converts a `Ipv6Addr` network mask into a prefix.
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
    let mask = mask.segments();
    let mut mask_iter = mask.into_iter();

    // Count the number of set bits from the start of the address
    let mut prefix = 0;
    for &segment in &mut mask_iter {
        if segment == 0xffff {
            prefix += IPV6_SEGMENT_BITS;
        } else if segment == 0 {
            // Prefix finishes on a segment boundary
            break;
        } else {
            let prefix_bits = (!segment).leading_zeros() as u8;
            // Check that the remainder of the bits are all unset
            if segment << prefix_bits != 0 {
                return Err(IpNetworkError::InvalidPrefix);
            }
            prefix += prefix_bits;
            break;
        }
    }

    // Now check all the remaining bits are unset
    for &segment in mask_iter {
        if segment != 0 {
            return Err(IpNetworkError::InvalidPrefix);
        }
    }

    Ok(prefix)
}

#[cfg(test)]
mod test {
    use std::net::Ipv6Addr;
    use super::*;

    #[test]
    fn create_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24).unwrap();
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn create_v6_invalid_prefix() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129);
        assert!(cidr.is_err());
    }

    #[test]
    fn parse_v6() {
        let cidr: Ipv6Network = "::1/0".parse().unwrap();
        assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
        assert_eq!(cidr.prefix(), 0);
    }

    #[test]
    fn parse_v6_2() {
        let cidr: Ipv6Network = "FF01:0:0:17:0:0:0:2/64".parse().unwrap();
        assert_eq!(cidr.ip(), Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2));
        assert_eq!(cidr.prefix(), 64);
    }

    #[test]
    fn parse_v6_fail_addr() {
        let cidr: Option<Ipv6Network> = "2001::1::/8".parse().ok();
        assert_eq!(None, cidr);
    }

    #[test]
    fn parse_v6_fail_prefix() {
        let cidr: Option<Ipv6Network> = "::1/129".parse().ok();
        assert_eq!(None, cidr);
    }

    #[test]
    fn mask_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 40).unwrap();
        let mask = cidr.mask();
        assert_eq!(mask, Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0));
    }

    #[test]
    fn contains_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
        let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0x7fff, 0, 0, 0x2);
        assert!(cidr.contains(ip));
    }

    #[test]
    fn not_contains_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
        let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0xffff, 0, 0, 0x2);
        assert!(!cidr.contains(ip));
    }

    #[test]
    fn v6_mask_to_prefix() {
        let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
        let prefix = ipv6_mask_to_prefix(mask).unwrap();
        assert_eq!(prefix, 48);
    }

    #[test]
    fn invalid_v6_mask_to_prefix() {
        let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
        let prefix = ipv6_mask_to_prefix(mask);
        assert!(prefix.is_err());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6() {
        let cidr: Ipv6Network = "2001:db8::/126".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 3), iter.next().unwrap());
        assert_eq!(None, iter.next());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6_tiny() {
        let cidr: Ipv6Network = "2001:db8::/128".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(None, iter.next());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6_huge() {
        let cidr: Ipv6Network = "2001:db8::/0".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
    }

    #[test]
    #[cfg(feature = "ipv6-methods")]
    fn network_v6() {
        let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
        let net = cidr.network();
        let expected: Ipv6Addr = "2001:db8::".parse().unwrap();
        assert_eq!(net, expected);
    }

    #[test]
    #[cfg(feature = "ipv6-methods")]
    fn broadcast_v6() {
        let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
        let net = cidr.broadcast();
        let expected: Ipv6Addr = "2001:db8::ffff:ffff".parse().unwrap();
        assert_eq!(net, expected);
    }
}
<commit_msg>Add a size method for Ipv6Network<commit_after>use std::cmp;
use std::fmt;
use std::net::Ipv6Addr;
use std::str::FromStr;

use common::{IpNetworkError, cidr_parts, parse_prefix};

const IPV6_BITS: u8 = 128;
const IPV6_SEGMENT_BITS: u8 = 16;

/// Represents a network range where the IP addresses are of v6
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq,PartialOrd,Ord)]
pub struct Ipv6Network {
    addr: Ipv6Addr,
    prefix: u8,
}

impl Ipv6Network {
    /// Constructs a new `Ipv6Network` from any `Ipv6Addr` and a prefix denoting the network size.
    /// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`.
    pub fn new(addr: Ipv6Addr, prefix: u8) -> Result<Ipv6Network, IpNetworkError> {
        if prefix > IPV6_BITS {
            Err(IpNetworkError::InvalidPrefix)
        } else {
            Ok(Ipv6Network {
                addr: addr,
                prefix: prefix,
            })
        }
    }

    /// Returns an iterator over `Ipv6Network`. Each call to `next` will return the next
    /// `Ipv6Addr` in the given network. `None` will be returned when there are no more
    /// addresses.
    #[cfg(feature = "ipv6-iterator")]
    pub fn iter(&self) -> Ipv6NetworkIterator {

        let dec = u128::from(self.addr);
        let max = u128::max_value();
        let prefix = self.prefix;

        let mask = max.checked_shl((IPV6_BITS - prefix) as u32).unwrap_or(0);
        let start: u128 = dec & mask;

        let mask = max.checked_shr(prefix as u32).unwrap_or(0);
        let end: u128 = dec | mask;

        Ipv6NetworkIterator{
            next: start,
            end: end,
        }

    }

    /// Returns the address of the network denoted by this `Ipv6Network`.
    /// This means the lowest possible IPv6 address inside of the network.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
    /// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
    /// ```
    #[cfg(feature = "ipv6-methods")]
    pub fn network(&self) -> Ipv6Addr {
        let mask = u128::from(self.mask());
        let ip = u128::from(self.addr) & mask;
        Ipv6Addr::from(ip)
    }

    /// Returns the broadcast address of this `Ipv6Network`.
    /// This means the highest possible IPv4 address inside of the network.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
    /// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff));
    /// ```
    #[cfg(feature = "ipv6-methods")]
    pub fn broadcast(&self) -> Ipv6Addr {
        let mask = u128::from(self.mask());
        let broadcast = u128::from(self.addr) | !mask;
        Ipv6Addr::from(broadcast)
    }

    pub fn ip(&self) -> Ipv6Addr {
        self.addr
    }

    pub fn prefix(&self) -> u8 {
        self.prefix
    }

    /// Returns the mask for this `Ipv6Network`.
    /// That means the `prefix` most significant bits will be 1 and the rest 0
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
    /// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
    /// ```
    pub fn mask(&self) -> Ipv6Addr {
        // Ipv6Addr::from is only implemented for [u8; 16]
        let mut segments = [0; 16];
        for (i, segment) in segments.iter_mut().enumerate() {
            let bits_remaining = self.prefix.saturating_sub(i as u8 * 8);
            let set_bits = cmp::min(bits_remaining, 8);
            *segment = !(0xff as u16 >> set_bits) as u8;
        }
        Ipv6Addr::from(segments)
    }

    /// Checks if a given `Ipv6Addr` is in this `Ipv6Network`
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
    /// assert!(net.contains(Ipv6Addr::new(0xff01, 0, 0, 0, 0, 0, 0, 0x1)));
    /// assert!(!net.contains(Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0x1)));
    /// ```
    pub fn contains(&self, ip: Ipv6Addr) -> bool {
        let a = self.addr.segments();
        let b = ip.segments();
        let addrs = Iterator::zip(a.iter(), b.iter());
        self.mask().segments().iter().zip(addrs).all(|(mask, (a, b))| a & mask == b & mask)
    }

    /// Returns number of possible host addresses in this `Ipv6Network`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::net::Ipv6Addr;
    /// use ipnetwork::Ipv6Network;
    ///
    /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
    /// assert_eq!(net.size(), 79228162514264337593543950336);
    ///
    /// let tinynet: Ipv6Network = "ff01::0/128".parse().unwrap();
    /// assert_eq!(tinynet.size(), 1);
    /// ```
    #[cfg(feature = "ipv6-methods")]
    pub fn size(&self) -> u128 {
        let host_bits = (IPV6_BITS - self.prefix) as u32;
        (2 as u128).pow(host_bits)
    }
}

impl FromStr for Ipv6Network {
    type Err = IpNetworkError;
    fn from_str(s: &str) -> Result<Ipv6Network, IpNetworkError> {
        let (addr_str, prefix_str) = cidr_parts(s)?;
        let addr = Ipv6Addr::from_str(addr_str).map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
        let prefix = parse_prefix(prefix_str, IPV6_BITS)?;
        Ipv6Network::new(addr, prefix)
    }
}

#[cfg(feature = "ipv6-iterator")]
pub struct Ipv6NetworkIterator {
    next: u128,
    end: u128,
}

#[cfg(feature = "ipv6-iterator")]
impl Iterator for Ipv6NetworkIterator {
    type Item = Ipv6Addr;

    fn next(&mut self) -> Option<Ipv6Addr> {

        if self.next <= self.end {

            let next = Ipv6Addr::from(self.next);
            self.next += 1;
            Some(next)

        } else {

            None

        }

    }
}

impl fmt::Display for Ipv6Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}/{}", self.ip(), self.prefix())
    }
}

/// Converts a `Ipv6Addr` network mask into a prefix.
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
    let mask = mask.segments();
    let mut mask_iter = mask.into_iter();

    // Count the number of set bits from the start of the address
    let mut prefix = 0;
    for &segment in &mut mask_iter {
        if segment == 0xffff {
            prefix += IPV6_SEGMENT_BITS;
        } else if segment == 0 {
            // Prefix finishes on a segment boundary
            break;
        } else {
            let prefix_bits = (!segment).leading_zeros() as u8;
            // Check that the remainder of the bits are all unset
            if segment << prefix_bits != 0 {
                return Err(IpNetworkError::InvalidPrefix);
            }
            prefix += prefix_bits;
            break;
        }
    }

    // Now check all the remaining bits are unset
    for &segment in mask_iter {
        if segment != 0 {
            return Err(IpNetworkError::InvalidPrefix);
        }
    }

    Ok(prefix)
}

#[cfg(test)]
mod test {
    use std::net::Ipv6Addr;
    use super::*;

    #[test]
    fn create_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24).unwrap();
        assert_eq!(cidr.prefix(), 24);
    }

    #[test]
    fn create_v6_invalid_prefix() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129);
        assert!(cidr.is_err());
    }

    #[test]
    fn parse_v6() {
        let cidr: Ipv6Network = "::1/0".parse().unwrap();
        assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
        assert_eq!(cidr.prefix(), 0);
    }

    #[test]
    fn parse_v6_2() {
        let cidr: Ipv6Network = "FF01:0:0:17:0:0:0:2/64".parse().unwrap();
        assert_eq!(cidr.ip(), Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2));
        assert_eq!(cidr.prefix(), 64);
    }

    #[test]
    fn parse_v6_fail_addr() {
        let cidr: Option<Ipv6Network> = "2001::1::/8".parse().ok();
        assert_eq!(None, cidr);
    }

    #[test]
    fn parse_v6_fail_prefix() {
        let cidr: Option<Ipv6Network> = "::1/129".parse().ok();
        assert_eq!(None, cidr);
    }

    #[test]
    fn mask_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 40).unwrap();
        let mask = cidr.mask();
        assert_eq!(mask, Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0));
    }

    #[test]
    fn contains_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
        let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0x7fff, 0, 0, 0x2);
        assert!(cidr.contains(ip));
    }

    #[test]
    fn not_contains_v6() {
        let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
        let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0xffff, 0, 0, 0x2);
        assert!(!cidr.contains(ip));
    }

    #[test]
    fn v6_mask_to_prefix() {
        let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
        let prefix = ipv6_mask_to_prefix(mask).unwrap();
        assert_eq!(prefix, 48);
    }

    #[test]
    fn invalid_v6_mask_to_prefix() {
        let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
        let prefix = ipv6_mask_to_prefix(mask);
        assert!(prefix.is_err());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6() {
        let cidr: Ipv6Network = "2001:db8::/126".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 3), iter.next().unwrap());
        assert_eq!(None, iter.next());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6_tiny() {
        let cidr: Ipv6Network = "2001:db8::/128".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(None, iter.next());
    }

    #[test]
    #[cfg(feature = "ipv6-iterator")]
    fn iterator_v6_huge() {
        let cidr: Ipv6Network = "2001:db8::/0".parse().unwrap();
        let mut iter = cidr.iter();
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
        assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
    }

    #[test]
    #[cfg(feature = "ipv6-methods")]
    fn network_v6() {
        let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
        let net = cidr.network();
        let expected: Ipv6Addr = "2001:db8::".parse().unwrap();
        assert_eq!(net, expected);
    }

    #[test]
    #[cfg(feature = "ipv6-methods")]
    fn broadcast_v6() {
        let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
        let net = cidr.broadcast();
        let expected: Ipv6Addr = "2001:db8::ffff:ffff".parse().unwrap();
        assert_eq!(net, expected);
    }

    #[test]
    #[cfg(feature = "ipv6-methods")]
    fn size_v6() {
        let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
        assert_eq!(cidr.size(), 4294967296);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate error_chain;
#[macro_use] extern crate stderr;
extern crate chan;
extern crate clap;
extern crate glob;

#[macro_use]
mod errors;
mod structs;
mod compression;

use clap::{App, Arg};
use errors::*;
use structs::*;
use std::sync::Arc;
use compression::*;

quick_main!(run);

fn run() -> Result<()> {
    let matches =
        App::new("static-compress")
        .version("0.1")
        .about("Create statically-compresed copies of matching files")
        .author("NeoSmart Technologies")
        .arg(
            Arg::with_name("compressor")
            .short("c")
            .long("compressor")
            .value_name("[brotli|gzip|zopfli]")
            .help("The compressor to use")
            .takes_value(true)
            )
        .arg(
            Arg::with_name("threads")
            .short("j")
            .long("threads")
            .value_name("COUNT")
            .help("The number of simultaneous conversions")
            .takes_value(true)
            )
        .arg(
            Arg::with_name("filters")
            .value_name("FILTER")
            .multiple(true)
            .required(true)
            )
        .get_matches();

    fn get_parameter<'a, T>(matches: &clap::ArgMatches, name: &str, default_value: T) -> Result<T>
        where T: std::str::FromStr,
    {
        match matches.value_of(name) {
            Some(v) => Ok(v.parse().map_err(|_| ErrorKind::InvalidParameterValue(name.to_owned()))?),
            None => Ok(default_value)
        }
    }

    let compressor = get_parameter(&matches, "compressor", CompressionAlgorithm::GZip)?;
    let temp = Parameters {
        extension: matches.value_of("ext")
            .unwrap_or(compressor.extension())
            .trim_matches(|c: char| c.is_whitespace() || c.is_control() || c == '.')
            .to_owned(),
        compressor: compressor,
        include_filters: match matches.values_of("filters") {
            Some(values) => Ok(values.map(|s| s.to_owned()).collect()),
            None => Err(ErrorKind::InvalidUsage)
        }?,
        threads: get_parameter(&matches, "threads", 1)?,
    };

    let parameters = Arc::<Parameters>::new(temp);
    let (send_queue, wait_group) = start_workers(&parameters);

    //convert filters to paths and deal out conversion jobs
    dispatch_jobs(send_queue, &parameters.include_filters)?;

    //wait for all jobs to finish
    wait_group.wait();

    Ok(())
}

type ThreadParam = std::path::PathBuf;

fn start_workers<'a>(params: &Arc<Parameters>) -> (chan::Sender<ThreadParam>, chan::WaitGroup) {
    let (tx, rx) = chan::sync::<ThreadParam>(params.threads);
    let wg = chan::WaitGroup::new();

    for _ in 0..params.threads {
        let local_params = params.clone();
        let local_rx = rx.clone();
        let local_wg = wg.clone();
        wg.add(1);
        std::thread::spawn(move || {
            worker_thread(local_params, local_rx);
            local_wg.done();
        });
    }

    (tx, wg)
}

fn dispatch_jobs(send_queue: chan::Sender<ThreadParam>, filters: &Vec<String>) -> Result<()> {
    for filter in filters {
        for entry in glob::glob(filter).map_err(|_| ErrorKind::InvalidIncludeFilter)? {
            match entry {
                Ok(path) => send_queue.send(path),
                Err(e) => errstln!("{:?}", e) //error reading file, but don't bail
            }
        };
    }
    Ok(())
}

fn worker_thread(params: Arc<Parameters>, rx: chan::Receiver<ThreadParam>) {
    loop {
        let src = match rx.recv() {
            Some(task) => task,
            None => return //no more tasks
        };

        let dst_path = format!("{}.{}", src.to_str().unwrap(), params.extension);
        let dst = std::path::Path::new(&dst_path);

        println!("{}", src.to_string_lossy());

        //params.compressor.compress(src, dst, params.level);
        if let Err(e) = params.compressor.compress(src.as_path(), dst) {
            errstln!("Error compressing {}: {}", src.to_string_lossy(), e);
        }
    }
}

<commit_msg>Validate glob matches and clean up output on file compression error<commit_after>#[macro_use] extern crate error_chain;
#[macro_use] extern crate stderr;
extern crate chan;
extern crate clap;
extern crate glob;

#[macro_use]
mod errors;
mod structs;
mod compression;

use clap::{App, Arg};
use errors::*;
use structs::*;
use std::sync::Arc;
use compression::*;

quick_main!(run);

fn run() -> Result<()> {
    let matches =
        App::new("static-compress")
        .version("0.1")
        .about("Create statically-compresed copies of matching files")
        .author("NeoSmart Technologies")
        .arg(
            Arg::with_name("compressor")
            .short("c")
            .long("compressor")
            .value_name("[brotli|gzip|zopfli]")
            .help("The compressor to use")
            .takes_value(true)
            )
        .arg(
            Arg::with_name("threads")
            .short("j")
            .long("threads")
            .value_name("COUNT")
            .help("The number of simultaneous conversions")
            .takes_value(true)
            )
        .arg(
            Arg::with_name("filters")
            .value_name("FILTER")
            .multiple(true)
            .required(true)
            )
        .get_matches();

    fn get_parameter<'a, T>(matches: &clap::ArgMatches, name: &str, default_value: T) -> Result<T>
        where T: std::str::FromStr,
    {
        match matches.value_of(name) {
            Some(v) => Ok(v.parse().map_err(|_| ErrorKind::InvalidParameterValue(name.to_owned()))?),
            None => Ok(default_value)
        }
    }

    let compressor = get_parameter(&matches, "compressor", CompressionAlgorithm::GZip)?;
    let temp = Parameters {
        extension: matches.value_of("ext")
            .unwrap_or(compressor.extension())
            .trim_matches(|c: char| c.is_whitespace() || c.is_control() || c == '.')
            .to_owned(),
        compressor: compressor,
        include_filters: match matches.values_of("filters") {
            Some(values) => Ok(values.map(|s| s.to_owned()).collect()),
            None => Err(ErrorKind::InvalidUsage)
        }?,
        threads: get_parameter(&matches, "threads", 1)?,
    };

    let parameters = Arc::<Parameters>::new(temp);
    let (send_queue, wait_group) = start_workers(&parameters);

    //convert filters to paths and deal out conversion jobs
    dispatch_jobs(send_queue, &parameters.include_filters)?;

    //wait for all jobs to finish
    wait_group.wait();

    Ok(())
}

type ThreadParam = std::path::PathBuf;

fn start_workers<'a>(params: &Arc<Parameters>) -> (chan::Sender<ThreadParam>, chan::WaitGroup) {
    let (tx, rx) = chan::sync::<ThreadParam>(params.threads);
    let wg = chan::WaitGroup::new();

    for _ in 0..params.threads {
        let local_params = params.clone();
        let local_rx = rx.clone();
        let local_wg = wg.clone();
        wg.add(1);
        std::thread::spawn(move || {
            worker_thread(local_params, local_rx);
            local_wg.done();
        });
    }

    (tx, wg)
}

fn dispatch_jobs(send_queue: chan::Sender<ThreadParam>, filters: &Vec<String>) -> Result<()> {
    for filter in filters {
        for entry in glob::glob(filter).map_err(|_| ErrorKind::InvalidIncludeFilter)? {
            match entry {
                Ok(path) => {
                    //make sure this is a file, not a folder
                    match std::fs::metadata(&path) {
                        Ok(metadata) => {
                            if metadata.is_file() {
                                send_queue.send(path);
                            }
                            continue; //skip otherwise
                        },
                        Err(e) => errstln!("{}: {}", path.to_string_lossy(), e)
                    }
                },
                Err(e) => errstln!("{}", e)
            };
        };
    }
    Ok(())
}

fn worker_thread(params: Arc<Parameters>, rx: chan::Receiver<ThreadParam>) {
    loop {
        let src = match rx.recv() {
            Some(task) => task,
            None => return //no more tasks
        };

        let dst_path = format!("{}.{}", src.to_str().unwrap(), params.extension);
        let dst = std::path::Path::new(&dst_path);

        println!("{}", src.to_string_lossy());

        //params.compressor.compress(src, dst, params.level);
        if let Err(e) = params.compressor.compress(src.as_path(), dst) {
            errstln!("Error compressing {}: {}", src.to_string_lossy(), e);
            //try deleting the invalid destination file, but don't care if we can't
            std::fs::remove_file(dst).unwrap_or_default();
        }
    }
}

<|endoftext|>


<commit_before>extern crate iron;
#[macro_use]
extern crate router;
extern crate urlencoded;
extern crate rustc_serialize;
extern crate html5ever;
extern crate regex;
extern crate uuid;

use std::net::SocketAddrV4;
use std::net::Ipv4Addr;
use iron::prelude::*;
use iron::status;
use iron::headers::{ContentType};
use iron::modifiers::Header;
use iron::mime::Mime;
use router::{Router};
use urlencoded::UrlEncodedQuery;
use urlencoded::UrlEncodedBody;
use std::str::FromStr;

#[macro_use]
extern crate string_cache;

extern crate pink_spider;

use pink_spider::error::Error;
use pink_spider::scraper::extract;
use pink_spider::model::{Track, Entry, Provider};
use rustc_serialize::json::{ToJson, Json};

pub fn playlistify(req: &mut Request) -> IronResult<Response> {
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    match req.get_ref::<UrlEncodedQuery>() {
        Ok(ref params) => {
            match params.get("url") {
                Some(url) => {
                    let defaults = &vec!("false".to_string());
                    let force    = params.get("force").unwrap_or(defaults);
                    match find_or_playlistify_entry(&url[0], force.len() > 0 && &force[0] == "true") {
                        Ok(entry) => {
                            let json_obj: Json   = entry.to_json();
                            let json_str: String = json_obj.to_string();
                            Ok(Response::with((status::Ok, json_type, json_str)))
                        },
                        Err(e) => Ok(e.as_response())
                    }
                },
                None => Ok(Error::BadRequest.as_response())
            }
        }
        Err(_) => Ok(Error::BadRequest.as_response())
    }
}

pub fn find_or_playlistify_entry(url: &str, force: bool) -> Result<Entry, Error> {
    match Entry::find_by_url(url) {
        Ok(entry) => {
            println!("Get entry from database cache: {}", url);
            if force {
                println!("Update entry: {}", url);
                let entry = try!(playlistify_entry(entry));
                Ok(entry)
            } else {
                Ok(entry)
            }
        },
        Err(_) => {
            let entry = try!(Entry::create_by_url(url.to_string()));
            let entry = try!(playlistify_entry(entry));
            println!("Create new entry to database cache: {}", url);
            Ok(entry)
        },
    }
}

pub fn playlistify_entry(entry: Entry) -> Result<Entry, Error> {
    let mut e = entry.clone();
    let product = try!(extract(&entry.url));
    match product.og_obj {
        Some(og_obj) => {
            e.title       = Some(og_obj.title);
            e.description = og_obj.description;
            e.locale      = og_obj.locale;
            e.visual_url  = og_obj.images.first().map(|i| i.url.clone());
        },
        None => (),
    }
    for t in product.tracks {
        let track = try!(Track::find_or_create(t.provider,
                                               t.title,
                                               t.url,
                                               t.identifier));
        match entry.tracks.iter().find(|&t| t.id == track.id) {
            Some(_) => (),
            None    => e.add_track(track.clone())
        }
    }
    try!(e.save());
    Ok(e)
}

pub fn show_track(req: &mut Request) -> IronResult<Response> {
    let ref track_id = req.extensions.get::<Router>().unwrap()
                          .find("track_id").unwrap();
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    match Track::find_by_id(track_id) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               json_type,
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn show_track_by_provider_id(req: &mut Request) -> IronResult<Response> {
    let provider   = req.extensions.get::<Router>().unwrap().find("provider").unwrap();
    let identifier = req.extensions.get::<Router>().unwrap().find("id").unwrap();
    let json_type  = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    let p = &Provider::new(provider.to_string());
    match Track::find_by(p, identifier) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               json_type,
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn update_track(req: &mut Request) -> IronResult<Response> {
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    fn param_as_string(req: &mut Request, key: &str) -> Option<String> {
        match req.get_ref::<UrlEncodedBody>() {
            Ok(ref params) => match params.get(key) {
                Some(val) => Some(val[0].clone()),
                None      => None
            },
            Err(_) => None
        }
    }

    fn query_as_string(req: &mut Request, key: &str) -> String {
        return req.extensions.get::<Router>().unwrap()
                  .find(key).unwrap().to_string();
    }

    match Track::find_by_id(&query_as_string(req, "track_id")) {
        Ok(mut track) => {
            match param_as_string(req, "title") {
                Some(title) => track.title = title,
                None        => println!("no title")
            }
            match param_as_string(req, "url") {
                Some(url) => track.url = url,
                None      => println!("no url")
            }
            match track.save() {
                Ok(_) => Ok(Response::with((status::Ok,
                                         json_type,
                                         track.to_json().to_string()))),
                Err(e) =>  Ok(e.as_response())
            }
        },
        Err(e) =>  {
            Ok(e.as_response())
        }
    }
}

pub fn main() {
    let router = router!(playlistify: get  "/playlistify"          => playlistify,
                          show_track: get  "/tracks/:track_id"     => show_track,
                        update_track: post "/tracks/:track_id"     => update_track,
           show_track_by_provider_id: get  "/tracks/:provider/:id" => show_track_by_provider_id);

    let port_str = match std::env::var("PORT") {
        Ok(n)    => n,
        Err(_) => "8080".to_string()
    };
    let port: u16 = match port_str.trim().parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Faild to parse port");
            return;
        }
    };
    println!("PORT {}", port_str);
    let ip = Ipv4Addr::new(0, 0, 0, 0);
    Iron::new(router).http(SocketAddrV4::new(ip, port)).unwrap();
}
<commit_msg>Serve static files in 'public' as '/web/*'<commit_after>extern crate iron;
#[macro_use]
extern crate router;
extern crate staticfile;
extern crate mount;
extern crate urlencoded;
extern crate rustc_serialize;
extern crate html5ever;
extern crate regex;
extern crate uuid;

use std::net::SocketAddrV4;
use std::net::Ipv4Addr;
use std::path::Path;
use iron::prelude::*;
use iron::status;
use iron::headers::{ContentType};
use iron::modifiers::Header;
use iron::mime::Mime;
use staticfile::Static;
use mount::Mount;
use router::{Router};
use urlencoded::UrlEncodedQuery;
use urlencoded::UrlEncodedBody;
use std::str::FromStr;

#[macro_use]
extern crate string_cache;

extern crate pink_spider;

use pink_spider::error::Error;
use pink_spider::scraper::extract;
use pink_spider::model::{Track, Entry, Provider};
use rustc_serialize::json::{ToJson, Json};

pub fn playlistify(req: &mut Request) -> IronResult<Response> {
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    match req.get_ref::<UrlEncodedQuery>() {
        Ok(ref params) => {
            match params.get("url") {
                Some(url) => {
                    let defaults = &vec!("false".to_string());
                    let force    = params.get("force").unwrap_or(defaults);
                    match find_or_playlistify_entry(&url[0], force.len() > 0 && &force[0] == "true") {
                        Ok(entry) => {
                            let json_obj: Json   = entry.to_json();
                            let json_str: String = json_obj.to_string();
                            Ok(Response::with((status::Ok, json_type, json_str)))
                        },
                        Err(e) => Ok(e.as_response())
                    }
                },
                None => Ok(Error::BadRequest.as_response())
            }
        }
        Err(_) => Ok(Error::BadRequest.as_response())
    }
}

pub fn find_or_playlistify_entry(url: &str, force: bool) -> Result<Entry, Error> {
    match Entry::find_by_url(url) {
        Ok(entry) => {
            println!("Get entry from database cache: {}", url);
            if force {
                println!("Update entry: {}", url);
                let entry = try!(playlistify_entry(entry));
                Ok(entry)
            } else {
                Ok(entry)
            }
        },
        Err(_) => {
            let entry = try!(Entry::create_by_url(url.to_string()));
            let entry = try!(playlistify_entry(entry));
            println!("Create new entry to database cache: {}", url);
            Ok(entry)
        },
    }
}

pub fn playlistify_entry(entry: Entry) -> Result<Entry, Error> {
    let mut e = entry.clone();
    let product = try!(extract(&entry.url));
    match product.og_obj {
        Some(og_obj) => {
            e.title       = Some(og_obj.title);
            e.description = og_obj.description;
            e.locale      = og_obj.locale;
            e.visual_url  = og_obj.images.first().map(|i| i.url.clone());
        },
        None => (),
    }
    for t in product.tracks {
        let track = try!(Track::find_or_create(t.provider,
                                               t.title,
                                               t.url,
                                               t.identifier));
        match entry.tracks.iter().find(|&t| t.id == track.id) {
            Some(_) => (),
            None    => e.add_track(track.clone())
        }
    }
    try!(e.save());
    Ok(e)
}

pub fn show_track(req: &mut Request) -> IronResult<Response> {
    let ref track_id = req.extensions.get::<Router>().unwrap()
                          .find("track_id").unwrap();
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    match Track::find_by_id(track_id) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               json_type,
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn show_track_by_provider_id(req: &mut Request) -> IronResult<Response> {
    let provider   = req.extensions.get::<Router>().unwrap().find("provider").unwrap();
    let identifier = req.extensions.get::<Router>().unwrap().find("id").unwrap();
    let json_type  = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    let p = &Provider::new(provider.to_string());
    match Track::find_by(p, identifier) {
        Ok(track) => {
            Ok(Response::with((status::Ok,
                               json_type,
                               track.to_json().to_string())))
        },
        Err(e) => Ok(e.as_response())
    }
}

pub fn update_track(req: &mut Request) -> IronResult<Response> {
    let json_type = Header(ContentType(Mime::from_str("application/json").ok().unwrap()));
    fn param_as_string(req: &mut Request, key: &str) -> Option<String> {
        match req.get_ref::<UrlEncodedBody>() {
            Ok(ref params) => match params.get(key) {
                Some(val) => Some(val[0].clone()),
                None      => None
            },
            Err(_) => None
        }
    }

    fn query_as_string(req: &mut Request, key: &str) -> String {
        return req.extensions.get::<Router>().unwrap()
                  .find(key).unwrap().to_string();
    }

    match Track::find_by_id(&query_as_string(req, "track_id")) {
        Ok(mut track) => {
            match param_as_string(req, "title") {
                Some(title) => track.title = title,
                None        => println!("no title")
            }
            match param_as_string(req, "url") {
                Some(url) => track.url = url,
                None      => println!("no url")
            }
            match track.save() {
                Ok(_) => Ok(Response::with((status::Ok,
                                         json_type,
                                         track.to_json().to_string()))),
                Err(e) =>  Ok(e.as_response())
            }
        },
        Err(e) =>  {
            Ok(e.as_response())
        }
    }
}

pub fn main() {
    let path = Path::new("public");
    let mut mount = Mount::new();
    mount.mount("/web/", Static::new(Path::new(path)));
    let router = router!(
                         playlistify: get  "/playlistify"          => playlistify,
                          show_track: get  "/tracks/:track_id"     => show_track,
                        update_track: post "/tracks/:track_id"     => update_track,
           show_track_by_provider_id: get  "/tracks/:provider/:id" => show_track_by_provider_id,
                                 web: get  "/*"                    => mount,
    );
    let port_str = match std::env::var("PORT") {
        Ok(n)    => n,
        Err(_) => "8080".to_string()
    };
    let port: u16 = match port_str.trim().parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Faild to parse port");
            return;
        }
    };
    println!("PORT {}", port_str);
    let ip = Ipv4Addr::new(0, 0, 0, 0);
    Iron::new(router).http(SocketAddrV4::new(ip, port)).unwrap();
}
<|endoftext|>


<commit_before>extern crate app_dirs;
extern crate bincode;
extern crate chrono;
#[macro_use]
extern crate derive_builder;
extern crate fern;
extern crate inflector;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nalgebra;
extern crate notify;
extern crate petgraph;
extern crate rand;
extern crate rayon;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate spade;
extern crate statrs;
extern crate termion;
extern crate textwrap;
extern crate toml;
extern crate tui;

mod game_config;
mod resources;
mod generators;
mod astronomicals;
mod entities;
mod game;
mod gui;
mod event;
mod ship;
mod utils;
mod player;

use app_dirs::{get_data_root, AppDataType};
use generators::generate_galaxy;

/// Setup logging to file in user data dir.
pub fn setup_logger() -> Result<(), fern::InitError> {
    let output_path = get_data_root(AppDataType::UserConfig)
        .unwrap()
        .join("gemini/debug.log");
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Debug)
        .chain(fern::log_file(output_path)?)
        .apply()?;
    Ok(())
}

fn main() {
    // Init logger
    setup_logger();

    // Load GameConfig from disk
    let config = game_config::GameConfig::retrieve();
    info!("Initial config is: {:#?}", config);

    let enable_gui = config.enable_gui;

    // Inital game state
    let game_state = match game::Game::load() {
        Some(game_state) => game_state,
        None => {
            let game_state = game::Game::new();

            // Generate galaxy
            info!("Generating galaxy...");
            *game_state.galaxy.lock().unwrap() = generate_galaxy(&config);

            info!("Loading ships...");
            game_state
                .shipyard
                .lock()
                .unwrap()
                .add_ships(resources::fetch_resource::<resources::ShipResource>().unwrap());

            info!("Creating player...");
            *game_state.player.lock().unwrap() = player::Player::new(
                config.starting_credits,
                game_state.shipyard.lock().unwrap().create_base_ship(),
                // TODO: Replace starting point in config.
                game_state
                    .galaxy
                    .lock()
                    .unwrap()
                    .nearest(&utils::Point::origin())
                    .unwrap(),
            );

            game_state.save_all();
            game_state
        }
    };

    // Start event handler
    event::EventHandler::start();

    // Init and start gui
    if enable_gui {
        let mut gui = gui::Gui::new(game_state);
        gui.start();
    }
}
<commit_msg>Filter out logging from external crates (#107)<commit_after>extern crate app_dirs;
extern crate bincode;
extern crate chrono;
#[macro_use]
extern crate derive_builder;
extern crate fern;
extern crate inflector;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nalgebra;
extern crate notify;
extern crate petgraph;
extern crate rand;
extern crate rayon;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate spade;
extern crate statrs;
extern crate termion;
extern crate textwrap;
extern crate toml;
extern crate tui;

mod game_config;
mod resources;
mod generators;
mod astronomicals;
mod entities;
mod game;
mod gui;
mod event;
mod ship;
mod utils;
mod player;

use log::LevelFilter;
use app_dirs::{get_data_root, AppDataType};
use generators::generate_galaxy;

/// Setup logging to file in user data dir.
pub fn setup_logger() -> Result<(), fern::InitError> {
    let output_path = get_data_root(AppDataType::UserConfig)
        .unwrap()
        .join("gemini/debug.log");
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Off)
        .level_for("gemini", LevelFilter::Trace)
        .chain(fern::log_file(output_path)?)
        .apply()?;
    Ok(())
}

fn main() {
    // Init logger
    setup_logger();

    // Load GameConfig from disk
    let config = game_config::GameConfig::retrieve();
    info!("Initial config is: {:#?}", config);

    let enable_gui = config.enable_gui;

    // Inital game state
    let game_state = match game::Game::load() {
        Some(game_state) => game_state,
        None => {
            let game_state = game::Game::new();

            // Generate galaxy
            info!("Generating galaxy...");
            *game_state.galaxy.lock().unwrap() = generate_galaxy(&config);

            info!("Loading ships...");
            game_state
                .shipyard
                .lock()
                .unwrap()
                .add_ships(resources::fetch_resource::<resources::ShipResource>().unwrap());

            info!("Creating player...");
            *game_state.player.lock().unwrap() = player::Player::new(
                config.starting_credits,
                game_state.shipyard.lock().unwrap().create_base_ship(),
                // TODO: Replace starting point in config.
                game_state
                    .galaxy
                    .lock()
                    .unwrap()
                    .nearest(&utils::Point::origin())
                    .unwrap(),
            );

            game_state.save_all();
            game_state
        }
    };

    // Start event handler
    event::EventHandler::start();

    // Init and start gui
    if enable_gui {
        let mut gui = gui::Gui::new(game_state);
        gui.start();
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

mod cli;
mod yaml;

use docopt::Docopt;
use cli::Args;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Funzzy the watcher.

Usage:
  funzzy
  funzzy watch
  funzzy watch -c <command>
  funzzy init
  funzzy -h
  funzzy -v

Options:
  -h --help         Shows this message.
  -v --version      Shows version.
  -c                Execute given command for current folder
";

fn main() {
    let args: Args = Docopt::new(USAGE)
                         .and_then(|dopt| dopt.decode())
                         .unwrap_or_else(|e| e.exit());

    match args {
        Args { flag_v: true, .. } => show(VERSION),
        Args { flag_h: true, .. } => show(USAGE),
        _ => {
            if let Some(command) = cli::command(&args) {
                if let Err(err) = command.execute() {
                    println!("{}", err)
                }
            }
        }
    }
}

fn show(text: &str) -> ! {
    println!("{}", text);
    std::process::exit(0)
}
<commit_msg>feat: multiple commands for event close #7<commit_after>extern crate rustc_serialize;
extern crate docopt;

mod cli;
mod yaml;

use docopt::Docopt;
use cli::Args;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Funzzy the watcher.

Usage:
  funzzy
  funzzy watch
  funzzy watch -c <command>
  funzzy watch -s -c <command>
  funzzy init
  funzzy -h
  funzzy -v

Options:
  -h --help         Shows this message.
  -v --version      Shows version.
  -c                Execute given command for current folder
";

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|dopt| dopt.decode())
        .unwrap_or_else(|e| e.exit());

    match args {
        Args { flag_v: true, .. } => show(VERSION),
        Args { flag_h: true, .. } => show(USAGE),
        _ => {
            if let Some(command) = cli::command(&args) {
                if let Err(err) = command.execute() {
                    println!("{}", err)
                }
            }
        }
    }
}

fn show(text: &str) -> ! {
    println!("{}", text);
    std::process::exit(0)
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;

use std::num::{Int, FromPrimitive};
use std::os;
use std::io::File;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::iter::range_step;

struct EDS {
    needle: Vec<char>,
    haystack: Vec<char>,
    indexed_haystack: HashMap<char, Vec<usize>>,
    first_pos: Vec<usize>,
    second_pos: Vec<usize>,
}

impl Iterator for EDS {
    type Item = (usize, i64);

    fn next(&mut self) -> Option<(usize, i64)> {
        None
    }
}

impl EDS {
    fn find_first_second_pos(&mut self) {
        self.indexed_haystack = HashMap::new();
        for (idx, &c) in self.haystack.iter().enumerate() {
            match self.indexed_haystack.entry(c) {
                Vacant(entry) => { entry.insert(vec![idx]); },
                Occupied(mut entry) => { (*entry.get_mut()).push(idx); }
            }
        }

        match self.indexed_haystack.get(&self.needle[0]) {
            Some(ref entries) => {
                self.first_pos = (**entries).clone();
            },
            None => {
                self.first_pos = vec![];
                return
            }
        }

        match self.indexed_haystack.get(&self.needle[1]) {
            None => { self.second_pos = vec![]; }
            Some(ref entries) => {
                self.second_pos = (**entries).clone();
            }
        }

    }
}

fn search(needle: String, haystack: Vec<char>) -> Vec<(usize, i64)> {
    let needle: Vec<char> = needle.chars().collect();
    let len_needle = needle.len() as i64;
    let len_haystack: usize = haystack.len();
    let mut results : Vec<(usize, i64)> = Vec::new();

    let mut indexed_haystack = HashMap::new();
    for (idx, c) in haystack.iter().enumerate() {
        match indexed_haystack.entry(c) {
            Vacant(entry) => { entry.insert(vec![idx]); },
            Occupied(mut entry) => { (*entry.get_mut()).push(idx); }
        }
    }

    let possible_starts: &Vec<usize>;
    match indexed_haystack.get(&needle[0]) {
        Some(ref entries) => {
            possible_starts = *entries;
        },
        None => {
            return results;
        }
    }

    let second_pos: &Vec<usize>;
    match indexed_haystack.get(&needle[1]) {
        None => { return results; }
        Some(ref entries) => {
            second_pos = *entries;
        }
    }
    for first_pos in possible_starts.iter() {
        for second_pos in second_pos.iter() {
            debug!("first_pos {} second_pos {}", first_pos, second_pos);
            let start = *first_pos;
            let step: i64 = (*second_pos as i64) - (*first_pos as i64);
            if step == 1 {
                continue;
            }
            let positions: Vec<i64> = range_step(start as i64, step*len_needle, step).collect();
            debug!("Start {} step {}", start, step);
            if positions.iter().any(|&x| { x<0 }) {
                // TODO there's probably a better maths way to do this
                break;
            }
            let positions: Vec<usize> = positions.iter().map(|&x| { FromPrimitive::from_i64(x).unwrap() }).collect();
            debug!("positions {:?}", positions);
            if positions.len() == 0 {
                // WTF
                continue;
            }

            if positions[positions.len()-1] > len_haystack - 1 {
                break;
            }
            if positions.iter().map(|&c| { &haystack[c] }).zip(needle.iter()).all(|(&a, &b)| { a == b }) {
                println!("Found starting at {} with step of {}", start, step);
                results.push((start, step));
            }
        }
    }

    results
}

fn positions<T: Int>(start: T, step: T, len: T) -> Vec<T> {
    range(Int::zero(), len).map(|i| { start + step*i } ).collect()
}

fn only_alphanumeric(input: Vec<char>) -> Vec<char> {
    input.iter().filter(|&c| { c.is_alphabetic() }).map(|&c| { c.to_lowercase() }).collect()
}

//fn only_constants(input: Vec<char>) -> Vec<char> {
    //input.filter(|&c| { c.is_alphabetic() && ! ( c }).collect()
//}

fn main() {
    let file_to_search = &os::args()[1];
    println!("Reading in {}", file_to_search);
    let haystack: Vec<char> = File::open(&Path::new(file_to_search)).read_to_string().unwrap().chars().filter(|&x| { x.is_alphabetic() }).map(|x| { x.to_lowercase() }).collect();

    let needle = (&os::args()[2]).to_string();
    println!("Looking for {}", needle);
    for &(start, step) in search(needle, haystack).iter() {
        println!("Found starting at {} with step of {}", start, step);
    }
}
<commit_msg>possble fix<commit_after>#[macro_use] extern crate log;

use std::num::{Int, FromPrimitive};
use std::os;
use std::io::File;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::iter::range_step;

struct EDS {
    needle: Vec<char>,
    haystack: Vec<char>,
    indexed_haystack: HashMap<char, Vec<usize>>,
    first_pos: Vec<usize>,
    second_pos: Vec<usize>,
}

impl Iterator for EDS {
    type Item = (usize, i64);

    fn next(&mut self) -> Option<(usize, i64)> {
        None
    }
}

impl EDS {
    fn find_first_second_pos(&mut self) {
        self.indexed_haystack = HashMap::new();
        for (idx, &c) in self.haystack.iter().enumerate() {
            match self.indexed_haystack.entry(c) {
                Vacant(entry) => { entry.insert(vec![idx]); },
                Occupied(mut entry) => { (*entry.get_mut()).push(idx); }
            }
        }

        match self.indexed_haystack.get(&self.needle[0]) {
            Some(ref entries) => {
                self.first_pos = (**entries).clone();
            },
            None => {
                self.first_pos = vec![];
                return
            }
        }

        match self.indexed_haystack.get(&self.needle[1]) {
            None => { self.second_pos = vec![]; }
            Some(ref entries) => {
                self.second_pos = (**entries).clone();
            }
        }

    }
}

fn search(needle: String, haystack: Vec<char>) -> Vec<(usize, i64)> {
    let needle: Vec<char> = needle.chars().collect();
    let len_needle = needle.len() as i64;
    let len_haystack: usize = haystack.len();
    let mut results : Vec<(usize, i64)> = Vec::new();

    let mut indexed_haystack = HashMap::new();
    for (idx, c) in haystack.iter().enumerate() {
        match indexed_haystack.entry(c) {
            Vacant(entry) => { entry.insert(vec![idx]); },
            Occupied(mut entry) => { (*entry.get_mut()).push(idx); }
        }
    }

    let possible_starts: &Vec<usize>;
    match indexed_haystack.get(&needle[0]) {
        Some(ref entries) => {
            possible_starts = *entries;
        },
        None => {
            return results;
        }
    }

    let second_poses: &Vec<usize>;
    match indexed_haystack.get(&needle[1]) {
        None => { return results; }
        Some(ref entries) => {
            second_poses = *entries;
        }
    }
    for first_pos in possible_starts.iter() {
        for second_pos in second_poses.iter() {
            debug!("first_pos {} second_pos {}", first_pos, second_pos);
            let start = *first_pos;
            let step: i64 = (*second_pos as i64) - (*first_pos as i64);
            if step == 1 {
                continue;
            }
            let positions: Vec<i64> = range_step(start as i64, step*len_needle, step).collect();
            debug!("Start {} step {}", start, step);
            if positions.iter().any(|&x| { x<0 }) {
                // TODO there's probably a better maths way to do this
                break;
            }
            let positions: Vec<usize> = positions.iter().map(|&x| { FromPrimitive::from_i64(x).unwrap() }).collect();
            debug!("positions {:?}", positions);
            if positions.len() == 0 {
                // WTF
                continue;
            }

            if positions[positions.len()-1] > len_haystack - 1 {
                debug!("Past end");
                break;
            }
            if positions.iter().map(|&c| { &haystack[c] }).zip(needle.iter()).all(|(&a, &b)| { a == b }) {
                println!("Found starting at {} with step of {}", start, step);
                results.push((start, step));
            }
        }
    }

    results
}

fn positions<T: Int>(start: T, step: T, len: T) -> Vec<T> {
    range(Int::zero(), len).map(|i| { start + step*i } ).collect()
}

fn only_alphanumeric(input: Vec<char>) -> Vec<char> {
    input.iter().filter(|&c| { c.is_alphabetic() }).map(|&c| { c.to_lowercase() }).collect()
}

//fn only_constants(input: Vec<char>) -> Vec<char> {
    //input.filter(|&c| { c.is_alphabetic() && ! ( c }).collect()
//}

fn main() {
    let file_to_search = &os::args()[1];
    println!("Reading in {}", file_to_search);
    let haystack: Vec<char> = File::open(&Path::new(file_to_search)).read_to_string().unwrap().chars().filter(|&x| { x.is_alphabetic() }).map(|x| { x.to_lowercase() }).collect();

    let needle = (&os::args()[2]).to_string();
    println!("Looking for {}", needle);
    for &(start, step) in search(needle, haystack).iter() {
        println!("Found starting at {} with step of {}", start, step);
    }
}
<|endoftext|>


<commit_before>mod lib;

use lib::nfa::{NFA, StateNumberSet, powerset_construction, NFA_START};
use lib::dfa::{DFA_STUCK, DFA_START};

fn main() {
    dict_nfa();
    dict_dnfa();
    dict_dfa();
}

fn f((c, x): (usize, StateNumberSet)) -> Option<(usize, StateNumberSet)> {
    if x.len() == 0 {
        None
    } else {
        Some((c, x))
    }
}

fn dict_nfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let nfa = NFA::from_dictionary(dictionary);
    for (i, state) in nfa.states.into_iter().enumerate() {
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in state.transitions.into_iter().enumerate().filter_map(|(c,x)|
            if i == NFA_START && x.len() == 1 && x[0] == NFA_START {
                None
            }  else {
                f((c,x))
            }
        ) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if nfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}

fn dict_dnfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let nfa = powerset_construction(NFA::from_dictionary(dictionary));
    for (i, state) in nfa.states.into_iter().enumerate() {
        if i == DFA_STUCK {
            continue;
        }
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in state.transitions.into_iter().enumerate().filter_map(|(c,x)|
            if x.len() == 1 && x[0] == DFA_START {
                None
            }  else {
                f((c,x))
            }
        ) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if nfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}

fn dict_dfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let dfa = powerset_construction(NFA::from_dictionary(dictionary)).freeze().unwrap();
    for (i, state) in (*dfa.states).into_iter().enumerate() {
        if i == DFA_STUCK {
            continue;
        }
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in (*state.transitions).into_iter().enumerate().filter(|&(i,&x)| !(x == DFA_START || x == DFA_STUCK)) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if dfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}<commit_msg>Ignoring unused name<commit_after>mod lib;

use lib::nfa::{NFA, StateNumberSet, powerset_construction, NFA_START};
use lib::dfa::{DFA_STUCK, DFA_START};

fn main() {
    dict_nfa();
    dict_dnfa();
    dict_dfa();
}

fn f((c, x): (usize, StateNumberSet)) -> Option<(usize, StateNumberSet)> {
    if x.len() == 0 {
        None
    } else {
        Some((c, x))
    }
}

fn dict_nfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let nfa = NFA::from_dictionary(dictionary);
    for (i, state) in nfa.states.into_iter().enumerate() {
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in state.transitions.into_iter().enumerate().filter_map(|(c,x)|
            if i == NFA_START && x.len() == 1 && x[0] == NFA_START {
                None
            }  else {
                f((c,x))
            }
        ) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if nfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}

fn dict_dnfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let nfa = powerset_construction(NFA::from_dictionary(dictionary));
    for (i, state) in nfa.states.into_iter().enumerate() {
        if i == DFA_STUCK {
            continue;
        }
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in state.transitions.into_iter().enumerate().filter_map(|(c,x)|
            if x.len() == 1 && x[0] == DFA_START {
                None
            }  else {
                f((c,x))
            }
        ) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if nfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}

fn dict_dfa() {
    let dictionary = &["a", "ab", "bab", "bc", "bca", "c", "caa"];
    let dfa = powerset_construction(NFA::from_dictionary(dictionary)).freeze().unwrap();
    for (i, state) in (*dfa.states).into_iter().enumerate() {
        if i == DFA_STUCK {
            continue;
        }
        print!("{} -> [", i);
        if !state.transitions.is_empty() {
            println!("");
        }
        for (c, tr) in (*state.transitions).into_iter().enumerate().filter(|&(_,&x)|
                !(x == DFA_START || x == DFA_STUCK)) {
            println!("  {} -> {:?},", (c as u8) as char, tr)
        }
        print!("]");
        if dfa.finals[i] {
            print!(" -- final state");
        }
        println!(",");
    }
}<|endoftext|>


<commit_before>//! [한국 러스트 사용자 그룹 홈페이지][rust-kr] 소스코드
//! --------
//!
//! ### 사용법
//! ```bash
//! git clone https://github.com/rust-kr/rust-kr.org.wiki.git docs
//! cargo run
//! # 웹 브라우저에서 http://localhost:8000 를 열어보세요
//! ```
//!
//! [rust-kr]: http://rust-kr.org

#[macro_use(itry)] extern crate iron;
#[macro_use(router)] extern crate router;
extern crate mount;
extern crate staticfile;
extern crate handlebars_iron as hbs;
extern crate pulldown_cmark as cmark;

use std::collections::BTreeMap;
use iron::prelude::*;
use iron::status;
use iron::middleware::AfterMiddleware;
use mount::Mount;
use staticfile::Static;
use hbs::{Template, HandlebarsEngine, DirectorySource};

//
// Configs
//
/// 서버가 소켓을 열 주소
const ADDR: &'static str = "localhost:8000";
/// 마크다운 문서가 담긴 주소
const DOCS_DIR: &'static str = "docs";

/// Entry point
fn main() {
    // Declarative definition of request handling
    let app = Iron::new({
        let mut c = Chain::new({
            // '/static/*'          -> static file serving
            // '/'                  -> Main page
            // '/pages/:name'       -> Specific documents
            // '/pages/_pages'      -> See all documents

            let mut m = Mount::new();
            m.mount("/static/", Static::new("static/"));
            m.mount("/", router! {
                get "/" => index,
                get "/pages/:name" => page,
                get "/pages/_pages" => all_docs,
            });
            m
        });

        // Handlebar templating
        c.link_after({
            let mut hbr = HandlebarsEngine::new();
            hbr.add(Box::new(DirectorySource::new("templates", ".hbs")));
            if let Err(r) = hbr.reload() {
                use std::error::Error;
                panic!("{}", r.description());
            }
            hbr
        });
        // 404 page handler
        c.link_after(catch(|err: IronError| {
            if err.response.body.is_some() { return Err(err); }
            if err.response.status != Some(status::NotFound) { return Err(err); }

            // TODO
            Err(IronError { response: err.response.set("Not found"), ..err })
        }));
        c
    });

    println!("\nServer running at \x1b[33mhttp://{}\x1b[0m\n", ADDR);
    app.http(ADDR).unwrap();
}

/// Root page handler
fn index(_: &mut Request) -> IronResult<Response> {
    read_docs("Home")
}

/// Wiki page handler
fn page(req: &mut Request) -> IronResult<Response> {
    use router::Router;

    // Parse URL
    let name = req.extensions.get::<Router>().unwrap().find("name").unwrap_or("Home");
    read_docs(name)
}

/// "All documents" page handler
fn all_docs(_: &mut Request) -> IronResult<Response> {
    use std::fs::read_dir;

    let mut paths: Vec<_> = itry!(read_dir(DOCS_DIR))
        .filter_map(|f| f.ok())
        .map(|f| f.path())
        .filter(|p| p.is_file())
        .filter(|p| p.as_os_str().to_str()
            .map(|s| s.ends_with(".md"))
            .unwrap_or(false))
        .collect();
    paths.sort();

    let pages = paths.iter()
        .filter_map(|p| p.file_stem())
        .filter_map(|stem| stem.to_str());

    // TODO: 러스트로 이짓하지 말고 Handlebar로 대체
    let mut html = "
".to_string();
    for page in pages {
        use std::fmt::Write;
        write!(&mut html, r#"
{0}
"#, page).unwrap();
    }
    html.push_str("
");

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// `name`을 인자로 받아, `docs/<name>.md` 파일을 렌더링하여 `IronResult<Response>`로 반환합니다.
fn read_docs(name: &str) -> IronResult<Response> {
    use std::fs::File;
    use std::io::Read;
    use cmark::Parser;
    use cmark::html::push_html;

    // Read file
    let path = format!("{}/{}.md", DOCS_DIR, name);
    let mut file = itry!(File::open(&path));
    let mut md = String::new();
    itry!(file.read_to_string(&mut md));

    // Parse markdown
    let parser = Parser::new(&md);
    let mut html = String::new();
    push_html(&mut html, parser);

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// Helper struct for handle 404 page of rust-kr
struct RustkrHandler<F> { handler: F }
/// Helper function for handle 404 page of rust-kr
fn catch<F>(handler: F) -> RustkrHandler<F> { RustkrHandler { handler: handler } }
impl<F> AfterMiddleware for RustkrHandler<F>
    where F: Send + Sync + 'static + Fn(IronError) -> IronResult<Response> {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        (self.handler)(err)
    }
}
<commit_msg>Apply handlebars template to the error page<commit_after>//! [한국 러스트 사용자 그룹 홈페이지][rust-kr] 소스코드
//! --------
//!
//! ### 사용법
//! ```bash
//! git clone https://github.com/rust-kr/rust-kr.org.wiki.git docs
//! cargo run
//! # 웹 브라우저에서 http://localhost:8000 를 열어보세요
//! ```
//!
//! [rust-kr]: http://rust-kr.org

#[macro_use(itry)] extern crate iron;
#[macro_use(router)] extern crate router;
extern crate mount;
extern crate staticfile;
extern crate handlebars_iron as hbs;
extern crate pulldown_cmark as cmark;

use std::collections::BTreeMap;
use iron::prelude::*;
use iron::status;
use iron::middleware::AfterMiddleware;
use mount::Mount;
use staticfile::Static;
use hbs::{Template, HandlebarsEngine, DirectorySource};

//
// Configs
//
/// 서버가 소켓을 열 주소
const ADDR: &'static str = "localhost:8000";
/// 마크다운 문서가 담긴 주소
const DOCS_DIR: &'static str = "docs";

/// Entry point
fn main() {
    // Declarative definition of request handling
    let app = Iron::new({
        let mut c = Chain::new({
            // '/static/*'          -> static file serving
            // '/'                  -> Main page
            // '/pages/:name'       -> Specific documents
            // '/pages/_pages'      -> See all documents

            let mut m = Mount::new();
            m.mount("/static/", Static::new("static/"));
            m.mount("/", router! {
                get "/" => index,
                get "/pages/:name" => page,
                get "/pages/_pages" => all_docs,
            });
            m
        });
        // 404 page handler
        c.link_after(catch(|mut err: IronError| {
            if err.response.body.is_some() { return Err(err); }
            if err.response.status != Some(status::NotFound) { return Err(err); }

            // TODO: 좀더 내용 채우기
            let mut data = BTreeMap::new();
            data.insert("content", r#"

404

This is not the web page you are looking for.

            "#);
            err.response.set_mut(Template::new("default", data));
            Err(err)
        }));
        // Handlebar templating
        c.link_after({
            let mut hbr = HandlebarsEngine::new();
            hbr.add(Box::new(DirectorySource::new("templates", ".hbs")));
            if let Err(r) = hbr.reload() {
                use std::error::Error;
                panic!("{}", r.description());
            }
            hbr
        });
        c
    });

    println!("\nServer running at \x1b[33mhttp://{}\x1b[0m\n", ADDR);
    app.http(ADDR).unwrap();
}

/// Root page handler
fn index(_: &mut Request) -> IronResult<Response> {
    read_docs("Home")
}

/// Wiki page handler
fn page(req: &mut Request) -> IronResult<Response> {
    use router::Router;

    // Parse URL
    let name = req.extensions.get::<Router>().unwrap().find("name").unwrap_or("Home");
    read_docs(name)
}

/// "All documents" page handler
fn all_docs(_: &mut Request) -> IronResult<Response> {
    use std::fs::read_dir;

    let mut paths: Vec<_> = itry!(read_dir(DOCS_DIR))
        .filter_map(|f| f.ok())
        .map(|f| f.path())
        .filter(|p| p.is_file())
        .filter(|p| p.as_os_str().to_str()
            .map(|s| s.ends_with(".md"))
            .unwrap_or(false))
        .collect();
    paths.sort();

    let pages = paths.iter()
        .filter_map(|p| p.file_stem())
        .filter_map(|stem| stem.to_str());

    // TODO: 러스트로 이짓하지 말고 Handlebar로 대체
    let mut html = "
".to_string();
    for page in pages {
        use std::fmt::Write;
        write!(&mut html, r#"
{0}
"#, page).unwrap();
    }
    html.push_str("
");

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// `name`을 인자로 받아, `docs/<name>.md` 파일을 렌더링하여 `IronResult<Response>`로 반환합니다.
fn read_docs(name: &str) -> IronResult<Response> {
    use std::fs::File;
    use std::io::Read;
    use cmark::Parser;
    use cmark::html::push_html;

    // Read file
    let path = format!("{}/{}.md", DOCS_DIR, name);
    let mut file = itry!(File::open(&path));
    let mut md = String::new();
    itry!(file.read_to_string(&mut md));

    // Parse markdown
    let parser = Parser::new(&md);
    let mut html = String::new();
    push_html(&mut html, parser);

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// Helper struct for handle 404 page of rust-kr
struct RustkrHandler<F> { handler: F }
/// Helper function for handle 404 page of rust-kr
fn catch<F>(handler: F) -> RustkrHandler<F> { RustkrHandler { handler: handler } }
impl<F> AfterMiddleware for RustkrHandler<F>
    where F: Send + Sync + 'static + Fn(IronError) -> IronResult<Response> {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        (self.handler)(err)
    }
}
<|endoftext|>