<commit_before>use std::rc::Rc;

use interpreter::InterpretResult;
use variable::Variable;

/// Handles the dispatching of named functions using an array of arguments.
pub trait FnDispatcher {
    /// Dispatches and interprets a function by name.
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult;
}

/// Built-in function implementations.
#[derive(Clone)]
pub struct BuiltinFunctions;

impl BuiltinFunctions {
    /// Creates a new Builtins function dispatcher.
    pub fn new() -> BuiltinFunctions {
        BuiltinFunctions
    }
}

/// Validates the arity of a function.
#[inline]
fn arity(fn_name: &str, arity: usize, args: &Vec<Rc<Variable>>) -> Result<(), String> {
    if args.len() != arity {
        Err(format!("{} arity error: expected {} args, found {}", fn_name, arity, args.len()))
    } else {
        Ok(())
    }
}

/// Macro used to format a type error.
macro_rules! type_err {
    ($f:expr, $position:expr, $expected:expr, $given:expr) => {
        format!("{} expects arg {} to be {}. Given {:?}", $f, $position, $expected, $given)
    }
}

/// Macro used to variadically validate validate Variable and argument arity.
macro_rules! validate {
    // Validate positional arguments only.
    ($name:expr, $args:expr, $($x:expr),*) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            try!(arity($name, validators.len(), $args));
            for (k, v) in $args.iter().enumerate() {
                if let Err(e) = validators[k](v) {
                    return Err(format!("{} error at argument {}: {}", $name, k, e));
                }
            }
        }
    );
    // Validate positional arguments with a variadic validator.
    ($name:expr, $args:expr, $($x:expr),* ...$variadic:expr ) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            if $args.len() < validators.len() {
                return Err(format!("{} arity error: expected at least {} args, found {}",
                                   $name, validators.len(), $args.len()));
            }
            for (k, v) in $args.iter().enumerate() {
                try!((match validators.get(k) {
                    Some(validator) => validator(v),
                    None => $variadic(v)
                }).or_else(|e| Err(format!("{} error at argument {}: {}", $name, k, e))));
            }
        }
    );
}

/// Creates a closure used to validate the type of the given variable.
macro_rules! jptype {
    // Validate a type against a single acceptable type.
    ($type_name:ident) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let arg_type = arg.get_type();
            if arg_type != "any" && arg_type != stringify!($type_name) {
                Err(format!("type {}", stringify!($type_name)))
            } else {
                Ok(())
            }
        })
    ];
    // Validate a type against multiple acceptable types
    ($($x:ident)|*) => [
        {
            Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
                let valid_types = vec![$(stringify!($x)), *];
                let arg_type = arg.get_type();
                if arg_type != "any" && !valid_types.contains(&arg_type) {
                    Err(format!("type {}", valid_types.join("|")))
                } else {
                    Ok(())
                }
            })
        }
    ];
}

impl FnDispatcher for BuiltinFunctions {
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult {
        match fn_name {
            "abs" => {
                validate!("abs", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().abs())))
            },
            "avg" => {
                try!(arity("avg", 1, args));
                args[0].as_array()
                    .and_then(|array| {
                        let mut total = 0f64;
                        for value in array {
                            match **value {
                                Variable::I64(n) => total += n as f64,
                                Variable::U64(n) => total += n as f64,
                                Variable::F64(n) => total += n,
                                _ => return None
                            }
                        }
                        Some(Rc::new(Variable::F64(total / array.len() as f64)))
                    })
                    .ok_or(type_err!("avg", 0, "array[number]", args[0]))
            },
            "ceil" => {
                validate!("ceil", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().ceil())))
            },
            "contains" => {
                validate!("contains", args, jptype![array|string], jptype![any]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::Boolean(a.contains(&args[1])))),
                    Variable::String(ref subj) => {
                        match args[1].as_string() {
                            Some(search) => Ok(Rc::new(Variable::Boolean(subj.contains(search)))),
                            None => Err(format!("contains found {:?} for string search arg",
                                                args[1]))
                        }
                    },
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "ends_with" => {
                validate!("ends_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.ends_with(search))))
            },
            "floor" => {
                validate!("floor", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().floor())))
            },
            "keys" => {
                validate!("keys", args, jptype![object]);
                Ok(Rc::new(args[0].object_keys_to_array().unwrap()))
            },
            "length" => {
                validate!("length", args, jptype![array|object|string]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::U64(a.len() as u64))),
                    Variable::Object(ref m) => Ok(Rc::new(Variable::U64(m.len() as u64))),
                    Variable::String(ref s) => Ok(Rc::new(Variable::U64(s.len() as u64))),
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "not_null" => {
                validate!("not_null", args, jptype![any] ...jptype![any]);
                for arg in args {
                    if **arg != Variable::Null {
                        return Ok(arg.clone());
                    }
                }
                Ok(Rc::new(Variable::Null))
            },
            "starts_with" => {
                validate!("starts_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.starts_with(search))))
            },
            "type" => {
                validate!("type", args, jptype![any]);
                Ok(Rc::new(Variable::String(args[0].get_type().to_string())))
            },
            "values" => {
                validate!("values", args, jptype![object]);
                Ok(Rc::new(args[0].object_values_to_array().unwrap()))
            },
            _ => Err(format!("Unknown function: {}", fn_name))
        }
    }
}
<commit_msg>Adding to_number and to_array<commit_after>use std::rc::Rc;

use interpreter::InterpretResult;
use variable::Variable;

/// Handles the dispatching of named functions using an array of arguments.
pub trait FnDispatcher {
    /// Dispatches and interprets a function by name.
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult;
}

/// Built-in function implementations.
#[derive(Clone)]
pub struct BuiltinFunctions;

impl BuiltinFunctions {
    /// Creates a new Builtins function dispatcher.
    pub fn new() -> BuiltinFunctions {
        BuiltinFunctions
    }
}

/// Validates the arity of a function.
#[inline]
fn arity(fn_name: &str, arity: usize, args: &Vec<Rc<Variable>>) -> Result<(), String> {
    if args.len() != arity {
        Err(format!("{} arity error: expected {} args, found {}", fn_name, arity, args.len()))
    } else {
        Ok(())
    }
}

/// Macro used to format a type error.
macro_rules! type_err {
    ($f:expr, $position:expr, $expected:expr, $given:expr) => {
        format!("{} expects arg {} to be {}. Given {:?}", $f, $position, $expected, $given)
    }
}

/// Macro used to variadically validate validate Variable and argument arity.
macro_rules! validate {
    // Validate positional arguments only.
    ($name:expr, $args:expr, $($x:expr),*) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            try!(arity($name, validators.len(), $args));
            for (k, v) in $args.iter().enumerate() {
                if let Err(e) = validators[k](v) {
                    return Err(format!("{} error at argument {}: {}", $name, k, e));
                }
            }
        }
    );
    // Validate positional arguments with a variadic validator.
    ($name:expr, $args:expr, $($x:expr),* ...$variadic:expr ) => (
        {
            let validators: Vec<Box<Fn(&Rc<Variable>) -> Result<(), String>>> = vec![$($x), *];
            if $args.len() < validators.len() {
                return Err(format!("{} arity error: expected at least {} args, found {}",
                                   $name, validators.len(), $args.len()));
            }
            for (k, v) in $args.iter().enumerate() {
                try!((match validators.get(k) {
                    Some(validator) => validator(v),
                    None => $variadic(v)
                }).or_else(|e| Err(format!("{} error at argument {}: {}", $name, k, e))));
            }
        }
    );
}

/// Creates a closure used to validate the type of the given variable.
macro_rules! jptype {
    // Validate a type against a single acceptable type.
    ($type_name:ident) => [
        Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
            let arg_type = arg.get_type();
            if arg_type != "any" && arg_type != stringify!($type_name) {
                Err(format!("type {}", stringify!($type_name)))
            } else {
                Ok(())
            }
        })
    ];
    // Validate a type against multiple acceptable types
    ($($x:ident)|*) => [
        {
            Box::new(move |arg: &Rc<Variable>| -> Result<(), String> {
                let valid_types = vec![$(stringify!($x)), *];
                let arg_type = arg.get_type();
                if arg_type != "any" && !valid_types.contains(&arg_type) {
                    Err(format!("type {}", valid_types.join("|")))
                } else {
                    Ok(())
                }
            })
        }
    ];
}

impl FnDispatcher for BuiltinFunctions {
    fn call(&self, fn_name: &str, args: &Vec<Rc<Variable>>) -> InterpretResult {
        match fn_name {
            "abs" => {
                validate!("abs", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().abs())))
            },
            "avg" => {
                try!(arity("avg", 1, args));
                args[0].as_array()
                    .and_then(|array| {
                        let mut total = 0f64;
                        for value in array {
                            match **value {
                                Variable::I64(n) => total += n as f64,
                                Variable::U64(n) => total += n as f64,
                                Variable::F64(n) => total += n,
                                _ => return None
                            }
                        }
                        Some(Rc::new(Variable::F64(total / array.len() as f64)))
                    })
                    .ok_or(type_err!("avg", 0, "array[number]", args[0]))
            },
            "ceil" => {
                validate!("ceil", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().ceil())))
            },
            "contains" => {
                validate!("contains", args, jptype![array|string], jptype![any]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::Boolean(a.contains(&args[1])))),
                    Variable::String(ref subj) => {
                        match args[1].as_string() {
                            Some(search) => Ok(Rc::new(Variable::Boolean(subj.contains(search)))),
                            None => Err(format!("contains found {:?} for string search arg",
                                                args[1]))
                        }
                    },
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "ends_with" => {
                validate!("ends_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.ends_with(search))))
            },
            "floor" => {
                validate!("floor", args, jptype![number]);
                Ok(Rc::new(Variable::F64(args[0].as_f64().unwrap().floor())))
            },
            "keys" => {
                validate!("keys", args, jptype![object]);
                Ok(Rc::new(args[0].object_keys_to_array().unwrap()))
            },
            "length" => {
                validate!("length", args, jptype![array|object|string]);
                match *args[0] {
                    Variable::Array(ref a) => Ok(Rc::new(Variable::U64(a.len() as u64))),
                    Variable::Object(ref m) => Ok(Rc::new(Variable::U64(m.len() as u64))),
                    Variable::String(ref s) => Ok(Rc::new(Variable::U64(s.len() as u64))),
                    _ => panic!() // never encountered due to previous validation
                }
            },
            "not_null" => {
                validate!("not_null", args, jptype![any] ...jptype![any]);
                for arg in args {
                    if **arg != Variable::Null {
                        return Ok(arg.clone());
                    }
                }
                Ok(Rc::new(Variable::Null))
            },
            "starts_with" => {
                validate!("starts_with", args, jptype![string], jptype![string]);
                let subject = args[0].as_string().unwrap();
                let search = args[1].as_string().unwrap();
                Ok(Rc::new(Variable::Boolean(subject.starts_with(search))))
            },
            "to_array" => {
                try!(arity("to_array", 1, args));
                match *args[0] {
                    Variable::Array(_) => Ok(args[0].clone()),
                    _ => Ok(Rc::new(Variable::Array(vec![args[0].clone()])))
                }
            },
            "to_number" => {
                try!(arity("to_number", 1, args));
                match *args[0] {
                    Variable::I64(_) | Variable::F64(_) | Variable::U64(_) => Ok(args[0].clone()),
                    Variable::String(ref s) => {
                        match s.parse::<f64>() {
                            Ok(f)  => Ok(Rc::new(Variable::F64(f))),
                            Err(_) => Ok(Rc::new(Variable::Null))
                        }
                    },
                    _ => Ok(Rc::new(Variable::Null))
                }
            },
            "type" => {
                try!(arity("type", 1, args));
                Ok(Rc::new(Variable::String(args[0].get_type().to_string())))
            },
            "values" => {
                validate!("values", args, jptype![object]);
                Ok(Rc::new(args[0].object_values_to_array().unwrap()))
            },
            _ => Err(format!("Unknown function: {}", fn_name))
        }
    }
}
<|endoftext|>


<commit_before>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#[desc = "Bindings for the GeoIP library."];
#[license = "BSD"];
#[link(package_id = "geoip", name = "geoip", vers = "0.1")];
#[crate_type = "lib"];

#[warn(non_camel_case_types,
       non_uppercase_statics,
       unnecessary_qualification,
       managed_heap_memory)];

use std::c_str::CString;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};
use std::libc::{c_void, c_char, c_int, c_ulong};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: CString, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    priv db: GeoIP_
}

pub struct ASInfo {
    asn: uint,
    name: ~str,
    netmask: uint
}

impl ToStr for ASInfo {
    fn to_str(&self) -> ~str {
        format!("{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, ~str> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_owned(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}
<commit_msg>Build rlib<commit_after>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#[desc = "Bindings for the GeoIP library."];
#[license = "BSD"];
#[link(package_id = "geoip", name = "geoip", vers = "0.1")];
#[crate_type = "rlib"];

#[warn(non_camel_case_types,
       non_uppercase_statics,
       unnecessary_qualification,
       managed_heap_memory)];

use std::c_str::CString;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};
use std::libc::{c_void, c_char, c_int, c_ulong};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: CString, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    priv db: GeoIP_
}

pub struct ASInfo {
    asn: uint,
    name: ~str,
    netmask: uint
}

impl ToStr for ASInfo {
    fn to_str(&self) -> ~str {
        format!("{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, ~str> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_owned(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}
<|endoftext|>


<commit_before>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![allow(unstable)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn.slice_from(2).parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<commit_msg>slice_from() -> a[x..]<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![allow(unstable)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>


<commit_before>//! Askama implements a type-safe compiler for Jinja-like templates.
//! It lets you write templates in a Jinja-like syntax,
//! which are linked to a `struct` defining the template context.
//! This is done using a custom derive implementation (implemented
//! in [`askama_derive`](https://crates.io/crates/askama_derive)).
//!
//! For feature highlights and a quick start, please review the
//! [README](https://github.com/djc/askama/blob/master/README.md).
//!
//! # Creating Askama templates
//!
//! An Askama template is just a text file, in the UTF-8 encoding.
//! It can be used to generate any kind of text-based format.
//! You can use whatever extension you like.
//!
//! A template consists of **text contents**, which are passed through as-is,
//! **expressions**, which get replaced with content while being rendered, and
//! **tags**, which control the template's logic.
//! The template syntax is very similar to [Jinja](http://jinja.pocoo.org/),
//! as well as Jinja-derivatives like [Twig](http://twig.sensiolabs.org/) or
//! [Tera](https://github.com/Keats/tera).
//!
//! ## Variables
//!
//! Template variables are defined by the template context linked to the
//! template by deriving a `struct`'s implementation for `Template` from
//! a template file's contents.
//! You can use a dot (`.`) to get a variable's attributes.
//! Reading from variables is subject to the usual borrowing policies.
//! For example, `{{ name }}` will get the ``name`` field from the template
//! context,
//! while `{{ user.name }}` will get the ``name`` field of the ``user``
//! field from the template context.
//!
//! ## Filters
//!
//! Values such as those obtained from variables can be post-processed
//! using **filters**.
//! Filters are applied to values using the pipe symbol (`|`) and may
//! have optional extra arguments in parentheses.
//! Filters can be chained, in which case the output from one filter
//! is passed to the next.
//!
//! For example, `{{ "{:?}"|format(name|escape) }}` will escape HTML
//! characters from the value obtained by accessing the `name` field,
//! and print the resulting string as a Rust literal.
//!
//! Consult the [filters module documentation](filters/index.html) for a list
//! of available filters.
//!
//! ## Whitespace control
//!
//! Askama preserves all whitespace in template code by default,
//! except that a single trailing newline characters are suppressed.
//! However, whitespace before and after expression and block delimiters
//! can be suppressed by writing a minus sign directly following a
//! start delimiter or leading into an end delimiter.
//! Askama considers all tabs, spaces, newlines and carriage returns to be
//! whitespace.
//!
//! ## Template inheritance
//!
//! Template inheritance allows you to build a base template with common
//! elements that can then be shared by all inheriting templates.
//! A base template defines **blocks** that child templates can then override.
//!
//! ### Base template
//!
//! ```text
//! <!DOCTYPE html>
//! <html lang="en">
//!   <head>
//!     <title>{{ block title %}{{ title }}{% endblock %} - My Site</title>
//!     {% block head %}{% endblock %}
//!   </head>
//!   <body>
//!     <div id="content">
//!       {% block content %}{% endblock %}
//!     </div>
//!   </body>
//! </html>
//! ```
//!
//! The `block` tags define three blocks that can be filled in by child
//! templates. The base template defines a default version of the block.
//! A base template must define one or more blocks in order to be enable
//! inheritance. Blocks can only be specified at the top level of a template,
//! not inside `if`/`else` branches or in `for`-loop bodies.
//!
//! ### Child template
//!
//! Here's an example child template:
//!
//! ```text
//! {% extends "base.html" %}
//!
//! {% block title %}Index{% endblock %}
//!
//! {% block head %}
//!   <style>
//!   </style>
//! {% endblock %}
//!
//! {% block content %}
//!   <h1>Index</h1>
//!   <p>Hello, world!</p>
//! {% endblock %}
//! ```
//!
//! The `extends` tag tells the code generator that this template inherits
//! from another template. It will search for the base template relative to
//! itself before looking relative to the template base directory. It will
//! render the top-level content from the base template, and substitute
//! blocks from the base template with those from the child template. The
//! inheriting template context `struct` must have a field called `_parent` of
//! the type used as the base template context. Blocks can refer to the context
//! of both parent and child template.
//!
//! Note that, if the base template lives in another module than the child
//! template, the child template's module should import all symbols from the
//! base template's module in order for it to find the trait definition that
//! supports the inheritance mechanism.
//!
//! ## HTML escaping
//!
//! Askama does not yet support automatic escaping. Care must be taken to
//! escape content that may contain HTML control characters. You can use
//! the `escape` filter (or its `e` alias) to escape data for use in HTML.
//!
//! ## Control structures
//!
//! ### For
//!
//! Loop over each item in an iterator. For example:
//!
//! ```text
//! <h1>Users</h1>
//! <ul>
//! {% for user in users %}
//!   <li>{{ user.name|e }}</li>
//! {% endfor %}
//! </ul>
//! ```
//!
//! Inside for-loop blocks, some useful variables are accessible:
//!
//! * *loop.index*: current loop iteration (starting from 1)
//! * *loop.index0*: current loop iteration (starting from 0)
//!
//! ### If
//!
//! The *if* statement is used as you might expect:
//!
//! ```text
//! {% if users.len() == 0 %}
//!   No users
//! {% else if users.len() == 1 %}
//!   1 user
//! {% else %}
//!   {{ users.len() }} users
//! {% endif %}
//! ```
//!
//! ### Include
//!
//! The *include* statement lets you split large or repetitive blocks into
//! separate template files. Included templates get full access to the context
//! in which they're used, including local variables like those from loops:
//!
//! ```text
//! {% for i in iter %}
//!   {% include "item.html" %}
//! {% endfor %}
//! ```
//!
//! ```text
//! * Item: {{ i }}
//! ```
//!
//! The path to include must be a string literal, so that it is known at
//! compile time. Askama will try to find the specified template relative
//! to the including template's path before falling back to the absolute
//! template path. Use `include` within the branches of an `if`/`else`
//! block to use includes more dynamically.
//!
//! ## Expressions
//!
//! Askama supports string literals (`"foo"`) and integer literals (`1`).
//! It supports almost all binary operators that Rust supports,
//! including arithmetic, comparison and logic operators.
//! The parser applies the same precedence order as the Rust compiler.
//! Expressions can be grouped using parentheses.
//! The HTML special characters `&`, `<` and `>` will be replaced with their
//! character entities unless the `escape` mode is disabled for a template.

#![allow(unused_imports)]
#[macro_use]
extern crate askama_derive;
extern crate askama_shared as shared;

use shared::path;

use std::fs::{self, DirEntry};
use std::io;
use std::path::Path;

/// Main `Template` trait; implementations are generally derived
pub trait Template {
    /// Renders the template to the given `writer` buffer
    fn render_into(&self, writer: &mut std::fmt::Write) -> Result<()>;
    /// Helper method which allocates a new `String` and renders into it
    fn render(&self) -> Result<String> {
        let mut buf = String::new();
        self.render_into(&mut buf)?;
        Ok(buf)
    }
}

pub use shared::filters;
pub use askama_derive::*;
pub use shared::{Error, MarkupDisplay, Result};

#[cfg(feature = "with-iron")]
pub mod iron {
    extern crate iron;
    pub use self::iron::modifier::Modifier;
    pub use self::iron::response::Response;
}

#[cfg(feature = "with-rocket")]
pub mod rocket {
    extern crate rocket;
    pub use self::rocket::http::{ContentType, Status};
    pub use self::rocket::request::Request;
    pub use self::rocket::response::{Responder, Response};
}

fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

/// Build script helper to rebuild crates if contained templates have changed
///
/// Iterates over all files in the template dir (`templates` in
/// `CARGO_MANIFEST_DIR`) and writes a `cargo:rerun-if-changed=` line for each
/// of them to stdout.
///
/// This helper method can be used in build scripts (`build.rs`) in crates
/// that have templates, to make sure the crate gets rebuilt when template
/// source code changes.
pub fn rerun_if_templates_changed() {
    visit_dirs(&path::template_dir(), &|e: &DirEntry| {
        println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
    }).unwrap();
}
<commit_msg>Slightly extend introduction to creating templates<commit_after>//! Askama implements a type-safe compiler for Jinja-like templates.
//! It lets you write templates in a Jinja-like syntax,
//! which are linked to a `struct` defining the template context.
//! This is done using a custom derive implementation (implemented
//! in [`askama_derive`](https://crates.io/crates/askama_derive)).
//!
//! For feature highlights and a quick start, please review the
//! [README](https://github.com/djc/askama/blob/master/README.md).
//!
//! # Creating Askama templates
//!
//! An Askama template is a `struct` definition which provides the template
//! context combined with a UTF-8 encoded text file (or inline source, see
//! below). Askama can be used to generate any kind of text-based format.
//! The template file's extension may can be used to provide content type hints.
//!
//! A template consists of **text contents**, which are passed through as-is,
//! **expressions**, which get replaced with content while being rendered, and
//! **tags**, which control the template's logic.
//! The template syntax is very similar to [Jinja](http://jinja.pocoo.org/),
//! as well as Jinja-derivatives like [Twig](http://twig.sensiolabs.org/) or
//! [Tera](https://github.com/Keats/tera).
//!
//! ## Variables
//!
//! Template variables are defined by the template context linked to the
//! template by deriving a `struct`'s implementation for `Template` from
//! a template file's contents.
//! You can use a dot (`.`) to get a variable's attributes.
//! Reading from variables is subject to the usual borrowing policies.
//! For example, `{{ name }}` will get the ``name`` field from the template
//! context,
//! while `{{ user.name }}` will get the ``name`` field of the ``user``
//! field from the template context.
//!
//! ## Filters
//!
//! Values such as those obtained from variables can be post-processed
//! using **filters**.
//! Filters are applied to values using the pipe symbol (`|`) and may
//! have optional extra arguments in parentheses.
//! Filters can be chained, in which case the output from one filter
//! is passed to the next.
//!
//! For example, `{{ "{:?}"|format(name|escape) }}` will escape HTML
//! characters from the value obtained by accessing the `name` field,
//! and print the resulting string as a Rust literal.
//!
//! Consult the [filters module documentation](filters/index.html) for a list
//! of available filters.
//!
//! ## Whitespace control
//!
//! Askama preserves all whitespace in template code by default,
//! except that a single trailing newline characters are suppressed.
//! However, whitespace before and after expression and block delimiters
//! can be suppressed by writing a minus sign directly following a
//! start delimiter or leading into an end delimiter.
//! Askama considers all tabs, spaces, newlines and carriage returns to be
//! whitespace.
//!
//! ## Template inheritance
//!
//! Template inheritance allows you to build a base template with common
//! elements that can then be shared by all inheriting templates.
//! A base template defines **blocks** that child templates can then override.
//!
//! ### Base template
//!
//! ```text
//! <!DOCTYPE html>
//! <html lang="en">
//!   <head>
//!     <title>{{ block title %}{{ title }}{% endblock %} - My Site</title>
//!     {% block head %}{% endblock %}
//!   </head>
//!   <body>
//!     <div id="content">
//!       {% block content %}{% endblock %}
//!     </div>
//!   </body>
//! </html>
//! ```
//!
//! The `block` tags define three blocks that can be filled in by child
//! templates. The base template defines a default version of the block.
//! A base template must define one or more blocks in order to be enable
//! inheritance. Blocks can only be specified at the top level of a template,
//! not inside `if`/`else` branches or in `for`-loop bodies.
//!
//! ### Child template
//!
//! Here's an example child template:
//!
//! ```text
//! {% extends "base.html" %}
//!
//! {% block title %}Index{% endblock %}
//!
//! {% block head %}
//!   <style>
//!   </style>
//! {% endblock %}
//!
//! {% block content %}
//!   <h1>Index</h1>
//!   <p>Hello, world!</p>
//! {% endblock %}
//! ```
//!
//! The `extends` tag tells the code generator that this template inherits
//! from another template. It will search for the base template relative to
//! itself before looking relative to the template base directory. It will
//! render the top-level content from the base template, and substitute
//! blocks from the base template with those from the child template. The
//! inheriting template context `struct` must have a field called `_parent` of
//! the type used as the base template context. Blocks can refer to the context
//! of both parent and child template.
//!
//! Note that, if the base template lives in another module than the child
//! template, the child template's module should import all symbols from the
//! base template's module in order for it to find the trait definition that
//! supports the inheritance mechanism.
//!
//! ## HTML escaping
//!
//! Askama does not yet support automatic escaping. Care must be taken to
//! escape content that may contain HTML control characters. You can use
//! the `escape` filter (or its `e` alias) to escape data for use in HTML.
//!
//! ## Control structures
//!
//! ### For
//!
//! Loop over each item in an iterator. For example:
//!
//! ```text
//! <h1>Users</h1>
//! <ul>
//! {% for user in users %}
//!   <li>{{ user.name|e }}</li>
//! {% endfor %}
//! </ul>
//! ```
//!
//! Inside for-loop blocks, some useful variables are accessible:
//!
//! * *loop.index*: current loop iteration (starting from 1)
//! * *loop.index0*: current loop iteration (starting from 0)
//!
//! ### If
//!
//! The *if* statement is used as you might expect:
//!
//! ```text
//! {% if users.len() == 0 %}
//!   No users
//! {% else if users.len() == 1 %}
//!   1 user
//! {% else %}
//!   {{ users.len() }} users
//! {% endif %}
//! ```
//!
//! ### Include
//!
//! The *include* statement lets you split large or repetitive blocks into
//! separate template files. Included templates get full access to the context
//! in which they're used, including local variables like those from loops:
//!
//! ```text
//! {% for i in iter %}
//!   {% include "item.html" %}
//! {% endfor %}
//! ```
//!
//! ```text
//! * Item: {{ i }}
//! ```
//!
//! The path to include must be a string literal, so that it is known at
//! compile time. Askama will try to find the specified template relative
//! to the including template's path before falling back to the absolute
//! template path. Use `include` within the branches of an `if`/`else`
//! block to use includes more dynamically.
//!
//! ## Expressions
//!
//! Askama supports string literals (`"foo"`) and integer literals (`1`).
//! It supports almost all binary operators that Rust supports,
//! including arithmetic, comparison and logic operators.
//! The parser applies the same precedence order as the Rust compiler.
//! Expressions can be grouped using parentheses.
//! The HTML special characters `&`, `<` and `>` will be replaced with their
//! character entities unless the `escape` mode is disabled for a template.

#![allow(unused_imports)]
#[macro_use]
extern crate askama_derive;
extern crate askama_shared as shared;

use shared::path;

use std::fs::{self, DirEntry};
use std::io;
use std::path::Path;

/// Main `Template` trait; implementations are generally derived
pub trait Template {
    /// Renders the template to the given `writer` buffer
    fn render_into(&self, writer: &mut std::fmt::Write) -> Result<()>;
    /// Helper method which allocates a new `String` and renders into it
    fn render(&self) -> Result<String> {
        let mut buf = String::new();
        self.render_into(&mut buf)?;
        Ok(buf)
    }
}

pub use shared::filters;
pub use askama_derive::*;
pub use shared::{Error, MarkupDisplay, Result};

#[cfg(feature = "with-iron")]
pub mod iron {
    extern crate iron;
    pub use self::iron::modifier::Modifier;
    pub use self::iron::response::Response;
}

#[cfg(feature = "with-rocket")]
pub mod rocket {
    extern crate rocket;
    pub use self::rocket::http::{ContentType, Status};
    pub use self::rocket::request::Request;
    pub use self::rocket::response::{Responder, Response};
}

fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

/// Build script helper to rebuild crates if contained templates have changed
///
/// Iterates over all files in the template dir (`templates` in
/// `CARGO_MANIFEST_DIR`) and writes a `cargo:rerun-if-changed=` line for each
/// of them to stdout.
///
/// This helper method can be used in build scripts (`build.rs`) in crates
/// that have templates, to make sure the crate gets rebuilt when template
/// source code changes.
pub fn rerun_if_templates_changed() {
    visit_dirs(&path::template_dir(), &|e: &DirEntry| {
        println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
    }).unwrap();
}
<|endoftext|>


<commit_before>//! Render Queue

#![crate_name = "render"]

extern crate cgmath;
extern crate ecs;
extern crate gfx;

// normalized depth
pub type Depth = u16;

pub type Scalar = f32;

pub type Transform = cgmath::Decomposed<
	Scalar,
	cgmath::Quaternion<Scalar>,
	cgmath::Point3<Scalar>
>;

struct Camera {
	transform: Transform,
	frustum: cgmath::Frustum<Scalar>,
}

pub struct Object<L, T> {
	batch: gfx::batch::RefBatch<L, T>,
    bounding_sphere: cgmath::Sphere<Scalar>,
	depth: Depth,
}

pub fn order_opaque<L, T>(a: &Object<L, T>, b: &Object<L, T>) -> Ordering {
    Equal
}

type Index = uint;

pub struct Queue<L, T> {
    pub objects: Vec<Object<L, T>>,
    indices: Vec<Index>,
	context: gfx::batch::Context,
}

impl<L, T> Queue<L, T> {
    fn update(&mut self, order: |&Object<L, T>, &Object<L, T>| -> Ordering) {
        // synchronize indices to have the same length
        let ni = self.indices.len();
        if self.objects.len() > ni {
            self.indices.grow_fn(self.objects.len() - ni, |i| (ni + i) as Index);
        }else
        if self.objects.len() < ni {
            self.indices.retain(|&i| (i as uint) < ni);
        }
        debug_assert_eq!(self.objects.len(), self.indices.len());
        // sort
        let objects = self.objects.as_slice();
        self.indices.sort_by(|&ia, &ib|
            (order)(&objects[ia], &objects[ib])
        );
        // done
    }
}

pub struct View<L, T> {
    pub frame: gfx::Frame,
    pub camera: Camera,
	pub queue: Queue<L, T>,
}

impl<L, T> View<L, T> {
	fn add(&mut self, batch: gfx::batch::RefBatch<L, T>, bound: cgmath::Sphere<Scalar>) {
		let _depth = 0u;
		//self.queue.objects.push(Object {})
	}
}
<commit_msg>Implemented draw call sorting and rendering<commit_after>//! Render Queue

#![crate_name = "render"]

extern crate cgmath;
extern crate ecs;
extern crate gfx;

use std::{iter, slice};

// normalized depth
pub type Depth = u16;

pub type Scalar = f32;

pub type Transform = cgmath::Decomposed<
    Scalar,
    cgmath::Quaternion<Scalar>,
    cgmath::Point3<Scalar>
>;

struct Camera {
    transform: Transform,
    frustum: cgmath::Frustum<Scalar>,
}

pub struct Object<L, T> {
    batch: gfx::batch::RefBatch<L, T>,
    data: T,
    bounding_sphere: cgmath::Sphere<Scalar>,
    depth: Depth,
}

pub fn order_opaque<L, T>(a: &Object<L, T>, b: &Object<L, T>) -> Ordering {
    (&a.batch, a.depth).cmp(&(&b.batch, b.depth))
}

type Index = uint;

pub struct Queue<L, T> {
    pub objects: Vec<Object<L, T>>,
    indices: Vec<Index>,
    context: gfx::batch::Context,
}

struct ObjectIter<'a, L: 'a, T: 'a> {
    objects: &'a [Object<L, T>],
    id_iter: slice::Items<'a, Index>,
}

impl<'a, L, T> Iterator<&'a Object<L, T>> for ObjectIter<'a, L, T> {
    fn next(&mut self) -> Option<&'a Object<L, T>> {
        self.id_iter.next().map(|&i| &self.objects[i])
    }
}

impl<L, T: gfx::shade::ShaderParam<L>> Queue<L, T> {
    fn is_updated(&self) -> bool {
        self.objects.len() == self.indices.len()
    }

    /// Synchronize indices to have the same length as objects
    pub fn update(&mut self) {
        let ni = self.indices.len();
        if self.objects.len() > ni {
            self.indices.grow_fn(self.objects.len() - ni, |i| (ni + i) as Index);
        }else
        if self.objects.len() < ni {
            self.indices.retain(|&i| (i as uint) < ni);
        }
        debug_assert!(self.is_updated());
    }

    /// Sort objects with the given order
    pub fn sort(&mut self, order: |&Object<L, T>, &Object<L, T>| -> Ordering) {
        debug_assert!(self.is_updated());
        let objects = self.objects.as_slice();
        self.indices.sort_by(|&ia, &ib|
            (order)(&objects[ia], &objects[ib])
        );
    }

    /// Iterate objects in the sorted order
    pub fn objects<'a>(&'a self) -> ObjectIter<'a, L, T>  {
        debug_assert!(self.is_updated());
        ObjectIter {
            objects: self.objects.as_slice(),
            id_iter: self.indices.iter(),
        }
    }

    // render everything to the given frame
    pub fn render<C: gfx::CommandBuffer>(&self, renderer: &mut gfx::Renderer<C>,
                  frame: &gfx::Frame) {
        debug_assert!(self.is_updated());
        for &i in self.indices.iter() {
            let ob = &self.objects[i];
            renderer.draw((&ob.batch, &ob.data, &self.context), frame);
        }
    }
}

pub struct View<L, T> {
    pub frame: gfx::Frame,
    pub camera: Camera,
    pub queue: Queue<L, T>,
}

impl<L, T> View<L, T> {
    fn add(&mut self, batch: gfx::batch::RefBatch<L, T>, bound: cgmath::Sphere<Scalar>) {
        let _depth = 0u;
        //self.queue.objects.push(Object {})
    }
}
<|endoftext|>


<commit_before>use getset::Getters;
use std::env::temp_dir;
use std::fs::{create_dir, read_dir, File};
use std::io::{copy, Error, ErrorKind};
use std::path::PathBuf;
use zip::ZipArchive;

const ARCHIVE: &str = "https://github.com/github/gitignore/archive/master.zip";

/// A template represents a `*.gitignore` file in a repository.
#[derive(Clone, Debug, Getters)]
pub struct Template {
    /// Returns the name of the template.
    #[getset(get = "pub")]
    name: String,

    /// Returns the file name of the template.
    #[getset(get = "pub")]
    file_name: String,

    /// Returns a sanitized version of the template's name for comparisons.
    #[getset(get = "pub")]
    comparator: String,
}

impl Template {
    /// Returns a new instance of a template for the given file name.
    pub fn new(file_name: &str) -> Template {
        let name = file_name.replace(".gitignore", "");

        Template {
            comparator: name.to_lowercase(),
            file_name: String::from(file_name),
            name,
        }
    }
}

/// The repository with `.gitignore` files
///
/// The repository is a copy of the [github/gitignore](https://github.com/github/gitignore) Git
/// repository, and contains all `.gitignore` files. A local copy of the remote repository is
/// maintained on disk to speed up searches and to provide offline capabilities.
#[derive(Debug, Getters)]
pub struct Repository {
    /// Returns the path to the repository.
    #[getset(get = "pub")]
    path: PathBuf,
}

impl Repository {
    /// Returns a new instance for the repository at the given location.
    ///
    /// When initializing a repository, a number of prerequisites is checked to ensure the path
    /// points to a valid repository. A result is returned that either contains an instance of the
    /// repository, or an error that explains why the repository could not be initialized.
    pub fn new(path: PathBuf) -> Result<Self, Error> {
        if !path.exists() {
            create_dir(&path)?;
        }

        Ok(Repository { path })
    }

    /// Returns a list of templates in the repository.
    ///
    /// The templates in a repository are all the `*.gitignore` files in its path. Since this is a
    /// convention, only the base names of the files are returned without their ending. For example,
    /// only `GitHub` is returned for the template `GitHub.gitignore`.
    pub fn templates(&self) -> Result<Vec<Template>, Error> {
        let entries = read_dir(self.path())?;

        let mut templates: Vec<Template> = entries
            .filter_map(|entry| {
                let entry = match entry {
                    Ok(entry) => entry,
                    Err(_) => return None,
                };

                let file_name = entry.file_name();
                let file_name = match file_name.to_str() {
                    Some(file_name) => file_name,
                    None => return None,
                };

                if file_name.ends_with(".gitignore") {
                    Some(Template::new(file_name))
                } else {
                    None
                }
            })
            .collect();

        templates.sort_by(|a, b| a.name().cmp(b.name()));

        Ok(templates)
    }

    /// Update a repository with the latest templates from GitHub.
    ///
    /// When updating a repository, the latest templates are downloaded from GitHub and copied into
    /// the repository. This operation thus requires an internet connection.
    pub fn update(&self) -> Result<(), Error> {
        let archive = self.download_archive(None)?;
        self.extract_archive(&archive)?;

        Ok(())
    }

    /// Download an archive with all .gitignore templates.
    ///
    /// `alfred-gitignore` uses the `.gitignore` templates that GitHub provides in the
    /// [github/gitignore](https://github.com/github/gitignore) repository. An archive with with
    /// latest version of the `master` branch can be downloaded and stored at a temporary location
    /// using this function. It either returns the path to the file, or the error that occurred
    /// while attempting the download.
    fn download_archive(&self, url: Option<String>) -> Result<PathBuf, Error> {
        let url = url.unwrap_or_else(|| String::from(ARCHIVE));
        let mut response = reqwest::blocking::get(&url).unwrap();

        let file_path = temp_dir().join(
            PathBuf::from(ARCHIVE)
                .file_name()
                .unwrap_or_else(|| "archive.zip".as_ref()),
        );
        let mut destination = File::create(&file_path)?;

        copy(&mut response, &mut destination)?;

        Ok(file_path)
    }

    /// Extract an archive and write its files into a repository.
    ///
    /// An archive is a `.zip` file that contains many `.gitignore` files. When extracting it, these
    /// files are written to the given repository. While doing so, the directory structure is
    /// flattened. For example, `github/gitignore.gitignore` is written to `gitignore.gitignore` and
    /// the `github` directory is ignored.
    fn extract_archive(&self, archive: &PathBuf) -> Result<(), Error> {
        let file = File::open(archive)?;

        let mut archive = match ZipArchive::new(file) {
            Ok(archive) => archive,
            Err(error) => {
                return Err(Error::new(
                    ErrorKind::InvalidData,
                    format!("Open ZIP archive failed with error '{}'", error),
                ))
            }
        };

        for i in 0..archive.len() {
            let mut file = match archive.by_index(i) {
                Ok(file) => file,
                Err(_) => continue, // TODO Log an error
            };

            let sanitized_name = file.sanitized_name();

            let file_name = match sanitized_name.file_name() {
                Some(base_name) => base_name,
                None => continue, // TODO Log an error
            };

            let file_name_as_str = match file_name.to_str() {
                Some(name) => name,
                None => continue, // TODO Log an error
            };

            if file_name_as_str.ends_with("gitignore") {
                let file_path = self.path().join(file_name_as_str);
                let mut destination = File::create(file_path)?;

                copy(&mut file, &mut destination)?;
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::repository::Repository;
    use crate::testing::initialize_repository;
    use mockito::{mock, Mock};
    use std::fs::{remove_file, File};
    use std::io::Write;
    use tempfile::TempDir;

    const ARCHIVE: &[u8] = include_bytes!("../tests/files/gitignore-master.zip");

    fn mock_get_archive() -> Mock {
        mock("GET", "/")
            .with_status(200)
            .with_header("Content-Type", "application/zip")
            .with_body(ARCHIVE.as_ref())
            .create()
    }

    #[test]
    fn new_with_existing_path() {
        let directory = TempDir::new().unwrap();
        let repository = Repository::new(directory.into_path());

        assert!(repository.is_ok())
    }

    #[test]
    fn new_with_empty_path() {
        let directory = TempDir::new().unwrap();
        let path = directory.path().join("does-not-exist");

        let repository = Repository::new(path.clone());

        assert!(repository.is_ok());
        assert!(path.exists());
    }

    #[test]
    fn download_archive() {
        let directory = TempDir::new().unwrap();
        let repository = Repository::new(directory.into_path()).unwrap();

        let _mock = mock_get_archive();

        let file = repository
            .download_archive(Some(mockito::server_url()))
            .unwrap();

        assert!(file.exists());
        assert_eq!("zip", file.extension().unwrap());
        assert_eq!(99108, file.metadata().unwrap().len());

        remove_file(file).unwrap();
    }

    #[test]
    fn extract_archive() {
        let repository_path = TempDir::new().unwrap();
        let repository = Repository::new(repository_path.into_path()).unwrap();

        let tempdir = TempDir::new().unwrap();
        let archive_path = tempdir.path().join("archive.zip");
        let mut archive = File::create(&archive_path).unwrap();
        archive.write_all(ARCHIVE).unwrap();
        archive.sync_all().unwrap();

        repository.extract_archive(&archive_path).unwrap();

        let gitignore_count = repository.templates().unwrap().len();
        assert_eq!(229, gitignore_count);
    }

    #[test]
    fn templates_returns_names() {
        let repository_path = TempDir::new().unwrap();
        let repository = initialize_repository(repository_path.path()).unwrap();

        let templates = repository.templates().unwrap();
        let template_names: Vec<&String> = templates.iter().map(|t| t.name()).collect();

        assert_eq!(vec!["apples", "oranges"], template_names);
    }
}
<commit_msg>Replace deprecated sanitized_name() method<commit_after>use getset::Getters;
use std::env::temp_dir;
use std::fs::{create_dir, read_dir, File};
use std::io::{copy, Error, ErrorKind};
use std::path::PathBuf;
use zip::ZipArchive;

const ARCHIVE: &str = "https://github.com/github/gitignore/archive/master.zip";

/// A template represents a `*.gitignore` file in a repository.
#[derive(Clone, Debug, Getters)]
pub struct Template {
    /// Returns the name of the template.
    #[getset(get = "pub")]
    name: String,

    /// Returns the file name of the template.
    #[getset(get = "pub")]
    file_name: String,

    /// Returns a sanitized version of the template's name for comparisons.
    #[getset(get = "pub")]
    comparator: String,
}

impl Template {
    /// Returns a new instance of a template for the given file name.
    pub fn new(file_name: &str) -> Template {
        let name = file_name.replace(".gitignore", "");

        Template {
            comparator: name.to_lowercase(),
            file_name: String::from(file_name),
            name,
        }
    }
}

/// The repository with `.gitignore` files
///
/// The repository is a copy of the [github/gitignore](https://github.com/github/gitignore) Git
/// repository, and contains all `.gitignore` files. A local copy of the remote repository is
/// maintained on disk to speed up searches and to provide offline capabilities.
#[derive(Debug, Getters)]
pub struct Repository {
    /// Returns the path to the repository.
    #[getset(get = "pub")]
    path: PathBuf,
}

impl Repository {
    /// Returns a new instance for the repository at the given location.
    ///
    /// When initializing a repository, a number of prerequisites is checked to ensure the path
    /// points to a valid repository. A result is returned that either contains an instance of the
    /// repository, or an error that explains why the repository could not be initialized.
    pub fn new(path: PathBuf) -> Result<Self, Error> {
        if !path.exists() {
            create_dir(&path)?;
        }

        Ok(Repository { path })
    }

    /// Returns a list of templates in the repository.
    ///
    /// The templates in a repository are all the `*.gitignore` files in its path. Since this is a
    /// convention, only the base names of the files are returned without their ending. For example,
    /// only `GitHub` is returned for the template `GitHub.gitignore`.
    pub fn templates(&self) -> Result<Vec<Template>, Error> {
        let entries = read_dir(self.path())?;

        let mut templates: Vec<Template> = entries
            .filter_map(|entry| {
                let entry = match entry {
                    Ok(entry) => entry,
                    Err(_) => return None,
                };

                let file_name = entry.file_name();
                let file_name = match file_name.to_str() {
                    Some(file_name) => file_name,
                    None => return None,
                };

                if file_name.ends_with(".gitignore") {
                    Some(Template::new(file_name))
                } else {
                    None
                }
            })
            .collect();

        templates.sort_by(|a, b| a.name().cmp(b.name()));

        Ok(templates)
    }

    /// Update a repository with the latest templates from GitHub.
    ///
    /// When updating a repository, the latest templates are downloaded from GitHub and copied into
    /// the repository. This operation thus requires an internet connection.
    pub fn update(&self) -> Result<(), Error> {
        let archive = self.download_archive(None)?;
        self.extract_archive(&archive)?;

        Ok(())
    }

    /// Download an archive with all .gitignore templates.
    ///
    /// `alfred-gitignore` uses the `.gitignore` templates that GitHub provides in the
    /// [github/gitignore](https://github.com/github/gitignore) repository. An archive with with
    /// latest version of the `master` branch can be downloaded and stored at a temporary location
    /// using this function. It either returns the path to the file, or the error that occurred
    /// while attempting the download.
    fn download_archive(&self, url: Option<String>) -> Result<PathBuf, Error> {
        let url = url.unwrap_or_else(|| String::from(ARCHIVE));
        let mut response = reqwest::blocking::get(&url).unwrap();

        let file_path = temp_dir().join(
            PathBuf::from(ARCHIVE)
                .file_name()
                .unwrap_or_else(|| "archive.zip".as_ref()),
        );
        let mut destination = File::create(&file_path)?;

        copy(&mut response, &mut destination)?;

        Ok(file_path)
    }

    /// Extract an archive and write its files into a repository.
    ///
    /// An archive is a `.zip` file that contains many `.gitignore` files. When extracting it, these
    /// files are written to the given repository. While doing so, the directory structure is
    /// flattened. For example, `github/gitignore.gitignore` is written to `gitignore.gitignore` and
    /// the `github` directory is ignored.
    fn extract_archive(&self, archive: &PathBuf) -> Result<(), Error> {
        let file = File::open(archive)?;

        let mut archive = match ZipArchive::new(file) {
            Ok(archive) => archive,
            Err(error) => {
                return Err(Error::new(
                    ErrorKind::InvalidData,
                    format!("Open ZIP archive failed with error '{}'", error),
                ))
            }
        };

        for i in 0..archive.len() {
            let mut file = match archive.by_index(i) {
                Ok(file) => file,
                Err(_) => continue, // TODO Log an error
            };

            let sanitized_name = PathBuf::from(file.name());

            let file_name = match sanitized_name.file_name() {
                Some(base_name) => base_name,
                None => continue, // TODO Log an error
            };

            let file_name_as_str = match file_name.to_str() {
                Some(name) => name,
                None => continue, // TODO Log an error
            };

            if file_name_as_str.ends_with("gitignore") {
                let file_path = self.path().join(file_name_as_str);
                let mut destination = File::create(file_path)?;

                copy(&mut file, &mut destination)?;
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::repository::Repository;
    use crate::testing::initialize_repository;
    use mockito::{mock, Mock};
    use std::fs::{remove_file, File};
    use std::io::Write;
    use tempfile::TempDir;

    const ARCHIVE: &[u8] = include_bytes!("../tests/files/gitignore-master.zip");

    fn mock_get_archive() -> Mock {
        mock("GET", "/")
            .with_status(200)
            .with_header("Content-Type", "application/zip")
            .with_body(ARCHIVE.as_ref())
            .create()
    }

    #[test]
    fn new_with_existing_path() {
        let directory = TempDir::new().unwrap();
        let repository = Repository::new(directory.into_path());

        assert!(repository.is_ok())
    }

    #[test]
    fn new_with_empty_path() {
        let directory = TempDir::new().unwrap();
        let path = directory.path().join("does-not-exist");

        let repository = Repository::new(path.clone());

        assert!(repository.is_ok());
        assert!(path.exists());
    }

    #[test]
    fn download_archive() {
        let directory = TempDir::new().unwrap();
        let repository = Repository::new(directory.into_path()).unwrap();

        let _mock = mock_get_archive();

        let file = repository
            .download_archive(Some(mockito::server_url()))
            .unwrap();

        assert!(file.exists());
        assert_eq!("zip", file.extension().unwrap());
        assert_eq!(99108, file.metadata().unwrap().len());

        remove_file(file).unwrap();
    }

    #[test]
    fn extract_archive() {
        let repository_path = TempDir::new().unwrap();
        let repository = Repository::new(repository_path.into_path()).unwrap();

        let tempdir = TempDir::new().unwrap();
        let archive_path = tempdir.path().join("archive.zip");
        let mut archive = File::create(&archive_path).unwrap();
        archive.write_all(ARCHIVE).unwrap();
        archive.sync_all().unwrap();

        repository.extract_archive(&archive_path).unwrap();

        let gitignore_count = repository.templates().unwrap().len();
        assert_eq!(229, gitignore_count);
    }

    #[test]
    fn templates_returns_names() {
        let repository_path = TempDir::new().unwrap();
        let repository = initialize_repository(repository_path.path()).unwrap();

        let templates = repository.templates().unwrap();
        let template_names: Vec<&String> = templates.iter().map(|t| t.name()).collect();

        assert_eq!(vec!["apples", "oranges"], template_names);
    }
}
<|endoftext|>


<commit_before>//! Retry sending records configuration.

/// You can calculate retrying interval as the following equation:
///
/// `retry_interval = exp ** (multiplier + retry_counts)`
///
/// see: https://github.com/jimmycuadra/retry/blob/v0.4.0/src/lib.rs#L142-L143
///
/// You can estimate to caluculate concrete values like:
///
/// * retry_counts: 10, e ^ (10 + 10)/1000.0/60.0/60.0 = 0.908060381242253, about 0.9 hour
///
/// * retry_counts: 11, e ^ (5 + 11)/1000.0/60.0/60.0 = 2.4683640334744092, about 2.5 hours
///
/// * retry_counts: 12, e ^ (5 + 12)/1000.0/60.0/60.0 = 6.709709098215361, about 6.7 hours
///
/// where multiplier = 5,
/// e is [exponential function](https://doc.rust-lang.org/std/primitive.f64.html#method.exp).
///
/// ## Default values
///
/// * multiplier: 5_f64
/// * max: 10
#[derive(Debug, Clone, PartialEq)]
pub struct RetryConf {
    max: u64,
    multiplier: f64,
}

impl Default for RetryConf {
    fn default() -> RetryConf {
        RetryConf {
            max: 10,
            multiplier: 5_f64,
        }
    }
}

impl RetryConf {
    pub fn new() -> RetryConf {
        Default::default()
    }

    pub fn max(mut self, max: u64) -> RetryConf {
        self.max = max;
        self
    }

    pub fn multiplier(mut self, multiplier: f64) -> RetryConf {
        self.multiplier = multiplier;
        self
    }

    pub fn build(self) -> (u64, f64) {
        (self.max, self.multiplier)
    }
}
<commit_msg>retry_conf: Fix rustdoc<commit_after>//! Retry sending records configuration.

/// You can calculate retrying interval as the following equation:
///
/// `retry_interval = exp ** (multiplier + retry_counts)`
///
/// see: https://github.com/jimmycuadra/retry/blob/v0.4.0/src/lib.rs#L142-L143
///
/// You can estimate to caluculate concrete values like:
///
/// * retry_counts: 10, e ^ (5 + 10)/1000.0/60.0/60.0 = 0.908060381242253, about 0.9 hour
///
/// * retry_counts: 11, e ^ (5 + 11)/1000.0/60.0/60.0 = 2.4683640334744092, about 2.5 hours
///
/// * retry_counts: 12, e ^ (5 + 12)/1000.0/60.0/60.0 = 6.709709098215361, about 6.7 hours
///
/// where multiplier = 5,
/// e is [exponential function](https://doc.rust-lang.org/std/primitive.f64.html#method.exp).
///
/// ## Default values
///
/// * multiplier: 5_f64
/// * max: 10
#[derive(Debug, Clone, PartialEq)]
pub struct RetryConf {
    max: u64,
    multiplier: f64,
}

impl Default for RetryConf {
    fn default() -> RetryConf {
        RetryConf {
            max: 10,
            multiplier: 5_f64,
        }
    }
}

impl RetryConf {
    pub fn new() -> RetryConf {
        Default::default()
    }

    pub fn max(mut self, max: u64) -> RetryConf {
        self.max = max;
        self
    }

    pub fn multiplier(mut self, multiplier: f64) -> RetryConf {
        self.multiplier = multiplier;
        self
    }

    pub fn build(self) -> (u64, f64) {
        (self.max, self.multiplier)
    }
}
<|endoftext|>


<commit_before>
//! Back-end agnostic controller events.

use {Event, Input, Motion};

/// Components of a controller button event. Not guaranteed consistent across
/// backends.
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct ControllerButton {
    /// Which controller was the button on.
    pub id: i32,
    /// Which button was pressed.
    pub button: u8,
}

impl ControllerButton {
    /// Create a new ControllerButton object. Intended for use by backends when
    /// emitting events.
    pub fn new(id: i32, button: u8) -> Self {
        ControllerButton {
            id: id,
            button: button,
        }
    }
}

/// Components of a controller hat move event (d-Pad).
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct ControllerHat {
  /// Which Controller was the button on.
  pub id: i32,
  /// Which button was pressed.
  pub state: ::HatState,
  /// Which hat on the controller was changed
  pub which: u8,
}

impl ControllerHat {
  /// Create a new ControllerButton object. Intended for use by backends when
  /// emitting events.
  pub fn new(id: i32, which: u8, state: ::HatState) -> Self {
    ControllerHat {
      id, state, which,
    }
  }
}

/// Components of a controller axis move event. Not guaranteed consistent across
/// backends.
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd, Debug)]
pub struct ControllerAxisArgs {
    /// Which controller moved.
    pub id: i32,
    /// The axis that moved.
    pub axis: u8,
    /// Position of the controller. Usually [-1.0, 1.0], though backends may use
    /// a different range for various devices.
    pub position: f64,
}

impl ControllerAxisArgs {
    /// Create a new ControllerAxisArgs object. Intended for use by backends when
    /// emitting events.
    pub fn new(id: i32, axis: u8, position: f64) -> Self {
        ControllerAxisArgs {
            id: id,
            axis: axis,
            position: position,
        }
    }
}

/// The position of a controller axis changed.
pub trait ControllerAxisEvent: Sized {
    /// Creates a controller axis event.
    ///
    /// Preserves time stamp from original input event, if any.
    fn from_controller_axis_args(args: ControllerAxisArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a controller axis event.
    fn controller_axis<U, F>(&self, f: F) -> Option<U> where F: FnMut(ControllerAxisArgs) -> U;
    /// Returns controller axis arguments.
    fn controller_axis_args(&self) -> Option<ControllerAxisArgs> {
        self.controller_axis(|args| args)
    }
}

impl ControllerAxisEvent for Event {
    fn from_controller_axis_args(args: ControllerAxisArgs, old_event: &Self) -> Option<Self> {
        let timestamp = if let Event::Input(_, x) = old_event {*x} else {None};
        Some(Event::Input(Input::Move(Motion::ControllerAxis(args)), timestamp))
    }

    fn controller_axis<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(ControllerAxisArgs) -> U
    {
        match *self {
            Event::Input(Input::Move(Motion::ControllerAxis(args)), _) => Some(f(args)),
            _ => None,
        }
    }
}

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

    #[test]
    fn test_input_controller_axis() {
        let e: Event = ControllerAxisArgs::new(0, 1, 0.9).into();
        let a: Option<Event> =
            ControllerAxisEvent::from_controller_axis_args(ControllerAxisArgs::new(0, 1, 0.9), &e);
        let b: Option<Event> = a.clone()
            .unwrap()
            .controller_axis(|cnt| {
                ControllerAxisEvent::from_controller_axis_args(
                ControllerAxisArgs::new(cnt.id, cnt.axis, cnt.position),
                a.as_ref().unwrap())
            })
            .unwrap();
        assert_eq!(a, b);
    }
}
<commit_msg>Changed `ControllerHat::id` into `u32`<commit_after>
//! Back-end agnostic controller events.

use {Event, Input, Motion};

/// Components of a controller button event. Not guaranteed consistent across
/// backends.
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct ControllerButton {
    /// Which controller was the button on.
    pub id: u32,
    /// Which button was pressed.
    pub button: u8,
}

impl ControllerButton {
    /// Create a new ControllerButton object. Intended for use by backends when
    /// emitting events.
    pub fn new(id: u32, button: u8) -> Self {
        ControllerButton {
            id: id,
            button: button,
        }
    }
}

/// Components of a controller hat move event (d-Pad).
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct ControllerHat {
  /// Which Controller was the button on.
  pub id: u32,
  /// Which button was pressed.
  pub state: ::HatState,
  /// Which hat on the controller was changed
  pub which: u8,
}

impl ControllerHat {
  /// Create a new ControllerButton object. Intended for use by backends when
  /// emitting events.
  pub fn new(id: u32, which: u8, state: ::HatState) -> Self {
    ControllerHat {
      id, state, which,
    }
  }
}

/// Components of a controller axis move event. Not guaranteed consistent across
/// backends.
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd, Debug)]
pub struct ControllerAxisArgs {
    /// Which controller moved.
    pub id: u32,
    /// The axis that moved.
    pub axis: u8,
    /// Position of the controller. Usually [-1.0, 1.0], though backends may use
    /// a different range for various devices.
    pub position: f64,
}

impl ControllerAxisArgs {
    /// Create a new ControllerAxisArgs object. Intended for use by backends when
    /// emitting events.
    pub fn new(id: u32, axis: u8, position: f64) -> Self {
        ControllerAxisArgs {
            id: id,
            axis: axis,
            position: position,
        }
    }
}

/// The position of a controller axis changed.
pub trait ControllerAxisEvent: Sized {
    /// Creates a controller axis event.
    ///
    /// Preserves time stamp from original input event, if any.
    fn from_controller_axis_args(args: ControllerAxisArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a controller axis event.
    fn controller_axis<U, F>(&self, f: F) -> Option<U> where F: FnMut(ControllerAxisArgs) -> U;
    /// Returns controller axis arguments.
    fn controller_axis_args(&self) -> Option<ControllerAxisArgs> {
        self.controller_axis(|args| args)
    }
}

impl ControllerAxisEvent for Event {
    fn from_controller_axis_args(args: ControllerAxisArgs, old_event: &Self) -> Option<Self> {
        let timestamp = if let Event::Input(_, x) = old_event {*x} else {None};
        Some(Event::Input(Input::Move(Motion::ControllerAxis(args)), timestamp))
    }

    fn controller_axis<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(ControllerAxisArgs) -> U
    {
        match *self {
            Event::Input(Input::Move(Motion::ControllerAxis(args)), _) => Some(f(args)),
            _ => None,
        }
    }
}

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

    #[test]
    fn test_input_controller_axis() {
        let e: Event = ControllerAxisArgs::new(0, 1, 0.9).into();
        let a: Option<Event> =
            ControllerAxisEvent::from_controller_axis_args(ControllerAxisArgs::new(0, 1, 0.9), &e);
        let b: Option<Event> = a.clone()
            .unwrap()
            .controller_axis(|cnt| {
                ControllerAxisEvent::from_controller_axis_args(
                ControllerAxisArgs::new(cnt.id, cnt.axis, cnt.position),
                a.as_ref().unwrap())
            })
            .unwrap();
        assert_eq!(a, b);
    }
}
<|endoftext|>