<commit_before>use std::collections::HashMap;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::ToJson;
#[cfg(feature = "serde_type")]
use serde_json::value::ToJson;

use regex::{Regex, Captures};

use template::Template;
use render::{Renderable, RenderError, RenderContext};
use context::Context;
use helpers::{self, HelperDef};
use directives::{self, DirectiveDef};
use support::str::StringWriter;
use error::{TemplateError, TemplateFileError, TemplateRenderError};

lazy_static!{
    static ref DEFAULT_REPLACE: Regex = Regex::new(">|<|\"|&").unwrap();
}

/// This type represents an *escape fn*, that is a function who's purpose it is
/// to escape potentially problematic characters in a string.
///
/// An *escape fn* is represented as a `Box` to avoid unnecessary type
/// parameters (and because traits cannot be aliased using `type`).
pub type EscapeFn = Box<Fn(&str) -> String + Send + Sync>;

/// The default *escape fn* replaces the characters `&"<>`
/// with the equivalent html / xml entities.
pub fn html_escape(data: &str) -> String {
    DEFAULT_REPLACE.replace_all(data, |cap: &Captures| {
            match cap.get(0).map(|m| m.as_str()) {
                    Some("<") => "<",
                    Some(">") => ">",
                    Some("\"") => """,
                    Some("&") => "&",
                    _ => unreachable!(),
                }
                .to_owned()
        })
        .into_owned()
}

/// `EscapeFn` that donot change any thing. Useful when using in a non-html
/// environment.
pub fn no_escape(data: &str) -> String {
    data.to_owned()
}

/// The single entry point of your Handlebars templates
///
/// It maintains compiled templates and registered helpers.
pub struct Registry {
    templates: HashMap<String, Template>,
    helpers: HashMap<String, Box<HelperDef + 'static>>,
    directives: HashMap<String, Box<DirectiveDef + 'static>>,
    escape_fn: EscapeFn,
    source_map: bool,
}

impl Registry {
    pub fn new() -> Registry {
        let r = Registry {
            templates: HashMap::new(),
            helpers: HashMap::new(),
            directives: HashMap::new(),
            escape_fn: Box::new(html_escape),
            source_map: true,
        };

        r.setup_builtins()
    }

    #[cfg(feature="partial_legacy")]
    fn setup_builtins(mut self) -> Registry {
        self.register_helper("if", Box::new(helpers::IF_HELPER));
        self.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        self.register_helper("each", Box::new(helpers::EACH_HELPER));
        self.register_helper("with", Box::new(helpers::WITH_HELPER));
        self.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        self.register_helper("raw", Box::new(helpers::RAW_HELPER));
        self.register_helper(">", Box::new(helpers::INCLUDE_HELPER));
        self.register_helper("block", Box::new(helpers::BLOCK_HELPER));
        self.register_helper("partial", Box::new(helpers::PARTIAL_HELPER));
        self.register_helper("log", Box::new(helpers::LOG_HELPER));

        self.register_decorator("inline", Box::new(directives::INLINE_DIRECTIVE));
        self
    }

    #[cfg(not(feature = "partial_legacy"))]
    fn setup_builtins(mut self) -> Registry {
        self.register_helper("if", Box::new(helpers::IF_HELPER));
        self.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        self.register_helper("each", Box::new(helpers::EACH_HELPER));
        self.register_helper("with", Box::new(helpers::WITH_HELPER));
        self.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        self.register_helper("raw", Box::new(helpers::RAW_HELPER));
        self.register_helper("log", Box::new(helpers::LOG_HELPER));

        self.register_decorator("inline", Box::new(directives::INLINE_DIRECTIVE));
        self
    }

    /// Enable handlebars template source map
    ///
    /// Source map provides line/col reporting on error. It uses slightly
    /// more memory to maintain the data.
    ///
    /// Default is true.
    pub fn source_map_enable(&mut self, enable: bool) {
        self.source_map = enable;
    }

    /// Register a template string
    ///
    /// Returns `TemplateError` if there is syntax error on parsing template.
    pub fn register_template_string<S>(&mut self,
                                       name: &str,
                                       tpl_str: S)
                                       -> Result<(), TemplateError>
        where S: AsRef<str>
    {
        try!(Template::compile_with_name(tpl_str, name.to_owned(), self.source_map)
                 .and_then(|t| Ok(self.templates.insert(name.to_string(), t))));
        Ok(())
    }

    /// Register a partial string
    ///
    /// A named partial will be added to the registry. It will overwrite template with
    /// same name. Currently registered partial is just identical to template.
    pub fn register_partial<S>(&mut self, name: &str, partial_str: S) -> Result<(), TemplateError>
        where S: AsRef<str>
    {
        self.register_template_string(name, partial_str)
    }

    /// Register a template from a path
    pub fn register_template_file
(&mut self,
                                     name: &str,
                                     tpl_path: P)
                                     -> Result<(), TemplateFileError>
        where P: AsRef<Path>
    {
        let mut file =
            try!(File::open(tpl_path).map_err(|e| TemplateFileError::IOError(e, name.to_owned())));
        self.register_template_source(name, &mut file)
    }

    /// Register a template from `std::io::Read` source
    pub fn register_template_source(&mut self,
                                    name: &str,
                                    tpl_source: &mut Read)
                                    -> Result<(), TemplateFileError> {
        let mut buf = String::new();
        try!(tpl_source.read_to_string(&mut buf)
                       .map_err(|e| TemplateFileError::IOError(e, name.to_owned())));
        try!(self.register_template_string(name, buf));
        Ok(())
    }

    /// remove a template from the registry
    pub fn unregister_template(&mut self, name: &str) {
        self.templates.remove(name);
    }

    /// register a helper
    pub fn register_helper(&mut self,
                           name: &str,
                           def: Box<HelperDef + 'static>)
                           -> Option<Box<HelperDef + 'static>> {
        self.helpers.insert(name.to_string(), def)
    }

    /// register a decorator
    pub fn register_decorator(&mut self,
                              name: &str,
                              def: Box<DirectiveDef + 'static>)
                              -> Option<Box<DirectiveDef + 'static>> {
        self.directives.insert(name.to_string(), def)
    }

    /// Register a new *escape fn* to be used from now on by this registry.
    pub fn register_escape_fn<F: 'static + Fn(&str) -> String + Send + Sync>(&mut self,
                                                                             escape_fn: F) {
        self.escape_fn = Box::new(escape_fn);
    }

    /// Restore the default *escape fn*.
    pub fn unregister_escape_fn(&mut self) {
        self.escape_fn = Box::new(html_escape);
    }

    /// Get a reference to the current *escape fn*.
    pub fn get_escape_fn(&self) -> &Fn(&str) -> String {
        &*self.escape_fn
    }

    /// Return a registered template,
    pub fn get_template(&self, name: &str) -> Option<&Template> {
        self.templates.get(name)
    }

    /// Return a registered helper
    pub fn get_helper(&self, name: &str) -> Option<&Box<HelperDef + 'static>> {
        self.helpers.get(name)
    }

    /// Return a registered directive, aka decorator
    pub fn get_decorator(&self, name: &str) -> Option<&Box<DirectiveDef + 'static>> {
        self.directives.get(name)
    }

    /// Return all templates registered
    pub fn get_templates(&self) -> &HashMap<String, Template> {
        &self.templates
    }

    /// Unregister all templates
    pub fn clear_templates(&mut self) {
        self.templates.clear();
    }

    /// Render a registered template with some data into a string
    ///
    /// * `name` is the template name you registred previously
    /// * `ctx` is the data that implements `ToJson` of either rustc_serialize or serde_json
    ///
    /// Returns rendered string or an struct with error information
    pub fn render<T>(&self, name: &str, data: &T) -> Result<String, RenderError>
        where T: ToJson
    {
        let mut writer = StringWriter::new();
        {
            try!(self.renderw(name, data, &mut writer));
        }
        Ok(writer.to_string())
    }

    /// Render a registered template and write some data to the `std::io::Write`
    pub fn renderw<T>(&self, name: &str, data: &T, writer: &mut Write) -> Result<(), RenderError>
        where T: ToJson
    {
        self.get_template(&name.to_string())
            .ok_or(RenderError::new(format!("Template not found: {}", name)))
            .and_then(|t| {
                let mut ctx = Context::wraps(data);
                let mut local_helpers = HashMap::new();
                let mut render_context = RenderContext::new(&mut ctx, &mut local_helpers, writer);
                render_context.root_template = t.name.clone();
                t.render(self, &mut render_context)
            })
    }

    /// render a template string using current registry without register it
    pub fn template_render<T>(&self,
                              template_string: &str,
                              data: &T)
                              -> Result<String, TemplateRenderError>
        where T: ToJson
    {
        let mut writer = StringWriter::new();
        {
            try!(self.template_renderw(template_string, data, &mut writer));
        }
        Ok(writer.to_string())
    }

    /// render a template string using current registry without register it
    pub fn template_renderw<T>(&self,
                               template_string: &str,
                               data: &T,
                               writer: &mut Write)
                               -> Result<(), TemplateRenderError>
        where T: ToJson
    {
        let tpl = try!(Template::compile(template_string));
        let mut ctx = Context::wraps(data);
        let mut local_helpers = HashMap::new();
        let mut render_context = RenderContext::new(&mut ctx, &mut local_helpers, writer);
        tpl.render(self, &mut render_context).map_err(TemplateRenderError::from)
    }

    /// render a template source using current registry without register it
    pub fn template_renderw2<T>(&self,
                                template_source: &mut Read,
                                data: &T,
                                writer: &mut Write)
                                -> Result<(), TemplateRenderError>
        where T: ToJson
    {
        let mut tpl_str = String::new();
        try!(template_source.read_to_string(&mut tpl_str).map_err(|e| {
            TemplateRenderError::IOError(e, "Unamed template source".to_owned())
        }));
        self.template_renderw(&tpl_str, data, writer)
    }
}

#[cfg(test)]
mod test {
    use registry::Registry;
    use render::{RenderContext, Renderable, RenderError, Helper};
    use helpers::HelperDef;
    use support::str::StringWriter;
    #[cfg(feature = "partial_legacy")]
    use error::TemplateRenderError;

    #[derive(Clone, Copy)]
    struct DummyHelper;

    impl HelperDef for DummyHelper {
        fn call(&self,
                h: &Helper,
                r: &Registry,
                rc: &mut RenderContext)
                -> Result<(), RenderError> {
            try!(h.template().unwrap().render(r, rc));
            Ok(())
        }
    }

    static DUMMY_HELPER: DummyHelper = DummyHelper;

    #[test]
    fn test_registry_operations() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());
        assert!(r.register_template_string("index2", "

").is_ok());

        assert_eq!(r.templates.len(), 2);

        r.unregister_template("index");
        assert_eq!(r.templates.len(), 1);

        r.clear_templates();
        assert_eq!(r.templates.len(), 0);

        r.register_helper("dummy", Box::new(DUMMY_HELPER));

        // built-in helpers plus 1
        #[cfg(feature = "partial_legacy")]
        assert_eq!(r.helpers.len(), 10 + 1);

        #[cfg(not(feature = "partial_legacy"))]
        assert_eq!(r.helpers.len(), 7 + 1);
    }

    #[test]
    fn test_renderw() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());

        let mut sw = StringWriter::new();
        {
            r.renderw("index", &(), &mut sw).ok().unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

    }

    #[test]
    fn test_escape_fn() {
        let mut r = Registry::new();

        let input = String::from("\"<>&");

        r.register_template_string("test", String::from("{{this}}")).unwrap();

        assert_eq!(""<>&", r.render("test", &input).unwrap());

        r.register_escape_fn(|s| s.into());

        assert_eq!("\"<>&", r.render("test", &input).unwrap());

        r.unregister_escape_fn();

        assert_eq!(""<>&", r.render("test", &input).unwrap());
    }

    #[test]
    #[cfg(feature="partial_legacy")]
    fn test_template_render() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());

        assert_eq!("

".to_string(),
                   r.template_render("{{> index}}", &{}).unwrap());

        assert_eq!("hello world".to_string(),
                   r.template_render("hello {{this}}", &"world".to_string()).unwrap());

        let mut sw = StringWriter::new();

        {
            r.template_renderw("{{> index}}", &{}, &mut sw).unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

        // fail for template error
        match r.template_render("{{ hello", &{}).unwrap_err() {
            TemplateRenderError::TemplateError(_) => {}
            _ => {
                panic!();
            }
        }

        // fail to render error
        match r.template_render("{{> notfound}}", &{}).unwrap_err() {
            TemplateRenderError::RenderError(_) => {}
            _ => {
                panic!();
            }
        }
    }
}
<commit_msg>donot -> do not<commit_after>use std::collections::HashMap;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::ToJson;
#[cfg(feature = "serde_type")]
use serde_json::value::ToJson;

use regex::{Regex, Captures};

use template::Template;
use render::{Renderable, RenderError, RenderContext};
use context::Context;
use helpers::{self, HelperDef};
use directives::{self, DirectiveDef};
use support::str::StringWriter;
use error::{TemplateError, TemplateFileError, TemplateRenderError};

lazy_static!{
    static ref DEFAULT_REPLACE: Regex = Regex::new(">|<|\"|&").unwrap();
}

/// This type represents an *escape fn*, that is a function who's purpose it is
/// to escape potentially problematic characters in a string.
///
/// An *escape fn* is represented as a `Box` to avoid unnecessary type
/// parameters (and because traits cannot be aliased using `type`).
pub type EscapeFn = Box<Fn(&str) -> String + Send + Sync>;

/// The default *escape fn* replaces the characters `&"<>`
/// with the equivalent html / xml entities.
pub fn html_escape(data: &str) -> String {
    DEFAULT_REPLACE.replace_all(data, |cap: &Captures| {
            match cap.get(0).map(|m| m.as_str()) {
                    Some("<") => "<",
                    Some(">") => ">",
                    Some("\"") => """,
                    Some("&") => "&",
                    _ => unreachable!(),
                }
                .to_owned()
        })
        .into_owned()
}

/// `EscapeFn` that do not change any thing. Useful when using in a non-html
/// environment.
pub fn no_escape(data: &str) -> String {
    data.to_owned()
}

/// The single entry point of your Handlebars templates
///
/// It maintains compiled templates and registered helpers.
pub struct Registry {
    templates: HashMap<String, Template>,
    helpers: HashMap<String, Box<HelperDef + 'static>>,
    directives: HashMap<String, Box<DirectiveDef + 'static>>,
    escape_fn: EscapeFn,
    source_map: bool,
}

impl Registry {
    pub fn new() -> Registry {
        let r = Registry {
            templates: HashMap::new(),
            helpers: HashMap::new(),
            directives: HashMap::new(),
            escape_fn: Box::new(html_escape),
            source_map: true,
        };

        r.setup_builtins()
    }

    #[cfg(feature="partial_legacy")]
    fn setup_builtins(mut self) -> Registry {
        self.register_helper("if", Box::new(helpers::IF_HELPER));
        self.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        self.register_helper("each", Box::new(helpers::EACH_HELPER));
        self.register_helper("with", Box::new(helpers::WITH_HELPER));
        self.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        self.register_helper("raw", Box::new(helpers::RAW_HELPER));
        self.register_helper(">", Box::new(helpers::INCLUDE_HELPER));
        self.register_helper("block", Box::new(helpers::BLOCK_HELPER));
        self.register_helper("partial", Box::new(helpers::PARTIAL_HELPER));
        self.register_helper("log", Box::new(helpers::LOG_HELPER));

        self.register_decorator("inline", Box::new(directives::INLINE_DIRECTIVE));
        self
    }

    #[cfg(not(feature = "partial_legacy"))]
    fn setup_builtins(mut self) -> Registry {
        self.register_helper("if", Box::new(helpers::IF_HELPER));
        self.register_helper("unless", Box::new(helpers::UNLESS_HELPER));
        self.register_helper("each", Box::new(helpers::EACH_HELPER));
        self.register_helper("with", Box::new(helpers::WITH_HELPER));
        self.register_helper("lookup", Box::new(helpers::LOOKUP_HELPER));
        self.register_helper("raw", Box::new(helpers::RAW_HELPER));
        self.register_helper("log", Box::new(helpers::LOG_HELPER));

        self.register_decorator("inline", Box::new(directives::INLINE_DIRECTIVE));
        self
    }

    /// Enable handlebars template source map
    ///
    /// Source map provides line/col reporting on error. It uses slightly
    /// more memory to maintain the data.
    ///
    /// Default is true.
    pub fn source_map_enable(&mut self, enable: bool) {
        self.source_map = enable;
    }

    /// Register a template string
    ///
    /// Returns `TemplateError` if there is syntax error on parsing template.
    pub fn register_template_string<S>(&mut self,
                                       name: &str,
                                       tpl_str: S)
                                       -> Result<(), TemplateError>
        where S: AsRef<str>
    {
        try!(Template::compile_with_name(tpl_str, name.to_owned(), self.source_map)
                 .and_then(|t| Ok(self.templates.insert(name.to_string(), t))));
        Ok(())
    }

    /// Register a partial string
    ///
    /// A named partial will be added to the registry. It will overwrite template with
    /// same name. Currently registered partial is just identical to template.
    pub fn register_partial<S>(&mut self, name: &str, partial_str: S) -> Result<(), TemplateError>
        where S: AsRef<str>
    {
        self.register_template_string(name, partial_str)
    }

    /// Register a template from a path
    pub fn register_template_file
(&mut self,
                                     name: &str,
                                     tpl_path: P)
                                     -> Result<(), TemplateFileError>
        where P: AsRef<Path>
    {
        let mut file =
            try!(File::open(tpl_path).map_err(|e| TemplateFileError::IOError(e, name.to_owned())));
        self.register_template_source(name, &mut file)
    }

    /// Register a template from `std::io::Read` source
    pub fn register_template_source(&mut self,
                                    name: &str,
                                    tpl_source: &mut Read)
                                    -> Result<(), TemplateFileError> {
        let mut buf = String::new();
        try!(tpl_source.read_to_string(&mut buf)
                       .map_err(|e| TemplateFileError::IOError(e, name.to_owned())));
        try!(self.register_template_string(name, buf));
        Ok(())
    }

    /// remove a template from the registry
    pub fn unregister_template(&mut self, name: &str) {
        self.templates.remove(name);
    }

    /// register a helper
    pub fn register_helper(&mut self,
                           name: &str,
                           def: Box<HelperDef + 'static>)
                           -> Option<Box<HelperDef + 'static>> {
        self.helpers.insert(name.to_string(), def)
    }

    /// register a decorator
    pub fn register_decorator(&mut self,
                              name: &str,
                              def: Box<DirectiveDef + 'static>)
                              -> Option<Box<DirectiveDef + 'static>> {
        self.directives.insert(name.to_string(), def)
    }

    /// Register a new *escape fn* to be used from now on by this registry.
    pub fn register_escape_fn<F: 'static + Fn(&str) -> String + Send + Sync>(&mut self,
                                                                             escape_fn: F) {
        self.escape_fn = Box::new(escape_fn);
    }

    /// Restore the default *escape fn*.
    pub fn unregister_escape_fn(&mut self) {
        self.escape_fn = Box::new(html_escape);
    }

    /// Get a reference to the current *escape fn*.
    pub fn get_escape_fn(&self) -> &Fn(&str) -> String {
        &*self.escape_fn
    }

    /// Return a registered template,
    pub fn get_template(&self, name: &str) -> Option<&Template> {
        self.templates.get(name)
    }

    /// Return a registered helper
    pub fn get_helper(&self, name: &str) -> Option<&Box<HelperDef + 'static>> {
        self.helpers.get(name)
    }

    /// Return a registered directive, aka decorator
    pub fn get_decorator(&self, name: &str) -> Option<&Box<DirectiveDef + 'static>> {
        self.directives.get(name)
    }

    /// Return all templates registered
    pub fn get_templates(&self) -> &HashMap<String, Template> {
        &self.templates
    }

    /// Unregister all templates
    pub fn clear_templates(&mut self) {
        self.templates.clear();
    }

    /// Render a registered template with some data into a string
    ///
    /// * `name` is the template name you registred previously
    /// * `ctx` is the data that implements `ToJson` of either rustc_serialize or serde_json
    ///
    /// Returns rendered string or an struct with error information
    pub fn render<T>(&self, name: &str, data: &T) -> Result<String, RenderError>
        where T: ToJson
    {
        let mut writer = StringWriter::new();
        {
            try!(self.renderw(name, data, &mut writer));
        }
        Ok(writer.to_string())
    }

    /// Render a registered template and write some data to the `std::io::Write`
    pub fn renderw<T>(&self, name: &str, data: &T, writer: &mut Write) -> Result<(), RenderError>
        where T: ToJson
    {
        self.get_template(&name.to_string())
            .ok_or(RenderError::new(format!("Template not found: {}", name)))
            .and_then(|t| {
                let mut ctx = Context::wraps(data);
                let mut local_helpers = HashMap::new();
                let mut render_context = RenderContext::new(&mut ctx, &mut local_helpers, writer);
                render_context.root_template = t.name.clone();
                t.render(self, &mut render_context)
            })
    }

    /// render a template string using current registry without register it
    pub fn template_render<T>(&self,
                              template_string: &str,
                              data: &T)
                              -> Result<String, TemplateRenderError>
        where T: ToJson
    {
        let mut writer = StringWriter::new();
        {
            try!(self.template_renderw(template_string, data, &mut writer));
        }
        Ok(writer.to_string())
    }

    /// render a template string using current registry without register it
    pub fn template_renderw<T>(&self,
                               template_string: &str,
                               data: &T,
                               writer: &mut Write)
                               -> Result<(), TemplateRenderError>
        where T: ToJson
    {
        let tpl = try!(Template::compile(template_string));
        let mut ctx = Context::wraps(data);
        let mut local_helpers = HashMap::new();
        let mut render_context = RenderContext::new(&mut ctx, &mut local_helpers, writer);
        tpl.render(self, &mut render_context).map_err(TemplateRenderError::from)
    }

    /// render a template source using current registry without register it
    pub fn template_renderw2<T>(&self,
                                template_source: &mut Read,
                                data: &T,
                                writer: &mut Write)
                                -> Result<(), TemplateRenderError>
        where T: ToJson
    {
        let mut tpl_str = String::new();
        try!(template_source.read_to_string(&mut tpl_str).map_err(|e| {
            TemplateRenderError::IOError(e, "Unamed template source".to_owned())
        }));
        self.template_renderw(&tpl_str, data, writer)
    }
}

#[cfg(test)]
mod test {
    use registry::Registry;
    use render::{RenderContext, Renderable, RenderError, Helper};
    use helpers::HelperDef;
    use support::str::StringWriter;
    #[cfg(feature = "partial_legacy")]
    use error::TemplateRenderError;

    #[derive(Clone, Copy)]
    struct DummyHelper;

    impl HelperDef for DummyHelper {
        fn call(&self,
                h: &Helper,
                r: &Registry,
                rc: &mut RenderContext)
                -> Result<(), RenderError> {
            try!(h.template().unwrap().render(r, rc));
            Ok(())
        }
    }

    static DUMMY_HELPER: DummyHelper = DummyHelper;

    #[test]
    fn test_registry_operations() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());
        assert!(r.register_template_string("index2", "

").is_ok());

        assert_eq!(r.templates.len(), 2);

        r.unregister_template("index");
        assert_eq!(r.templates.len(), 1);

        r.clear_templates();
        assert_eq!(r.templates.len(), 0);

        r.register_helper("dummy", Box::new(DUMMY_HELPER));

        // built-in helpers plus 1
        #[cfg(feature = "partial_legacy")]
        assert_eq!(r.helpers.len(), 10 + 1);

        #[cfg(not(feature = "partial_legacy"))]
        assert_eq!(r.helpers.len(), 7 + 1);
    }

    #[test]
    fn test_renderw() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());

        let mut sw = StringWriter::new();
        {
            r.renderw("index", &(), &mut sw).ok().unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

    }

    #[test]
    fn test_escape_fn() {
        let mut r = Registry::new();

        let input = String::from("\"<>&");

        r.register_template_string("test", String::from("{{this}}")).unwrap();

        assert_eq!(""<>&", r.render("test", &input).unwrap());

        r.register_escape_fn(|s| s.into());

        assert_eq!("\"<>&", r.render("test", &input).unwrap());

        r.unregister_escape_fn();

        assert_eq!(""<>&", r.render("test", &input).unwrap());
    }

    #[test]
    #[cfg(feature="partial_legacy")]
    fn test_template_render() {
        let mut r = Registry::new();

        assert!(r.register_template_string("index", "

").is_ok());

        assert_eq!("

".to_string(),
                   r.template_render("{{> index}}", &{}).unwrap());

        assert_eq!("hello world".to_string(),
                   r.template_render("hello {{this}}", &"world".to_string()).unwrap());

        let mut sw = StringWriter::new();

        {
            r.template_renderw("{{> index}}", &{}, &mut sw).unwrap();
        }

        assert_eq!("

".to_string(), sw.to_string());

        // fail for template error
        match r.template_render("{{ hello", &{}).unwrap_err() {
            TemplateRenderError::TemplateError(_) => {}
            _ => {
                panic!();
            }
        }

        // fail to render error
        match r.template_render("{{> notfound}}", &{}).unwrap_err() {
            TemplateRenderError::RenderError(_) => {}
            _ => {
                panic!();
            }
        }
    }
}
<|endoftext|>


<commit_before>use search::Search;
use text::Text;

pub struct Renderer;

impl Renderer {
    pub fn render(&self, search: &Search) -> Vec<Text> {
        let mut result = Vec::new();
        result.push(Text::Normal(self.header(search)));

        for position in 0..search.config.visible_limit {
            let element = match search.result.get(position) {
                Some(choice) if position == search.current => Text::Highlight(choice.clone()),
                Some(choice) => Text::Normal(choice.clone()),
                None => Text::Blank
            };
            result.push(element);
        }
        result
    }

    pub fn header(&self, search: &Search) -> String {
        format!("{} > {}", search.num_matches(), search.query)
    }
}

#[cfg(test)]
mod tests {
    use search::Search;
    use text::Text;
    use configuration::Configuration;
    use super::*;

    fn input() -> Vec<String> {
        vec!["one".to_string(), "two".to_string(), "three".to_string()]
    }

    #[test]
    fn it_renders_selected_matches_with_a_highlight() {
        let config = Configuration::from_inputs(vec!["one".to_string(),
                                                     "one".to_string(),
                                                     "one".to_string()], None, Some(2));
        let search = Search::blank(&config).down();
        let renderer = Renderer;

        let output = renderer.render(&search);

        assert_eq!(vec![Text::Normal("3 > ".to_string()),
                        Text::Normal("one".to_string()),
                        Text::Highlight("one".to_string())], output);
    }

    #[test]
    fn it_renders_a_mismatch() {
        let config = Configuration::from_inputs(vec!["one".to_string(),
                                                     "two".to_string(),
                                                     "three".to_string()], None, Some(2));

        let search = Search::blank(&config).append_to_search("z");
        let renderer = Renderer;

        let output = renderer.render(&search);

        assert_eq!(vec![Text::Normal("0 > z".to_string()),
                        Text::Blank,
                        Text::Blank], output);
    }
}
<commit_msg>removes unused test function<commit_after>use search::Search;
use text::Text;

pub struct Renderer;

impl Renderer {
    pub fn render(&self, search: &Search) -> Vec<Text> {
        let mut result = Vec::new();
        result.push(Text::Normal(self.header(search)));

        for position in 0..search.config.visible_limit {
            let element = match search.result.get(position) {
                Some(choice) if position == search.current => Text::Highlight(choice.clone()),
                Some(choice) => Text::Normal(choice.clone()),
                None => Text::Blank
            };
            result.push(element);
        }
        result
    }

    pub fn header(&self, search: &Search) -> String {
        format!("{} > {}", search.num_matches(), search.query)
    }
}

#[cfg(test)]
mod tests {
    use search::Search;
    use text::Text;
    use configuration::Configuration;
    use super::*;

    #[test]
    fn it_renders_selected_matches_with_a_highlight() {
        let config = Configuration::from_inputs(vec!["one".to_string(),
                                                     "one".to_string(),
                                                     "one".to_string()], None, Some(2));
        let search = Search::blank(&config).down();
        let renderer = Renderer;

        let output = renderer.render(&search);

        assert_eq!(vec![Text::Normal("3 > ".to_string()),
                        Text::Normal("one".to_string()),
                        Text::Highlight("one".to_string())], output);
    }

    #[test]
    fn it_renders_a_mismatch() {
        let config = Configuration::from_inputs(vec!["one".to_string(),
                                                     "two".to_string(),
                                                     "three".to_string()], None, Some(2));

        let search = Search::blank(&config).append_to_search("z");
        let renderer = Renderer;

        let output = renderer.render(&search);

        assert_eq!(vec![Text::Normal("0 > z".to_string()),
                        Text::Blank,
                        Text::Blank], output);
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::convert::From;
use std::str;
use hyper;
use hyper::header::{Headers, ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use json;

pub use hyper::status::StatusCode;
pub type Codes = StatusCode;
pub type HyperResponse = hyper::client::Response;

#[derive(Debug)]
pub struct Response {
    content: Vec<u8>,
    hr: HyperResponse,
}

impl From<HyperResponse> for Response {
    fn from(mut raw: HyperResponse) -> Self {
        let mut content = match raw.headers.get::<ContentLength>() {
            Some(&ContentLength(length)) => Vec::with_capacity(length as usize),
            None => Vec::new(),
        };

        if raw.read_to_end(&mut content).is_err() {
            content = Vec::new()
        }

        Response {
            content: content,
            hr: raw,
        }
    }
}

impl<'a> Response {
    pub fn url(&self) -> &str {
        self.hr.url.as_str()
    }

    pub fn status_code(&self) -> Codes {
        self.hr.status
    }

    pub fn reason(&self) -> &str {
        self.hr.status.canonical_reason().unwrap_or("UNAVAILABLE")
    }

    pub fn ok(&self) -> bool {
        self.hr.status == StatusCode::Ok
    }

    pub fn text(&'a self) -> Option<&'a str> {
        str::from_utf8(&self.content).ok()
    }

    pub fn content(&'a self) -> &'a Vec<u8> {
        &self.content
    }

    pub fn json(&self) -> json::JsonResult<json::JsonValue> {
        self.text().map(|t| json::parse(t)).unwrap()
    }

    pub fn is_json(&self) -> bool {
        match self.hr.headers.get::<ContentType>() {
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::Json, _))) => true,
            _ => false,
        }
    }

    pub fn headers(&self) -> &Headers {
        &self.hr.headers
    }
}
<commit_msg>Apparently JsonResult is deprecated<commit_after>use std::io::Read;
use std::convert::From;
use std::str;
use hyper;
use hyper::header::{Headers, ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use json;

pub use hyper::status::StatusCode;
pub type Codes = StatusCode;
pub type HyperResponse = hyper::client::Response;

#[derive(Debug)]
pub struct Response {
    content: Vec<u8>,
    hr: HyperResponse,
}

impl From<HyperResponse> for Response {
    fn from(mut raw: HyperResponse) -> Self {
        let mut content = match raw.headers.get::<ContentLength>() {
            Some(&ContentLength(length)) => Vec::with_capacity(length as usize),
            None => Vec::new(),
        };

        if raw.read_to_end(&mut content).is_err() {
            content = Vec::new()
        }

        Response {
            content: content,
            hr: raw,
        }
    }
}

impl<'a> Response {
    pub fn url(&self) -> &str {
        self.hr.url.as_str()
    }

    pub fn status_code(&self) -> Codes {
        self.hr.status
    }

    pub fn reason(&self) -> &str {
        self.hr.status.canonical_reason().unwrap_or("UNAVAILABLE")
    }

    pub fn ok(&self) -> bool {
        self.hr.status == StatusCode::Ok
    }

    pub fn text(&'a self) -> Option<&'a str> {
        str::from_utf8(&self.content).ok()
    }

    pub fn content(&'a self) -> &'a Vec<u8> {
        &self.content
    }

    pub fn json(&self) -> json::Result<json::JsonValue> {
        self.text().map(|t| json::parse(t)).unwrap()
    }

    pub fn is_json(&self) -> bool {
        match self.hr.headers.get::<ContentType>() {
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::Json, _))) => true,
            _ => false,
        }
    }

    pub fn headers(&self) -> &Headers {
        &self.hr.headers
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate std;
extern crate sync;

use super::liblua;
use super::Index;
use super::Lua;
use super::Pushable;
use super::Readable;

extern fn wrapper1(lua: *mut liblua::lua_State) -> libc::c_int {
    unsafe {
        let argumentsCount = liblua::lua_gettop(lua);

        let data = liblua::lua_touserdata(lua, liblua::lua_upvalueindex(2));
        let wrapper2ptr = liblua::lua_touserdata(lua, liblua::lua_upvalueindex(1));
        let wrapper2: &fn(*mut liblua::lua_State, libc::c_int, *mut libc::c_void)->libc::c_int = std::mem::transmute(wrapper2ptr);

        (*wrapper2)(lua, argumentsCount, data)
    }
}

/*fn wrapper2proc<Ret: Pushable>(lua: *mut liblua::lua_State, argumentsCount: libc::c_int, p: &proc()->Ret) -> libc::c_int {
    let ret = p();
    ret.push_to_lua(Lua{lua:lua});
    1
}*/

fn wrapper2fn<Ret: Pushable>(lua: *mut liblua::lua_State, argumentsCount: libc::c_int, p: &fn()->Ret) -> libc::c_int {
    let ret = (*p)();
    ret.push_to_lua(&Lua{lua:lua});
    1
}

/*impl<Ret: Pushable> Pushable for proc()->Ret {
    fn push_to_lua(&self, lua: &Lua) {
        unsafe {
            // pushing the std::raw::Procedure as a userdata
            let rawProc: &std::raw::Procedure = std::mem::transmute(self);
            let mut userDataPtr = liblua::lua_newuserdata(lua.lua, std::mem::size_of_val(rawProc) as libc::size_t);
            let userData: &mut std::raw::Procedure = std::mem::transmute(userDataPtr);
            userData.code = rawProc.code;
            userData.env = rawProc.env;

            // pushing wrapper2 as a lightuserdata
            let wrapper2: &fn(*mut liblua::lua_State, libc::c_int, &proc()->Ret)->libc::c_int = &wrapper2proc;
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(wrapper2));

            // pushing wrapper1 as a closure
            liblua::lua_pushcclosure(lua.lua, std::mem::transmute(wrapper1), 2);
        }
    }
}*/

impl<Ret: Pushable> Pushable for fn()->Ret {
    fn push_to_lua(&self, lua: &Lua) {
        unsafe {
            // pushing the function pointer as a lightuserdata
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(self));

            // pushing wrapper2 as a lightuserdata
            let wrapper2: &fn(*mut liblua::lua_State, libc::c_int, &fn()->Ret)->libc::c_int = &wrapper2fn;
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(wrapper2));

            // pushing wrapper1 as a closure
            liblua::lua_pushcclosure(lua.lua, std::mem::transmute(wrapper1), 2);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn execute() {
        let mut lua = super::super::Lua::new();

        let val: int = lua.execute("return 5").unwrap();
        assert_eq!(val, 5);
    }

    /*#[test]
    fn simple_function() {
        let mut lua = super::super::Lua::new();

        fn ret5() -> int { 5 };
        lua.set("ret5", &ret5);

        let val: int = lua.execute("return ret5()").unwrap();
        assert_eq!(val, 5);
    }*/
}
<commit_msg>Functions pushing now working<commit_after>extern crate libc;
extern crate std;
extern crate sync;

use super::liblua;
use super::Index;
use super::Lua;
use super::Pushable;
use super::Readable;

extern fn wrapper1(lua: *mut liblua::lua_State) -> libc::c_int {
    unsafe {
        let argumentsCount = liblua::lua_gettop(lua);

        let data = liblua::lua_touserdata(lua, liblua::lua_upvalueindex(1));
        let wrapper2ptr = liblua::lua_touserdata(lua, liblua::lua_upvalueindex(2));
        let wrapper2: fn(*mut liblua::lua_State, libc::c_int, *mut libc::c_void)->libc::c_int = std::mem::transmute(wrapper2ptr);

        wrapper2(lua, argumentsCount, data)
    }
}

/*fn wrapper2proc<Ret: Pushable>(lua: *mut liblua::lua_State, argumentsCount: libc::c_int, p: &proc()->Ret) -> libc::c_int {
    let ret = p();
    ret.push_to_lua(Lua{lua:lua});
    1
}*/

fn wrapper2fn<Ret: Pushable>(lua: *mut liblua::lua_State, argumentsCount: libc::c_int, p: fn()->Ret) -> libc::c_int {
    let ret = p();
    ret.push_to_lua(&Lua{lua:lua});
    1
}

/*impl<Ret: Pushable> Pushable for proc()->Ret {
    fn push_to_lua(&self, lua: &Lua) {
        unsafe {
            // pushing the std::raw::Procedure as a userdata
            let rawProc: &std::raw::Procedure = std::mem::transmute(self);
            let mut userDataPtr = liblua::lua_newuserdata(lua.lua, std::mem::size_of_val(rawProc) as libc::size_t);
            let userData: &mut std::raw::Procedure = std::mem::transmute(userDataPtr);
            userData.code = rawProc.code;
            userData.env = rawProc.env;

            // pushing wrapper2 as a lightuserdata
            let wrapper2: &fn(*mut liblua::lua_State, libc::c_int, &proc()->Ret)->libc::c_int = &wrapper2proc;
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(wrapper2));

            // pushing wrapper1 as a closure
            liblua::lua_pushcclosure(lua.lua, std::mem::transmute(wrapper1), 2);
        }
    }
}*/

impl<Ret: Pushable> Pushable for fn()->Ret {
    fn push_to_lua(&self, lua: &Lua) {
        unsafe {
            // pushing the function pointer as a lightuserdata
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(*self));

            // pushing wrapper2 as a lightuserdata
            let wrapper2: fn(*mut liblua::lua_State, libc::c_int, fn()->Ret)->libc::c_int = wrapper2fn;
            liblua::lua_pushlightuserdata(lua.lua, std::mem::transmute(wrapper2));

            // pushing wrapper1 as a closure
            liblua::lua_pushcclosure(lua.lua, std::mem::transmute(wrapper1), 2);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn execute() {
        let mut lua = super::super::Lua::new();

        let val: int = lua.execute("return 5").unwrap();
        assert_eq!(val, 5);
    }

    #[test]
    fn simple_function() {
        let mut lua = super::super::Lua::new();

        fn ret5() -> int { 5 };
        lua.set("ret5", ret5);

        let val: int = lua.execute("return ret5()").unwrap();
        assert_eq!(val, 5);
    }
}
<|endoftext|>


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

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, path, std_misc)]

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::old_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::Debug 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");
}
<commit_msg>+ feature(io)<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, path, std_misc, io)]

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::old_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::Debug 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>
#![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::Debug 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");
}
<commit_msg>io -> old_io<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::old_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::Debug 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>extern crate ructe;
extern crate core;
use self::ructe::templates::ToHtml;
use self::core::fmt::Display;
use std::io::{Write,Error};
use std::mem::transmute;

use std::collections::HashMap as Dict;
fn parse_linkmark(text:&[u8])->(&[u8], Vec<u8>){

    let mut contents:Option<(usize, usize)> = None;
    let mut link :(Option<usize>, Option<usize>) = (None, None);
    for (i,ch) in text.iter().enumerate(){
        if let None = contents{
            if *ch == b']'{
                contents = Some((1,i));
            }
        }
        else{
            if *ch == b'('{
                link.0 = Some(i + 1);
            }
            if let Some( _ ) = link.0{
                if *ch == b')'{
                    link.1 = Some(i);
                    break;
                }
            }
        }
    }
    let r = match (link.0, link.1, contents){
        (Some(l1), Some(l2), Some(c))=>(l1,l2,c.0, c.1),
        _=>{
            return (&text[1..],vec![b'[']);
        }
    };
    let mut link = Vec::<u8>::new();
    let mut contents = Vec::<u8>::new();
    for ch in &text[r.0..r.1]{
        if *ch == b'"'{
            link.extend(b"%22");
        }
        else{
            link.push(*ch);
        }
    }
    for ch in &text[r.2 .. r.3]{
        match *ch{
            b'<'=>{
                contents.extend(b"<");
            },
            b'>'=>{
                contents.extend(b">");
            }
            _=>{
                contents.push(*ch);
            }
        }
    }
    let mut msg = Vec::<u8>::new();
    msg.extend(b"");
    msg.append(&mut contents);
    msg.extend(b"");
    return (&text[(r.3)+1..], msg);
}
fn parse_nomark(text:&[u8])->(&[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() >= 2{
        if &t[..2] == b"}}"{
            return (&t[2..], res); //(&t[2..], format!("{}",link, res));
        }
        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],vec![b'{']);
}
fn parse_strikethrough(text:&[u8])->(&[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == b"~~"{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        res.push(t[0]);
        t = &t[1..];
    }

    return (&text[1..],vec!(b'~'));
}

fn parse(text:&str)->Vec<u8>{
    let mut res = Vec::<u8>::new();
    let mut t:&[u8] = text.as_bytes();
    res.extend(b"
");
    while t.len() != 0{
        if t.len() > 2{
            if &t[..2] == b"\n\n"{
                res.extend(b"

");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"  "{
                res.extend(b"
");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"{{"{
                let mut r = parse_nomark(t);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }
        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }
    res.extend(b"
");
    return res;
    //return String::from_utf8(res).unwrap();
}

pub struct Markdown{
    obj:*const str
}
impl Markdown{
    pub fn to_html(&self, out: &mut Write) -> Result<(),Error>{
        let text:&str =
        unsafe{std::mem::transmute::<_, &str>(self.obj)};
        out.write_all(&parse(text));
        Ok(())
    }
}
pub fn render(obj:&str)->Markdown{
    Raw{obj:obj}
}<commit_msg>마크다운 적용<commit_after>extern crate ructe;
extern crate core;
use self::ructe::templates::ToHtml;
use self::core::fmt::Display;
use std::io::{Write,Error};
use std::mem::transmute;

use std::collections::HashMap as Dict;
fn parse_linkmark(text:&[u8])->(&[u8], Vec<u8>){

    let mut contents:Option<(usize, usize)> = None;
    let mut link :(Option<usize>, Option<usize>) = (None, None);
    for (i,ch) in text.iter().enumerate(){
        if let None = contents{
            if *ch == b']'{
                contents = Some((1,i));
            }
        }
        else{
            if *ch == b'('{
                link.0 = Some(i + 1);
            }
            if let Some( _ ) = link.0{
                if *ch == b')'{
                    link.1 = Some(i);
                    break;
                }
            }
        }
    }
    let r = match (link.0, link.1, contents){
        (Some(l1), Some(l2), Some(c))=>(l1,l2,c.0, c.1),
        _=>{
            return (&text[1..],vec![b'[']);
        }
    };
    let mut link = Vec::<u8>::new();
    let mut contents = Vec::<u8>::new();
    for ch in &text[r.0..r.1]{
        if *ch == b'"'{
            link.extend(b"%22");
        }
        else{
            link.push(*ch);
        }
    }
    for ch in &text[r.2 .. r.3]{
        match *ch{
            b'<'=>{
                contents.extend(b"<");
            },
            b'>'=>{
                contents.extend(b">");
            }
            _=>{
                contents.push(*ch);
            }
        }
    }
    let mut msg = Vec::<u8>::new();
    msg.extend(b"");
    msg.append(&mut contents);
    msg.extend(b"");
    return (&text[(r.1)+1..], msg);
}
fn parse_nomark(text:&[u8])->(&[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() >= 2{
        if &t[..2] == b"}}"{
            return (&t[2..], res); //(&t[2..], format!("{}",link, res));
        }
        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }

    return (&text[1..],vec![b'{']);
}
fn parse_strikethrough<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        res.push(t[0]);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse_bold<'a>(text:&'a [u8], tag:&'a [u8])->(&'a[u8], Vec<u8>){
    let mut res = Vec::<u8>::new();
    let mut t = &text[2..];

    while t.len() > 0{
        if t.len() >= 2{
            if &t[..2] == tag{
                let mut msg = Vec::<u8>::new();
                msg.extend(b"");
                msg.append(&mut res);
                msg.extend(b"");
                return (&t[2..], msg); //(&t[2..], format!("{}",link, res));
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        res.push(t[0]);
        t = &t[1..];
    }

    return (&text[1..],Vec::from(&tag[..1]));
}
fn parse(text:&str)->Vec<u8>{
    let mut res = Vec::<u8>::new();
    let mut t:&[u8] = text.as_bytes();
    res.extend(b"
");
    while t.len() != 0{
        if t.len() > 2{
            if &t[..2] == b"\n\n"{
                res.extend(b"

");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"  "{
                res.extend(b"
");
                t = &t[2..];
                continue;
            }
            if &t[..2] == b"~~"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"--"{
                let mut r = parse_strikethrough(t, &t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"{{"{
                let mut r = parse_nomark(t);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"__"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
            if &t[..2] == b"**"{
                let mut r = parse_bold(t,&t[..2]);
                res.append(&mut r.1);
                t = r.0;
                continue;
            }
        }
        if &t[..1] == b"["{
            let mut r = parse_linkmark(t);
            res.append(&mut r.1);
            t = r.0;
            continue;
        }

        let s = match &t[0..1]{
            b"<"=>b"<",
            b">"=>b">",
            c@_=>c
        } ;
        res.extend(s);
        t = &t[1..];
    }
    res.extend(b"
");
    return res;
    //return String::from_utf8(res).unwrap();
}

pub struct Markdown{
    obj:*const str
}
impl Markdown{
    pub fn to_html(&self, out: &mut Write) -> Result<(),Error>{
        let text:&str =
        unsafe{transmute::<_, &str>(self.obj)};
        out.write_all(&parse(text));
        Ok(())
    }
}
pub fn render(obj:&str)->Markdown{
    Markdown{obj:obj}
}<|endoftext|>