<commit_before>//! Additional methods for Read and Write
//!
//! The additional methods implemented allow reading and writing integers and floats
//! in the specified endianness.
//!
//! # Usage
//!
//! Basically, you need to `use` the trait WritePodExt or ReadPodExt.
//!
//! # Examples
//!
//! ## Reading
//!
//! To read some value from a reader, import ReadPodExt and the needed endianness.
//!
//! ```
//! use podio::{ReadPodExt, BigEndian};
//!
//! let slice: &[u8] = &[0x10, 0x20, 0x30, 0x40];
//! let mut reader = std::io::Cursor::new(slice);
//!
//! let value = reader.read_u32::<BigEndian>().unwrap();
//!
//! assert_eq!(value, 0x10203040);
//! ```
//!
//! ## Writing
//!
//! For writing, you need to import the trait WritePodExt.
//!
//! ```
//! use podio::{WritePodExt, LittleEndian};
//!
//! let slice: &mut [u8] = &mut [0; 2];
//! let mut writer = std::io::Cursor::new(slice);
//!
//! writer.write_u16::<LittleEndian>(0x8802).unwrap();
//!
//! assert_eq!(writer.into_inner(), &[0x02, 0x88]);
//! ```
//!
//! ## Read exact
//!
//! One additional method, not really dealing with POD, is `read_exact`.
//!
//! ```
//! use podio::ReadPodExt;
//!
//! let slice: &[u8] = &[0, 1, 2, 3];
//! let mut reader = std::io::Cursor::new(slice);
//!
//! assert_eq!(reader.read_exact(1).unwrap(), [0]);
//! assert_eq!(reader.read_exact(2).unwrap(), [1,2]);
//! assert_eq!(reader.read_exact(0).unwrap(), []);
//! assert_eq!(reader.read_exact(1).unwrap(), [3]);
//! assert!(reader.read_exact(1).is_err());

#![warn(missing_docs)]

use std::io;
use std::io::prelude::*;

/// Little endian. The number `0xABCD` is stored `[0xCD, 0xAB]`
pub enum LittleEndian {}
/// Big endian. The number `0xABCD` is stored `[0xAB, 0xCD]`
pub enum BigEndian {}

/// Trait implementing conversion methods for a specific endianness
pub trait Endianness {
    /// Converts a value from the platform type to the specified endianness
    fn int_to_target<T: EndianConvert>(val: T) -> T;
    /// Converts a value from the sepcified endianness to the platform type
    fn int_from_target<T: EndianConvert>(val: T) -> T;
}

/// Generic trait for endian conversions on integers
pub trait EndianConvert {
    /// Convert self to a big-endian value
    fn to_be(self) -> Self;
    /// Convert self to a little-endian value
    fn to_le(self) -> Self;
    /// Convert a big-endian value to the target endianness
    fn from_be(x: Self) -> Self;
    /// Convert a little-endian value to the target endiannes
    fn from_le(x: Self) -> Self;
}

/// Additional write methods for a io::Write
pub trait WritePodExt {
    /// Write a u64
    fn write_u64<T: Endianness>(&mut self, u64) -> io::Result<()>;
    /// Write a u32
    fn write_u32<T: Endianness>(&mut self, u32) -> io::Result<()>;
    /// Write a u16
    fn write_u16<T: Endianness>(&mut self, u16) -> io::Result<()>;
    /// Write a u8
    fn write_u8(&mut self, u8) -> io::Result<()>;
    /// Write a i64
    fn write_i64<T: Endianness>(&mut self, i64) -> io::Result<()>;
    /// Write a i32
    fn write_i32<T: Endianness>(&mut self, i32) -> io::Result<()>;
    /// Write a i16
    fn write_i16<T: Endianness>(&mut self, i16) -> io::Result<()>;
    /// Write a i8
    fn write_i8(&mut self, i8) -> io::Result<()>;
    /// Write a f32
    fn write_f32<T: Endianness>(&mut self, f32) -> io::Result<()>;
    /// Write a f64
    fn write_f64<T: Endianness>(&mut self, f64) -> io::Result<()>;
}

/// Additional read methods for a io::Read
pub trait ReadPodExt {
    /// Read a u64
    fn read_u64<T: Endianness>(&mut self) -> io::Result<u64>;
    /// Read a u32
    fn read_u32<T: Endianness>(&mut self) -> io::Result<u32>;
    /// Read a u16
    fn read_u16<T: Endianness>(&mut self) -> io::Result<u16>;
    /// Read a u8
    fn read_u8(&mut self) -> io::Result<u8>;
    /// Read a i64
    fn read_i64<T: Endianness>(&mut self) -> io::Result<i64>;
    /// Read a i32
    fn read_i32<T: Endianness>(&mut self) -> io::Result<i32>;
    /// Read a i16
    fn read_i16<T: Endianness>(&mut self) -> io::Result<i16>;
    /// Read a i8
    fn read_i8(&mut self) -> io::Result<i8>;
    /// Read a f32
    fn read_f32<T: Endianness>(&mut self) -> io::Result<f32>;
    /// Read a f64
    fn read_f64<T: Endianness>(&mut self) -> io::Result<f64>;
    /// Read a specific number of bytes
    fn read_exact(&mut self, usize) -> io::Result<Vec<u8>>;
}

impl Endianness for LittleEndian {
    #[inline]
    fn int_to_target<T: EndianConvert>(val: T) -> T {
        val.to_le()
    }
    #[inline]
    fn int_from_target<T: EndianConvert>(val: T) -> T {
        <T as EndianConvert>::from_le(val)
    }
}

impl Endianness for BigEndian {
    #[inline]
    fn int_to_target<T: EndianConvert>(val: T) -> T {
        val.to_be()
    }
    #[inline]
    fn int_from_target<T: EndianConvert>(val: T) -> T {
        <T as EndianConvert>::from_be(val)
    }
}

macro_rules! impl_platform_convert {
    ($T:ty) => {
        impl EndianConvert for $T {
            #[inline]
            fn to_be(self) -> $T {
                self.to_be()
            }

            #[inline]
            fn to_le(self) -> $T {
                self.to_le()
            }

            #[inline]
            fn from_be(x: $T) -> $T {
                if cfg!(target_endian = "big") { x } else { x.swap_bytes() }
            }

            #[inline]
            fn from_le(x: $T) -> $T {
                if cfg!(target_endian = "little") { x } else { x.swap_bytes() }
            }
        }
    };
}

impl_platform_convert!(u8);
impl_platform_convert!(u16);
impl_platform_convert!(u32);
impl_platform_convert!(u64);

#[cfg(target_endian = "little")]
macro_rules! val_to_buf {
    ($val:ident, $T:expr) => {
        {
            let mut buf = [0; $T];
            for i in 0..buf.len() {
                buf[i] = ($val >> (i * 8)) as u8;
            }
            buf
        }
    };
}

#[cfg(target_endian = "big")]
macro_rules! val_to_buf {
    ($val:ident, $T:expr) => {
        {
            let mut buf = [0; $T];
            for i in 0..buf.len() {
                buf[buf.len() - 1 - i] = ($val >> (i * 8)) as u8;
            }
            buf
        }
    };
}

impl<W: Write> WritePodExt for W {
    fn write_u64<T: Endianness>(&mut self, val: u64) -> io::Result<()> {
        let tval = <T as Endianness>::int_to_target(val);
        let buf = val_to_buf!(tval, 8);
        self.write_all(&buf)
    }

    fn write_u32<T: Endianness>(&mut self, val: u32) -> io::Result<()> {
        let tval = <T as Endianness>::int_to_target(val);
        let buf = val_to_buf!(tval, 4);
        self.write_all(&buf)
    }

    fn write_u16<T: Endianness>(&mut self, val: u16) -> io::Result<()> {
        let tval = <T as Endianness>::int_to_target(val);
        let buf = val_to_buf!(tval, 2);
        self.write_all(&buf)
    }

    fn write_u8(&mut self, val: u8) -> io::Result<()> {
        self.write_all(&[val])
    }

    fn write_i64<T: Endianness>(&mut self, val: i64) -> io::Result<()> {
        self.write_u64::<T>(val as u64)
    }

    fn write_i32<T: Endianness>(&mut self, val: i32) -> io::Result<()> {
        self.write_u32::<T>(val as u32)
    }

    fn write_i16<T: Endianness>(&mut self, val: i16) -> io::Result<()> {
        self.write_u16::<T>(val as u16)
    }

    fn write_i8(&mut self, val: i8) -> io::Result<()> {
        self.write_u8(val as u8)
    }

    fn write_f32<T: Endianness>(&mut self, val: f32) -> io::Result<()> {
        let tval: u32 = val.to_bits();
        self.write_u32::<T>(tval)
    }

    fn write_f64<T: Endianness>(&mut self, val: f64) -> io::Result<()> {
        let tval: u64 = val.to_bits();
        self.write_u64::<T>(tval)
    }
}

#[inline]
fn fill_buf<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
    let mut idx = 0;
    while idx != buf.len() {
        match reader.read(&mut buf[idx..]) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "Could not read enough bytes")),
            Ok(v) => { idx += v; }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}

#[cfg(target_endian = "little")]
macro_rules! buf_to_val {
    ($buf:ident, $T:ty) => {
        {
            let mut val: $T = 0;
            for i in 0..$buf.len() {
                val |= ($buf[i] as $T) << (i * 8);
            }
            val
        }
    };
}

#[cfg(target_endian = "big")]
macro_rules! buf_to_val {
    ($buf:ident, $T:ty) => {
        {
            let mut val: $T = 0;
            for i in 0..$buf.len() {
                val |= ($buf[$buf.len() - 1 - i] as $T) << (i * 8);
            }
            val
        }
    };
}

impl<R: Read> ReadPodExt for R {
    fn read_u64<T: Endianness>(&mut self) -> io::Result<u64> {
        let buf = &mut [0u8; 8];
        try!(fill_buf(self, buf));
        let tval = buf_to_val!(buf, u64);
        Ok(<T as Endianness>::int_from_target(tval))
    }

    fn read_u32<T: Endianness>(&mut self) -> io::Result<u32> {
        let buf = &mut [0u8; 4];
        try!(fill_buf(self, buf));
        let tval = buf_to_val!(buf, u32);
        Ok(<T as Endianness>::int_from_target(tval))
    }

    fn read_u16<T: Endianness>(&mut self) -> io::Result<u16> {
        let buf = &mut [0u8; 2];
        try!(fill_buf(self, buf));
        let tval = buf_to_val!(buf, u16);
        Ok(<T as Endianness>::int_from_target(tval))
    }

    fn read_u8(&mut self) -> io::Result<u8> {
        let buf = &mut [0u8; 1];
        try!(fill_buf(self, buf));
        Ok(buf[0])
    }

    fn read_i64<T: Endianness>(&mut self) -> io::Result<i64> {
        self.read_u64::<T>().map(|v| v as i64)
    }

    fn read_i32<T: Endianness>(&mut self) -> io::Result<i32> {
        self.read_u32::<T>().map(|v| v as i32)
    }

    fn read_i16<T: Endianness>(&mut self) -> io::Result<i16> {
        self.read_u16::<T>().map(|v| v as i16)
    }

    fn read_i8(&mut self) -> io::Result<i8> {
        self.read_u8().map(|v| v as i8)
    }

    fn read_f64<T: Endianness>(&mut self) -> io::Result<f64> {
        self.read_u64::<T>().map(|v| f64::from_bits(v))
    }

    fn read_f32<T: Endianness>(&mut self) -> io::Result<f32> {
        self.read_u32::<T>().map(|v| f32::from_bits(v))
    }

    fn read_exact(&mut self, len: usize) -> io::Result<Vec<u8>> {
        let mut res = vec![0; len];
        try!(fill_buf(self, &mut res));
        Ok(res)
    }
}
<commit_msg>Simplify conversion code<commit_after>//! Additional methods for Read and Write
//!
//! The additional methods implemented allow reading and writing integers and floats
//! in the specified endianness.
//!
//! # Usage
//!
//! Basically, you need to `use` the trait WritePodExt or ReadPodExt.
//!
//! # Examples
//!
//! ## Reading
//!
//! To read some value from a reader, import ReadPodExt and the needed endianness.
//!
//! ```
//! use podio::{ReadPodExt, BigEndian};
//!
//! let slice: &[u8] = &[0x10, 0x20, 0x30, 0x40];
//! let mut reader = std::io::Cursor::new(slice);
//!
//! let value = reader.read_u32::<BigEndian>().unwrap();
//!
//! assert_eq!(value, 0x10203040);
//! ```
//!
//! ## Writing
//!
//! For writing, you need to import the trait WritePodExt.
//!
//! ```
//! use podio::{WritePodExt, LittleEndian};
//!
//! let slice: &mut [u8] = &mut [0; 2];
//! let mut writer = std::io::Cursor::new(slice);
//!
//! writer.write_u16::<LittleEndian>(0x8802).unwrap();
//!
//! assert_eq!(writer.into_inner(), &[0x02, 0x88]);
//! ```
//!
//! ## Read exact
//!
//! One additional method, not really dealing with POD, is `read_exact`.
//!
//! ```
//! use podio::ReadPodExt;
//!
//! let slice: &[u8] = &[0, 1, 2, 3];
//! let mut reader = std::io::Cursor::new(slice);
//!
//! assert_eq!(reader.read_exact(1).unwrap(), [0]);
//! assert_eq!(reader.read_exact(2).unwrap(), [1,2]);
//! assert_eq!(reader.read_exact(0).unwrap(), []);
//! assert_eq!(reader.read_exact(1).unwrap(), [3]);
//! assert!(reader.read_exact(1).is_err());

#![warn(missing_docs)]

use std::io;
use std::io::prelude::*;

/// Little endian. The number `0xABCD` is stored `[0xCD, 0xAB]`
pub enum LittleEndian {}
/// Big endian. The number `0xABCD` is stored `[0xAB, 0xCD]`
pub enum BigEndian {}

/// Trait to determine the conversion methods for a specific endianness
pub trait Endianness {
    /// Converts a value between little-endian and the specified endianness
    fn is_little_endian() -> bool;
}

/// Additional write methods for a io::Write
pub trait WritePodExt {
    /// Write a u64
    fn write_u64<T: Endianness>(&mut self, u64) -> io::Result<()>;
    /// Write a u32
    fn write_u32<T: Endianness>(&mut self, u32) -> io::Result<()>;
    /// Write a u16
    fn write_u16<T: Endianness>(&mut self, u16) -> io::Result<()>;
    /// Write a u8
    fn write_u8(&mut self, u8) -> io::Result<()>;
    /// Write a i64
    fn write_i64<T: Endianness>(&mut self, i64) -> io::Result<()>;
    /// Write a i32
    fn write_i32<T: Endianness>(&mut self, i32) -> io::Result<()>;
    /// Write a i16
    fn write_i16<T: Endianness>(&mut self, i16) -> io::Result<()>;
    /// Write a i8
    fn write_i8(&mut self, i8) -> io::Result<()>;
    /// Write a f32
    fn write_f32<T: Endianness>(&mut self, f32) -> io::Result<()>;
    /// Write a f64
    fn write_f64<T: Endianness>(&mut self, f64) -> io::Result<()>;
}

/// Additional read methods for a io::Read
pub trait ReadPodExt {
    /// Read a u64
    fn read_u64<T: Endianness>(&mut self) -> io::Result<u64>;
    /// Read a u32
    fn read_u32<T: Endianness>(&mut self) -> io::Result<u32>;
    /// Read a u16
    fn read_u16<T: Endianness>(&mut self) -> io::Result<u16>;
    /// Read a u8
    fn read_u8(&mut self) -> io::Result<u8>;
    /// Read a i64
    fn read_i64<T: Endianness>(&mut self) -> io::Result<i64>;
    /// Read a i32
    fn read_i32<T: Endianness>(&mut self) -> io::Result<i32>;
    /// Read a i16
    fn read_i16<T: Endianness>(&mut self) -> io::Result<i16>;
    /// Read a i8
    fn read_i8(&mut self) -> io::Result<i8>;
    /// Read a f32
    fn read_f32<T: Endianness>(&mut self) -> io::Result<f32>;
    /// Read a f64
    fn read_f64<T: Endianness>(&mut self) -> io::Result<f64>;
    /// Read a specific number of bytes
    fn read_exact(&mut self, usize) -> io::Result<Vec<u8>>;
}

impl Endianness for LittleEndian {
    fn is_little_endian() -> bool {
        true
    }
}

impl Endianness for BigEndian {
    fn is_little_endian() -> bool {
        false
    }
}

impl<W: Write> WritePodExt for W {
    fn write_u64<T: Endianness>(&mut self, val: u64) -> io::Result<()> {
        let buf = match <T as Endianness>::is_little_endian() {
            true => u64::to_le_bytes(val),
            false => u64::to_be_bytes(val),
        };
        self.write_all(&buf)
    }

    fn write_u32<T: Endianness>(&mut self, val: u32) -> io::Result<()> {
        let buf = match <T as Endianness>::is_little_endian() {
            true => u32::to_le_bytes(val),
            false => u32::to_be_bytes(val),
        };
        self.write_all(&buf)
    }

    fn write_u16<T: Endianness>(&mut self, val: u16) -> io::Result<()> {
        let buf = match <T as Endianness>::is_little_endian() {
            true => u16::to_le_bytes(val),
            false => u16::to_be_bytes(val),
        };
        self.write_all(&buf)
    }

    fn write_u8(&mut self, val: u8) -> io::Result<()> {
        self.write_all(&[val])
    }

    fn write_i64<T: Endianness>(&mut self, val: i64) -> io::Result<()> {
        self.write_u64::<T>(val as u64)
    }

    fn write_i32<T: Endianness>(&mut self, val: i32) -> io::Result<()> {
        self.write_u32::<T>(val as u32)
    }

    fn write_i16<T: Endianness>(&mut self, val: i16) -> io::Result<()> {
        self.write_u16::<T>(val as u16)
    }

    fn write_i8(&mut self, val: i8) -> io::Result<()> {
        self.write_u8(val as u8)
    }

    fn write_f32<T: Endianness>(&mut self, val: f32) -> io::Result<()> {
        let tval: u32 = val.to_bits();
        self.write_u32::<T>(tval)
    }

    fn write_f64<T: Endianness>(&mut self, val: f64) -> io::Result<()> {
        let tval: u64 = val.to_bits();
        self.write_u64::<T>(tval)
    }
}

#[inline]
fn fill_buf<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
    let mut idx = 0;
    while idx != buf.len() {
        match reader.read(&mut buf[idx..]) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "Could not read enough bytes")),
            Ok(v) => { idx += v; }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}

impl<R: Read> ReadPodExt for R {
    fn read_u64<T: Endianness>(&mut self) -> io::Result<u64> {
        let mut buf = [0u8; 8];
        try!(fill_buf(self, &mut buf));
        let val = match <T as Endianness>::is_little_endian() {
            true => u64::from_le_bytes(buf),
            false => u64::from_be_bytes(buf),
        };
        Ok(val)
    }

    fn read_u32<T: Endianness>(&mut self) -> io::Result<u32> {
        let mut buf = [0u8; 4];
        try!(fill_buf(self, &mut buf));
        let val = match <T as Endianness>::is_little_endian() {
            true => u32::from_le_bytes(buf),
            false => u32::from_be_bytes(buf),
        };
        Ok(val)
    }

    fn read_u16<T: Endianness>(&mut self) -> io::Result<u16> {
        let mut buf = [0u8; 2];
        try!(fill_buf(self, &mut buf));
        let val = match <T as Endianness>::is_little_endian() {
            true => u16::from_le_bytes(buf),
            false => u16::from_be_bytes(buf),
        };
        Ok(val)
    }

    fn read_u8(&mut self) -> io::Result<u8> {
        let buf = &mut [0u8; 1];
        try!(fill_buf(self, buf));
        Ok(buf[0])
    }

    fn read_i64<T: Endianness>(&mut self) -> io::Result<i64> {
        self.read_u64::<T>().map(|v| v as i64)
    }

    fn read_i32<T: Endianness>(&mut self) -> io::Result<i32> {
        self.read_u32::<T>().map(|v| v as i32)
    }

    fn read_i16<T: Endianness>(&mut self) -> io::Result<i16> {
        self.read_u16::<T>().map(|v| v as i16)
    }

    fn read_i8(&mut self) -> io::Result<i8> {
        self.read_u8().map(|v| v as i8)
    }

    fn read_f64<T: Endianness>(&mut self) -> io::Result<f64> {
        self.read_u64::<T>().map(|v| f64::from_bits(v))
    }

    fn read_f32<T: Endianness>(&mut self) -> io::Result<f32> {
        self.read_u32::<T>().map(|v| f32::from_bits(v))
    }

    fn read_exact(&mut self, len: usize) -> io::Result<Vec<u8>> {
        let mut res = vec![0; len];
        try!(fill_buf(self, &mut res));
        Ok(res)
    }
}
<|endoftext|>


<commit_before>//!
//! robots.txt parser for Rust
//!
//! The robots.txt Exclusion Protocol is implemented as specified in
//! http://www.robotstxt.org/norobots-rfc.txt
//!
extern crate url;
extern crate time;
extern crate hyper;

use std::io::Read;
use std::cell::{Cell, RefCell};
use url::Url;
use hyper::{Client};
use hyper::status::StatusCode;

/// A rule line is a single "Allow:" (allowance==True) or "Disallow:"
/// (allowance==False) followed by a path."""
#[derive(Debug, Eq, PartialEq, Clone)]
struct RuleLine {
    path: String,
    allowance: bool,
}

/// An entry has one or more user-agents and zero or more rulelines
#[derive(Debug, Eq, PartialEq, Clone)]
struct Entry {
    useragents: RefCell<Vec<String>>,
    rulelines: RefCell<Vec<RuleLine>>,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RobotFileParser {
    entries: RefCell<Vec<Entry>>,
    default_entry: RefCell<Entry>,
    disallow_all: Cell<bool>,
    allow_all: Cell<bool>,
    url: Url,
    host: String,
    path: String,
    last_checked: Cell<i64>,
}

impl RuleLine {
    fn new(path: &str, allowance: bool) -> RuleLine {
        let mut allow = allowance;
        if path == "" && !allowance {
            // an empty value means allow all
            allow = true;
        }
        RuleLine {
            path: path.to_owned(),
            allowance: allow,
        }
    }

    fn applies_to(&self, filename: &str) -> bool {
        &self.path == "*" || filename.starts_with(&self.path)
    }
}

impl Entry {
    fn new() -> Entry {
        Entry {
            useragents: RefCell::new(vec![]),
            rulelines: RefCell::new(vec![]),
        }
    }

    /// check if this entry applies to the specified agent
    fn applies_to(&self, useragent: &str) -> bool {
        let ua = useragent.split("/").nth(0).unwrap_or("").to_lowercase();
        let useragents = self.useragents.borrow();
        for agent in &*useragents {
            if agent == "*" {
                return true;
            }
            if ua.contains(agent) {
                return true;
            }
        }
        false
    }

    /// Preconditions:
    /// - our agent applies to this entry
    /// - filename is URL decoded
    fn allowance(&self, filename: &str) -> bool {
        let rulelines = self.rulelines.borrow();
        for line in &*rulelines {
            if line.applies_to(filename) {
                return line.allowance
            }
        }
        true
    }

    fn push_useragent(&self, useragent: &str) {
        let mut useragents = self.useragents.borrow_mut();
        useragents.push(useragent.to_lowercase().to_owned());
    }

    fn push_ruleline(&self, ruleline: RuleLine) {
        let mut rulelines = self.rulelines.borrow_mut();
        rulelines.push(ruleline);
    }

    fn has_useragent(&self, useragent: &str) -> bool {
        let useragents = self.useragents.borrow();
        useragents.contains(&useragent.to_owned())
    }

    fn is_empty(&self) -> bool {
        let useragents = self.useragents.borrow();
        let rulelines = self.rulelines.borrow();
        useragents.is_empty() && rulelines.is_empty()
    }
}

impl RobotFileParser {
    pub fn new(url: &str) -> RobotFileParser {
        let parsed_url = Url::parse(url).unwrap();
        RobotFileParser {
            entries: RefCell::new(vec![]),
            default_entry: RefCell::new(Entry::new()),
            disallow_all: Cell::new(false),
            allow_all: Cell::new(false),
            url: parsed_url.clone(),
            host: parsed_url.domain().unwrap().to_owned(),
            path: parsed_url.path().unwrap().connect("/"),
            last_checked: Cell::new(0i64),
        }
    }

    /// Returns the time the robots.txt file was last fetched.
    ///
    /// This is useful for long-running web spiders that need to
    /// check for new robots.txt files periodically.
    pub fn mtime(&self) -> i64 {
        self.last_checked.get()
    }

    /// Sets the time the robots.txt file was last fetched to the
    /// current time.
    pub fn modified(&self) {
        use time::get_time;

        self.last_checked.set(get_time().sec);
    }

    /// Sets the URL referring to a robots.txt file.
    pub fn set_url(&mut self, url: &str) {
        let parsed_url = Url::parse(url).unwrap();
        self.url = parsed_url.clone();
        self.host = parsed_url.domain().unwrap().to_owned();
        self.path = parsed_url.path().unwrap().connect("/");
        self.last_checked.set(0i64);
    }

    /// Reads the robots.txt URL and feeds it to the parser.
    pub fn read(&self) {
        let client = Client::new();
        let mut res = match client.get(self.url.clone()).send() {
            Ok(res) => res,
            Err(_) => {
                return;
            }
        };
        match res.status {
            StatusCode::Unauthorized | StatusCode::Forbidden => {
                self.disallow_all.set(true);
            },
            status if status >= StatusCode::BadRequest && status < StatusCode::InternalServerError => {
                self.allow_all.set(true);
            },
            StatusCode::Ok => {
                let mut buf = String::new();
                res.read_to_string(&mut buf).unwrap();
                let lines: Vec<&str> = buf.split("\n").collect();
                self.parse(lines);
            },
            _ => {},
        }
    }

    fn _add_entry(&self, entry: Entry) {
        if entry.has_useragent("*") {
            // the default entry is considered last
            let mut default_entry = self.default_entry.borrow_mut();
            if default_entry.is_empty() {
                // the first default entry wins
                *default_entry = entry;
            }
        } else {
            let mut entries = self.entries.borrow_mut();
            entries.push(entry);
        }
    }

    ///
    /// Parse the input lines from a robots.txt file
    ///
    /// We allow that a user-agent: line is not preceded by
    /// one or more blank lines.
    ///
    pub fn parse(&self, lines: Vec<&str>) {
        use url::percent_encoding::percent_decode;

        // states:
        //   0: start state
        //   1: saw user-agent line
        //   2: saw an allow or disallow line
        let mut state = 0;
        let mut entry = Entry::new();

        self.modified();
        for line in &lines {
            let mut ln = line.clone();
            if ln.is_empty() {
                match state {
                    1 => {
                        entry = Entry::new();
                        state = 0;
                    },
                    2 => {
                        self._add_entry(entry);
                        entry = Entry::new();
                        state = 0;
                    },
                    _ => {},
                }
            }
            // remove optional comment and strip line
            match ln.find("#") {
                Some(i) => {
                    ln = &ln[0..i];
                },
                None => {},
            }
            ln = ln.trim();
            if ln.is_empty() {
                continue;
            }
            let parts: Vec<&str> = ln.splitn(2, ":").collect();
            if parts.len() == 2 {
                let part0 = parts[0].trim().to_lowercase();
                let part1 = String::from_utf8(percent_decode(parts[1].trim().as_bytes())).unwrap_or("".to_owned());
                match part0 {
                    ref x if x == "user-agent" => {
                        if state == 2 {
                            self._add_entry(entry);
                            entry = Entry::new();
                        }
                        entry.push_useragent(&part1);
                        state = 1;
                    },
                    ref x if x == "disallow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(&part1, false));
                            state = 2;
                        }
                    },
                    ref x if x == "allow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(&part1, true));
                            state = 2;
                        }
                    },
                    _ => {},
                }
            }
        }
        if state == 2 {
            self._add_entry(entry);
        }
    }

    /// Using the parsed robots.txt decide if useragent can fetch url
    pub fn can_fetch(&self, useragent: &str, url: &str) -> bool {
        use url::percent_encoding::percent_decode;

        if self.disallow_all.get() {
            return false;
        }
        if self.allow_all.get() {
            return true;
        }
        // Until the robots.txt file has been read or found not
        // to exist, we must assume that no url is allowable.
        // This prevents false positives when a user erronenously
        // calls can_fetch() before calling read().
        if self.last_checked.get() == 0 {
            return false;
        }
        // search for given user agent matches
        // the first match counts
        let decoded_url = String::from_utf8(percent_decode(url.trim().as_bytes())).unwrap_or("".to_owned());
        let url_str = match decoded_url {
            ref u if !u.is_empty() => u.to_owned(),
            _ => "/".to_owned(),
        };
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.allowance(&url_str);
            }
        }
        // try the default entry last
        let default_entry = self.default_entry.borrow();
        if !default_entry.is_empty() {
            return default_entry.allowance(&url_str);
        }
        // agent not found ==> access granted
        true
    }
}
<commit_msg>Use join instead of connect<commit_after>//!
//! robots.txt parser for Rust
//!
//! The robots.txt Exclusion Protocol is implemented as specified in
//! http://www.robotstxt.org/norobots-rfc.txt
//!
extern crate url;
extern crate time;
extern crate hyper;

use std::io::Read;
use std::cell::{Cell, RefCell};
use url::Url;
use hyper::{Client};
use hyper::status::StatusCode;

/// A rule line is a single "Allow:" (allowance==True) or "Disallow:"
/// (allowance==False) followed by a path."""
#[derive(Debug, Eq, PartialEq, Clone)]
struct RuleLine {
    path: String,
    allowance: bool,
}

/// An entry has one or more user-agents and zero or more rulelines
#[derive(Debug, Eq, PartialEq, Clone)]
struct Entry {
    useragents: RefCell<Vec<String>>,
    rulelines: RefCell<Vec<RuleLine>>,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RobotFileParser {
    entries: RefCell<Vec<Entry>>,
    default_entry: RefCell<Entry>,
    disallow_all: Cell<bool>,
    allow_all: Cell<bool>,
    url: Url,
    host: String,
    path: String,
    last_checked: Cell<i64>,
}

impl RuleLine {
    fn new(path: &str, allowance: bool) -> RuleLine {
        let mut allow = allowance;
        if path == "" && !allowance {
            // an empty value means allow all
            allow = true;
        }
        RuleLine {
            path: path.to_owned(),
            allowance: allow,
        }
    }

    fn applies_to(&self, filename: &str) -> bool {
        &self.path == "*" || filename.starts_with(&self.path)
    }
}

impl Entry {
    fn new() -> Entry {
        Entry {
            useragents: RefCell::new(vec![]),
            rulelines: RefCell::new(vec![]),
        }
    }

    /// check if this entry applies to the specified agent
    fn applies_to(&self, useragent: &str) -> bool {
        let ua = useragent.split("/").nth(0).unwrap_or("").to_lowercase();
        let useragents = self.useragents.borrow();
        for agent in &*useragents {
            if agent == "*" {
                return true;
            }
            if ua.contains(agent) {
                return true;
            }
        }
        false
    }

    /// Preconditions:
    /// - our agent applies to this entry
    /// - filename is URL decoded
    fn allowance(&self, filename: &str) -> bool {
        let rulelines = self.rulelines.borrow();
        for line in &*rulelines {
            if line.applies_to(filename) {
                return line.allowance
            }
        }
        true
    }

    fn push_useragent(&self, useragent: &str) {
        let mut useragents = self.useragents.borrow_mut();
        useragents.push(useragent.to_lowercase().to_owned());
    }

    fn push_ruleline(&self, ruleline: RuleLine) {
        let mut rulelines = self.rulelines.borrow_mut();
        rulelines.push(ruleline);
    }

    fn has_useragent(&self, useragent: &str) -> bool {
        let useragents = self.useragents.borrow();
        useragents.contains(&useragent.to_owned())
    }

    fn is_empty(&self) -> bool {
        let useragents = self.useragents.borrow();
        let rulelines = self.rulelines.borrow();
        useragents.is_empty() && rulelines.is_empty()
    }
}

impl RobotFileParser {
    pub fn new(url: &str) -> RobotFileParser {
        let parsed_url = Url::parse(url).unwrap();
        RobotFileParser {
            entries: RefCell::new(vec![]),
            default_entry: RefCell::new(Entry::new()),
            disallow_all: Cell::new(false),
            allow_all: Cell::new(false),
            url: parsed_url.clone(),
            host: parsed_url.domain().unwrap().to_owned(),
            path: parsed_url.path().unwrap().join("/"),
            last_checked: Cell::new(0i64),
        }
    }

    /// Returns the time the robots.txt file was last fetched.
    ///
    /// This is useful for long-running web spiders that need to
    /// check for new robots.txt files periodically.
    pub fn mtime(&self) -> i64 {
        self.last_checked.get()
    }

    /// Sets the time the robots.txt file was last fetched to the
    /// current time.
    pub fn modified(&self) {
        use time::get_time;

        self.last_checked.set(get_time().sec);
    }

    /// Sets the URL referring to a robots.txt file.
    pub fn set_url(&mut self, url: &str) {
        let parsed_url = Url::parse(url).unwrap();
        self.url = parsed_url.clone();
        self.host = parsed_url.domain().unwrap().to_owned();
        self.path = parsed_url.path().unwrap().join("/");
        self.last_checked.set(0i64);
    }

    /// Reads the robots.txt URL and feeds it to the parser.
    pub fn read(&self) {
        let client = Client::new();
        let mut res = match client.get(self.url.clone()).send() {
            Ok(res) => res,
            Err(_) => {
                return;
            }
        };
        match res.status {
            StatusCode::Unauthorized | StatusCode::Forbidden => {
                self.disallow_all.set(true);
            },
            status if status >= StatusCode::BadRequest && status < StatusCode::InternalServerError => {
                self.allow_all.set(true);
            },
            StatusCode::Ok => {
                let mut buf = String::new();
                res.read_to_string(&mut buf).unwrap();
                let lines: Vec<&str> = buf.split("\n").collect();
                self.parse(lines);
            },
            _ => {},
        }
    }

    fn _add_entry(&self, entry: Entry) {
        if entry.has_useragent("*") {
            // the default entry is considered last
            let mut default_entry = self.default_entry.borrow_mut();
            if default_entry.is_empty() {
                // the first default entry wins
                *default_entry = entry;
            }
        } else {
            let mut entries = self.entries.borrow_mut();
            entries.push(entry);
        }
    }

    ///
    /// Parse the input lines from a robots.txt file
    ///
    /// We allow that a user-agent: line is not preceded by
    /// one or more blank lines.
    ///
    pub fn parse(&self, lines: Vec<&str>) {
        use url::percent_encoding::percent_decode;

        // states:
        //   0: start state
        //   1: saw user-agent line
        //   2: saw an allow or disallow line
        let mut state = 0;
        let mut entry = Entry::new();

        self.modified();
        for line in &lines {
            let mut ln = line.clone();
            if ln.is_empty() {
                match state {
                    1 => {
                        entry = Entry::new();
                        state = 0;
                    },
                    2 => {
                        self._add_entry(entry);
                        entry = Entry::new();
                        state = 0;
                    },
                    _ => {},
                }
            }
            // remove optional comment and strip line
            match ln.find("#") {
                Some(i) => {
                    ln = &ln[0..i];
                },
                None => {},
            }
            ln = ln.trim();
            if ln.is_empty() {
                continue;
            }
            let parts: Vec<&str> = ln.splitn(2, ":").collect();
            if parts.len() == 2 {
                let part0 = parts[0].trim().to_lowercase();
                let part1 = String::from_utf8(percent_decode(parts[1].trim().as_bytes())).unwrap_or("".to_owned());
                match part0 {
                    ref x if x == "user-agent" => {
                        if state == 2 {
                            self._add_entry(entry);
                            entry = Entry::new();
                        }
                        entry.push_useragent(&part1);
                        state = 1;
                    },
                    ref x if x == "disallow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(&part1, false));
                            state = 2;
                        }
                    },
                    ref x if x == "allow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(&part1, true));
                            state = 2;
                        }
                    },
                    _ => {},
                }
            }
        }
        if state == 2 {
            self._add_entry(entry);
        }
    }

    /// Using the parsed robots.txt decide if useragent can fetch url
    pub fn can_fetch(&self, useragent: &str, url: &str) -> bool {
        use url::percent_encoding::percent_decode;

        if self.disallow_all.get() {
            return false;
        }
        if self.allow_all.get() {
            return true;
        }
        // Until the robots.txt file has been read or found not
        // to exist, we must assume that no url is allowable.
        // This prevents false positives when a user erronenously
        // calls can_fetch() before calling read().
        if self.last_checked.get() == 0 {
            return false;
        }
        // search for given user agent matches
        // the first match counts
        let decoded_url = String::from_utf8(percent_decode(url.trim().as_bytes())).unwrap_or("".to_owned());
        let url_str = match decoded_url {
            ref u if !u.is_empty() => u.to_owned(),
            _ => "/".to_owned(),
        };
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.allowance(&url_str);
            }
        }
        // try the default entry last
        let default_entry = self.default_entry.borrow();
        if !default_entry.is_empty() {
            return default_entry.allowance(&url_str);
        }
        // agent not found ==> access granted
        true
    }
}
<|endoftext|>


<commit_before>extern crate ruster_unsafe;
pub use self::ruster_unsafe::{ ERL_NIF_TERM, ErlNifResourceFlags, ErlNifResourceType };

extern crate libc;
pub use libc::{ c_char, size_t, c_int, c_uint, c_void };

use std::mem;
use std::marker::PhantomData;

mod types;
pub use self::types::{ NifEncoder, NifDecoder };

mod resource;
pub use self::resource::{ open_resource_type_raw, alloc_resource_raw };
pub use self::resource::{ open_struct_resource_type, alloc_struct_resource, get_struct_resource };

mod binary;
pub use self::binary::{ NifBinary, alloc_binary, make_binary, get_binary };

pub struct NifEnv {
    pub env: *mut ruster_unsafe::ErlNifEnv,
}
impl NifEnv {
    unsafe fn as_c_arg(&self) -> *mut ruster_unsafe::ErlNifEnv {
        self.env
    }
}

#[derive(Clone, Copy)]
pub enum NifError {
    BadArg,
    AllocFail,
    Atom(&'static str),
}

#[derive(Clone, Copy)]
pub struct NifTerm<'a> {
    pub term: ERL_NIF_TERM,
    env_life: PhantomData<&'a NifEnv>,
}
impl<'a> NifTerm<'a> {
    pub fn new(_env: &'a NifEnv, inner: ERL_NIF_TERM) -> Self {
        NifTerm {
            term: inner,
            env_life: PhantomData
        }
    }
    unsafe fn as_c_arg(&self) -> ERL_NIF_TERM {
        self.term
    }
}

pub struct NifResourceType {
    pub res: *mut ErlNifResourceType
}
pub struct NifStructResourceType<T> {
    pub res: NifResourceType,
    pub struct_type: PhantomData<T>,
}

// Atoms are a special case of a term. They can be stored and used on all envs regardless of where
// it lives and when it is created.
#[derive(PartialEq, Eq)]
pub struct NifAtom {
    term: ERL_NIF_TERM,
}

pub fn get_tuple<'a>(env: &'a NifEnv, term: NifTerm) -> Result<Vec<NifTerm<'a>>, NifError> {
    let mut arity: c_int = 0;
    let mut array_ptr: *const ERL_NIF_TERM = unsafe { mem::uninitialized() };
    let success = unsafe { ruster_unsafe::enif_get_tuple(env.env, term.term,
                                                         &mut arity as *mut c_int,
                                                         &mut array_ptr as *mut *const ERL_NIF_TERM) };
    if success == 0 {
        return Err(NifError::BadArg);
    }
    let term_array = unsafe { std::slice::from_raw_parts(array_ptr, arity as usize) };
    Ok(term_array.iter().map(|x| { NifTerm::new(env, *x) }).collect::<Vec<NifTerm>>())
}

pub fn decode_type<T: NifDecoder>(term: NifTerm, env: &NifEnv) -> Result<T, NifError> {
    NifDecoder::decode(term, env)
}

#[macro_export]
macro_rules! decode_tuple {
    (@count ()) => { 0 };
    (@count ($_i:ty, $($rest:tt)*)) => { 1 + decode_tuple!(@count ($($rest)*)) };
    (@accum $_env:expr, $_list:expr, $_num:expr, ($(,)*) -> ($($body:tt)*)) => {
        decode_tuple!(@as_expr ($($body)*))
    };
    (@accum $env:expr, $list:expr, $num:expr, ($head:ty, $($tail:tt)*) -> ($($body:tt)*)) => {
        decode_tuple!(@accum $env, $list, ($num+1), ($($tail)*) -> ($($body)* decode_tuple!(@decode_arg $env, $head, $list[$num]),))
    };
    (@as_expr $e:expr) => {$e};
    (@decode_arg $env:expr, $typ:ty, $val:expr) => {
        match $crate::nif::decode_type::<$typ>($val, $env) {
            Ok(val) => val,
            Err(val) => return Err(val),
        }
    };
    ($env:expr, $term:expr, ($($typs:ty),*)) => {
        {
            let num_expr: usize = decode_tuple!(@count ($($typs,)*));
            let terms = try!($crate::nif::get_tuple($env, $term));
            if terms.len() != num_expr {
                Err($crate::nif::NifError::BadArg)
            } else {
                Ok(decode_tuple!(@accum $env, terms, 0, ($($typs),*,) -> ()))
            }
        }
    }
}

//macro_rules! decode_tuple {
//    ($env:expr, $term:expr, ($($typ:ty),+)) => {
//        let num_expr: usize = count_typ!($($typ,)*);
//        let terms = try!($crate::nif::get_tuple($env, $term));
//        if terms.len() != num_expr {
//            Err($crate::nif::NifError::BadArg)
//        } else {
//            Ok((decode_tuple_arg!($env, terms, 0, ($($typ,)*))))
//        }
//    }
//}

#[macro_export]
macro_rules! nif_func {
    ($name:ident, $fun:expr) => (
        extern "C" fn $name(r_env: *mut ErlNifEnv,
                            argc: c_int,
                            argv: *const ERL_NIF_TERM) -> ERL_NIF_TERM {
            use $crate::nif::{ NifEnv, NifTerm, NifError, size_t };
            let env = NifEnv { env: r_env };
            let terms = unsafe { std::slice::from_raw_parts(argv, argc as usize) }.iter()
                .map(|x| { NifTerm::new(&env, *x) }).collect::<Vec<NifTerm>>();
            let inner_fun: &for<'a> Fn(&'a NifEnv, &Vec<NifTerm>) -> Result<NifTerm<'a>, NifError> = &$fun;
            let res: Result<NifTerm, NifError> = inner_fun(&env, &terms);
            match res {
                Ok(ret) => ret.term,
                Err(NifError::BadArg) =>
                    unsafe { ruster_unsafe::enif_make_badarg(r_env) },
                Err(NifError::Atom(name)) =>
                    unsafe { ruster_unsafe::enif_make_atom_len(r_env,
                                                               name.as_ptr() as *const u8,
                                                               name.len() as size_t) },
            }
        });
}

#[macro_export]
macro_rules! nif_atom {
    ($env:expr, $name:ident) => ({
        const atom_name: &'static str = stringify!($name);
        ruster_unsafe::enif_make_atom_len(
            $env.env,
            atom_name.as_ptr() as *const u8,
            atom_name.len() as $crate::nif::size_t)
    });
}

pub fn nif_atom<'a>(env: &'a NifEnv, name: &str) -> NifTerm<'a> {
    unsafe {
        NifTerm::new(env, ruster_unsafe::enif_make_atom_len(
            env.env,
            name.as_ptr() as *const u8,
            name.len() as size_t))
    }
}
<commit_msg>reimplement macros previously from ruster_unsafe<commit_after>extern crate ruster_unsafe;
pub use self::ruster_unsafe::{ ERL_NIF_TERM, ErlNifResourceFlags, ErlNifResourceType };

extern crate libc;
pub use libc::{ c_char, size_t, c_int, c_uint, c_void };

use std::mem;
use std::marker::PhantomData;

mod types;
pub use self::types::{ NifEncoder, NifDecoder };

mod resource;
pub use self::resource::{ open_resource_type_raw, alloc_resource_raw };
pub use self::resource::{ open_struct_resource_type, alloc_struct_resource, get_struct_resource };

mod binary;
pub use self::binary::{ NifBinary, alloc_binary, make_binary, get_binary };

pub struct NifEnv {
    pub env: *mut ruster_unsafe::ErlNifEnv,
}
impl NifEnv {
    unsafe fn as_c_arg(&self) -> *mut ruster_unsafe::ErlNifEnv {
        self.env
    }
}

#[derive(Clone, Copy)]
pub enum NifError {
    BadArg,
    AllocFail,
    Atom(&'static str),
}

#[derive(Clone, Copy)]
pub struct NifTerm<'a> {
    pub term: ERL_NIF_TERM,
    env_life: PhantomData<&'a NifEnv>,
}
impl<'a> NifTerm<'a> {
    pub fn new(_env: &'a NifEnv, inner: ERL_NIF_TERM) -> Self {
        NifTerm {
            term: inner,
            env_life: PhantomData
        }
    }
    unsafe fn as_c_arg(&self) -> ERL_NIF_TERM {
        self.term
    }
}

pub struct NifResourceType {
    pub res: *mut ErlNifResourceType
}
pub struct NifStructResourceType<T> {
    pub res: NifResourceType,
    pub struct_type: PhantomData<T>,
}

// Atoms are a special case of a term. They can be stored and used on all envs regardless of where
// it lives and when it is created.
#[derive(PartialEq, Eq)]
pub struct NifAtom {
    term: ERL_NIF_TERM,
}

pub fn get_tuple<'a>(env: &'a NifEnv, term: NifTerm) -> Result<Vec<NifTerm<'a>>, NifError> {
    let mut arity: c_int = 0;
    let mut array_ptr: *const ERL_NIF_TERM = unsafe { mem::uninitialized() };
    let success = unsafe { ruster_unsafe::enif_get_tuple(env.env, term.term,
                                                         &mut arity as *mut c_int,
                                                         &mut array_ptr as *mut *const ERL_NIF_TERM) };
    if success == 0 {
        return Err(NifError::BadArg);
    }
    let term_array = unsafe { std::slice::from_raw_parts(array_ptr, arity as usize) };
    Ok(term_array.iter().map(|x| { NifTerm::new(env, *x) }).collect::<Vec<NifTerm>>())
}

pub fn decode_type<T: NifDecoder>(term: NifTerm, env: &NifEnv) -> Result<T, NifError> {
    NifDecoder::decode(term, env)
}

#[macro_export]
macro_rules! decode_tuple {
    (@count ()) => { 0 };
    (@count ($_i:ty, $($rest:tt)*)) => { 1 + decode_tuple!(@count ($($rest)*)) };
    (@accum $_env:expr, $_list:expr, $_num:expr, ($(,)*) -> ($($body:tt)*)) => {
        decode_tuple!(@as_expr ($($body)*))
    };
    (@accum $env:expr, $list:expr, $num:expr, ($head:ty, $($tail:tt)*) -> ($($body:tt)*)) => {
        decode_tuple!(@accum $env, $list, ($num+1), ($($tail)*) -> ($($body)* decode_tuple!(@decode_arg $env, $head, $list[$num]),))
    };
    (@as_expr $e:expr) => {$e};
    (@decode_arg $env:expr, $typ:ty, $val:expr) => {
        match $crate::nif::decode_type::<$typ>($val, $env) {
            Ok(val) => val,
            Err(val) => return Err(val),
        }
    };
    ($env:expr, $term:expr, ($($typs:ty),*)) => {
        {
            let num_expr: usize = decode_tuple!(@count ($($typs,)*));
            let terms = try!($crate::nif::get_tuple($env, $term));
            if terms.len() != num_expr {
                Err($crate::nif::NifError::BadArg)
            } else {
                Ok(decode_tuple!(@accum $env, terms, 0, ($($typs),*,) -> ()))
            }
        }
    }
}

//macro_rules! decode_tuple {
//    ($env:expr, $term:expr, ($($typ:ty),+)) => {
//        let num_expr: usize = count_typ!($($typ,)*);
//        let terms = try!($crate::nif::get_tuple($env, $term));
//        if terms.len() != num_expr {
//            Err($crate::nif::NifError::BadArg)
//        } else {
//            Ok((decode_tuple_arg!($env, terms, 0, ($($typ,)*))))
//        }
//    }
//}

#[macro_export]
macro_rules! nif_func {
    ($name:ident, $fun:expr) => (
        extern "C" fn $name(r_env: *mut ErlNifEnv,
                            argc: c_int,
                            argv: *const ERL_NIF_TERM) -> ERL_NIF_TERM {
            use $crate::nif::{ NifEnv, NifTerm, NifError, size_t };
            let env = NifEnv { env: r_env };
            let terms = unsafe { std::slice::from_raw_parts(argv, argc as usize) }.iter()
                .map(|x| { NifTerm::new(&env, *x) }).collect::<Vec<NifTerm>>();
            let inner_fun: &for<'a> Fn(&'a NifEnv, &Vec<NifTerm>) -> Result<NifTerm<'a>, NifError> = &$fun;
            let res: Result<NifTerm, NifError> = inner_fun(&env, &terms);
            match res {
                Ok(ret) => ret.term,
                Err(NifError::BadArg) =>
                    unsafe { ruster_unsafe::enif_make_badarg(r_env) },
                Err(NifError::Atom(name)) =>
                    unsafe { ruster_unsafe::enif_make_atom_len(r_env,
                                                               name.as_ptr() as *const u8,
                                                               name.len() as size_t) },
            }
        });
}

#[macro_export]
macro_rules! nif_atom {
    ($env:expr, $name:ident) => ({
        const atom_name: &'static str = stringify!($name);
        ruster_unsafe::enif_make_atom_len(
            $env.env,
            atom_name.as_ptr() as *const u8,
            atom_name.len() as $crate::nif::size_t)
    });
}

pub fn nif_atom<'a>(env: &'a NifEnv, name: &str) -> NifTerm<'a> {
    unsafe {
        NifTerm::new(env, ruster_unsafe::enif_make_atom_len(
            env.env,
            name.as_ptr() as *const u8,
            name.len() as size_t))
    }
}

#[macro_export]
macro_rules! nif_func_entry {
    ($name:expr, $arity:expr, $function:ident, $flags:expr) => {
        {
            let c_name = $crate::std::ffi::CString::new($name).unwrap();
            $crate::ruster_unsafe::ErlNifFunc {
                name: c_name.as_ptr(),
                arity: $arity,
                function: $function,
                flags: $flags,
            };
        }
    };
    ($name:expr, $arity:expr, $function:expr, $flags:expr) => {
        nif_func_entry
    }
}

#[macro_export]
macro_rules! nif_init {
    ($module:expr, $load:expr, $reload:expr, $upgrade:expr, $unload:expr, [$($func:expr),*]) => {
        const NUM_FUNCS: usize = nif_init!(@count_expr $($func),*);
        const FUNCS: [$crate::ruster_unsafe::ErlNifFunc; NUM_FUNCS] = [$($func),*];

        let c_module = $crate::std::ffi::CString::new($module).unwrap();

        static mut ENTRY: $crate::ruster_unsafe::ErlNifEntry = $crate::ErlNifEntry {
            major: $crate::ruster_unsafe::NIF_MAJOR_VERSION,
            minor: $crate::ruster_unsafe::NIF_MINOR_VERSION,
            name: c_module,
            num_of_funcs: NUM_FUNCS as $crate::c_int,
            funcs: &FUNCS as *const $crate::ruster_unsafe::ErlNifFunc,
            load: $load,
            reload: $reload,
            upgrade: $upgrade,
            unload: $unload,
            vm_variant: b"beam.vanilla\0" as *const u8,
            options: 0,
        };

        #[no_mangle]
        pub extern "C" fn nif_init() -> *const $crate::ruster_unsafe::ErlNifEntry {
            unsafe { &ENTRY }
        }
    };

    (@count_expr ()) => { 0 };
    (@count_expr ($_e:expr)) => { 1 };
    (@count_expr ($_e:expr, $($tail:tt)*)) => { 1 + nif_init!(@count_expr $($rest)*) }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;

#[derive(Debug, Eq, PartialEq)]
pub struct Entry<'a> {
    key: &'a[u8],
    value: &'a[u8],
    timestamp: u32,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec().sec as u32,
            deleted: false,
        }
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key,
            value: &[],
            timestamp: time::now().to_timespec().sec as u32,
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: &bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize],
            value: &bytes[STATIC_SIZE + key_size as usize..],
            timestamp: timestamp,
            deleted: value_size == TOMBSTONE
        }
    }
}

#[cfg(test)]
mod tests {
    use Entry;

    #[test]
    fn test_serialization() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::new(&key, &value);

        assert_eq!(entry, Entry::from_bytes(&entry.to_bytes()));
        assert_eq!(entry.deleted(), Entry::from_bytes(&entry.deleted().to_bytes()));

        let empty_entry = Entry::new(&key, &[]);

        assert_eq!(empty_entry, Entry::from_bytes(&empty_entry.to_bytes()));
        assert_eq!(empty_entry.deleted(), Entry::from_bytes(&empty_entry.deleted().to_bytes()));

        assert!(Entry::from_bytes(&empty_entry.deleted().to_bytes()).deleted);
        assert!(empty_entry.deleted().deleted);
    }

    #[test]
    fn test_deleted() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::new(&key, &value);

        assert!(entry.deleted().deleted);
        assert_eq!(entry.deleted().value.len(), 0);
    }
}
<commit_msg>use copy-on-write for file entry kvs<commit_after>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::borrow::Cow;
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;

#[derive(Debug, Eq, PartialEq)]
pub struct Entry<'a> {
    key: Cow<'a, [u8]>,
    value: Cow<'a, [u8]>,
    timestamp: u32,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: Cow<'a, [u8]>, value: Cow<'a, [u8]>) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec().sec as u32,
            deleted: false,
        }
    }

    pub fn borrowed(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        Entry::new(Cow::from(key), Cow::from(value))
    }

    pub fn owned(key: Vec<u8>, value: Vec<u8>) -> Entry<'a> {
        Entry::new(Cow::from(key), Cow::from(value))
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key.clone(),
            value: Cow::Borrowed(&[]),
            timestamp: time::now().to_timespec().sec as u32,
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: Cow::from(&bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize]),
            value: Cow::from(&bytes[STATIC_SIZE + key_size as usize..]),
            timestamp: timestamp,
            deleted: value_size == TOMBSTONE,
        }
    }
}

#[cfg(test)]
mod tests {
    use Entry;

    #[test]
    fn test_serialization() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::borrowed(&key, &value);

        assert_eq!(entry, Entry::from_bytes(&entry.to_bytes()));
        assert_eq!(entry.deleted(),
                   Entry::from_bytes(&entry.deleted().to_bytes()));

        let empty_entry = Entry::borrowed(&key, &[]);

        assert_eq!(empty_entry, Entry::from_bytes(&empty_entry.to_bytes()));
        assert_eq!(empty_entry.deleted(),
                   Entry::from_bytes(&empty_entry.deleted().to_bytes()));

        assert!(Entry::from_bytes(&empty_entry.deleted().to_bytes()).deleted);
        assert!(empty_entry.deleted().deleted);
    }

    #[test]
    fn test_deleted() {
        let key = [0, 0, 0];
        let value = [0, 0, 0];
        let entry = Entry::borrowed(&key, &value);

        assert!(entry.deleted().deleted);
        assert_eq!(entry.deleted().value.len(), 0);
    }
}
<|endoftext|>


<commit_before>extern crate pad;

use pad::PadStr;
use std::fs;
use std::fmt;
use std::path::PathBuf;

// create a struct for displaying filenames/sizes in columns?
#[derive(Ord, Eq, PartialOrd, PartialEq, Debug, Copy, Clone)]
pub struct FileSize {
    size: u64,
}

impl FileSize {
    pub fn new(i: u64) -> FileSize {
        FileSize { size: i }
    }
    pub fn add(&mut self, other: FileSize) -> () {
        self.size += other.size;
    }
}

#[derive(Ord, Eq, PartialEq, PartialOrd, Clone)]
pub struct NamePair {
    bytes: FileSize,
    name: PathBuf,
    pub depth: u8,
}

impl NamePair {
    pub fn new(path: PathBuf, bytes_in: FileSize, d: u8) -> NamePair {
        NamePair { name: path, bytes: bytes_in, depth: d }
    }
    //fn cmp(&self, other: &NamePair) -> Ordering {
    //    self.bytes.cmp(&other.bytes)
    //}

}

pub struct FileTree {
    pub file_size: FileSize,
    files: Vec<NamePair>,
}

impl FileTree {
    pub fn sort(&mut self, maybe_num: Option<usize>, maybe_depth: Option<u8>) -> () {
        if let Some(n) = maybe_num {
            self.files.sort();
            self.files.reverse();
            self.files.filter(|a| { a <= maybe_depth.unwrap() });
            self.files = self.files.clone().into_iter().take(n).collect();
        }
        else {
            self.files.sort();
            self.files.reverse();
        }

    }
    pub fn new() -> FileTree {
        FileTree { file_size: FileSize::new(0), files: Vec::new() }
    }
    pub fn push(&mut self, path: PathBuf, size: FileSize, subtree: Option<&mut FileTree>, depth: u8) -> () {
        self.file_size.add(size); // add subdirectory or file size to total
        if let Some(s) = subtree {
            self.files.append(&mut s.files); // add subtree if desired
        }
        self.files.push(NamePair::new(path, size, depth)); // tag file or subdirectory with its size
        // by tracking total size nicely, we avoid the need to traverse the vector to sum it.
    }
    pub fn display_tree(&mut self, init_dir: PathBuf) -> () {
        // subdirs &c.
        let vec = &self.files;
        for ref name_pair in vec {
            let to_formatted = format!("{}", name_pair.bytes);
            println!("{} {}", &to_formatted.pad_to_width(8), name_pair.name.display());
        }

        // total
        let to_formatted = format!("{}", self.file_size);
        let path = init_dir.display(); // fix this!! better data structure
        println!("{} {}", &to_formatted.pad_to_width(8), path);
    }
}

impl fmt::Display for FileSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.size < 1024 {
            let pre_size = format!("{}", self.size);
            write!(f, "{} b", &pre_size.pad_to_width(4))
        }
        else if self.size < 1048576 { // 2^20 = 1024 * 1024
            let pre_size = format!("{}", self.size/1025);
            write!(f, "{} kB", &pre_size.pad_to_width(4))
        }
        else if self.size < 1073741824 { // 2^30
            let pre_size = format!("{}", self.size/1048576);
            write!(f, "{} MB", &pre_size.pad_to_width(4))
        }
        else if self.size < 1099511627776 { // 2^40
            let pre_size = format!("{}", self.size/1073741824);
            write!(f, "{} GB", &pre_size.pad_to_width(4))
        }
        else { // for 1 TB and above
            let pre_size = format!("{}", self.size/1099511627776);
            write!(f, "{} TB", &pre_size.pad_to_width(4))
        }
    }
}

pub fn read_files(in_paths: PathBuf, depth: u8) -> FileTree {
    let paths = fs::read_dir(in_paths.clone()).unwrap();
    let mut tree = FileTree::new();
    let mut total_size = FileSize::new(0);

    for p in paths {
        let path = p.unwrap().path(); // TODO no unwraps
        let metadata = fs::metadata(path.clone()).unwrap();

        // append file size/name for a file
        if metadata.is_file() {
            let file_size = FileSize::new(metadata.len());
            tree.push(path.clone(), file_size, None, depth + 1);
            total_size.add(file_size);
        }
        // otherwise, go deeper
        else if metadata.is_dir() {
            let mut subtree = read_files(path.clone(), depth + 1);
            let dir_size = subtree.file_size;
            tree.push(path, dir_size, Some(&mut subtree), depth + 1);
            total_size.add(dir_size);
        }
    }
    tree
}

<commit_msg>depth annotations<commit_after>extern crate pad;

use pad::PadStr;
use std::fs;
use std::fmt;
use std::path::PathBuf;

// create a struct for displaying filenames/sizes in columns?
#[derive(Ord, Eq, PartialOrd, PartialEq, Debug, Copy, Clone)]
pub struct FileSize {
    size: u64,
}

impl FileSize {
    pub fn new(i: u64) -> FileSize {
        FileSize { size: i }
    }
    pub fn add(&mut self, other: FileSize) -> () {
        self.size += other.size;
    }
}

#[derive(Ord, Eq, PartialEq, PartialOrd, Clone)]
pub struct NamePair {
    bytes: FileSize,
    name: PathBuf,
    pub depth: u8,
}

impl NamePair {
    pub fn new(path: PathBuf, bytes_in: FileSize, d: u8) -> NamePair {
        NamePair { name: path, bytes: bytes_in, depth: d }
    }
}

pub struct FileTree {
    pub file_size: FileSize,
    files: Vec<NamePair>,
}

impl FileTree {
    pub fn sort(&mut self, maybe_num: Option<usize>, maybe_depth: Option<u8>) -> () {
        if let Some(n) = maybe_num {
            self.files.sort();
            self.files.reverse();
            self.files.iter().filter(|a| { a.depth <= maybe_depth.unwrap() }).collect::<Vec<&NamePair>>(); // FIXME
            self.files = self.files.clone().into_iter().take(n).collect();
        }
        else {
            self.files.sort();
            self.files.reverse();
        }

    }
    pub fn new() -> FileTree {
        FileTree { file_size: FileSize::new(0), files: Vec::new() }
    }
    pub fn push(&mut self, path: PathBuf, size: FileSize, subtree: Option<&mut FileTree>, depth: u8) -> () {
        self.file_size.add(size); // add subdirectory or file size to total
        if let Some(s) = subtree {
            self.files.append(&mut s.files); // add subtree if desired
        }
        self.files.push(NamePair::new(path, size, depth)); // tag file or subdirectory with its size
        // by tracking total size nicely, we avoid the need to traverse the vector to sum it.
    }
    pub fn display_tree(&mut self, init_dir: PathBuf) -> () {
        // subdirs &c.
        let vec = &self.files;
        for ref name_pair in vec {
            let to_formatted = format!("{}", name_pair.bytes);
            println!("{} {}", &to_formatted.pad_to_width(8), name_pair.name.display());
        }

        // total
        let to_formatted = format!("{}", self.file_size);
        let path = init_dir.display(); // fix this!! better data structure
        println!("{} {}", &to_formatted.pad_to_width(8), path);
    }
}

impl fmt::Display for FileSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.size < 1024 {
            let pre_size = format!("{}", self.size);
            write!(f, "{} b", &pre_size.pad_to_width(4))
        }
        else if self.size < 1048576 { // 2^20 = 1024 * 1024
            let pre_size = format!("{}", self.size/1025);
            write!(f, "{} kB", &pre_size.pad_to_width(4))
        }
        else if self.size < 1073741824 { // 2^30
            let pre_size = format!("{}", self.size/1048576);
            write!(f, "{} MB", &pre_size.pad_to_width(4))
        }
        else if self.size < 1099511627776 { // 2^40
            let pre_size = format!("{}", self.size/1073741824);
            write!(f, "{} GB", &pre_size.pad_to_width(4))
        }
        else { // for 1 TB and above
            let pre_size = format!("{}", self.size/1099511627776);
            write!(f, "{} TB", &pre_size.pad_to_width(4))
        }
    }
}

pub fn read_files(in_paths: PathBuf, depth: u8) -> FileTree {
    let paths = fs::read_dir(in_paths.clone()).unwrap();
    let mut tree = FileTree::new();
    let mut total_size = FileSize::new(0);

    for p in paths {
        let path = p.unwrap().path(); // TODO no unwraps
        let metadata = fs::metadata(path.clone()).unwrap();

        // append file size/name for a file
        if metadata.is_file() {
            let file_size = FileSize::new(metadata.len());
            tree.push(path.clone(), file_size, None, depth + 1);
            total_size.add(file_size);
        }
        // otherwise, go deeper
        else if metadata.is_dir() {
            let mut subtree = read_files(path.clone(), depth + 1);
            let dir_size = subtree.file_size;
            tree.push(path, dir_size, Some(&mut subtree), depth + 1);
            total_size.add(dir_size);
        }
    }
    tree
}

<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! A generic library for textures.
//!
//! This library is used in Piston for generic code when working with textures.
//!
//! The `ImageSize` trait is used for passing textures around for rendering.
//! For more information, see
//! [Piston-Graphics](https://github.com/pistondevelopers/graphics).

pub mod ops;

/// Implemented by all images to be used with generic algorithms.
pub trait ImageSize {
    /// Get the image size.
    fn get_size(&self) -> (u32, u32);

    /// Gets the image width.
    #[inline(always)]
    fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the image height.
    #[inline(always)]
    fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

/// Texture creation parameters.
#[derive(Clone, Copy)]
pub struct TextureSettings {
    // Whether to convert gamma, treated as sRGB color space
    convert_gamma: bool,
    // Compress on GPU.
    compress: bool,
    // Generate mipmap chain.
    generate_mipmap: bool,
    // Filtering Mode for Minifying
    min: Filter,
    // Filtering Mode for Magnifying
    mag: Filter,
    // Filtering Mode for Minify Mipmapping
    mipmap: Filter,
    // Wrapping mode for s coordinate
    wrap_s: Wrap,
    // Wrapping mode for t coordinate
    wrap_t: Wrap,
}

impl TextureSettings {
    /// Create default settings.
    pub fn new() -> TextureSettings {
        TextureSettings {
            convert_gamma: false,
            compress: false,
            generate_mipmap: false,
            min: Filter::Linear,
            mag: Filter::Linear,
            mipmap: Filter::Linear,
            wrap_s: Wrap::ClampToEdge,
            wrap_t: Wrap::ClampToEdge
        }
    }

    /// Gets whether to convert gamma, treated as sRGB color space.
    pub fn get_convert_gamma(&self) -> bool { self.convert_gamma }
    /// Sets convert gamma.
    pub fn set_convert_gamma(&mut self, val: bool) { self.convert_gamma = val; }
    /// Sets convert gamma.
    pub fn convert_gamma(mut self, val: bool) -> Self {
        self.set_convert_gamma(val);
        self
    }

    /// Gets wheter compress on the GPU.
    pub fn get_compress(&self) -> bool { self.compress }
    /// Sets compress.
    pub fn set_compress(&mut self, val: bool) { self.compress = val; }
    /// Sets compress.
    pub fn compress(mut self, val: bool) -> Self {
        self.set_compress(val);
        self
    }

    /// Gets generate mipmap.
    pub fn get_generate_mipmap(&self) -> bool { self.generate_mipmap }
    /// Sets generate mipmap.
    pub fn set_generate_mipmap(&mut self, val: bool) {
        self.generate_mipmap = val;
    }
    /// Sets generate mipmap.
    pub fn generate_mipmap(mut self, val: bool) -> Self {
        self.set_generate_mipmap(val);
        self
    }

    /// Gets minify filter.
    pub fn get_min(&self) -> Filter { self.min }
    /// Sets minify filter.
    pub fn set_min(&mut self, val: Filter) {
        self.min = val
    }
    /// Sets minify filter.
    pub fn min(mut self, val: Filter) -> Self {
        self.set_min(val);
        self
    }

    /// Gets magnify filter
    pub fn get_mag(&self) -> Filter { self.mag }
    /// Sets magnify filter
    pub fn set_mag(&mut self, val: Filter) {
        self.mag = val;
    }
    /// Sets magnify filter
    pub fn mag(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Gets minify mipmap filter
    pub fn get_mipmap(&self) -> Filter { self.mipmap }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true.
    pub fn set_mipmap(&mut self, val: Filter) {
        self.set_generate_mipmap(true);
        self.mag = val;
    }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true
    pub fn mipmap(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Returns the min and mag filter
    pub fn get_filter(&self) -> (Filter, Filter) { (self.min, self.mag) }
    /// Sets the min and mag filter
    pub fn set_filter(&mut self, val: Filter) {
        self.set_min(val);
        self.set_mag(val);
    }

    /// Sets the min and mag filter
    pub fn filter(mut self, val: Filter) -> Self {
        self.set_filter(val);
        self
    }

    /// Gets the wrapping mode for the s coordinate
    pub fn get_wrap_s(&self) -> Wrap {
        self.wrap_s
    }
    /// Sets the wrapping mode for the s coordinate
    pub fn set_wrap_s(& mut self, val: Wrap) {
        self.wrap_s = val
    }
    /// Sets the wrapping mode for the s coordinate
    pub fn wrap_s(mut self, val: Wrap) -> Self {
        self.set_wrap_s(val);
        self
    }

    /// Gets the wrapping mode for the t coordinate
    pub fn get_wrap_t(&self) -> Wrap {
        self.wrap_t
    }
    /// Sets the wrapping mode for the t coordinate
    pub fn set_wrap_t(& mut self, val: Wrap) {
        self.wrap_t = val
    }
    /// Sets the wrapping mode for the t coordinate
    pub fn wrap_t(mut self, val: Wrap) -> Self {
        self.set_wrap_t(val);
        self
    }

}

/// Texture format.
#[derive(Copy, Clone, Debug)]
pub enum Format {
    /// `(red, green, blue, alpha)` with values 0-255.
    Rgba8,
}

/// Implemented by texture operations.
pub trait TextureOp<F> {
    /// The error when performing an operation.
    type Error;
}

/// Implemented by textures for creation.
pub trait CreateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Create texture from memory.
    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error>;
}

/// Implemented by textures for updating.
pub trait UpdateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Update the texture.
    ///
    /// The `offset` and `size` arguments represent the position and dimensions of the sub-section
    /// of the texture that is to be updated with the given `memory`.
    fn update<O, S>(
        &mut self,
        factory: &mut F,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>;
}

/// Sampling filter
#[derive(Copy, Clone, Debug)]
pub enum Filter {
    /// A Weighted Linear Blend
    Linear,
    /// Nearest Texel
    Nearest
}

/// Wrap mode
#[derive(Copy, Clone, Debug)]
pub enum Wrap {
    /// Repeats the texture by ignoring the integer part of the coordinate.
    Repeat,
    /// Repeats the texture and mirrors it, when the integer part of the coordinate is odd.
    MirroredRepeat,
    /// The coordinate will be clamped between 0 and 1.
    ClampToEdge,
    /// Coordinates outside the range [0.0, 1.0] will be given a border color.
    ClampToBorder {
        /// The border color to which to clamp to.
        color: [f32;4]
    }
}
<commit_msg>texture wrapping: add field and methods for border color<commit_after>#![deny(missing_docs)]

//! A generic library for textures.
//!
//! This library is used in Piston for generic code when working with textures.
//!
//! The `ImageSize` trait is used for passing textures around for rendering.
//! For more information, see
//! [Piston-Graphics](https://github.com/pistondevelopers/graphics).

pub mod ops;

/// Implemented by all images to be used with generic algorithms.
pub trait ImageSize {
    /// Get the image size.
    fn get_size(&self) -> (u32, u32);

    /// Gets the image width.
    #[inline(always)]
    fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the image height.
    #[inline(always)]
    fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

/// Texture creation parameters.
#[derive(Clone, Copy)]
pub struct TextureSettings {
    // Whether to convert gamma, treated as sRGB color space
    convert_gamma: bool,
    // Compress on GPU.
    compress: bool,
    // Generate mipmap chain.
    generate_mipmap: bool,
    // Filtering Mode for Minifying
    min: Filter,
    // Filtering Mode for Magnifying
    mag: Filter,
    // Filtering Mode for Minify Mipmapping
    mipmap: Filter,
    // Wrapping mode for s coordinate
    wrap_s: Wrap,
    // Wrapping mode for t coordinate
    wrap_t: Wrap,
    // Border Color if ClampToBorder is specified as wrap mode
    border_color: [f32; 4],
}

impl TextureSettings {
    /// Create default settings.
    pub fn new() -> TextureSettings {
        TextureSettings {
            convert_gamma: false,
            compress: false,
            generate_mipmap: false,
            min: Filter::Linear,
            mag: Filter::Linear,
            mipmap: Filter::Linear,
            wrap_s: Wrap::ClampToEdge,
            wrap_t: Wrap::ClampToEdge,
            border_color: [0.0, 0.0, 0.0, 1.0],
        }
    }

    /// Gets whether to convert gamma, treated as sRGB color space.
    pub fn get_convert_gamma(&self) -> bool { self.convert_gamma }
    /// Sets convert gamma.
    pub fn set_convert_gamma(&mut self, val: bool) { self.convert_gamma = val; }
    /// Sets convert gamma.
    pub fn convert_gamma(mut self, val: bool) -> Self {
        self.set_convert_gamma(val);
        self
    }

    /// Gets wheter compress on the GPU.
    pub fn get_compress(&self) -> bool { self.compress }
    /// Sets compress.
    pub fn set_compress(&mut self, val: bool) { self.compress = val; }
    /// Sets compress.
    pub fn compress(mut self, val: bool) -> Self {
        self.set_compress(val);
        self
    }

    /// Gets generate mipmap.
    pub fn get_generate_mipmap(&self) -> bool { self.generate_mipmap }
    /// Sets generate mipmap.
    pub fn set_generate_mipmap(&mut self, val: bool) {
        self.generate_mipmap = val;
    }
    /// Sets generate mipmap.
    pub fn generate_mipmap(mut self, val: bool) -> Self {
        self.set_generate_mipmap(val);
        self
    }

    /// Gets minify filter.
    pub fn get_min(&self) -> Filter { self.min }
    /// Sets minify filter.
    pub fn set_min(&mut self, val: Filter) {
        self.min = val
    }
    /// Sets minify filter.
    pub fn min(mut self, val: Filter) -> Self {
        self.set_min(val);
        self
    }

    /// Gets magnify filter
    pub fn get_mag(&self) -> Filter { self.mag }
    /// Sets magnify filter
    pub fn set_mag(&mut self, val: Filter) {
        self.mag = val;
    }
    /// Sets magnify filter
    pub fn mag(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Gets minify mipmap filter
    pub fn get_mipmap(&self) -> Filter { self.mipmap }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true.
    pub fn set_mipmap(&mut self, val: Filter) {
        self.set_generate_mipmap(true);
        self.mag = val;
    }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true
    pub fn mipmap(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Returns the min and mag filter
    pub fn get_filter(&self) -> (Filter, Filter) { (self.min, self.mag) }
    /// Sets the min and mag filter
    pub fn set_filter(&mut self, val: Filter) {
        self.set_min(val);
        self.set_mag(val);
    }

    /// Sets the min and mag filter
    pub fn filter(mut self, val: Filter) -> Self {
        self.set_filter(val);
        self
    }

    /// Gets the wrapping mode for the s coordinate
    pub fn get_wrap_s(&self) -> Wrap {
        self.wrap_s
    }
    /// Sets the wrapping mode for the s coordinate
    pub fn set_wrap_s(& mut self, val: Wrap) {
        self.wrap_s = val
    }
    /// Sets the wrapping mode for the s coordinate
    pub fn wrap_s(mut self, val: Wrap) -> Self {
        self.set_wrap_s(val);
        self
    }

    /// Gets the wrapping mode for the t coordinate
    pub fn get_wrap_t(&self) -> Wrap {
        self.wrap_t
    }
    /// Sets the wrapping mode for the t coordinate
    pub fn set_wrap_t(& mut self, val: Wrap) {
        self.wrap_t = val
    }
    /// Sets the wrapping mode for the t coordinate
    pub fn wrap_t(mut self, val: Wrap) -> Self {
        self.set_wrap_t(val);
        self
    }

    /// Gets the border color
    pub fn get_border_color(&self) -> [f32; 4] {
        self.border_color
    }
    /// Sets the border color
    pub fn set_border_color(&mut self, val: [f32; 4]) {
        self.border_color = val
    }
    /// Sets the border color
    pub fn border_color(mut self, val: [f32; 4]) -> Self {
        self.set_border_color(val);
        self
    }

}

/// Texture format.
#[derive(Copy, Clone, Debug)]
pub enum Format {
    /// `(red, green, blue, alpha)` with values 0-255.
    Rgba8,
}

/// Implemented by texture operations.
pub trait TextureOp<F> {
    /// The error when performing an operation.
    type Error;
}

/// Implemented by textures for creation.
pub trait CreateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Create texture from memory.
    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error>;
}

/// Implemented by textures for updating.
pub trait UpdateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Update the texture.
    ///
    /// The `offset` and `size` arguments represent the position and dimensions of the sub-section
    /// of the texture that is to be updated with the given `memory`.
    fn update<O, S>(
        &mut self,
        factory: &mut F,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>;
}

/// Sampling filter
#[derive(Copy, Clone, Debug)]
pub enum Filter {
    /// A Weighted Linear Blend
    Linear,
    /// Nearest Texel
    Nearest
}

/// Wrap mode
#[derive(Copy, Clone, Debug)]
pub enum Wrap {
    /// Repeats the texture by ignoring the integer part of the coordinate.
    Repeat,
    /// Repeats the texture and mirrors it, when the integer part of the coordinate is odd.
    MirroredRepeat,
    /// The coordinate will be clamped between 0 and 1.
    ClampToEdge,
    /// Coordinates outside the range [0.0, 1.0] will be given a border color.
    ClampToBorder,
}
<|endoftext|>