<commit_before>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std::env;
use std::fs::File;
use std::path::PathBuf;
use std::io::prelude::*;
use chrono::{UTC, DateTime, TimeZone};
use std::time::{UNIX_EPOCH, SystemTime};
use response::{ToOutput, Response};
use request::Request;

/// Convenience method for creating a response from the basic components
/// required (a request body, content type, and response status).
///
/// # Examples
///
/// ```rust
/// use canteen::{Request, Response};
/// use canteen::utils;
///
/// fn handler(_: &Request) -> Response {
///     utils::make_response("Hello, world!", "text/plain", 200)
/// }
/// ```
pub fn make_response<T: ToOutput>(body: T, c_type: &str, status: u16) -> Response {
    let mut res = Response::new();

    res.set_status(status);
    res.set_content_type(c_type);
    res.append(body);

    res
}

/// Converts std::time::SystemTime to chrono::DateTime<UTC>
///
/// Code from: https://users.rust-lang.org/t/convert-std-time-systemtime-to-chrono-datetime-datetime/7684/4
fn _conv_systemtime(t: SystemTime) -> DateTime<UTC> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => {
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());

            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };

    UTC.timestamp(sec, nsec)
}

/// Replace the URI escape codes with their ASCII equivalents.
pub fn replace_escape(path: &str) -> String {
    let mut fixed = String::from(path);
    let replaces: [(&str, &str); 175] = [
        ("%20", " "),
        ("%21", "!"),
        ("%22", "\""),
        ("%23", "#"),
        ("%24", "$"),
        ("%25", "%"),
        ("%26", "&"),
        ("%27", "'"),
        ("%28", "("),
        ("%29", ")"),
        ("%2A", "*"),
        ("%2B", "+"),
        ("%2C", ","),
        ("%2D", "-"),
        ("%2E", "."),
        ("%2F", "/"),
        ("%30", "0"),
        ("%31", "1"),
        ("%32", "2"),
        ("%33", "3"),
        ("%34", "4"),
        ("%35", "5"),
        ("%36", "6"),
        ("%37", "7"),
        ("%38", "8"),
        ("%39", "9"),
        ("%3A", ":"),
        ("%3B", ";"),
        ("%3C", "<"),
        ("%3D", "="),
        ("%3E", ">"),
        ("%3F", "?"),
        ("%40", "@"),
        ("%41", "A"),
        ("%42", "B"),
        ("%43", "C"),
        ("%44", "D"),
        ("%45", "E"),
        ("%46", "F"),
        ("%47", "G"),
        ("%48", "H"),
        ("%49", "I"),
        ("%4A", "J"),
        ("%4B", "K"),
        ("%4C", "L"),
        ("%4D", "M"),
        ("%4E", "N"),
        ("%4F", "O"),
        ("%50", "P"),
        ("%51", "Q"),
        ("%52", "R"),
        ("%53", "S"),
        ("%54", "T"),
        ("%55", "U"),
        ("%56", "V"),
        ("%57", "W"),
        ("%58", "X"),
        ("%59", "Y"),
        ("%5A", "Z"),
        ("%5B", "["),
        ("%5C", "\\"),
        ("%5D", "]"),
        ("%5E", "^"),
        ("%5F", "_"),
        ("%60", "`"),
        ("%61", "a"),
        ("%62", "b"),
        ("%63", "c"),
        ("%64", "d"),
        ("%65", "e"),
        ("%66", "f"),
        ("%67", "g"),
        ("%68", "h"),
        ("%69", "i"),
        ("%6A", "j"),
        ("%6B", "k"),
        ("%6C", "l"),
        ("%6D", "m"),
        ("%6E", "n"),
        ("%6F", "o"),
        ("%70", "p"),
        ("%71", "q"),
        ("%72", "r"),
        ("%73", "s"),
        ("%74", "t"),
        ("%75", "u"),
        ("%76", "v"),
        ("%77", "w"),
        ("%78", "x"),
        ("%79", "y"),
        ("%7A", "z"),
        ("%7B", "{"),
        ("%7C", "|"),
        ("%7D", "}"),
        ("%7E", "~"),
        ("%A2", "¢"),
        ("%A3", "£"),
        ("%A5", "¥"),
        ("%A6", "|"),
        ("%A7", "§"),
        ("%AB", "«"),
        ("%AC", "¬"),
        ("%AD", "¯"),
        ("%B0", "º"),
        ("%B1", "±"),
        ("%B2", "ª"),
        ("%B4", ","),
        ("%B5", "µ"),
        ("%BB", "»"),
        ("%BC", "¼"),
        ("%BD", "½"),
        ("%BF", "¿"),
        ("%C0", "À"),
        ("%C1", "Á"),
        ("%C2", "Â"),
        ("%C3", "Ã"),
        ("%C4", "Ä"),
        ("%C5", "Å"),
        ("%C6", "Æ"),
        ("%C7", "Ç"),
        ("%C8", "È"),
        ("%C9", "É"),
        ("%CA", "Ê"),
        ("%CB", "Ë"),
        ("%CC", "Ì"),
        ("%CD", "Í"),
        ("%CE", "Î"),
        ("%CF", "Ï"),
        ("%D0", "Ð"),
        ("%D1", "Ñ"),
        ("%D2", "Ò"),
        ("%D3", "Ó"),
        ("%D4", "Ô"),
        ("%D5", "Õ"),
        ("%D6", "Ö"),
        ("%D8", "Ø"),
        ("%D9", "Ù"),
        ("%DA", "Ú"),
        ("%DB", "Û"),
        ("%DC", "Ü"),
        ("%DD", "Ý"),
        ("%DE", "Þ"),
        ("%DF", "ß"),
        ("%E0", "à"),
        ("%E1", "á"),
        ("%E2", "â"),
        ("%E3", "ã"),
        ("%E4", "ä"),
        ("%E5", "å"),
        ("%E6", "æ"),
        ("%E7", "ç"),
        ("%E8", "è"),
        ("%E9", "é"),
        ("%EA", "ê"),
        ("%EB", "ë"),
        ("%EC", "ì"),
        ("%ED", "í"),
        ("%EE", "î"),
        ("%EF", "ï"),
        ("%F0", "ð"),
        ("%F1", "ñ"),
        ("%F2", "ò"),
        ("%F3", "ó"),
        ("%F4", "ô"),
        ("%F5", "õ"),
        ("%F6", "ö"),
        ("%F7", "÷"),
        ("%F8", "ø"),
        ("%F9", "ù"),
        ("%FA", "ú"),
        ("%FB", "û"),
        ("%FC", "ü"),
        ("%FD", "ý"),
        ("%FE", "þ"),
        ("%FF", "ÿ"),
    ];

    if !fixed.contains("%") {
        return fixed;
    }

    for &(from, to) in replaces.iter() {
        fixed = fixed.replace(from, to);
    }

    fixed
}

fn err_body(message: &str, path: &str) -> String {
    format!("\
             \

Your request failed

{}: {}
", message, path)
}

/// Default handler function for HTTP 403 errors.
pub fn err_403(req: &Request) -> Response {
    make_response(err_body("forbidden", &req.path), "text/html", 403)
}

/// Default handler function for HTTP 403 errors for XHR.
pub fn err_403_json(message: &str) -> Response {
    make_response(format!("{{ message: 'forbidden: {}' }}", message), "application/json", 403)
}

/// Default handler function for HTTP 404 errors.
pub fn err_404(req: &Request) -> Response {
    make_response(err_body("not found", &req.path), "text/html", 404)
}

/// Default handler function for HTTP 500 errors for XHR.
pub fn err_404_json(message: &str) -> Response {
    make_response(format!("{{ message: 'not found: {}' }}", message), "application/json", 404)
}

/// Default handler function for HTTP 500 errors.
pub fn err_500(req: &Request) -> Response {
    make_response(err_body("internal server error", &req.path), "text/html", 500)
}

/// Default handler function for HTTP 500 errors for XHR.
pub fn err_500_json(message: &str) -> Response {
    make_response(format!("{{ message: 'internal server error: {}' }}", message), "application/json", 500)
}

/// Handler that sends static files relative to the current working directory.
pub fn static_file(req: &Request) -> Response {
    let mut res = Response::new();

    let cwd = env::current_dir().unwrap();
    let clean = replace_escape(&req.path);
    let mut fpath = PathBuf::from(&cwd);
    let mut fbuf: Vec<u8> = Vec::new();

    for chunk in clean.split('/') {
        if chunk == "" || chunk == "." || chunk == ".." {
            /* bzzzzt */
            continue;
        }

        fpath.push(&chunk);
    }

    let file = File::open(&fpath);

    match file {
        Ok(mut f)   => {
            let last = match f.metadata() {
                Err(_)  => UTC::now(),
                Ok(md)  => {
                    match md.modified() {
                        Err(_)  => UTC::now(), // should never happen...
                        Ok(st)  => _conv_systemtime(st),
                    }
                }
            };

            match req.get_header("If-Modified-Since") {
                Some(hdr)   => {
                    let ims = UTC.datetime_from_str(&hdr, "%a, %d %b %Y, %H:%M:%S UTC");

                    match ims {
                        Err(_)      => {}, // flow through
                        Ok(dt_utc)  => {
                            if dt_utc >= last {
                                // it hasn't been modified, return a 304
                                res.set_status(304);
                                return res;
                            }
                        },
                    }
                },
                None        => {}, // flow through
            }

            match f.read_to_end(&mut fbuf) {
                Ok(_)   => {

                    res.add_header("Last-Modified", &last.format("%a, %d %b %Y, %H:%M:%S %Z").to_string());
                    res.set_status(200);
                    res.set_content_type("text/plain");
                    res.append(fbuf);
                },
                Err(e)  => {
                    return make_response(err_body("internal server error", e.description()), "text/html", 500);
                },
            }
        },
        Err(_)      => {
            return err_404(&req);
        }
    }

    res
}

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

    #[test]
    fn test_replace_escape() {
        let path = "%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F%70%71%72%73%74%75%76%77%78%79%7A";
        assert_eq!("abcdefghijklmnopqrstuvwxyz", replace_escape(&path));
    }
}
<commit_msg>replace a manual make_response with the err_500 utility function<commit_after>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std::env;
use std::fs::File;
use std::path::PathBuf;
use std::io::prelude::*;
use chrono::{UTC, DateTime, TimeZone};
use std::time::{UNIX_EPOCH, SystemTime};
use response::{ToOutput, Response};
use request::Request;

/// Convenience method for creating a response from the basic components
/// required (a request body, content type, and response status).
///
/// # Examples
///
/// ```rust
/// use canteen::{Request, Response};
/// use canteen::utils;
///
/// fn handler(_: &Request) -> Response {
///     utils::make_response("Hello, world!", "text/plain", 200)
/// }
/// ```
pub fn make_response<T: ToOutput>(body: T, c_type: &str, status: u16) -> Response {
    let mut res = Response::new();

    res.set_status(status);
    res.set_content_type(c_type);
    res.append(body);

    res
}

/// Converts std::time::SystemTime to chrono::DateTime<UTC>
///
/// Code from: https://users.rust-lang.org/t/convert-std-time-systemtime-to-chrono-datetime-datetime/7684/4
fn _conv_systemtime(t: SystemTime) -> DateTime<UTC> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => {
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());

            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };

    UTC.timestamp(sec, nsec)
}

/// Replace the URI escape codes with their ASCII equivalents.
pub fn replace_escape(path: &str) -> String {
    let mut fixed = String::from(path);
    let replaces: [(&str, &str); 175] = [
        ("%20", " "),
        ("%21", "!"),
        ("%22", "\""),
        ("%23", "#"),
        ("%24", "$"),
        ("%25", "%"),
        ("%26", "&"),
        ("%27", "'"),
        ("%28", "("),
        ("%29", ")"),
        ("%2A", "*"),
        ("%2B", "+"),
        ("%2C", ","),
        ("%2D", "-"),
        ("%2E", "."),
        ("%2F", "/"),
        ("%30", "0"),
        ("%31", "1"),
        ("%32", "2"),
        ("%33", "3"),
        ("%34", "4"),
        ("%35", "5"),
        ("%36", "6"),
        ("%37", "7"),
        ("%38", "8"),
        ("%39", "9"),
        ("%3A", ":"),
        ("%3B", ";"),
        ("%3C", "<"),
        ("%3D", "="),
        ("%3E", ">"),
        ("%3F", "?"),
        ("%40", "@"),
        ("%41", "A"),
        ("%42", "B"),
        ("%43", "C"),
        ("%44", "D"),
        ("%45", "E"),
        ("%46", "F"),
        ("%47", "G"),
        ("%48", "H"),
        ("%49", "I"),
        ("%4A", "J"),
        ("%4B", "K"),
        ("%4C", "L"),
        ("%4D", "M"),
        ("%4E", "N"),
        ("%4F", "O"),
        ("%50", "P"),
        ("%51", "Q"),
        ("%52", "R"),
        ("%53", "S"),
        ("%54", "T"),
        ("%55", "U"),
        ("%56", "V"),
        ("%57", "W"),
        ("%58", "X"),
        ("%59", "Y"),
        ("%5A", "Z"),
        ("%5B", "["),
        ("%5C", "\\"),
        ("%5D", "]"),
        ("%5E", "^"),
        ("%5F", "_"),
        ("%60", "`"),
        ("%61", "a"),
        ("%62", "b"),
        ("%63", "c"),
        ("%64", "d"),
        ("%65", "e"),
        ("%66", "f"),
        ("%67", "g"),
        ("%68", "h"),
        ("%69", "i"),
        ("%6A", "j"),
        ("%6B", "k"),
        ("%6C", "l"),
        ("%6D", "m"),
        ("%6E", "n"),
        ("%6F", "o"),
        ("%70", "p"),
        ("%71", "q"),
        ("%72", "r"),
        ("%73", "s"),
        ("%74", "t"),
        ("%75", "u"),
        ("%76", "v"),
        ("%77", "w"),
        ("%78", "x"),
        ("%79", "y"),
        ("%7A", "z"),
        ("%7B", "{"),
        ("%7C", "|"),
        ("%7D", "}"),
        ("%7E", "~"),
        ("%A2", "¢"),
        ("%A3", "£"),
        ("%A5", "¥"),
        ("%A6", "|"),
        ("%A7", "§"),
        ("%AB", "«"),
        ("%AC", "¬"),
        ("%AD", "¯"),
        ("%B0", "º"),
        ("%B1", "±"),
        ("%B2", "ª"),
        ("%B4", ","),
        ("%B5", "µ"),
        ("%BB", "»"),
        ("%BC", "¼"),
        ("%BD", "½"),
        ("%BF", "¿"),
        ("%C0", "À"),
        ("%C1", "Á"),
        ("%C2", "Â"),
        ("%C3", "Ã"),
        ("%C4", "Ä"),
        ("%C5", "Å"),
        ("%C6", "Æ"),
        ("%C7", "Ç"),
        ("%C8", "È"),
        ("%C9", "É"),
        ("%CA", "Ê"),
        ("%CB", "Ë"),
        ("%CC", "Ì"),
        ("%CD", "Í"),
        ("%CE", "Î"),
        ("%CF", "Ï"),
        ("%D0", "Ð"),
        ("%D1", "Ñ"),
        ("%D2", "Ò"),
        ("%D3", "Ó"),
        ("%D4", "Ô"),
        ("%D5", "Õ"),
        ("%D6", "Ö"),
        ("%D8", "Ø"),
        ("%D9", "Ù"),
        ("%DA", "Ú"),
        ("%DB", "Û"),
        ("%DC", "Ü"),
        ("%DD", "Ý"),
        ("%DE", "Þ"),
        ("%DF", "ß"),
        ("%E0", "à"),
        ("%E1", "á"),
        ("%E2", "â"),
        ("%E3", "ã"),
        ("%E4", "ä"),
        ("%E5", "å"),
        ("%E6", "æ"),
        ("%E7", "ç"),
        ("%E8", "è"),
        ("%E9", "é"),
        ("%EA", "ê"),
        ("%EB", "ë"),
        ("%EC", "ì"),
        ("%ED", "í"),
        ("%EE", "î"),
        ("%EF", "ï"),
        ("%F0", "ð"),
        ("%F1", "ñ"),
        ("%F2", "ò"),
        ("%F3", "ó"),
        ("%F4", "ô"),
        ("%F5", "õ"),
        ("%F6", "ö"),
        ("%F7", "÷"),
        ("%F8", "ø"),
        ("%F9", "ù"),
        ("%FA", "ú"),
        ("%FB", "û"),
        ("%FC", "ü"),
        ("%FD", "ý"),
        ("%FE", "þ"),
        ("%FF", "ÿ"),
    ];

    if !fixed.contains("%") {
        return fixed;
    }

    for &(from, to) in replaces.iter() {
        fixed = fixed.replace(from, to);
    }

    fixed
}

fn err_body(message: &str, path: &str) -> String {
    format!("\
             \

Your request failed

{}: {}
", message, path)
}

/// Default handler function for HTTP 403 errors.
pub fn err_403(req: &Request) -> Response {
    make_response(err_body("forbidden", &req.path), "text/html", 403)
}

/// Default handler function for HTTP 403 errors for XHR.
pub fn err_403_json(message: &str) -> Response {
    make_response(format!("{{ message: 'forbidden: {}' }}", message), "application/json", 403)
}

/// Default handler function for HTTP 404 errors.
pub fn err_404(req: &Request) -> Response {
    make_response(err_body("not found", &req.path), "text/html", 404)
}

/// Default handler function for HTTP 500 errors for XHR.
pub fn err_404_json(message: &str) -> Response {
    make_response(format!("{{ message: 'not found: {}' }}", message), "application/json", 404)
}

/// Default handler function for HTTP 500 errors.
pub fn err_500(req: &Request) -> Response {
    make_response(err_body("internal server error", &req.path), "text/html", 500)
}

/// Default handler function for HTTP 500 errors for XHR.
pub fn err_500_json(message: &str) -> Response {
    make_response(format!("{{ message: 'internal server error: {}' }}", message), "application/json", 500)
}

/// Handler that sends static files relative to the current working directory.
pub fn static_file(req: &Request) -> Response {
    let mut res = Response::new();

    let cwd = env::current_dir().unwrap();
    let clean = replace_escape(&req.path);
    let mut fpath = PathBuf::from(&cwd);
    let mut fbuf: Vec<u8> = Vec::new();

    for chunk in clean.split('/') {
        if chunk == "" || chunk == "." || chunk == ".." {
            /* bzzzzt */
            continue;
        }

        fpath.push(&chunk);
    }

    let file = File::open(&fpath);

    match file {
        Ok(mut f)   => {
            let last = match f.metadata() {
                Err(_)  => UTC::now(),
                Ok(md)  => {
                    match md.modified() {
                        Err(_)  => UTC::now(), // should never happen...
                        Ok(st)  => _conv_systemtime(st),
                    }
                }
            };

            match req.get_header("If-Modified-Since") {
                Some(hdr)   => {
                    let ims = UTC.datetime_from_str(&hdr, "%a, %d %b %Y, %H:%M:%S UTC");

                    match ims {
                        Err(_)      => {}, // flow through
                        Ok(dt_utc)  => {
                            if dt_utc >= last {
                                // it hasn't been modified, return a 304
                                res.set_status(304);
                                return res;
                            }
                        },
                    }
                },
                None        => {}, // flow through
            }

            match f.read_to_end(&mut fbuf) {
                Ok(_)   => {

                    res.add_header("Last-Modified", &last.format("%a, %d %b %Y, %H:%M:%S %Z").to_string());
                    res.set_status(200);
                    res.set_content_type("text/plain");
                    res.append(fbuf);
                },
                Err(_)  => {
                    return err_500(&req);
                },
            }
        },
        Err(_)      => {
            return err_404(&req);
        }
    }

    res
}

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

    #[test]
    fn test_replace_escape() {
        let path = "%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F%70%71%72%73%74%75%76%77%78%79%7A";
        assert_eq!("abcdefghijklmnopqrstuvwxyz", replace_escape(&path));
    }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016
 * Jeff Nettleton
 *
 * Licensed under the MIT license (http://opensource.org/licenses/MIT). This
 * file may not be copied, modified, or distributed except according to those
 * terms
 */

use response::{ToOutput, Response};

/* create a response from the basic components */
pub fn make_response<T: ToOutput>(body: T, c_type: &str, code: u16) -> Response {
    let mut res = Response::new();

    res.set_code(code);
    res.set_content_type(c_type);
    res.append(body);

    res
}
<commit_msg>update canteen::utils with the utility functions from canteen::route, add docs<commit_after>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std::env;
use std::fs::File;
use std::error::Error;
use std::path::PathBuf;
use std::io::prelude::*;
use response::{ToOutput, Response};
use request::Request;

/// Convenience method for creating a response from the basic components
/// required (a request body, content type, and response code).
///
/// # Examples
///
/// ```rust,ignore
/// fn handler(_: &Request) -> Response {
///     make_response("Hello, world!", "text/plain", 200)
/// }
/// ```
pub fn make_response<T: ToOutput>(body: T, c_type: &str, code: u16) -> Response {
    let mut res = Response::new();

    res.set_code(code);
    res.set_content_type(c_type);
    res.append(body);

    res
}

/// Replace the URI escape codes with their ASCII equivalents.
pub fn replace_escape(path: &str) -> String {
    let mut fixed = String::from(path);
    let replaces: [(&str, &str); 22] = [
            ("%20", " "),
            ("%3C", "<"),
            ("%3E", ">"),
            ("%23", "#"),
            ("%25", "%"),
            ("%7B", "["),
            ("%7D", ")"),
            ("%7C", "|"),
            ("%5C", "\\"),
            ("%5E", "^"),
            ("%7E", "~"),
            ("%5B", "["),
            ("%5D", ")"),
            ("%60", "`"),
            ("%3B", ";"),
            ("%2F", "/"),
            ("%3F", "?"),
            ("%3A", ":"),
            ("%40", "@"),
            ("%3D", "="),
            ("%26", "&"),
            ("%24", "$"),
    ];

    for &(from, to) in replaces.iter() {
        fixed = fixed.replace(from, to);
    }

    fixed
}

fn err_body(message: &str, path: &str) -> String {
    format!("\
             \

Your request failed

{}: {}
", message, path)
}

/// Default handler function for HTTP 403 errors.
pub fn err_403(req: &Request) -> Response {
    make_response(err_body("forbidden", &req.path), "text/html", 403)
}

/// Default handler function for HTTP 404 errors.
pub fn err_404(req: &Request) -> Response {
    make_response(err_body("not found", &req.path), "text/html", 404)
}

/// Default handler function for HTTP 500 errors.
pub fn err_500(req: &Request) -> Response {
    make_response(err_body("internal server error", &req.path), "text/html", 500)
}

/// Handler that sends static files relative to the current working directory.
pub fn static_file(req: &Request) -> Response {
    let mut res = Response::new();

    let cwd = env::current_dir().unwrap();
    let clean = replace_escape(&req.path);
    let mut fpath = PathBuf::from(&cwd);
    let mut fbuf: Vec<u8> = Vec::new();

    for chunk in clean.split('/') {
        if chunk == "" || chunk == "." || chunk == ".." {
            /* bzzzzt */
            continue;
        }

        fpath.push(&chunk);
    }

    let file = File::open(&fpath);

    match file {
        Ok(mut f)   => {
            match f.read_to_end(&mut fbuf) {
                Ok(_)   => {
                    res.set_code(200);
                    res.set_content_type("text/plain");
                    res.append(fbuf);
                },
                Err(e)  => {
                    return make_response(err_body("internal server error", e.description()), "text/html", 500);
                },
            }
        },
        Err(_)      => {
            return err_404(&req);
        }
    }

    res
}
<|endoftext|>


<commit_before>use libc::c_ulonglong;

#[doc(hidden)]
pub fn marshal<T, F>(buf: &[u8],
                     padbefore: usize,
                     bytestodrop: usize,
                     f: F
                     ) -> (Vec<u8>, T)
    where F: Fn(*mut u8, *const u8, c_ulonglong) -> T {
    let mut dst = Vec::with_capacity(buf.len() + padbefore);
    for _ in (0..padbefore) {
        dst.push(0);
    }
    dst.push_all(buf);
    let pdst = dst.as_mut_ptr();
    let psrc = dst.as_ptr();
    let res = f(pdst, psrc, dst.len() as c_ulonglong);
    (dst.into_iter().skip(bytestodrop).collect(), res)
}

macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                use libc::size_t;
                use ffi;
                let &mut $newtype(ref mut v) = self;
                unsafe {
                    ffi::sodium_memzero(v.as_mut_ptr(), v.len() as size_t);
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /**
         * `from_slice()` creates an object from a byte slice
         *
         * This function will fail and return None if the length of
         * the byte-slice isn't equal to the length of the object
         */
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[a..b] == y[a..b]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<Range<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: Range<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[..b] == y[..b]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeTo<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeTo<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[a..] == y[a..]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeFrom<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeFrom<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[] == y[]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeFull> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeFull) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    ));
<commit_msg>Replace `push_all()` with `extend()`, #51<commit_after>use libc::c_ulonglong;

#[doc(hidden)]
pub fn marshal<T, F>(buf: &[u8],
                     padbefore: usize,
                     bytestodrop: usize,
                     f: F
                     ) -> (Vec<u8>, T)
    where F: Fn(*mut u8, *const u8, c_ulonglong) -> T {
    let mut dst = Vec::with_capacity(buf.len() + padbefore);
    for _ in (0..padbefore) {
        dst.push(0);
    }
    dst.extend(buf.iter().cloned());
    let pdst = dst.as_mut_ptr();
    let psrc = dst.as_ptr();
    let res = f(pdst, psrc, dst.len() as c_ulonglong);
    (dst.into_iter().skip(bytestodrop).collect(), res)
}

macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                use libc::size_t;
                use ffi;
                let &mut $newtype(ref mut v) = self;
                unsafe {
                    ffi::sodium_memzero(v.as_mut_ptr(), v.len() as size_t);
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /**
         * `from_slice()` creates an object from a byte slice
         *
         * This function will fail and return None if the length of
         * the byte-slice isn't equal to the length of the object
         */
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[a..b] == y[a..b]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<Range<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: Range<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[..b] == y[..b]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeTo<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeTo<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[a..] == y[a..]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeFrom<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeFrom<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /**
     * Allows a user to access the byte contents of an object as a slice.
     *
     * WARNING: it might be tempting to do comparisons on objects
     * by using `x[] == y[]`. This will open up for timing attacks
     * when comparing for example authenticator tags. Because of this only
     * use the comparison functions exposed by the sodiumoxide API.
     */
    impl Index<RangeFull> for $newtype {
        type Output = [u8];
        fn index(&self, _index: RangeFull) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    ));
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

// Macros to help with json serializing of undeclared structs.
// json_value!({ }) retunns a serde_json::value::Value from an anonymous struct.
// json!({ }) returns a string from an anonymous struct.
// Combining both macros let you build arbitrary complex json objects easily.
//
// Examples: json!({ type: "light", value: 1.2, on: true });
//           json!({ type: "notification", value: json_value!({ r: 54, g: 12, b: 88 }) });

#[macro_export]
macro_rules! json {
    ({ $($i:ident: $v:expr),* }) => {
        {
            use std::collections::BTreeMap;
            let mut map: BTreeMap<String, serde_json::Value> = BTreeMap::new();
            $(
                map.insert(String::from(stringify!($i)), serde_json::to_value(&$v));
            )*
            serde_json::to_string(&map).unwrap_or("{}".to_owned())
        }
    };

    ([ $($v:expr),* ]) => {
        {
            let mut vec: Vec<serde_json::Value> = Vec::new();
            $(
                vec.push(serde_json::to_value(&$v));
            )*
            serde_json::to_string(&vec).unwrap_or("[]".to_owned())
        }
    }
}

#[macro_export]
macro_rules! json_value {
    ({ $($i:ident: $v:expr),* }) => {
        {
            let mut map: BTreeMap<String, serde_json::Value> = BTreeMap::new();
            $(
                map.insert(String::from(stringify!($i)), serde_json::to_value(&$v));
            )*
            serde_json::to_value(&map)
        }
    };

    ([ $($v:expr),* ]) => {
        {
            let mut vec: Vec<serde_json::Value> = Vec::new();
            $(
                vec.push(serde_json::to_value(&$v));
            )*
            serde_json::to_value(&vec)
        }
    }
}

#[test]
fn test_json_macro() {
    // Object tests.
    assert_eq!(json!({ foo: "bar" }), "{\"foo\":\"bar\"}");
    assert_eq!(json!({ val: 1.02 }), "{\"val\":1.02}");
    assert_eq!(json!({ type: "light", value: 1.2, on: true }), "{\"on\":true,\"type\":\"light\",\"value\":1.2}");
    assert_eq!(json!({ array: vec![1, 2, 3]}), "{\"array\":[1,2,3]}");
    assert_eq!(json!({ type: "complex", sub: json_value!({ a: 1, b: "foo" }) }), "{\"sub\":{\"a\":1,\"b\":\"foo\"},\"type\":\"complex\"}");

    // Array tests.
    assert_eq!(json!([1, 100, 1000]), "[1,100,1000]");
    assert_eq!(json!(["one", "two", json_value!(["three", "four"]), 5]), "[\"one\",\"two\",[\"three\",\"four\"],5]");
    assert_eq!(json!(["one", 2, 3.33, false]), "[\"one\",2,3.33,false]");
}
<commit_msg>utils: Break down into many small unit tests r=fabricedesre<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

// Macros to help with json serializing of undeclared structs.
// json_value!({ }) returns a serde_json::value::Value from an anonymous struct.
// json!({ }) returns a string from an anonymous struct.
// Combining both macros let you build arbitrary complex json objects easily.
//
// Examples: json!({ type: "light", value: 1.2, on: true });
//           json!({ type: "notification", value: json_value!({ r: 54, g: 12, b: 88 }) });

#[macro_export]
macro_rules! json {
    ({ $($i:ident: $v:expr),* }) => {
        {
            use std::collections::BTreeMap;
            let mut map: BTreeMap<String, serde_json::Value> = BTreeMap::new();
            $(
                map.insert(String::from(stringify!($i)), serde_json::to_value(&$v));
            )*
            serde_json::to_string(&map).unwrap_or("{}".to_owned())
        }
    };

    ([ $($v:expr),* ]) => {
        {
            let mut vec: Vec<serde_json::Value> = Vec::new();
            $(
                vec.push(serde_json::to_value(&$v));
            )*
            serde_json::to_string(&vec).unwrap_or("[]".to_owned())
        }
    }
}

#[macro_export]
macro_rules! json_value {
    ({ $($i:ident: $v:expr),* }) => {
        {
            use std::collections::BTreeMap;
            let mut map: BTreeMap<String, serde_json::Value> = BTreeMap::new();
            $(
                map.insert(String::from(stringify!($i)), serde_json::to_value(&$v));
            )*
            serde_json::to_value(&map)
        }
    };

    ([ $($v:expr),* ]) => {
        {
            let mut vec: Vec<serde_json::Value> = Vec::new();
            $(
                vec.push(serde_json::to_value(&$v));
            )*
            serde_json::to_value(&vec)
        }
    }
}

#[cfg(test)]
#[macro_use]
describe! json {
    before_each {
        extern crate serde_json;
    }

    describe! objects {
        it "should support string attributes" {
            assert_eq!(json!({ foo: "bar" }), r#"{"foo":"bar"}"#);
        }

        it "should support numbers" {
            assert_eq!(json!({ val: 1.02 }), r#"{"val":1.02}"#);
        }

        it "should support mixed types" {
            assert_eq!(json!({ type: "light", value: 1.2, on: true }),
                        r#"{"on":true,"type":"light","value":1.2}"#);
        }

        it "should support arrays" {
            assert_eq!(json!({ array: vec![1, 2, 3]}), r#"{"array":[1,2,3]}"#);
        }

        it "should support sub-objects" {
            assert_eq!(json!({ type: "complex", sub: json_value!({ a: 1, b: "foo" }) }),
                        r#"{"sub":{"a":1,"b":"foo"},"type":"complex"}"#);
        }

        it "should support sub-sub-objects" {
            assert_eq!(json!({ one: json_value!({ two: json_value!({ three: 3 }) }) }),
                        r#"{"one":{"two":{"three":3}}}"#);
        }
    }

    describe! arrays {
        it "should support numbers" {
            assert_eq!(json!([1, 100, 1000]), r#"[1,100,1000]"#);
        }

        it "should support sub-arrays" {
            assert_eq!(json!(["one", "two", json_value!(["three", "four"]), 5]),
                        r#"["one","two",["three","four"],5]"#);
        }

        it "should support sub-sub-arrays" {
            assert_eq!(json!(["one", json_value!(["two", json_value!(["three"]) ]) ]),
                        r#"["one",["two",["three"]]]"#);
        }

        it "should support mixed types" {
            assert_eq!(json!(["one", 2, 3.33, false]), r#"["one",2,3.33,false]"#);
        }

        it "should support objects" {
            assert_eq!(json!([ json_value!({foo: "bar"}) ]), r#"[{"foo":"bar"}]"#);
        }
    }
}
<|endoftext|>


<commit_before>//! The `XPath` functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::readonly::RoNode;
use crate::tree::{Document, DocumentRef, DocumentWeak, Node};
use libc;
use libc::{c_char, c_void, size_t};
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::rc::Rc;
use std::str;

///Thinly wrapped libxml2 xpath context
pub(crate) type ContextRef = Rc<RefCell<_Context>>;

#[derive(Debug)]
pub(crate) struct _Context(pub(crate) xmlXPathContextPtr);

impl Drop for _Context {
  ///free xpath context when it goes out of scope
  fn drop(&mut self) {
    unsafe {
      xmlXPathFreeContext(self.0);
    }
  }
}

/// An XPath context
#[derive(Clone)]
pub struct Context {
  /// Safe reference to the libxml2 context pointer
  pub(crate) context_ptr: ContextRef,
  ///Document contains pointer, needed for ContextPtr, so we need to borrow Document to prevent it's freeing
  pub(crate) document: DocumentWeak,
}

///Essentially, the result of the evaluation of some xpath expression
pub struct Object {
  ///libxml's `ObjectPtr`
  pub ptr: xmlXPathObjectPtr,
  document: DocumentWeak,
}

impl Context {
  ///create the xpath context for a document
  pub fn new(doc: &Document) -> Result<Context, ()> {
    let ctxtptr = unsafe { xmlXPathNewContext(doc.doc_ptr()) };
    if ctxtptr.is_null() {
      Err(())
    } else {
      Ok(Context {
        context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
        document: Rc::downgrade(&doc.0),
      })
    }
  }
  pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
    let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
    if ctxtptr.is_null() {
      Err(())
    } else {
      Ok(Context {
        context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
        document: Rc::downgrade(&docref),
      })
    }
  }

  /// Returns the raw libxml2 context pointer behind the struct
  pub fn as_ptr(&self) -> xmlXPathContextPtr {
    self.context_ptr.borrow().0
  }

  /// Instantiate a new Context for the Document of a given Node.
  /// Note: the Context is root-level for that document, use `.set_context_node` to limit scope to this node
  pub fn from_node(node: &Node) -> Result<Context, ()> {
    let docref = node.get_docref().upgrade().unwrap();
    Context::new_ptr(&docref)
  }

  /// Register a namespace prefix-href pair on the xpath context
  pub fn register_namespace(&self, prefix: &str, href: &str) -> Result<(), ()> {
    let c_prefix = CString::new(prefix).unwrap();
    let c_href = CString::new(href).unwrap();
    unsafe {
      let result = xmlXPathRegisterNs(
        self.as_ptr(),
        c_prefix.as_bytes().as_ptr(),
        c_href.as_bytes().as_ptr(),
      );
      if result != 0 {
        Err(())
      } else {
        Ok(())
      }
    }
  }

  ///evaluate an xpath
  pub fn evaluate(&self, xpath: &str) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr = unsafe { xmlXPathEvalExpression(c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  ///evaluate an xpath on a context Node
  pub fn node_evaluate(&self, xpath: &str, node: &Node) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr =
      unsafe { xmlXPathNodeEval(node.node_ptr(), c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  ///evaluate an xpath on a context RoNode
  pub fn node_evaluate_readonly(&self, xpath: &str, node: RoNode) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr = unsafe { xmlXPathNodeEval(node.0, c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  /// localize xpath context to a specific Node
  pub fn set_context_node(&mut self, node: &Node) -> Result<(), ()> {
    unsafe {
      let result = xmlXPathSetContextNode(node.node_ptr(), self.as_ptr());
      if result != 0 {
        return Err(());
      }
    }
    Ok(())
  }

  /// find nodes via xpath, at a specified node or the document root
  pub fn findnodes(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<Vec<Node>, ()> {
    let evaluated;
    if let Some(node) = node_opt {
      evaluated = self.node_evaluate(xpath, node)?;
    } else {
      evaluated = self.evaluate(xpath)?;
    }
    Ok(evaluated.get_nodes_as_vec())
  }

  /// find a literal value via xpath, at a specified node or the document root
  pub fn findvalue(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<String, ()> {
    let evaluated;
    if let Some(node) = node_opt {
      evaluated = self.node_evaluate(xpath, node)?;
    } else {
      evaluated = self.evaluate(xpath)?;
    }
    Ok(evaluated.to_string())
  }
}

impl Drop for Object {
  /// free the memory allocated
  fn drop(&mut self) {
    unsafe {
      xmlXPathFreeObject(self.ptr);
    }
  }
}

impl Object {
  ///get the number of nodes in the result set
  pub fn get_number_of_nodes(&self) -> usize {
    let v = xmlXPathObjectNumberOfNodes(self.ptr);
    if v == -1 {
      panic!("rust-libxml: xpath: Passed in null pointer!");
    }
    if v == -2 {
      // No nodes found!
      return 0;
    }
    if v < -2 {
      panic!("rust-libxml: xpath: expected non-negative number of result nodes");
    }
    v as usize
  }

  /// returns the result set as a vector of node references
  pub fn get_nodes_as_vec(&self) -> Vec<Node> {
    let n = self.get_number_of_nodes();
    let mut vec: Vec<Node> = Vec::with_capacity(n);
    let slice = if n > 0 {
      xmlXPathObjectGetNodes(self.ptr, n as size_t)
    } else {
      Vec::new()
    };
    for ptr in slice {
      if ptr.is_null() {
        panic!("rust-libxml: xpath: found null pointer result set");
      }
      let node = Node::wrap(ptr, &self.document.upgrade().unwrap());
      vec.push(node);
    }
    vec
  }

  /// returns the result set as a vector of node references
  pub fn get_readonly_nodes_as_vec(&self) -> Vec<RoNode> {
    let n = self.get_number_of_nodes();
    let mut vec: Vec<RoNode> = Vec::with_capacity(n);
    let slice = if n > 0 {
      xmlXPathObjectGetNodes(self.ptr, n as size_t)
    } else {
      Vec::new()
    };
    for ptr in slice {
      if ptr.is_null() {
        panic!("rust-libxml: xpath: found null pointer result set");
      }
      vec.push(RoNode(ptr));
    }
    vec
  }

  /// use if the XPath used was meant to return a string, such as string(//foo/@attr)
  pub fn to_string(&self) -> String {
    unsafe {
      let receiver = xmlXPathCastToString(self.ptr);
      let c_string = CStr::from_ptr(receiver as *const c_char);
      let rust_string = str::from_utf8(c_string.to_bytes()).unwrap().to_owned();
      libc::free(receiver as *mut c_void);
      rust_string
    }
  }
}
<commit_msg>impl Display for Object<commit_after>//! The `XPath` functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::readonly::RoNode;
use crate::tree::{Document, DocumentRef, DocumentWeak, Node};
use libc;
use libc::{c_char, c_void, size_t};
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::rc::Rc;
use std::str;
use std::fmt;

///Thinly wrapped libxml2 xpath context
pub(crate) type ContextRef = Rc<RefCell<_Context>>;

#[derive(Debug)]
pub(crate) struct _Context(pub(crate) xmlXPathContextPtr);

impl Drop for _Context {
  ///free xpath context when it goes out of scope
  fn drop(&mut self) {
    unsafe {
      xmlXPathFreeContext(self.0);
    }
  }
}

/// An XPath context
#[derive(Clone)]
pub struct Context {
  /// Safe reference to the libxml2 context pointer
  pub(crate) context_ptr: ContextRef,
  ///Document contains pointer, needed for ContextPtr, so we need to borrow Document to prevent it's freeing
  pub(crate) document: DocumentWeak,
}

///Essentially, the result of the evaluation of some xpath expression
#[derive(Debug)]
pub struct Object {
  ///libxml's `ObjectPtr`
  pub ptr: xmlXPathObjectPtr,
  document: DocumentWeak,
}

impl Context {
  ///create the xpath context for a document
  pub fn new(doc: &Document) -> Result<Context, ()> {
    let ctxtptr = unsafe { xmlXPathNewContext(doc.doc_ptr()) };
    if ctxtptr.is_null() {
      Err(())
    } else {
      Ok(Context {
        context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
        document: Rc::downgrade(&doc.0),
      })
    }
  }
  pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
    let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
    if ctxtptr.is_null() {
      Err(())
    } else {
      Ok(Context {
        context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
        document: Rc::downgrade(&docref),
      })
    }
  }

  /// Returns the raw libxml2 context pointer behind the struct
  pub fn as_ptr(&self) -> xmlXPathContextPtr {
    self.context_ptr.borrow().0
  }

  /// Instantiate a new Context for the Document of a given Node.
  /// Note: the Context is root-level for that document, use `.set_context_node` to limit scope to this node
  pub fn from_node(node: &Node) -> Result<Context, ()> {
    let docref = node.get_docref().upgrade().unwrap();
    Context::new_ptr(&docref)
  }

  /// Register a namespace prefix-href pair on the xpath context
  pub fn register_namespace(&self, prefix: &str, href: &str) -> Result<(), ()> {
    let c_prefix = CString::new(prefix).unwrap();
    let c_href = CString::new(href).unwrap();
    unsafe {
      let result = xmlXPathRegisterNs(
        self.as_ptr(),
        c_prefix.as_bytes().as_ptr(),
        c_href.as_bytes().as_ptr(),
      );
      if result != 0 {
        Err(())
      } else {
        Ok(())
      }
    }
  }

  ///evaluate an xpath
  pub fn evaluate(&self, xpath: &str) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr = unsafe { xmlXPathEvalExpression(c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  ///evaluate an xpath on a context Node
  pub fn node_evaluate(&self, xpath: &str, node: &Node) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr =
      unsafe { xmlXPathNodeEval(node.node_ptr(), c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  ///evaluate an xpath on a context RoNode
  pub fn node_evaluate_readonly(&self, xpath: &str, node: RoNode) -> Result<Object, ()> {
    let c_xpath = CString::new(xpath).unwrap();
    let ptr = unsafe { xmlXPathNodeEval(node.0, c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
    if ptr.is_null() {
      Err(())
    } else {
      Ok(Object {
        ptr,
        document: self.document.clone(),
      })
    }
  }

  /// localize xpath context to a specific Node
  pub fn set_context_node(&mut self, node: &Node) -> Result<(), ()> {
    unsafe {
      let result = xmlXPathSetContextNode(node.node_ptr(), self.as_ptr());
      if result != 0 {
        return Err(());
      }
    }
    Ok(())
  }

  /// find nodes via xpath, at a specified node or the document root
  pub fn findnodes(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<Vec<Node>, ()> {
    let evaluated;
    if let Some(node) = node_opt {
      evaluated = self.node_evaluate(xpath, node)?;
    } else {
      evaluated = self.evaluate(xpath)?;
    }
    Ok(evaluated.get_nodes_as_vec())
  }

  /// find a literal value via xpath, at a specified node or the document root
  pub fn findvalue(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<String, ()> {
    let evaluated;
    if let Some(node) = node_opt {
      evaluated = self.node_evaluate(xpath, node)?;
    } else {
      evaluated = self.evaluate(xpath)?;
    }
    Ok(evaluated.to_string())
  }
}

impl Drop for Object {
  /// free the memory allocated
  fn drop(&mut self) {
    unsafe {
      xmlXPathFreeObject(self.ptr);
    }
  }
}

impl Object {
  ///get the number of nodes in the result set
  pub fn get_number_of_nodes(&self) -> usize {
    let v = xmlXPathObjectNumberOfNodes(self.ptr);
    if v == -1 {
      panic!("rust-libxml: xpath: Passed in null pointer!");
    }
    if v == -2 {
      // No nodes found!
      return 0;
    }
    if v < -2 {
      panic!("rust-libxml: xpath: expected non-negative number of result nodes");
    }
    v as usize
  }

  /// returns the result set as a vector of node references
  pub fn get_nodes_as_vec(&self) -> Vec<Node> {
    let n = self.get_number_of_nodes();
    let mut vec: Vec<Node> = Vec::with_capacity(n);
    let slice = if n > 0 {
      xmlXPathObjectGetNodes(self.ptr, n as size_t)
    } else {
      Vec::new()
    };
    for ptr in slice {
      if ptr.is_null() {
        panic!("rust-libxml: xpath: found null pointer result set");
      }
      let node = Node::wrap(ptr, &self.document.upgrade().unwrap());
      vec.push(node);
    }
    vec
  }

  /// returns the result set as a vector of node references
  pub fn get_readonly_nodes_as_vec(&self) -> Vec<RoNode> {
    let n = self.get_number_of_nodes();
    let mut vec: Vec<RoNode> = Vec::with_capacity(n);
    let slice = if n > 0 {
      xmlXPathObjectGetNodes(self.ptr, n as size_t)
    } else {
      Vec::new()
    };
    for ptr in slice {
      if ptr.is_null() {
        panic!("rust-libxml: xpath: found null pointer result set");
      }
      vec.push(RoNode(ptr));
    }
    vec
  }
}

impl fmt::Display for Object {
  /// use if the XPath used was meant to return a string, such as string(//foo/@attr)
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    unsafe {
      let receiver = xmlXPathCastToString(self.ptr);
      let c_string = CStr::from_ptr(receiver as *const c_char);
      let rust_string = str::from_utf8(c_string.to_bytes()).unwrap().to_owned();
      libc::free(receiver as *mut c_void);
      write!(f, "{}", rust_string)
    }
  }
}<|endoftext|>


<commit_before>use chrono::prelude::*;
use chrono;
use std::thread;
use std::time;

use command::*;
use database::DataBase;

pub struct Engine {
    next_wakeup: Option<DateTime<Utc>>,
    data_base: DataBase,
    callback : Option<(fn(String, i64))>,
    stop_loop: bool,
}

impl Engine {
    pub fn new(open_in_memory: bool) -> Engine {
        info!("Initialize engine");
        Engine {
            stop_loop: false,
            next_wakeup: None,
            callback: None,
            data_base: DataBase::new(open_in_memory),
        }
    }

    // Normally should be run in another thread
    pub fn run(&mut self) {
        self.stop_loop = false;
        self.loop_thread();
    }

    fn loop_thread(&mut self) {
        info!("Start engine loop");
        while !self.stop_loop {
            self.tick();
            thread::sleep(time::Duration::from_millis(1000));
        }
    }

    pub fn handle_text_message(&mut self, uid: i64, text_message: &str) -> (String, i32) {
        info!("Handle text message : {}", text_message);
        let tz = self.data_base.get_user_timezone(uid);
        let com = parse_command(String::from(text_message), tz);
        match com {
            Command::BadCommand => self.process_bad_command(),
            Command::OneTimeEvent(ev) => self.process_one_time_event_command(uid, ev),
            Command::RepetitiveEvent(ev) => self.process_repetitive_event_command(uid, ev),
        }
    }

    pub fn register_callback(&mut self, f: fn(String, i64)){
        self.callback = Some(f);
    }

    pub fn stop(&mut self) {
        info!("Stoping engine");
        self.stop_loop = true;
    }

    pub fn get_active_event_list(&self, uid: i64) -> Vec<String> {
        let mut result = Vec::new();
        let command_vector = self.data_base.get_all_active_events(uid);
        let tz = self.data_base.get_user_timezone(uid) as i64;
        let dt = chrono::Duration::seconds(-tz * 60 * 60);
        for command in command_vector {
            match command {
                Command::OneTimeEvent(c) => {
                    let text: String = c.event_text.chars().take(40).collect();
                    let date: String = (c.event_time + dt).format("%e %b %l.%M").to_string();
                    result.push(format!("{} : _{}_", text, date));
                }
                Command::BadCommand => {}
                Command::RepetitiveEvent(_ev) => {}
            }
        }
        result
    }

    pub fn get_rep_event_list(&self, uid: i64) -> Vec<(String, i64)> {
        let mut result = Vec::new();
        let command_vector = self.data_base.get_all_rep_events(uid);
        // const DEFAULT_TZ: i64 = 3;
        // let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);
        for line in command_vector {
            let id: i64 = line.1;
            let command = line.0;
            match command {
                Command::RepetitiveEvent(ev) => {
                    let text: String = ev.event_text.chars().take(40).collect();
                    result.push((format!("{}", text), id));
                }
                Command::BadCommand => {}
                Command::OneTimeEvent(_ev) => {}
            }
        }
        result
    }

    pub fn delete_rep_event(&mut self, event_id: i64){
        self.data_base.delete_rep_event(event_id);
    }

    pub fn add_user(&mut self, uid: i64, username: &str, chat_id: i64, tz: i32){
        if !self.data_base.add_user(uid, username, chat_id, tz) {
            error!("Can't add user to database. UID - <{}>, username - <{}>, chat_id - <{}>, tz - <{}>",
                uid, username, chat_id, tz);
        }
    }

    pub fn get_user_chat_id_all(&self) -> Vec<i32> {
        self.data_base.get_user_chat_id_all()
    }

    pub fn get_user_groups(&self, uid: i64) -> Vec<(String, i64)> {
        self.data_base.get_groups_names(uid)
    }

    pub fn add_user_group(&self, uid: i64, group_name: &str){
        self.data_base.add_group(uid, group_name);
    }

    pub fn delete_user_group(&self, gid: i64){
        self.data_base.delete_group(gid);
    }

    pub fn get_group_items(&self, gid: i64) -> Vec<(String, i64)> {
        self.data_base.get_group_items(gid)
    }

    pub fn add_group_item(&self, gid: i64, group_item: &str){
        self.data_base.add_group_item(gid, group_item);
    }

    pub fn delete_group_item(&self, id: i64){
        self.data_base.delete_group_item(id);
    }

    fn process_bad_command(&self) -> (String, i32) {
        (String::from(""), 1)
    }

    fn process_one_time_event_command(&mut self, uid:i64, c: OneTimeEventImpl) -> (String, i32) {
        let tz = self.data_base.get_user_timezone(uid);
        let mut return_string = self.format_return_message_header(&c.event_time, tz);
        return_string.push('\n');
        return_string.push_str(&c.event_text);
        self.data_base.put(uid, Command::OneTimeEvent(c));
        self.next_wakeup = self.data_base.get_nearest_wakeup();

        // delete newline char to write to log
        let tmp_string = str::replace(&return_string[..], "\n", " ");
        info!("Successfully process command, return string - <{}>", tmp_string);

        (return_string, 0)
    }

    fn process_repetitive_event_command(&mut self, uid: i64, c: RepetitiveEventImpl) -> (String, i32) {
        let tz = self.data_base.get_user_timezone(uid);
        let mut return_string = self.format_return_message_header(&c.event_start_time, tz);
        return_string.push('\n');
        return_string.push_str(&c.event_text);
        self.data_base.put(uid, Command::RepetitiveEvent(c));
        self.next_wakeup = self.data_base.get_nearest_wakeup();

        // delete newline char to write to log
        let tmp_string = str::replace(&return_string[..], "\n", " ");
        info!("Successfully process command, return string - <{}>", tmp_string);

        (return_string, 0)
    }

    fn on_one_time_event(&self, event: OneTimeEventImpl, uid: i64){
        info!("Event time, text - <{}>", &event.event_text);
        (self.callback.unwrap())(event.event_text, uid);
    }

    fn on_repetitive_event(&self, event: RepetitiveEventImpl, uid: i64){
        info!("Event time, text - <{}>", &event.event_text);
        (self.callback.unwrap())(event.event_text, uid);
    }

    fn format_return_message_header(&self, event_time: &DateTime<Utc>, tz: i32) -> String {
        let tz = FixedOffset::west(tz * 3600);
        let t_event = event_time.with_timezone(&tz);
        let t_now = Utc::now().with_timezone(&tz);

        if t_event < t_now {
            return String::from("Event time is in the past. Is it right?");
        }

        // days from year 1 TODO: maybe day of the year?
        let day_event = t_event.num_days_from_ce();
        let day_now = t_now.num_days_from_ce();
        let dt = day_event - day_now;

        if dt == 0 {
            return t_event.format("I'll remind you today at %H:%M").to_string();
        } else if dt == 1 {
            return t_event.format("I'll remind you tomorrow at %H:%M").to_string();
        }
        t_event.format("I'll remind you %B %e at %H:%M").to_string()
    }

    fn tick(&mut self) {
        if self.next_wakeup.is_none() {
            self.next_wakeup = self.data_base.get_nearest_wakeup();
            return;
        }
        let next_wakeup = self.next_wakeup.unwrap();

        if Utc::now() > next_wakeup {
            if let Some((command, uid)) = self.data_base.pop(next_wakeup) {
                match command {
                    Command::OneTimeEvent(ev) => self.on_one_time_event(ev, uid),
                    Command::RepetitiveEvent(ev) => self.on_repetitive_event(ev, uid),
                    Command::BadCommand => warn!("Database::pop return BadCommand"),
                }
            }
            self.next_wakeup = self.data_base.get_nearest_wakeup();
        }
    }
}
<commit_msg>Change time format<commit_after>use chrono::prelude::*;
use chrono;
use std::thread;
use std::time;

use command::*;
use database::DataBase;

pub struct Engine {
    next_wakeup: Option<DateTime<Utc>>,
    data_base: DataBase,
    callback : Option<(fn(String, i64))>,
    stop_loop: bool,
}

impl Engine {
    pub fn new(open_in_memory: bool) -> Engine {
        info!("Initialize engine");
        Engine {
            stop_loop: false,
            next_wakeup: None,
            callback: None,
            data_base: DataBase::new(open_in_memory),
        }
    }

    // Normally should be run in another thread
    pub fn run(&mut self) {
        self.stop_loop = false;
        self.loop_thread();
    }

    fn loop_thread(&mut self) {
        info!("Start engine loop");
        while !self.stop_loop {
            self.tick();
            thread::sleep(time::Duration::from_millis(1000));
        }
    }

    pub fn handle_text_message(&mut self, uid: i64, text_message: &str) -> (String, i32) {
        info!("Handle text message : {}", text_message);
        let tz = self.data_base.get_user_timezone(uid);
        let com = parse_command(String::from(text_message), tz);
        match com {
            Command::BadCommand => self.process_bad_command(),
            Command::OneTimeEvent(ev) => self.process_one_time_event_command(uid, ev),
            Command::RepetitiveEvent(ev) => self.process_repetitive_event_command(uid, ev),
        }
    }

    pub fn register_callback(&mut self, f: fn(String, i64)){
        self.callback = Some(f);
    }

    pub fn stop(&mut self) {
        info!("Stoping engine");
        self.stop_loop = true;
    }

    pub fn get_active_event_list(&self, uid: i64) -> Vec<String> {
        let mut result = Vec::new();
        let command_vector = self.data_base.get_all_active_events(uid);
        let tz = self.data_base.get_user_timezone(uid) as i64;
        let dt = chrono::Duration::seconds(-tz * 60 * 60);
        for command in command_vector {
            match command {
                Command::OneTimeEvent(c) => {
                    let text: String = c.event_text.chars().take(40).collect();
                    let date: String = (c.event_time + dt).format("%e %b %k.%M").to_string();
                    result.push(format!("{} : _{}_", text, date));
                }
                Command::BadCommand => {}
                Command::RepetitiveEvent(_ev) => {}
            }
        }
        result
    }

    pub fn get_rep_event_list(&self, uid: i64) -> Vec<(String, i64)> {
        let mut result = Vec::new();
        let command_vector = self.data_base.get_all_rep_events(uid);
        // const DEFAULT_TZ: i64 = 3;
        // let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);
        for line in command_vector {
            let id: i64 = line.1;
            let command = line.0;
            match command {
                Command::RepetitiveEvent(ev) => {
                    let text: String = ev.event_text.chars().take(40).collect();
                    result.push((format!("{}", text), id));
                }
                Command::BadCommand => {}
                Command::OneTimeEvent(_ev) => {}
            }
        }
        result
    }

    pub fn delete_rep_event(&mut self, event_id: i64){
        self.data_base.delete_rep_event(event_id);
    }

    pub fn add_user(&mut self, uid: i64, username: &str, chat_id: i64, tz: i32){
        if !self.data_base.add_user(uid, username, chat_id, tz) {
            error!("Can't add user to database. UID - <{}>, username - <{}>, chat_id - <{}>, tz - <{}>",
                uid, username, chat_id, tz);
        }
    }

    pub fn get_user_chat_id_all(&self) -> Vec<i32> {
        self.data_base.get_user_chat_id_all()
    }

    pub fn get_user_groups(&self, uid: i64) -> Vec<(String, i64)> {
        self.data_base.get_groups_names(uid)
    }

    pub fn add_user_group(&self, uid: i64, group_name: &str){
        self.data_base.add_group(uid, group_name);
    }

    pub fn delete_user_group(&self, gid: i64){
        self.data_base.delete_group(gid);
    }

    pub fn get_group_items(&self, gid: i64) -> Vec<(String, i64)> {
        self.data_base.get_group_items(gid)
    }

    pub fn add_group_item(&self, gid: i64, group_item: &str){
        self.data_base.add_group_item(gid, group_item);
    }

    pub fn delete_group_item(&self, id: i64){
        self.data_base.delete_group_item(id);
    }

    fn process_bad_command(&self) -> (String, i32) {
        (String::from(""), 1)
    }

    fn process_one_time_event_command(&mut self, uid:i64, c: OneTimeEventImpl) -> (String, i32) {
        let tz = self.data_base.get_user_timezone(uid);
        let mut return_string = self.format_return_message_header(&c.event_time, tz);
        return_string.push('\n');
        return_string.push_str(&c.event_text);
        self.data_base.put(uid, Command::OneTimeEvent(c));
        self.next_wakeup = self.data_base.get_nearest_wakeup();

        // delete newline char to write to log
        let tmp_string = str::replace(&return_string[..], "\n", " ");
        info!("Successfully process command, return string - <{}>", tmp_string);

        (return_string, 0)
    }

    fn process_repetitive_event_command(&mut self, uid: i64, c: RepetitiveEventImpl) -> (String, i32) {
        let tz = self.data_base.get_user_timezone(uid);
        let mut return_string = self.format_return_message_header(&c.event_start_time, tz);
        return_string.push('\n');
        return_string.push_str(&c.event_text);
        self.data_base.put(uid, Command::RepetitiveEvent(c));
        self.next_wakeup = self.data_base.get_nearest_wakeup();

        // delete newline char to write to log
        let tmp_string = str::replace(&return_string[..], "\n", " ");
        info!("Successfully process command, return string - <{}>", tmp_string);

        (return_string, 0)
    }

    fn on_one_time_event(&self, event: OneTimeEventImpl, uid: i64){
        info!("Event time, text - <{}>", &event.event_text);
        (self.callback.unwrap())(event.event_text, uid);
    }

    fn on_repetitive_event(&self, event: RepetitiveEventImpl, uid: i64){
        info!("Event time, text - <{}>", &event.event_text);
        (self.callback.unwrap())(event.event_text, uid);
    }

    fn format_return_message_header(&self, event_time: &DateTime<Utc>, tz: i32) -> String {
        let tz = FixedOffset::west(tz * 3600);
        let t_event = event_time.with_timezone(&tz);
        let t_now = Utc::now().with_timezone(&tz);

        if t_event < t_now {
            return String::from("Event time is in the past. Is it right?");
        }

        // days from year 1 TODO: maybe day of the year?
        let day_event = t_event.num_days_from_ce();
        let day_now = t_now.num_days_from_ce();
        let dt = day_event - day_now;

        if dt == 0 {
            return t_event.format("I'll remind you today at %H:%M").to_string();
        } else if dt == 1 {
            return t_event.format("I'll remind you tomorrow at %H:%M").to_string();
        }
        t_event.format("I'll remind you %B %e at %H:%M").to_string()
    }

    fn tick(&mut self) {
        if self.next_wakeup.is_none() {
            self.next_wakeup = self.data_base.get_nearest_wakeup();
            return;
        }
        let next_wakeup = self.next_wakeup.unwrap();

        if Utc::now() > next_wakeup {
            if let Some((command, uid)) = self.data_base.pop(next_wakeup) {
                match command {
                    Command::OneTimeEvent(ev) => self.on_one_time_event(ev, uid),
                    Command::RepetitiveEvent(ev) => self.on_repetitive_event(ev, uid),
                    Command::BadCommand => warn!("Database::pop return BadCommand"),
                }
            }
            self.next_wakeup = self.data_base.get_nearest_wakeup();
        }
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::mem;

use basic::Type;
use rand::{Rng, Rand};
use util::memory::BytePtr;

// ----------------------------------------------------------------------
// Types connect Parquet physical types with Rust-specific types

// TODO: alignment?
// TODO: we could also use [u32; 3], however it seems there is no easy way
//   to convert [u32] to [u32; 3] in decoding.
#[derive(Clone, Debug)]
pub struct Int96 {
  value: Option<Vec<u32>>,
}

impl Int96 {
  pub fn new() -> Self {
    Int96 { value: None }
  }

  pub fn data(&self) -> &[u32] {
    assert!(self.value.is_some());
    &self.value.as_ref().unwrap()
  }

  pub fn set_data(&mut self, v: Vec<u32>) {
    assert_eq!(v.len(), 3);
    self.value = Some(v);
  }
}

impl Default for Int96 {
  fn default() -> Self { Int96 { value: None } }
}

impl PartialEq for Int96 {
  fn eq(&self, other: &Int96) -> bool {
    self.data() == other.data()
  }
}

impl Rand for Int96 {
  fn rand<R: Rng>(rng: &mut R) -> Self {
    let mut result = Int96::new();
    let mut value = vec!();
    for _ in 0..3 {
      value.push(rng.gen::<u32>());
    }
    result.set_data(value);
    result
  }
}

#[derive(Clone, Debug)]
pub struct ByteArray {
  data: Option<BytePtr>,
}

impl ByteArray {
  pub fn new() -> Self {
    ByteArray { data: None }
  }

  pub fn len(&self) -> usize {
    assert!(self.data.is_some());
    self.data.as_ref().unwrap().len()
  }

  pub fn data(&self) -> &[u8] {
    assert!(self.data.is_some());
    self.data.as_ref().unwrap().as_ref()
  }

  pub fn set_data(&mut self, data: BytePtr) {
    self.data = Some(data);
  }
}

impl Default for ByteArray {
  fn default() -> Self { ByteArray { data: None } }
}

impl PartialEq for ByteArray {
  fn eq(&self, other: &ByteArray) -> bool {
    self.data() == other.data()
  }
}

impl Rand for ByteArray {
  fn rand<R: Rng>(rng: &mut R) -> Self {
    let mut result = ByteArray::new();
    let mut value = vec!();
    let len = rng.gen_range::<usize>(0, 128);
    for _ in 0..len {
      value.push(rng.gen_range(0, 255) & 0xFF);
    }
    result.set_data(BytePtr::new(value));
    result
  }
}

// ----------------------------------------------------------------------
// AsBytes converts an instance of data type to a slice of u8

pub trait AsBytes {
  fn as_bytes(&self) -> &[u8];
}

impl AsBytes for bool {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const bool as *const u8, 1)
    }
  }
}

impl AsBytes for i32 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const i32 as *const u8, 4)
    }
  }
}

impl AsBytes for i64 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const i64 as *const u8, 8)
    }
  }
}

impl AsBytes for Int96 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self.data() as *const [u32] as *const u8, 12)
    }
  }
}

impl AsBytes for f32 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const f32 as *const u8, 4)
    }
  }
}

impl AsBytes for f64 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const f64 as *const u8, 8)
    }
  }
}

impl AsBytes for ByteArray {
  fn as_bytes(&self) -> &[u8] {
    self.data()
  }
}

impl AsBytes for Vec<u8> {
  fn as_bytes(&self) -> &[u8] {
    self.as_slice()
  }
}

impl<'a> AsBytes for &'a str {
  fn as_bytes(&self) -> &[u8] {
    (self as &str).as_bytes()
  }
}

// ----------------------------------------------------------------------
// DataType trait, which contains the Parquet physical type info as well as
// the Rust primitive type presentation.

pub trait DataType {
  type T: ::std::cmp::PartialEq + ::std::fmt::Debug + ::std::default::Default
    + ::std::clone::Clone + Rand;
  fn get_physical_type() -> Type;
  fn get_type_size() -> usize;
}

macro_rules! make_type {
  ($name:ident, $physical_ty:path, $native_ty:ty, $size:expr) => {
    pub struct $name {
    }

    impl DataType for $name {
      type T = $native_ty;

      fn get_physical_type() -> Type {
        $physical_ty
      }

      fn get_type_size() -> usize {
        $size
      }
    }
  };
}

/// Generate struct definitions for all physical types

make_type!(BoolType, Type::BOOLEAN, bool, 1);
make_type!(Int32Type, Type::INT32, i32, 4);
make_type!(Int64Type, Type::INT64, i64, 8);
make_type!(Int96Type, Type::INT96, Int96, mem::size_of::<Int96>());
make_type!(FloatType, Type::FLOAT, f32, 4);
make_type!(DoubleType, Type::DOUBLE, f64, 8);
make_type!(ByteArrayType, Type::BYTE_ARRAY, ByteArray, mem::size_of::<ByteArray>());
make_type!(FixedLenByteArrayType, Type::FIXED_LEN_BYTE_ARRAY,
           ByteArray, mem::size_of::<ByteArray>());

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

  #[test]
  fn test_as_bytes() {
    assert_eq!(false.as_bytes(), &[0]);
    assert_eq!(true.as_bytes(), &[1]);
    assert_eq!((7 as i32).as_bytes(), &[7, 0, 0, 0]);
    assert_eq!((555 as i32).as_bytes(), &[43, 2, 0, 0]);
    assert_eq!(i32::max_value().as_bytes(), &[255, 255, 255, 127]);
    assert_eq!(i32::min_value().as_bytes(), &[0, 0, 0, 128]);
    assert_eq!((7 as i64).as_bytes(), &[7, 0, 0, 0, 0, 0, 0, 0]);
    assert_eq!((555 as i64).as_bytes(), &[43, 2, 0, 0, 0, 0, 0, 0]);
    assert_eq!((i64::max_value()).as_bytes(), &[255, 255, 255, 255, 255, 255, 255, 127]);
    assert_eq!((i64::min_value()).as_bytes(), &[0, 0, 0, 0, 0, 0, 0, 128]);
    assert_eq!((3.14 as f32).as_bytes(), &[195, 245, 72, 64]);
    assert_eq!((3.14 as f64).as_bytes(), &[31, 133, 235, 81, 184, 30, 9, 64]);

    // Test Int96
    let mut i96 = Int96::new();
    i96.set_data(vec![1, 2, 3]);
    assert_eq!(i96.as_bytes(), &[1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);

    // Test ByteArray
    let mut ba = ByteArray::new();
    ba.set_data(BytePtr::new(vec![1, 2, 3]));
    assert_eq!(ba.as_bytes(), &[1, 2, 3]);
  }
}
<commit_msg>Add AsBytes for DataType item type<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::mem;

use basic::Type;
use rand::{Rng, Rand};
use util::memory::BytePtr;

// ----------------------------------------------------------------------
// Types connect Parquet physical types with Rust-specific types

// TODO: alignment?
// TODO: we could also use [u32; 3], however it seems there is no easy way
//   to convert [u32] to [u32; 3] in decoding.
#[derive(Clone, Debug)]
pub struct Int96 {
  value: Option<Vec<u32>>,
}

impl Int96 {
  pub fn new() -> Self {
    Int96 { value: None }
  }

  pub fn data(&self) -> &[u32] {
    assert!(self.value.is_some());
    &self.value.as_ref().unwrap()
  }

  pub fn set_data(&mut self, v: Vec<u32>) {
    assert_eq!(v.len(), 3);
    self.value = Some(v);
  }
}

impl Default for Int96 {
  fn default() -> Self { Int96 { value: None } }
}

impl PartialEq for Int96 {
  fn eq(&self, other: &Int96) -> bool {
    self.data() == other.data()
  }
}

impl Rand for Int96 {
  fn rand<R: Rng>(rng: &mut R) -> Self {
    let mut result = Int96::new();
    let mut value = vec!();
    for _ in 0..3 {
      value.push(rng.gen::<u32>());
    }
    result.set_data(value);
    result
  }
}

#[derive(Clone, Debug)]
pub struct ByteArray {
  data: Option<BytePtr>,
}

impl ByteArray {
  pub fn new() -> Self {
    ByteArray { data: None }
  }

  pub fn len(&self) -> usize {
    assert!(self.data.is_some());
    self.data.as_ref().unwrap().len()
  }

  pub fn data(&self) -> &[u8] {
    assert!(self.data.is_some());
    self.data.as_ref().unwrap().as_ref()
  }

  pub fn set_data(&mut self, data: BytePtr) {
    self.data = Some(data);
  }
}

impl Default for ByteArray {
  fn default() -> Self { ByteArray { data: None } }
}

impl PartialEq for ByteArray {
  fn eq(&self, other: &ByteArray) -> bool {
    self.data() == other.data()
  }
}

impl Rand for ByteArray {
  fn rand<R: Rng>(rng: &mut R) -> Self {
    let mut result = ByteArray::new();
    let mut value = vec!();
    let len = rng.gen_range::<usize>(0, 128);
    for _ in 0..len {
      value.push(rng.gen_range(0, 255) & 0xFF);
    }
    result.set_data(BytePtr::new(value));
    result
  }
}

// ----------------------------------------------------------------------
// AsBytes converts an instance of data type to a slice of u8

pub trait AsBytes {
  fn as_bytes(&self) -> &[u8];
}

impl AsBytes for bool {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const bool as *const u8, 1)
    }
  }
}

impl AsBytes for i32 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const i32 as *const u8, 4)
    }
  }
}

impl AsBytes for i64 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const i64 as *const u8, 8)
    }
  }
}

impl AsBytes for Int96 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self.data() as *const [u32] as *const u8, 12)
    }
  }
}

impl AsBytes for f32 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const f32 as *const u8, 4)
    }
  }
}

impl AsBytes for f64 {
  fn as_bytes(&self) -> &[u8] {
    unsafe {
      ::std::slice::from_raw_parts(self as *const f64 as *const u8, 8)
    }
  }
}

impl AsBytes for ByteArray {
  fn as_bytes(&self) -> &[u8] {
    self.data()
  }
}

impl AsBytes for Vec<u8> {
  fn as_bytes(&self) -> &[u8] {
    self.as_slice()
  }
}

impl<'a> AsBytes for &'a str {
  fn as_bytes(&self) -> &[u8] {
    (self as &str).as_bytes()
  }
}

// ----------------------------------------------------------------------
// DataType trait, which contains the Parquet physical type info as well as
// the Rust primitive type presentation.

pub trait DataType {
  type T: ::std::cmp::PartialEq + ::std::fmt::Debug + ::std::default::Default
    + ::std::clone::Clone + Rand + AsBytes;
  fn get_physical_type() -> Type;
  fn get_type_size() -> usize;
}

macro_rules! make_type {
  ($name:ident, $physical_ty:path, $native_ty:ty, $size:expr) => {
    pub struct $name {
    }

    impl DataType for $name {
      type T = $native_ty;

      fn get_physical_type() -> Type {
        $physical_ty
      }

      fn get_type_size() -> usize {
        $size
      }
    }
  };
}

/// Generate struct definitions for all physical types

make_type!(BoolType, Type::BOOLEAN, bool, 1);
make_type!(Int32Type, Type::INT32, i32, 4);
make_type!(Int64Type, Type::INT64, i64, 8);
make_type!(Int96Type, Type::INT96, Int96, mem::size_of::<Int96>());
make_type!(FloatType, Type::FLOAT, f32, 4);
make_type!(DoubleType, Type::DOUBLE, f64, 8);
make_type!(ByteArrayType, Type::BYTE_ARRAY, ByteArray, mem::size_of::<ByteArray>());
make_type!(FixedLenByteArrayType, Type::FIXED_LEN_BYTE_ARRAY,
           ByteArray, mem::size_of::<ByteArray>());

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

  #[test]
  fn test_as_bytes() {
    assert_eq!(false.as_bytes(), &[0]);
    assert_eq!(true.as_bytes(), &[1]);
    assert_eq!((7 as i32).as_bytes(), &[7, 0, 0, 0]);
    assert_eq!((555 as i32).as_bytes(), &[43, 2, 0, 0]);
    assert_eq!(i32::max_value().as_bytes(), &[255, 255, 255, 127]);
    assert_eq!(i32::min_value().as_bytes(), &[0, 0, 0, 128]);
    assert_eq!((7 as i64).as_bytes(), &[7, 0, 0, 0, 0, 0, 0, 0]);
    assert_eq!((555 as i64).as_bytes(), &[43, 2, 0, 0, 0, 0, 0, 0]);
    assert_eq!((i64::max_value()).as_bytes(), &[255, 255, 255, 255, 255, 255, 255, 127]);
    assert_eq!((i64::min_value()).as_bytes(), &[0, 0, 0, 0, 0, 0, 0, 128]);
    assert_eq!((3.14 as f32).as_bytes(), &[195, 245, 72, 64]);
    assert_eq!((3.14 as f64).as_bytes(), &[31, 133, 235, 81, 184, 30, 9, 64]);
    assert_eq!("hello".as_bytes(), &[b'h', b'e', b'l', b'l', b'o']);
    assert_eq!(Vec::from("hello".as_bytes()).as_bytes(), &[b'h', b'e', b'l', b'l', b'o']);

    // Test Int96
    let mut i96 = Int96::new();
    i96.set_data(vec![1, 2, 3]);
    assert_eq!(i96.as_bytes(), &[1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);

    // Test ByteArray
    let mut ba = ByteArray::new();
    ba.set_data(BytePtr::new(vec![1, 2, 3]));
    assert_eq!(ba.as_bytes(), &[1, 2, 3]);
  }
}
<|endoftext|>


<commit_before>//! Functions for filtering images.

use image::{
    GenericImage,
    ImageBuffer,
    Luma,
    Pixel
};

use integralimage::{
    column_running_sum,
    row_running_sum
};

use traits::{
    Clamp,
    ToFloat
};

/// Convolves an 8bpp grayscale image with a kernel of width (2 * x_radius + 1)
/// and height (2 * y_radius + 1) whose entries are equal and
/// sum to one. i.e. each output pixel is the unweighted mean of
/// a rectangular region surrounding its corresponding input pixel.
/// We handle locations where the kernel would extend past the image's
/// boundary by treating the image as if its boundary pixels were
/// repeated indefinitely.
// TODO: for small kernels we probably want to do the convolution
// TODO: directly instead of using an integral image.
// TODO: more formats!
// TODO: number of operations is constant with kernel size,
// TODO: but this is still _really_ slow. fix!
pub fn box_filter<I: GenericImage<Pixel=Luma<u8>> + 'static>(
    image: &I,
    x_radius: u32,
    y_radius: u32)
    -> ImageBuffer<Luma<u8>, Vec<u8>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let kernel_width = 2 * x_radius + 1;
    let kernel_height = 2 * y_radius + 1;

    let mut row_buffer = vec![0; (width + 2 * x_radius) as usize];
    for y in 0..height {
        row_running_sum(image, y, &mut row_buffer, x_radius);
        let val = row_buffer[(2 * x_radius) as usize]/kernel_width;
        out.put_pixel(0, y, Luma([val as u8]));
        for x in 1..width {
            // TODO: This way we pay rounding errors for each of the
            // TODO: x and y convolutions. Is there a better way?
            let u = (x + 2 * x_radius) as usize;
            let l = (x - 1) as usize;
            let val = (row_buffer[u] - row_buffer[l])/kernel_width;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    let mut col_buffer = vec![0; (height + 2 * y_radius) as usize];
    for x in 0..width {
        column_running_sum(&out, x, &mut col_buffer, y_radius);
        let val = col_buffer[(2 * y_radius) as usize]/kernel_height;
        out.put_pixel(x, 0, Luma([val as u8]));
        for y in 1..height {
            let u = (y + 2 * y_radius) as usize;
            let l = (y - 1) as usize;
            let val = (col_buffer[u] - col_buffer[l])/kernel_height;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    out
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
pub fn horizontal_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);
    out.copy_from(image, 0, 0);

    if width == 0 || height == 0 {
        return out;
    }

    // TODO: Add Default instances for pixels
    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (width + k/2 + k/2) as usize];

	for y in 0..height {
        let left_pad  = image.get_pixel(0, y);
        let right_pad = image.get_pixel(width - 1, y);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for x in 0..width {
            buffer[(x + k/2) as usize] = image.get_pixel(x, y);
        }

        for x in k/2..(width + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(x + z - k/2) as usize];
                for i in 0..num_channels {
                    acc[i] += p.channels()[i].to_float() * kernel[z as usize];
                }
            }

            for c in acc.iter_mut() {
                *c /= k as f32;
            }

            let mut out_pixel = pix;

            for i in 0..num_channels {
                out_pixel.channels_mut()[i]
                    = <I::Pixel as Pixel>::Subpixel::clamp(acc[i]);
            }

            out.put_pixel(x - k/2, y, out_pixel);
        }
	}

    out
}

/// Fills the left margin entries in buffer with left_val and the
// right margin entries with right_val
fn pad_buffer<T>(buffer: &mut[T], margin: u32, left_val: T, right_val: T)
    where T : Copy {
    for i in 0..margin {
        buffer[i as usize] = left_val;
    }

    for i in (buffer.len() - margin as usize)..buffer.len() {
        buffer[i] = right_val;
    }
}

#[cfg(test)]
mod test {

    use super::{
        box_filter,
        horizontal_filter,
        pad_buffer
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer
    };
    use test;

    #[test]
    fn test_box_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 2u8, 3u8,
            4u8, 5u8, 6u8,
            7u8, 8u8, 9u8]).unwrap();

        // For this image we get the same answer from the two 1d
        // convolutions as from doing the 2d convolution in one step
        // (but we needn't in general, as in the former case we're
        // clipping to an integer value twice).
        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 3u8, 3u8,
            4u8, 5u8, 5u8,
            6u8, 7u8, 7u8]).unwrap();

        assert_pixels_eq!(box_filter(&image, 1, 1), expected);
    }

    #[bench]
    fn bench_box_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let filtered = box_filter(&image, 7, 7);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_pad_buffer() {
        let mut a = [0, 1, 2, 3, 0];
        pad_buffer(&mut a, 1, 4, 5);
        assert_eq!(a, [4, 1, 2, 3, 5]);
        pad_buffer(&mut a, 2, 8, 9);
        assert_eq!(a, [8, 8, 2, 9, 9]);

        let mut b = [0, 1, 2, 0];
        pad_buffer(&mut b, 1, 4, 5);
        assert_eq!(b, [4, 1, 2, 5]);
        pad_buffer(&mut b, 2, 8, 9);
        assert_eq!(b, [8, 8, 9, 9]);
    }

    #[test]
    fn test_horizontal_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 2u8, 2u8,
            5u8, 5u8, 5u8,
            2u8, 2u8, 2u8]).unwrap();

        let kernel = vec![1f32, 1f32, 1f32];
        let filtered = horizontal_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }
}
<commit_msg>Extract a couple of methods. Pretty clunky now, but when we have enough small helpers like this we should be able to decide on what set of operations to add for Pixel<commit_after>//! Functions for filtering images.
// TODO: unify with image::sample

use image::{
    GenericImage,
    ImageBuffer,
    Luma,
    Pixel
};

use integralimage::{
    column_running_sum,
    row_running_sum
};

use traits::{
    Clamp,
    ToFloat
};

/// Convolves an 8bpp grayscale image with a kernel of width (2 * x_radius + 1)
/// and height (2 * y_radius + 1) whose entries are equal and
/// sum to one. i.e. each output pixel is the unweighted mean of
/// a rectangular region surrounding its corresponding input pixel.
/// We handle locations where the kernel would extend past the image's
/// boundary by treating the image as if its boundary pixels were
/// repeated indefinitely.
// TODO: for small kernels we probably want to do the convolution
// TODO: directly instead of using an integral image.
// TODO: more formats!
// TODO: number of operations is constant with kernel size,
// TODO: but this is still _really_ slow. fix!
pub fn box_filter<I: GenericImage<Pixel=Luma<u8>> + 'static>(
    image: &I,
    x_radius: u32,
    y_radius: u32)
    -> ImageBuffer<Luma<u8>, Vec<u8>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let kernel_width = 2 * x_radius + 1;
    let kernel_height = 2 * y_radius + 1;

    let mut row_buffer = vec![0; (width + 2 * x_radius) as usize];
    for y in 0..height {
        row_running_sum(image, y, &mut row_buffer, x_radius);
        let val = row_buffer[(2 * x_radius) as usize]/kernel_width;
        out.put_pixel(0, y, Luma([val as u8]));
        for x in 1..width {
            // TODO: This way we pay rounding errors for each of the
            // TODO: x and y convolutions. Is there a better way?
            let u = (x + 2 * x_radius) as usize;
            let l = (x - 1) as usize;
            let val = (row_buffer[u] - row_buffer[l])/kernel_width;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    let mut col_buffer = vec![0; (height + 2 * y_radius) as usize];
    for x in 0..width {
        column_running_sum(&out, x, &mut col_buffer, y_radius);
        let val = col_buffer[(2 * y_radius) as usize]/kernel_height;
        out.put_pixel(x, 0, Luma([val as u8]));
        for y in 1..height {
            let u = (y + 2 * y_radius) as usize;
            let l = (y - 1) as usize;
            let val = (col_buffer[u] - col_buffer[l])/kernel_height;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    out
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
pub fn horizontal_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);
    out.copy_from(image, 0, 0);

    if width == 0 || height == 0 {
        return out;
    }

    // TODO: Add Default instances for pixels
    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (width + k/2 + k/2) as usize];

	for y in 0..height {
        let left_pad  = image.get_pixel(0, y);
        let right_pad = image.get_pixel(width - 1, y);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for x in 0..width {
            buffer[(x + k/2) as usize] = image.get_pixel(x, y);
        }

        for x in k/2..(width + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(x + z - k/2) as usize];
                let weight = kernel[z as usize];
                accumulate(&mut acc, &p, weight, num_channels);
            }

            for c in acc.iter_mut() {
                *c /= k as f32;
            }

            let mut out_pixel = pix;
            clamp_acc(&acc, &mut out_pixel, num_channels);

            out.put_pixel(x - k/2, y, out_pixel);
        }
	}

    out
}

fn accumulate<P>(acc: &mut [f32], pixel: &P, weight: f32, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel : ToFloat {
    for i in 0..num_channels {
        acc[i as usize]
            += pixel.channels()[i].to_float() * weight;
    }
}

fn clamp_acc<P>(acc: &[f32], pixel: &mut P, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel: Clamp{
    for i in 0..num_channels {
        pixel.channels_mut()[i] = P::Subpixel::clamp(acc[i]);
    }
}

/// Fills the left margin entries in buffer with left_val and the
// right margin entries with right_val
fn pad_buffer<T>(buffer: &mut[T], margin: u32, left_val: T, right_val: T)
    where T : Copy {
    for i in 0..margin {
        buffer[i as usize] = left_val;
    }

    for i in (buffer.len() - margin as usize)..buffer.len() {
        buffer[i] = right_val;
    }
}

#[cfg(test)]
mod test {

    use super::{
        box_filter,
        horizontal_filter,
        pad_buffer
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer
    };
    use test;

    #[test]
    fn test_box_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 2u8, 3u8,
            4u8, 5u8, 6u8,
            7u8, 8u8, 9u8]).unwrap();

        // For this image we get the same answer from the two 1d
        // convolutions as from doing the 2d convolution in one step
        // (but we needn't in general, as in the former case we're
        // clipping to an integer value twice).
        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 3u8, 3u8,
            4u8, 5u8, 5u8,
            6u8, 7u8, 7u8]).unwrap();

        assert_pixels_eq!(box_filter(&image, 1, 1), expected);
    }

    #[bench]
    fn bench_box_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let filtered = box_filter(&image, 7, 7);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_pad_buffer() {
        let mut a = [0, 1, 2, 3, 0];
        pad_buffer(&mut a, 1, 4, 5);
        assert_eq!(a, [4, 1, 2, 3, 5]);
        pad_buffer(&mut a, 2, 8, 9);
        assert_eq!(a, [8, 8, 2, 9, 9]);

        let mut b = [0, 1, 2, 0];
        pad_buffer(&mut b, 1, 4, 5);
        assert_eq!(b, [4, 1, 2, 5]);
        pad_buffer(&mut b, 2, 8, 9);
        assert_eq!(b, [8, 8, 9, 9]);
    }

    #[test]
    fn test_horizontal_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 2u8, 2u8,
            5u8, 5u8, 5u8,
            2u8, 2u8, 2u8]).unwrap();

        let kernel = vec![1f32, 1f32, 1f32];
        let filtered = horizontal_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_horizontal_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let kernel = vec![1f32; 5];
        b.iter(|| {
            let filtered = horizontal_filter(&image, &kernel);
            test::black_box(filtered);
            });
    }
}
<|endoftext|>