<commit_before>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote)]

#![allow(unstable)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.view_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser).into_iter() {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, vec![router_trait_use], calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
                    routes.push_all(subroutes.as_slice());

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser).into_iter() {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser).into_iter() {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<commit_msg>Update for libsyntax changes<commit_after>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote)]

#![allow(unstable)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.item_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_item(sp, router_trait_use), cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser).into_iter() {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
                    routes.push_all(subroutes.as_slice());

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser).into_iter() {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser).into_iter() {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<|endoftext|>


<commit_before>//! List project issues
//!
//! https://docs.gitlab.com/ce/api/issues.html#list-project-issues
//!
//! # List project issues
//!
//! Get a list of a project's issues.
//!
//! ```text
//! GET /projects/ID/issues
//! GET /projects/ID/issues?state=opened
//! GET /projects/ID/issues?state=closed
//! GET /projects/ID/issues?labels=foo
//! GET /projects/ID/issues?labels=foo,bar
//! GET /projects/ID/issues?labels=foo,bar&state=opened
//! GET /projects/ID/issues?milestone=1.0.0
//! GET /projects/ID/issues?milestone=1.0.0&state=opened
//! GET /projects/ID/issues?iid=42
//! ```
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id`      | integer | yes   | The ID of a project |
//! | `iid`     | integer | no    | Return the issue having the given `iid` |
//! | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
//! | `labels`  | string  | no    | Comma-separated list of label names, issues with any of the labels will be returned |
//! | `milestone` | string| no    | The milestone title |
//! | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
//! | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
//!

use serde_urlencoded;

use BuildQuery;
use Issues;
use issues::ProjectsIssuesListerInternal;

use ::errors::*;

#[derive(Debug, Clone)]
pub struct IssuesLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a group
    id: i64,
    internal: ProjectsIssuesListerInternal,
}

impl<'a> IssuesLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister {
        IssuesLister {
            gl: gl,
            id: id,
            internal: ProjectsIssuesListerInternal {
                iid: None,
                state: None,
                labels: None,
                milestone: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn iid(&'a mut self, iid: i64) -> &'a mut IssuesLister {
        self.internal.iid = Some(iid);
        self
    }

    pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister {
        self.internal.state = Some(state);
        self
    }

    pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister {
        self.internal.milestone = Some(milestone);
        self
    }

    pub fn labels(&'a mut self, labels: Vec<String>) -> &'a mut IssuesLister {
        self.internal.labels = Some(labels);
        self
    }

    pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister {
        self.internal.order_by = Some(order_by);
        self
    }

    pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of issues.
    pub fn list(&self) -> Result<Issues> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for IssuesLister<'a> {
    fn build_query(&self) -> String {
        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/issues", self.id);
        let amp_char = "&";
        let comma_char = ",";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.state,
                              &self.internal.labels,
                              &self.internal.milestone,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("iid=");
            query.push_str(&iid.to_string());
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                ::issues::State::Opened => "opened",
                ::issues::State::Closed => "closed",
            });
        });

        self.internal.labels.as_ref().map(|labels| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("labels=");

            let mut array_split_char = &none_char;
            for label in labels {
                query.push_str(array_split_char);
                query.push_str(&label.to_string());
                array_split_char = &comma_char;
            }
        });

        self.internal.milestone.as_ref().map(|milestone| {
            query.push_str(split_char);
            split_char = &amp_char;

            let params = &[("milestone", milestone)];
            query.push_str(&serde_urlencoded::to_string(&params).unwrap());
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ::issues::ListingOrderBy::CreatedAt => "created_at",
                ::issues::ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

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

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues", TEST_PROJECT_ID);

        let lister = gl.issues();
        let lister = lister.project(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.issues().project(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.issues().project(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?state=opened", TEST_PROJECT_ID);
        let query =
            gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?state=closed", TEST_PROJECT_ID);
        let query =
            gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_skip_groups() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?labels=label1,label2,label3",
                                      TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")])
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?order_by=created_at", TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?order_by=updated_at", TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?sort=asc", TEST_PROJECT_ID);
        let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?sort=desc", TEST_PROJECT_ID);
        let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?order_by=created_at&sort=asc",
                                      TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .sort(::ListingSort::Asc)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Add `issues::projects` unit test `build_query_iid()`, catching a bug<commit_after>//! List project issues
//!
//! https://docs.gitlab.com/ce/api/issues.html#list-project-issues
//!
//! # List project issues
//!
//! Get a list of a project's issues.
//!
//! ```text
//! GET /projects/ID/issues
//! GET /projects/ID/issues?state=opened
//! GET /projects/ID/issues?state=closed
//! GET /projects/ID/issues?labels=foo
//! GET /projects/ID/issues?labels=foo,bar
//! GET /projects/ID/issues?labels=foo,bar&state=opened
//! GET /projects/ID/issues?milestone=1.0.0
//! GET /projects/ID/issues?milestone=1.0.0&state=opened
//! GET /projects/ID/issues?iid=42
//! ```
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id`      | integer | yes   | The ID of a project |
//! | `iid`     | integer | no    | Return the issue having the given `iid` |
//! | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
//! | `labels`  | string  | no    | Comma-separated list of label names, issues with any of the labels will be returned |
//! | `milestone` | string| no    | The milestone title |
//! | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
//! | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
//!

use serde_urlencoded;

use BuildQuery;
use Issues;
use issues::ProjectsIssuesListerInternal;

use ::errors::*;

#[derive(Debug, Clone)]
pub struct IssuesLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a group
    id: i64,
    internal: ProjectsIssuesListerInternal,
}

impl<'a> IssuesLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister {
        IssuesLister {
            gl: gl,
            id: id,
            internal: ProjectsIssuesListerInternal {
                iid: None,
                state: None,
                labels: None,
                milestone: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn iid(&'a mut self, iid: i64) -> &'a mut IssuesLister {
        self.internal.iid = Some(iid);
        self
    }

    pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister {
        self.internal.state = Some(state);
        self
    }

    pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister {
        self.internal.milestone = Some(milestone);
        self
    }

    pub fn labels(&'a mut self, labels: Vec<String>) -> &'a mut IssuesLister {
        self.internal.labels = Some(labels);
        self
    }

    pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister {
        self.internal.order_by = Some(order_by);
        self
    }

    pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of issues.
    pub fn list(&self) -> Result<Issues> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for IssuesLister<'a> {
    fn build_query(&self) -> String {
        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/issues", self.id);
        let amp_char = "&";
        let comma_char = ",";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.iid,
                              &self.internal.state,
                              &self.internal.labels,
                              &self.internal.milestone,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("iid=");
            query.push_str(&iid.to_string());
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                ::issues::State::Opened => "opened",
                ::issues::State::Closed => "closed",
            });
        });

        self.internal.labels.as_ref().map(|labels| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("labels=");

            let mut array_split_char = &none_char;
            for label in labels {
                query.push_str(array_split_char);
                query.push_str(&label.to_string());
                array_split_char = &comma_char;
            }
        });

        self.internal.milestone.as_ref().map(|milestone| {
            query.push_str(split_char);
            split_char = &amp_char;

            let params = &[("milestone", milestone)];
            query.push_str(&serde_urlencoded::to_string(&params).unwrap());
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ::issues::ListingOrderBy::CreatedAt => "created_at",
                ::issues::ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

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

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues", TEST_PROJECT_ID);

        let lister = gl.issues();
        let lister = lister.project(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.issues().project(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.issues().project(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_iid() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?iid=42", TEST_PROJECT_ID);
        let query = gl.issues().project(TEST_PROJECT_ID).iid(42).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?state=opened", TEST_PROJECT_ID);
        let query =
            gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?state=closed", TEST_PROJECT_ID);
        let query =
            gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_skip_groups() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?labels=label1,label2,label3",
                                      TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")])
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?order_by=created_at", TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?order_by=updated_at", TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .order_by(::issues::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?sort=asc", TEST_PROJECT_ID);
        let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/issues?sort=desc", TEST_PROJECT_ID);
        let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/issues?order_by=created_at&sort=asc",
                                      TEST_PROJECT_ID);
        let query = gl.issues()
            .project(TEST_PROJECT_ID)
            .sort(::ListingSort::Asc)
            .order_by(::issues::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>


<commit_before>extern crate tiny;
extern crate rustbox;

use tiny::text_field::TextField;

use rustbox::{RustBox, InitOptions, InputMode, Event, Key};

fn loop_() -> Option<String> {
    let rustbox = RustBox::init(InitOptions {
        input_mode: InputMode::Esc,
        buffer_stderr: false,
    }).unwrap();

    let mut text_field = TextField::new(20);

    loop {
        rustbox.clear();
        text_field.draw(&rustbox, 0, 0);
        rustbox.present();

        match rustbox.poll_event(false) {
            Err(err) => return Some(format!("{:?}", err)),
            Ok(Event::KeyEvent(Key::Esc)) => return None,
            Ok(Event::KeyEvent(key)) => {
                text_field.keypressed(key);
            },
            Ok(_) => {},
        }
    }
}

fn main() {
    loop_().map(|err| println!("{}", err));
}
<commit_msg>remove unused test app<commit_after><|endoftext|>


<commit_before>use std::error;
use std::fs;
use std::io;
use std::io::Read;
use std::io::Write;
use std::mem;

use fat::RootEntry;

pub const BYTES_PER_SECTOR: usize = 512;
const SECTORS_PER_FAT: usize = 9;
const SECTORS_PER_ROOT: usize = 14;
const SECTORS_PER_DATA_AREA: usize = 2847;

const BYTES_PER_FAT: usize = BYTES_PER_SECTOR * SECTORS_PER_FAT;
const BYTES_PER_ROOT: usize
    = BYTES_PER_SECTOR * SECTORS_PER_ROOT;
const BYTES_PER_DATA_AREA: usize
    = BYTES_PER_SECTOR * SECTORS_PER_DATA_AREA;

const BYTES_PER_ROOT_ENTRY: usize = 32;
#[test]
fn test_root_entry_size() {
    assert_eq!(mem::size_of::<RootEntry>(), BYTES_PER_ROOT_ENTRY);
}

#[derive(Debug)]
pub struct Image {
    boot_sector: Vec<u8>,
    fat_1: Vec<u8>,
    fat_2: Vec<u8>,
    root_dir: Vec<u8>,
    data_area: Vec<u8>,
}

impl Image {
    pub fn new() -> Image {
        Image {
            boot_sector: vec![0; 1 * BYTES_PER_SECTOR],
            fat_1: vec![0; BYTES_PER_FAT],
            fat_2: vec![0; BYTES_PER_FAT],
            root_dir: vec![0; BYTES_PER_ROOT],
            data_area: vec![0; BYTES_PER_DATA_AREA],
        }
    }

    fn blank_image() -> Image {
        Image {
            boot_sector: vec![0; BYTES_PER_SECTOR],
            fat_1: vec![0; BYTES_PER_FAT],
            fat_2: vec![0; BYTES_PER_FAT],
            root_dir: vec![0; BYTES_PER_ROOT],
            data_area: vec![0; BYTES_PER_DATA_AREA],
        }
    }

    pub fn from(image_fn: String)
        -> Result<Image, Box<error::Error>>
    {
        let mut file = fs::File::open(image_fn)?;
        let mut image = Image::blank_image();

        try!(file.read_exact(&mut image.boot_sector));
        try!(file.read_exact(&mut image.fat_1));
        try!(file.read_exact(&mut image.fat_2));
        try!(file.read_exact(&mut image.root_dir));
        try!(file.read_exact(&mut image.data_area));

        Ok(image)
    }

    pub fn save(&self, image_fn: String)
        -> Result<(), io::Error>
    {
        let mut file = fs::File::create(image_fn)?;

        try!(file.write_all(&self.boot_sector));
        try!(file.write_all(&self.fat_1));
        try!(file.write_all(&self.fat_2));
        try!(file.write_all(&self.root_dir));
        try!(file.write_all(&self.data_area));

        Ok(())
    }

    // TODO: Make this an iterator
    pub fn root_entries(&self) -> Vec<RootEntry> {
        self.root_dir
            .chunks(BYTES_PER_ROOT_ENTRY)
            .map(|chunk| {
                let mut entry_bytes = [0; BYTES_PER_ROOT_ENTRY];
                entry_bytes.clone_from_slice(chunk);

                let entry: RootEntry;
                unsafe { entry = mem::transmute(entry_bytes); }
                entry
            })
            .filter(|entry| { entry.filename[0] != 0xe5 })
            .take_while(|entry| { entry.filename[0] != 0 })
            .collect::<Vec<RootEntry>>()
    }

    pub fn get_file_entry(&self, filename: String)
        -> Result<RootEntry, Box<error::Error>>
    {
        let entries = self.root_entries();
        for entry in entries {
            let entry_name: String;
            match entry.filename() {
                Ok(name) => entry_name = name,
                Err(_) => continue,
            }

            if entry_name.to_lowercase() == filename.to_lowercase() {
                return Ok(entry);
            }
        }

        Err(From::from(format!("file {} not found", filename)))
    }

    pub fn create_file_entry(&self, filename: String)
        -> Result<(RootEntry, u16), Box<error::Error>>
    {
        for (index, e) in self.root_entries().iter().enumerate() {
            if !e.is_free() { continue; }

            let mut entry = RootEntry::new();
            try!(entry.set_filename(filename));
            return Ok((entry.clone(), index as u16));
        }

        Err(From::from("no free entries"))
    }

    pub fn save_file_entry(&mut self, entry: RootEntry, index: u16)
        -> Result<(), Box<error::Error>>
    {
        let entry_bytes: [u8; BYTES_PER_ROOT_ENTRY];
        unsafe { entry_bytes = mem::transmute(entry); }

        self.root_dir[
            index as usize * BYTES_PER_ROOT_ENTRY ..
            (index as usize + 1) * BYTES_PER_ROOT_ENTRY
        ].clone_from_slice(&entry_bytes[..]);
        Ok(())
    }

    pub fn fat_entries<'a>(&'a self) -> Box<Iterator<Item=u16> + 'a> {
        Box::new(
            self.fat_1
            .windows(2)
            .enumerate()
            .filter_map(|(i, w)|
                if i % 3 == 2 {
                    None
                } else {
                    let val = if i % 3 == 0 {
                        ((w[1] as u16 & 0xf) << 8) | w[0] as u16
                    } else {
                        (w[1] as u16 & 0xf0) | ((w[1] as u16) << 4)
                    };
                    Some(val)
                }
            )
        )
    }

    pub fn get_fat_entry(&self, cluster_num: u16) -> u16 {
        let offset: usize = cluster_num as usize * 3 / 2;
        let byte_1: u16 = self.fat_1[offset] as u16;
        let byte_2: u16 = self.fat_1[offset + 1] as u16;

        if cluster_num % 2 == 0 { byte_1 | ((byte_2 & 0x0f) << 8) }
        else                    { (byte_1 >> 4) | (byte_2 << 4) }
    }

    pub fn get_free_fat_entry(&self) -> Option<u16> {
        self.fat_entries()
            .filter(|&e| e == 0)
            .nth(0)
    }
}
<commit_msg>Add write_data_sector<commit_after>use std::error;
use std::fs;
use std::io;
use std::io::Read;
use std::io::Write;
use std::mem;

use fat::RootEntry;

pub const BYTES_PER_SECTOR: usize = 512;
const SECTORS_PER_FAT: usize = 9;
const SECTORS_PER_ROOT: usize = 14;
const SECTORS_PER_DATA_AREA: usize = 2847;

const BYTES_PER_FAT: usize = BYTES_PER_SECTOR * SECTORS_PER_FAT;
const BYTES_PER_ROOT: usize
    = BYTES_PER_SECTOR * SECTORS_PER_ROOT;
const BYTES_PER_DATA_AREA: usize
    = BYTES_PER_SECTOR * SECTORS_PER_DATA_AREA;

const BYTES_PER_ROOT_ENTRY: usize = 32;
#[test]
fn test_root_entry_size() {
    assert_eq!(mem::size_of::<RootEntry>(), BYTES_PER_ROOT_ENTRY);
}

#[derive(Debug)]
pub struct Image {
    boot_sector: Vec<u8>,
    fat_1: Vec<u8>,
    fat_2: Vec<u8>,
    root_dir: Vec<u8>,
    data_area: Vec<u8>,
}

impl Image {
    pub fn new() -> Image {
        Image {
            boot_sector: vec![0; 1 * BYTES_PER_SECTOR],
            fat_1: vec![0; BYTES_PER_FAT],
            fat_2: vec![0; BYTES_PER_FAT],
            root_dir: vec![0; BYTES_PER_ROOT],
            data_area: vec![0; BYTES_PER_DATA_AREA],
        }
    }

    fn blank_image() -> Image {
        Image {
            boot_sector: vec![0; BYTES_PER_SECTOR],
            fat_1: vec![0; BYTES_PER_FAT],
            fat_2: vec![0; BYTES_PER_FAT],
            root_dir: vec![0; BYTES_PER_ROOT],
            data_area: vec![0; BYTES_PER_DATA_AREA],
        }
    }

    pub fn from(image_fn: String)
        -> Result<Image, Box<error::Error>>
    {
        let mut file = fs::File::open(image_fn)?;
        let mut image = Image::blank_image();

        try!(file.read_exact(&mut image.boot_sector));
        try!(file.read_exact(&mut image.fat_1));
        try!(file.read_exact(&mut image.fat_2));
        try!(file.read_exact(&mut image.root_dir));
        try!(file.read_exact(&mut image.data_area));

        Ok(image)
    }

    pub fn save(&self, image_fn: String)
        -> Result<(), io::Error>
    {
        let mut file = fs::File::create(image_fn)?;

        try!(file.write_all(&self.boot_sector));
        try!(file.write_all(&self.fat_1));
        try!(file.write_all(&self.fat_2));
        try!(file.write_all(&self.root_dir));
        try!(file.write_all(&self.data_area));

        Ok(())
    }

    // TODO: Make this an iterator
    pub fn root_entries(&self) -> Vec<RootEntry> {
        self.root_dir
            .chunks(BYTES_PER_ROOT_ENTRY)
            .map(|chunk| {
                let mut entry_bytes = [0; BYTES_PER_ROOT_ENTRY];
                entry_bytes.clone_from_slice(chunk);

                let entry: RootEntry;
                unsafe { entry = mem::transmute(entry_bytes); }
                entry
            })
            .filter(|entry| { entry.filename[0] != 0xe5 })
            .take_while(|entry| { entry.filename[0] != 0 })
            .collect::<Vec<RootEntry>>()
    }

    pub fn get_file_entry(&self, filename: String)
        -> Result<RootEntry, Box<error::Error>>
    {
        let entries = self.root_entries();
        for entry in entries {
            let entry_name: String;
            match entry.filename() {
                Ok(name) => entry_name = name,
                Err(_) => continue,
            }

            if entry_name.to_lowercase() == filename.to_lowercase() {
                return Ok(entry);
            }
        }

        Err(From::from(format!("file {} not found", filename)))
    }

    pub fn create_file_entry(&self, filename: String)
        -> Result<(RootEntry, u16), Box<error::Error>>
    {
        for (index, e) in self.root_entries().iter().enumerate() {
            if !e.is_free() { continue; }

            let mut entry = RootEntry::new();
            try!(entry.set_filename(filename));
            return Ok((entry.clone(), index as u16));
        }

        Err(From::from("no free entries"))
    }

    pub fn save_file_entry(&mut self, entry: RootEntry, index: u16)
        -> Result<(), Box<error::Error>>
    {
        let entry_bytes: [u8; BYTES_PER_ROOT_ENTRY];
        unsafe { entry_bytes = mem::transmute(entry); }

        self.root_dir[
            index as usize * BYTES_PER_ROOT_ENTRY ..
            (index as usize + 1) * BYTES_PER_ROOT_ENTRY
        ].clone_from_slice(&entry_bytes[..]);
        Ok(())
    }

    pub fn fat_entries<'a>(&'a self)
        -> Box<Iterator<Item=(usize, u16)> + 'a>
    {
        Box::new(
            self.fat_1
            .windows(2)
            .enumerate()
            .filter_map(|(i, w)|
                if i % 3 == 2 {
                    None
                } else {
                    let val = if i % 3 == 0 {
                        ((w[1] as u16 & 0xf) << 8) | w[0] as u16
                    } else {
                        (w[1] as u16 & 0xf0) | ((w[1] as u16) << 4)
                    };
                    Some((i, val))
                }
            )
        )
    }

    pub fn get_fat_entry(&self, cluster_num: u16) -> u16 {
        let offset: usize = cluster_num as usize * 3 / 2;
        let byte_1: u16 = self.fat_1[offset] as u16;
        let byte_2: u16 = self.fat_1[offset + 1] as u16;

        if cluster_num % 2 == 0 { byte_1 | ((byte_2 & 0x0f) << 8) }
        else                    { (byte_1 >> 4) | (byte_2 << 4) }
    }

    pub fn get_free_fat_entry(&self) -> Option<usize> {
        self.fat_entries()
            .filter(|&(_, e)| e == 0)
            .map(|(i, _)| i + 2)
            .nth(0)
    }

    pub fn write_data_sector(&mut self, sector: usize, data: &[u8])
        -> Result<(), Box<error::Error>>
    {
        if sector >= SECTORS_PER_DATA_AREA {
            return Err(errorf!("sector {} too high to write to", sector));
        }

        let start_index = BYTES_PER_SECTOR * sector;
        let mut target_slice = &mut self.data_area[
            BYTES_PER_SECTOR * sector ..
            BYTES_PER_SECTOR * (sector + 1)
        ];

        target_slice.copy_from_slice(data);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::os::raw::{c_char, c_void};
use std::default::Default;

pub type PmDeviceId = i32;
pub type PortMidiStream = c_void;
pub type PmMessage = i32;
pub type PmTimestamp = u32;
pub const PM_NO_DEVICE: PmDeviceId = -1;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct PmEvent {
    pub message: PmMessage,
    pub timestamp: PmTimestamp,
}
impl Default for PmEvent {
    fn default() -> Self {
        PmEvent {
            message: 0,
            timestamp: 0,
        }
    }
}

#[repr(C)]
pub struct PmDeviceInfo {
    pub struct_version: i32, // < this internal structure version
    pub interf: *const c_char, // < underlying MIDI API, e.g. MMSystem or DirectX
    pub name: *const c_char, // < device name, e.g. USB MidiSport 1x1
    pub input: i32, // < true iff input is available
    pub output: i32, // < true iff output is available
    pub opened: i32, // < used by generic PortMidi code to do error checking on arguments
}

#[derive(Debug)]
#[repr(C)]
pub enum PmError {
    PmNoError = 0,
    PmGotData = 1, // < A "no error" return that also indicates data available
    PmHostError = -10000,
    PmInvalidDeviceId = -9999, /* out of range or
                                * output device when input is requested or
                                * input device when output is requested or
                                * device is already opened
                                * */
    PmInsufficientMemory = -9998,
    PmBufferTooSmall = -9997,
    PmBufferOverflow = -9996,
    PmBadPtr = -9995, /* PortMidiStream parameter is NULL or
                       * stream is not opened or
                       * stream is output when input is required or
                       * stream is input when output is required */
    PmBadData = -9994, // illegal midi data, e.g. missing EOX
    PmInternalError = -9993,
    PmBufferMaxSize = -9992, // buffer is already as large as it can be
}

// while we wait for FromPrimitive to stabilise
pub fn tmp_from_primitive(i: i32) -> Option<PmError> {
    match i {
        0 => Some(PmError::PmNoError),
        1 => Some(PmError::PmGotData),
        -10000 => Some(PmError::PmHostError),
        -9999 => Some(PmError::PmInvalidDeviceId),
        -9998 => Some(PmError::PmInsufficientMemory),
        -9997 => Some(PmError::PmBufferTooSmall),
        -9996 => Some(PmError::PmBufferOverflow),
        -9995 => Some(PmError::PmBadPtr),
        -9994 => Some(PmError::PmBadData),
        -9993 => Some(PmError::PmInternalError),
        -9992 => Some(PmError::PmBufferMaxSize),
        _ => None,
    }
}
<commit_msg>Reformat `PmError` rustdoc<commit_after>use std::os::raw::{c_char, c_void};
use std::default::Default;

pub type PmDeviceId = i32;
pub type PortMidiStream = c_void;
pub type PmMessage = i32;
pub type PmTimestamp = u32;
pub const PM_NO_DEVICE: PmDeviceId = -1;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct PmEvent {
    pub message: PmMessage,
    pub timestamp: PmTimestamp,
}
impl Default for PmEvent {
    fn default() -> Self {
        PmEvent {
            message: 0,
            timestamp: 0,
        }
    }
}

#[repr(C)]
pub struct PmDeviceInfo {
    pub struct_version: i32, // < this internal structure version
    pub interf: *const c_char, // < underlying MIDI API, e.g. MMSystem or DirectX
    pub name: *const c_char, // < device name, e.g. USB MidiSport 1x1
    pub input: i32, // < true iff input is available
    pub output: i32, // < true iff output is available
    pub opened: i32, // < used by generic PortMidi code to do error checking on arguments
}

#[derive(Debug)]
#[repr(C)]
pub enum PmError {
    /// "no error" return that also indicates data available
    PmNoError = 0,
    /// "no error" return that also indicates data available
    PmGotData = 1,
    PmHostError = -10000,
    /// out of range or
    /// output device when input is requested or
    /// input device when output is requested or
    /// device is already opened
    PmInvalidDeviceId = -9999,
    PmInsufficientMemory = -9998,
    PmBufferTooSmall = -9997,
    PmBufferOverflow = -9996,
    /// PortMidiStream parameter is NULL or
    /// stream is not opened or
    /// stream is output when input is required or
    /// stream is input when output is required
    PmBadPtr = -9995,
    /// illegal midi data, e.g. missing EOX
    PmBadData = -9994,
    PmInternalError = -9993,
    /// buffer is already as large as it can be
    PmBufferMaxSize = -9992,
}

// while we wait for FromPrimitive to stabilise
pub fn tmp_from_primitive(i: i32) -> Option<PmError> {
    match i {
        0 => Some(PmError::PmNoError),
        1 => Some(PmError::PmGotData),
        -10000 => Some(PmError::PmHostError),
        -9999 => Some(PmError::PmInvalidDeviceId),
        -9998 => Some(PmError::PmInsufficientMemory),
        -9997 => Some(PmError::PmBufferTooSmall),
        -9996 => Some(PmError::PmBufferOverflow),
        -9995 => Some(PmError::PmBadPtr),
        -9994 => Some(PmError::PmBadData),
        -9993 => Some(PmError::PmInternalError),
        -9992 => Some(PmError::PmBufferMaxSize),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use float::Float;

intrinsics! {
    #[arm_aeabi_alias = __aeabi_fsub]
    pub extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
        a + f32::from_repr(b.repr() ^ f32::SIGN_MASK)
    }

    #[arm_aeabi_alias = __aeabi_dsub]
    pub extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
        a + f64::from_repr(b.repr() ^ f64::SIGN_MASK)
    }

    #[cfg(target_arch = "arm")]
    pub extern "C" fn __subsf3vfp(a: f32, b: f32) -> f32 {
        a - b
    }

    #[cfg(target_arch = "arm")]
    pub extern "C" fn __subdf3vfp(a: f64, b: f64) -> f64 {
        a - b
    }
}
<commit_msg>Fix __subsf3 and __subdf3 on x86<commit_after>use float::Float;
use float::add::__addsf3;
use float::add::__adddf3;

intrinsics! {
    #[arm_aeabi_alias = __aeabi_fsub]
    pub extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
        __addsf3(a, f32::from_repr(b.repr() ^ f32::SIGN_MASK))
    }

    #[arm_aeabi_alias = __aeabi_dsub]
    pub extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
        __adddf3(a, f64::from_repr(b.repr() ^ f64::SIGN_MASK))
    }

    #[cfg(target_arch = "arm")]
    pub extern "C" fn __subsf3vfp(a: f32, b: f32) -> f32 {
        a - b
    }

    #[cfg(target_arch = "arm")]
    pub extern "C" fn __subdf3vfp(a: f64, b: f64) -> f64 {
        a - b
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use super::Position;
use super::symbols::SymbolTable;

#[derive(PartialEq, Debug)]
pub struct Node<I> {
    pub pos: Position,
    pub node: I,
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Type {
    Void,
    Bool,
    Int,
    Float,
    Str,
}

#[derive(PartialEq, Debug)]
pub enum Literal {
    Bool(bool),
    Int(i32),
    Float(f32),
    Str(String),
}

impl Literal {
    fn get_type(&self) -> Type {
        match *self {
            Literal::Bool(_) => Type::Bool,
            Literal::Int(_) => Type::Int,
            Literal::Float(_) => Type::Float,
            Literal::Str(_) => Type::Str,
        }
    }
}

#[derive(Debug)]
pub struct Variable {
    pub id: u32,
    pub type_: Type,
    pub name: String,
}

impl Variable {
    pub fn stub(name: String) -> Variable {
        Variable {
            id: 0,
            type_: Type::Void,
            name: name,
        }
    }

    pub fn eq_name_type(&self, other: &Variable) -> bool {
        self.type_ == other.type_ && self.name == other.name
    }
}

impl PartialEq for Variable {
    fn eq(&self, other: &Variable) -> bool {
        if self.id == 0 || other.id == 0 {
            false
        } else {
            self.id == other.id
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum UnaryOp {
    NOT,
    MINUS,
}

#[derive(PartialEq, Debug)]
pub enum BinaryOp {
    ADD,
    SUB,
    MUL,
    DIV,

    EQ,
    LT,
    LE,
}

#[derive(PartialEq, Debug)]
pub struct Program {
    pub functions: HashMap<String, Node<Function>>,
}

#[derive(PartialEq, Debug)]
pub struct Function {
    pub name: String,
    pub filename: String,
    pub body: Node<Statement>,
    pub args: Vec<Rc<Variable>>,
    pub ret_type: Type,
    pub symbols: Rc<RefCell<SymbolTable>>,
}

#[derive(PartialEq, Debug)]
pub enum Statement {
    Expression {
        expr: Box<Node<Expression>>,
    },
    Declaration {
        var: Rc<Variable>,
    },
    Assignment {
        var: Rc<Variable>,
        expr: Box<Node<Expression>>,
    },
    If {
        cond: Box<Node<Expression>>,
        on_true: Box<Node<Statement>>,
        on_false: Option<Box<Node<Statement>>>,
    },
    While {
        cond: Box<Node<Expression>>,
        body: Box<Node<Statement>>,
    },
    Return {
        expr: Option<Box<Node<Expression>>>,
    },
    Compound {
        stmts: Vec<Node<Statement>>,
        symbols: Rc<RefCell<SymbolTable>>,
    },
}

impl Node<Statement> {
    pub fn walk<S, E>(&self, do_stmt: &mut S, do_expr: &mut E)
        where S: FnMut(&Node<Statement>),
              E: FnMut(&Node<Expression>)
    {
        do_stmt(self);
        match self.node {
            Statement::Expression { ref expr } => expr.walk(do_expr),
            Statement::Assignment { ref expr, .. } => expr.walk(do_expr),
            Statement::Return { expr: Some(ref expr) } => expr.walk(do_expr),
            Statement::If { ref cond, ref on_true, ref on_false } => {
                cond.walk(do_expr);
                on_true.walk(do_stmt, do_expr);
                if let Some(ref stmt) = *on_false {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            Statement::While { ref cond, ref body } => {
                cond.walk(do_expr);
                body.walk(do_stmt, do_expr);
            }
            Statement::Compound { ref stmts, .. } => {
                for stmt in stmts.iter() {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            _ => (),
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum Expression {
    Literal {
        lit: Literal,
    },
    Variable {
        var: Rc<Variable>,
    },
    Call {
        function: String,
        args: Vec<Node<Expression>>,
    },
    Unary {
        op: UnaryOp,
        expr: Box<Node<Expression>>,
    },
    Binary {
        op: BinaryOp,
        left: Box<Node<Expression>>,
        right: Box<Node<Expression>>,
    },
    Parenthesis {
        expr: Box<Node<Expression>>,
    },
}

impl Node<Expression> {
    pub fn walk<E>(&self, do_expr: &mut E)
        where E: FnMut(&Node<Expression>)
    {
        do_expr(self);
        match self.node {
            Expression::Call { ref args, .. } => {
                for arg in args.iter() {
                    arg.walk(do_expr);
                }
            }
            Expression::Unary { ref expr, .. } => expr.walk(do_expr),
            Expression::Parenthesis { ref expr } => expr.walk(do_expr),
            Expression::Binary { ref left, ref right, .. } => {
                left.walk(do_expr);
                right.walk(do_expr);
            }
            _ => (),
        }
    }
}

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

    #[test]
    fn literals() {
        assert_eq!(Type::Bool, Literal::Bool(false).get_type());
        assert_eq!(Type::Int, Literal::Int(0).get_type());
        assert_eq!(Type::Float, Literal::Float(0.0).get_type());
    }
}
<commit_msg>Front: Refactor AST walk<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use super::Position;
use super::symbols::SymbolTable;

#[derive(PartialEq, Debug)]
pub struct Node<I> {
    pub pos: Position,
    pub node: I,
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Type {
    Void,
    Bool,
    Int,
    Float,
    Str,
}

#[derive(PartialEq, Debug)]
pub enum Literal {
    Bool(bool),
    Int(i32),
    Float(f32),
    Str(String),
}

impl Literal {
    fn get_type(&self) -> Type {
        match *self {
            Literal::Bool(_) => Type::Bool,
            Literal::Int(_) => Type::Int,
            Literal::Float(_) => Type::Float,
            Literal::Str(_) => Type::Str,
        }
    }
}

#[derive(Debug)]
pub struct Variable {
    pub id: u32,
    pub type_: Type,
    pub name: String,
}

impl Variable {
    pub fn stub(name: String) -> Variable {
        Variable {
            id: 0,
            type_: Type::Void,
            name: name,
        }
    }

    pub fn eq_name_type(&self, other: &Variable) -> bool {
        self.type_ == other.type_ && self.name == other.name
    }
}

impl PartialEq for Variable {
    fn eq(&self, other: &Variable) -> bool {
        if self.id == 0 || other.id == 0 {
            false
        } else {
            self.id == other.id
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum UnaryOp {
    NOT,
    MINUS,
}

#[derive(PartialEq, Debug)]
pub enum BinaryOp {
    ADD,
    SUB,
    MUL,
    DIV,

    EQ,
    LT,
    LE,
}

#[derive(PartialEq, Debug)]
pub struct Program {
    pub functions: HashMap<String, Node<Function>>,
}

#[derive(PartialEq, Debug)]
pub struct Function {
    pub name: String,
    pub filename: String,
    pub body: Node<Statement>,
    pub args: Vec<Rc<Variable>>,
    pub ret_type: Type,
    pub symbols: Rc<RefCell<SymbolTable>>,
}

#[derive(PartialEq, Debug)]
pub enum Statement {
    Expression {
        expr: Box<Node<Expression>>,
    },
    Declaration {
        var: Rc<Variable>,
    },
    Assignment {
        var: Rc<Variable>,
        expr: Box<Node<Expression>>,
    },
    If {
        cond: Box<Node<Expression>>,
        on_true: Box<Node<Statement>>,
        on_false: Option<Box<Node<Statement>>>,
    },
    While {
        cond: Box<Node<Expression>>,
        body: Box<Node<Statement>>,
    },
    Return {
        expr: Option<Box<Node<Expression>>>,
    },
    Compound {
        stmts: Vec<Node<Statement>>,
        symbols: Rc<RefCell<SymbolTable>>,
    },
}

impl Node<Statement> {
    pub fn walk_stmt(&self, do_stmt: &mut FnMut(&Node<Statement>)) {
        do_stmt(self);
        match self.node {
            Statement::If { ref on_true, ref on_false, .. } => {
                on_true.walk_stmt(do_stmt);
                if let Some(ref stmt) = *on_false {
                    stmt.walk_stmt(do_stmt);
                }
            }
            Statement::While { ref body, .. } => {
                body.walk_stmt(do_stmt);
            }
            Statement::Compound { ref stmts, .. } => {
                for stmt in stmts.iter() {
                    stmt.walk_stmt(do_stmt);
                }
            }
            _ => (),
        }
    }

    pub fn walk_expr(&self, do_expr: &mut FnMut(&Node<Expression>)) {
        self.walk_stmt(&mut |stmt| {
            match stmt.node {
                Statement::Expression { ref expr } => do_expr(expr),
                Statement::Assignment { ref expr, .. } => do_expr(expr),
                Statement::If { ref cond, .. } => do_expr(cond),
                Statement::While { ref cond, .. } => do_expr(cond),
                Statement::Return { expr: Some(ref expr) } => do_expr(expr),
                _ => (),
            }
        })
    }
}

#[derive(PartialEq, Debug)]
pub enum Expression {
    Literal {
        lit: Literal,
    },
    Variable {
        var: Rc<Variable>,
    },
    Call {
        function: String,
        args: Vec<Node<Expression>>,
    },
    Unary {
        op: UnaryOp,
        expr: Box<Node<Expression>>,
    },
    Binary {
        op: BinaryOp,
        left: Box<Node<Expression>>,
        right: Box<Node<Expression>>,
    },
    Parenthesis {
        expr: Box<Node<Expression>>,
    },
}

impl Node<Expression> {
    pub fn walk_expr(&self, do_expr: &mut FnMut(&Node<Expression>)) {
        do_expr(self);
        match self.node {
            Expression::Call { ref args, .. } => {
                for arg in args.iter() {
                    arg.walk_expr(do_expr);
                }
            }
            Expression::Unary { ref expr, .. } => expr.walk_expr(do_expr),
            Expression::Parenthesis { ref expr } => expr.walk_expr(do_expr),
            Expression::Binary { ref left, ref right, .. } => {
                left.walk_expr(do_expr);
                right.walk_expr(do_expr);
            }
            _ => (),
        }
    }
}

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

    #[test]
    fn literals() {
        assert_eq!(Type::Bool, Literal::Bool(false).get_type());
        assert_eq!(Type::Int, Literal::Int(0).get_type());
        assert_eq!(Type::Float, Literal::Float(0.0).get_type());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

extern crate libc;

use std::mem;

use constants::LINE_SIZE;

#[repr(C)]
#[derive(PartialEq)]
#[allow(missing_copy_implementations)]
pub struct GCHeader {
    reference_count: libc::size_t,
    spans_lines: bool,
    forwarded: bool,
    logged: bool,
    marked: bool,
    pinned: bool,
    new: bool,
}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct GCRTTI {
    object_size: libc::size_t,
    members: libc::size_t,
}

#[repr(C)]
#[derive(PartialEq)]
#[allow(raw_pointer_derive)]
pub struct GCObject {
    header: GCHeader,
    rtti: *const GCRTTI
}

pub type GCObjectRef = *mut GCObject;

impl GCRTTI {
    pub fn new(object_size: usize, members: usize) -> GCRTTI {
        return GCRTTI {
            object_size: object_size as libc::size_t,
            members: members as libc::size_t,
        };
    }

    pub fn object_size(&self) -> usize {
        return self.object_size as usize;
    }

    pub fn members(&self) -> usize {
        return self.members as usize;
    }
}

impl GCObject {
    pub fn new(rtti: *const GCRTTI, mark: bool) -> GCObject {
        debug!("GCobject::new(rtti={:p}, mark={})", rtti, mark);
        let size = unsafe{ (*rtti).object_size() };
        return GCObject {
            header: GCHeader {
                reference_count: 0,
                spans_lines: size > LINE_SIZE,
                forwarded: false,
                logged: false,
                marked: mark,
                pinned: false,
                new: true,
            },
            rtti: rtti,
        }
    }

    pub fn set_logged(&mut self, new: bool) -> bool {
        debug!("Set object {:p} logged={}", self, new);
        let logged = self.header.logged;
        self.header.logged = new;
        return logged;
    }

    pub fn set_marked(&mut self, next: bool) -> bool {
        debug!("Set object {:p} marked={}", self, next);
        let marked = self.header.marked;
        self.header.marked = next;
        return marked == next;
    }

    pub fn is_marked(&self, next: bool) -> bool {
        return self.header.marked == next;
    }

    pub fn set_pinned(&mut self, pinned: bool) {
        debug!("Set object {:p} pinned={}", self, pinned);
        self.header.pinned = pinned;
    }

    pub fn is_pinned(&self) -> bool {
        return self.header.pinned;
    }

    pub fn set_forwarded(&mut self, new: GCObjectRef) {
        debug!("Set object {:p} forwarded to {:p}", self, new);
        self.header.forwarded = true;
        self.rtti = new as *const GCRTTI;
    }

    pub fn is_forwarded(&self) -> Option<GCObjectRef> {
        if self.header.forwarded {
            return Some(self.rtti as GCObjectRef);
        }
        return None;
    }

    pub fn spans_lines(&self) -> bool {
        return self.header.spans_lines;
    }

    pub fn object_size(&self) -> usize {
        return unsafe{ (*self.rtti).object_size() };
    }

    pub fn decrement(&mut self) -> bool {
        if self.header.reference_count == 0 {
            return false;
        }
        self.header.reference_count -= 1;
        debug!("Decrement object {:p} to {}", self, self.header.reference_count);
        return self.header.reference_count == 0;
    }

    pub fn increment(&mut self) -> bool {
        self.header.reference_count += 1;
        debug!("Increment object {:p} to {} (new={})", self,
            self.header.reference_count, self.header.new);
        if self.header.new {
            self.header.new = false;
            return true;
        }
        return false;
    }

    pub fn set_member(&mut self, num: usize, member: GCObjectRef) {
        unsafe {
            let base: *mut GCObjectRef = mem::transmute(&self.rtti);
            let address = base.offset((num + 1) as isize);
            *address = member;
        }
    }

    pub fn children(&mut self) -> Vec<GCObjectRef> {
        let base: *const GCObjectRef = unsafe{ mem::transmute(&self.rtti) };
        let members = unsafe{ (*self.rtti).members() };
        debug!("Requested children for object: {:p} (rtti: {:p}, count: {})",
               self, self.rtti, members);
        return (1..(members + 1)).map(|i| unsafe{ *base.offset(i as isize) })
                                 .filter(|o| !o.is_null())
                                 .collect();
    }
}
<commit_msg>Add rustdoc for gc_object.rs<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

extern crate libc;

use std::mem;

use constants::LINE_SIZE;

/// Structs that comprise the structure of an object as needed by the garbage
/// collector.
///
/// Include the `GCObject` struct as the first member in your object struct.
///
/// _Note:_ Only allocate and initialize the `GCRTTI`. `RCImmixCons.allocate()`
/// will return an initialized `GCObject` with a valid `GCHeader`.

/// The `GCHeader` contains field for the garbage collector algorithms.
#[repr(C)]
#[derive(PartialEq)]
#[allow(missing_copy_implementations)]
pub struct GCHeader {
    /// How many objects point to this object.
    reference_count: libc::size_t,

    /// If this object is greater than `LINE_SIZE`.
    spans_lines: bool,

    /// If the object at this address was forwarded somewhere else.
    forwarded: bool,

    /// If this object was pushed on the `modBuffer` in `RCCollector`.
    logged: bool,

    /// If this object was already visited by the tracing collector.
    ///
    /// _Note_: true/false do not mean marked/unmarked. The tracing collector
    /// will flip the meaning of the value for every collection cycle. See
    /// `Spaces.current_live_mark`.
    marked: bool,

    /// If this object must not be evacuated (moved) by the collector.
    pinned: bool,

    /// If this object was never touched by the collectors.
    new: bool,
}

/// The `GCRTTI` contains runtime type information about an object for the
/// garbage collector.
#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct GCRTTI {
    /// The objects size in bytes.
    object_size: libc::size_t,

    /// How many pointers to other objects does this object contain.
    members: libc::size_t,
}

/// The `GCObject` is the base struct for every object managed by the garbage
/// collector.
///
/// Please include this as the first member in your object structs. The
/// members of this object _must_ be a contiguous array of `GCobject` pointers
/// of size `rtti.members`.
#[repr(C)]
#[derive(PartialEq)]
#[allow(raw_pointer_derive)]
pub struct GCObject {
    /// The `GCHeader` for this object. This is initialized by the allocation
    /// routine.
    header: GCHeader,

    /// A pointer to the objects runtime type information struct.
    rtti: *const GCRTTI
}

/// A type alias for the mutable `GCObject` pointer.
pub type GCObjectRef = *mut GCObject;

impl GCRTTI {
    /// Create a new `GCRTTI` for an object with `object_size` bytes and
    /// `members` members.
    pub fn new(object_size: usize, members: usize) -> GCRTTI {
        return GCRTTI {
            object_size: object_size as libc::size_t,
            members: members as libc::size_t,
        };
    }

    /// Return the objects size in bytes.
    pub fn object_size(&self) -> usize {
        return self.object_size as usize;
    }

    /// Return the number of members.
    pub fn members(&self) -> usize {
        return self.members as usize;
    }
}

impl GCObject {
    /// Create a new `GCObject` with `rtti` as the runtime typeinformation
    /// struct pointer and the current `mark` value.
    pub fn new(rtti: *const GCRTTI, mark: bool) -> GCObject {
        debug!("GCobject::new(rtti={:p}, mark={})", rtti, mark);
        let size = unsafe{ (*rtti).object_size() };
        return GCObject {
            header: GCHeader {
                reference_count: 0,
                spans_lines: size > LINE_SIZE,
                forwarded: false,
                logged: false,
                marked: mark,
                pinned: false,
                new: true,
            },
            rtti: rtti,
        }
    }

    /// Set the `logged` state and return the previous value.
    pub fn set_logged(&mut self, new: bool) -> bool {
        debug!("Set object {:p} logged={}", self, new);
        let logged = self.header.logged;
        self.header.logged = new;
        return logged;
    }

    /// Set the `marked` state and return if the state has not
    /// changed.
    pub fn set_marked(&mut self, next: bool) -> bool {
        debug!("Set object {:p} marked={}", self, next);
        let marked = self.header.marked;
        self.header.marked = next;
        return marked == next;
    }

    /// Return if this object is currently marked with `next`.
    pub fn is_marked(&self, next: bool) -> bool {
        return self.header.marked == next;
    }

    /// Set the `pinned` state for this object.
    pub fn set_pinned(&mut self, pinned: bool) {
        debug!("Set object {:p} pinned={}", self, pinned);
        self.header.pinned = pinned;
    }

    /// Return if this object is pinned.
    pub fn is_pinned(&self) -> bool {
        return self.header.pinned;
    }

    /// Set the `forwarded` state and install a forewarding pointer to `new`.
    pub fn set_forwarded(&mut self, new: GCObjectRef) {
        debug!("Set object {:p} forwarded to {:p}", self, new);
        self.header.forwarded = true;
        self.rtti = new as *const GCRTTI;
    }

    /// Return a pointer to the forwarded object if this object was forwarded,
    /// otherwise `None`.
    pub fn is_forwarded(&self) -> Option<GCObjectRef> {
        if self.header.forwarded {
            return Some(self.rtti as GCObjectRef);
        }
        return None;
    }

    /// Returns if this object spans lines (is greater than `LINE_SIZE`
    /// bytes).
    pub fn spans_lines(&self) -> bool {
        return self.header.spans_lines;
    }

    /// Return the objects size in bytes.
    pub fn object_size(&self) -> usize {
        return unsafe{ (*self.rtti).object_size() };
    }

    /// Decrement the referece counter and return true if the reference count
    /// is zero.
    ///
    /// This will not decrement the reference count if it is already zero.
    pub fn decrement(&mut self) -> bool {
        if self.header.reference_count == 0 {
            return false;
        }
        self.header.reference_count -= 1;
        debug!("Decrement object {:p} to {}", self, self.header.reference_count);
        return self.header.reference_count == 0;
    }

    /// Increment the reference count, set the `new` state to `false` and
    /// return the previous `new` state.
    pub fn increment(&mut self) -> bool {
        self.header.reference_count += 1;
        debug!("Increment object {:p} to {} (new={})", self,
            self.header.reference_count, self.header.new);
        if self.header.new {
            self.header.new = false;
            return true;
        }
        return false;
    }

    /// Set the member at position `num` in the member array to `member`.
    pub fn set_member(&mut self, num: usize, member: GCObjectRef) {
        unsafe {
            let base: *mut GCObjectRef = mem::transmute(&self.rtti);
            let address = base.offset((num + 1) as isize);
            *address = member;
        }
    }

    /// Return a vector of all the members of this object that are not null.
    ///
    /// The members of an objects are the `GCRTTI.members` pointers after the
    /// `GCHeader.rtti` pointer in the `GCObject`.
    pub fn children(&mut self) -> Vec<GCObjectRef> {
        let base: *const GCObjectRef = unsafe{ mem::transmute(&self.rtti) };
        let members = unsafe{ (*self.rtti).members() };
        debug!("Requested children for object: {:p} (rtti: {:p}, count: {})",
               self, self.rtti, members);
        return (1..(members + 1)).map(|i| unsafe{ *base.offset(i as isize) })
                                 .filter(|o| !o.is_null())
                                 .collect();
    }
}
<|endoftext|>


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

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_name = "geoip"]
#![crate_type = "rlib"]

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

extern crate libc;
extern crate serialize;

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

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

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

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

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: &GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: &GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIPRecord_;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIPRecord_;
    fn GeoIPRecord_delete(gir: *const GeoIPRecord_);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

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 {
    db: RawGeoIp
}

#[repr(C)]
pub struct GeoIPRecord_ {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
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: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &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 uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

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

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_open(file.to_c_str().unwrap(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, 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 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_record_by_ipnum(self.db, ipnum)
                }
            },
            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_record_by_ipnum_v6(self.db, in6_addr)
                }
            }
        };

        if cres.is_null() { return None; }

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

    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 as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

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) => panic!(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.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = 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>GeoIPRecord_ -> GeoIpRecord<commit_after>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_name = "geoip"]
#![crate_type = "rlib"]

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

extern crate libc;
extern crate serialize;

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

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

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

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

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: &GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: &GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIpRecord;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIpRecord;
    fn GeoIPRecord_delete(gir: *const GeoIpRecord);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

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 {
    db: RawGeoIp
}

#[repr(C)]
pub struct GeoIpRecord {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
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: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &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 uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

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

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_open(file.to_c_str().unwrap(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, 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 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_record_by_ipnum(self.db, ipnum)
                }
            },
            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_record_by_ipnum_v6(self.db, in6_addr)
                }
            }
        };

        if cres.is_null() { return None; }

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

    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 as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

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) => panic!(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.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = 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|>