<commit_before>extern crate osmpbfreader;
use std::collections::HashMap;
use models::*;
use categorize::*;
use std;

// Way as represented in OpenStreetMap
struct Way {
    id: i64,
    nodes: Vec<i64>,
    properties: EdgeProperties,
}

struct Reader {
    nodes: HashMap<i64, Node>,
    ways: Vec<Way>,
}

impl Reader {
    fn new() -> Reader {
        Reader {
            nodes: HashMap::new(),
            ways: Vec::new(),
        }
    }

    fn count_nodes_uses(&mut self) {
        for way in &self.ways {
            for (i, node_id) in way.nodes.iter().enumerate() {
                if let Some(node) = self.nodes.get_mut(node_id) {
                    // Count double extremities nodes
                    if i == 0 || i == way.nodes.len() - 1 {
                        node.uses += 2;
                    } else {
                        node.uses += 1;
                    }
                } else {
                    panic!("Missing node, id: {}", node_id)
                }
            }
        }
    }

    fn split_way(&self, way: &Way) -> Vec<Edge> {
        let mut result = Vec::new();

        let mut source = 0;
        let mut geometry = Vec::new();
        for (i, &node_id) in way.nodes.iter().enumerate() {
            let node = self.nodes[&node_id];
            if i == 0 {
                source = node_id;
                geometry.push(node.coord);
            } else {
                geometry.push(node.coord);

                if node.uses > 1 {
                    result.push(Edge {
                        id: way.id,
                        source: source,
                        target: node_id,
                        geometry: geometry,
                        properties: way.properties,
                    });

                    source = node_id;
                    geometry = vec![node.coord];
                }
            }
        }
        result
    }

    fn read(&mut self, filename: &str) -> Result<(), String> {
        let path = std::path::Path::new(filename);
        let r = try!(std::fs::File::open(&path).map_err(|e| e.to_string()));
        let mut pbf = osmpbfreader::OsmPbfReader::new(r);
        for obj in pbf.iter() {
            match obj {
                osmpbfreader::OsmObj::Node(node) => {
                    let mut n = Node::new();
                    n.id = node.id;
                    n.coord = Coord {
                        lon: node.lon,
                        lat: node.lat,
                    };
                    self.nodes.insert(node.id, n);
                }
                osmpbfreader::OsmObj::Way(way) => {
                    let mut properties = EdgeProperties::new();
                    for (key, val) in way.tags {
                        properties.update(key, val);
                    }
                    properties.normalize();
                    if properties.accessible() {
                        self.ways.push(Way {
                            id: way.id,
                            nodes: way.nodes,
                            properties: properties,
                        })
                    }
                }
                osmpbfreader::OsmObj::Relation(_) => {}
            }
        }
        Ok(())
    }

    fn nodes(self) -> Vec<Node> {
        self.nodes
            .into_iter()
            .map(|(_, node)| node)
            .filter(|node| node.uses > 1)
            .collect()
    }

    fn edges(&self) -> Vec<Edge> {
        self.ways.iter().flat_map(|way| self.split_way(way)).collect()
    }
}

// Read all the nodes and ways of the osm.pbf file
pub fn read(filename: &str) -> Result<(Vec<Node>, Vec<Edge>), String> {
    let mut r = Reader::new();
    try!(r.read(filename));
    r.count_nodes_uses();
    let edges = r.edges();
    Ok((r.nodes(), edges)
}

#[test]
fn test_real_all() {
    let (nodes, ways) = read("src/osm4routing/test_data/minimal.osm.pbf");
    assert_eq!(2, nodes.len());
    assert_eq!(1, ways.len());
}

#[test]
fn test_count_nodes() {
    let ways = vec![Way {
                        id: 0,
                        nodes: vec![1, 2, 3],
                        properties: EdgeProperties::new(),
                    }];
    let mut nodes = HashMap::new();
    nodes.insert(1, Node::new());
    nodes.insert(2, Node::new());
    nodes.insert(3, Node::new());
    let mut r = Reader {
        ways: ways,
        nodes: nodes,
    };
    r.count_nodes_uses();
    assert_eq!(2, r.nodes[&1].uses);
    assert_eq!(1, r.nodes[&2].uses);
    assert_eq!(2, r.nodes[&3].uses);

    assert_eq!(2, r.nodes().len());
}

#[test]
fn test_split() {
    let mut nodes = HashMap::new();
    nodes.insert(1, Node::new());
    nodes.insert(2, Node::new());
    nodes.insert(3, Node::new());
    nodes.insert(4, Node::new());
    nodes.insert(5, Node::new());
    let ways = vec![Way {
                        id: 0,
                        nodes: vec![1, 2, 3],
                        properties: EdgeProperties::new(),
                    },
                    Way {
                        id: 0,
                        nodes: vec![4, 5, 2],
                        properties: EdgeProperties::new(),
                    }];
    let mut r = Reader {
        nodes: nodes,
        ways: ways,
    };
    r.count_nodes_uses();
    let edges = r.edges();
    assert_eq!(3, edges.len());
}

#[test]
#[should_panic]
fn test_wrong_file() {
    read("i hope you have no file name like this one");
}
<commit_msg>test correction after rebase<commit_after>extern crate osmpbfreader;
use std::collections::HashMap;
use models::*;
use categorize::*;
use std;

// Way as represented in OpenStreetMap
struct Way {
    id: i64,
    nodes: Vec<i64>,
    properties: EdgeProperties,
}

struct Reader {
    nodes: HashMap<i64, Node>,
    ways: Vec<Way>,
}

impl Reader {
    fn new() -> Reader {
        Reader {
            nodes: HashMap::new(),
            ways: Vec::new(),
        }
    }

    fn count_nodes_uses(&mut self) {
        for way in &self.ways {
            for (i, node_id) in way.nodes.iter().enumerate() {
                if let Some(node) = self.nodes.get_mut(node_id) {
                    // Count double extremities nodes
                    if i == 0 || i == way.nodes.len() - 1 {
                        node.uses += 2;
                    } else {
                        node.uses += 1;
                    }
                } else {
                    panic!("Missing node, id: {}", node_id)
                }
            }
        }
    }

    fn split_way(&self, way: &Way) -> Vec<Edge> {
        let mut result = Vec::new();

        let mut source = 0;
        let mut geometry = Vec::new();
        for (i, &node_id) in way.nodes.iter().enumerate() {
            let node = self.nodes[&node_id];
            if i == 0 {
                source = node_id;
                geometry.push(node.coord);
            } else {
                geometry.push(node.coord);

                if node.uses > 1 {
                    result.push(Edge {
                        id: way.id,
                        source: source,
                        target: node_id,
                        geometry: geometry,
                        properties: way.properties,
                    });

                    source = node_id;
                    geometry = vec![node.coord];
                }
            }
        }
        result
    }

    fn read(&mut self, filename: &str) -> Result<(), String> {
        let path = std::path::Path::new(filename);
        let r = try!(std::fs::File::open(&path).map_err(|e| e.to_string()));
        let mut pbf = osmpbfreader::OsmPbfReader::new(r);
        for obj in pbf.iter() {
            match obj {
                osmpbfreader::OsmObj::Node(node) => {
                    let mut n = Node::new();
                    n.id = node.id;
                    n.coord = Coord {
                        lon: node.lon,
                        lat: node.lat,
                    };
                    self.nodes.insert(node.id, n);
                }
                osmpbfreader::OsmObj::Way(way) => {
                    let mut properties = EdgeProperties::new();
                    for (key, val) in way.tags {
                        properties.update(key, val);
                    }
                    properties.normalize();
                    if properties.accessible() {
                        self.ways.push(Way {
                            id: way.id,
                            nodes: way.nodes,
                            properties: properties,
                        })
                    }
                }
                osmpbfreader::OsmObj::Relation(_) => {}
            }
        }
        Ok(())
    }

    fn nodes(self) -> Vec<Node> {
        self.nodes
            .into_iter()
            .map(|(_, node)| node)
            .filter(|node| node.uses > 1)
            .collect()
    }

    fn edges(&self) -> Vec<Edge> {
        self.ways.iter().flat_map(|way| self.split_way(way)).collect()
    }
}

// Read all the nodes and ways of the osm.pbf file
pub fn read(filename: &str) -> Result<(Vec<Node>, Vec<Edge>), String> {
    let mut r = Reader::new();
    try!(r.read(filename));
    r.count_nodes_uses();
    let edges = r.edges();
    Ok((r.nodes(), edges))
}

#[test]
fn test_real_all() {
    let (nodes, ways) = read("src/osm4routing/test_data/minimal.osm.pbf").unwrap();
    assert_eq!(2, nodes.len());
    assert_eq!(1, ways.len());
}

#[test]
fn test_count_nodes() {
    let ways = vec![Way {
                        id: 0,
                        nodes: vec![1, 2, 3],
                        properties: EdgeProperties::new(),
                    }];
    let mut nodes = HashMap::new();
    nodes.insert(1, Node::new());
    nodes.insert(2, Node::new());
    nodes.insert(3, Node::new());
    let mut r = Reader {
        ways: ways,
        nodes: nodes,
    };
    r.count_nodes_uses();
    assert_eq!(2, r.nodes[&1].uses);
    assert_eq!(1, r.nodes[&2].uses);
    assert_eq!(2, r.nodes[&3].uses);

    assert_eq!(2, r.nodes().len());
}

#[test]
fn test_split() {
    let mut nodes = HashMap::new();
    nodes.insert(1, Node::new());
    nodes.insert(2, Node::new());
    nodes.insert(3, Node::new());
    nodes.insert(4, Node::new());
    nodes.insert(5, Node::new());
    let ways = vec![Way {
                        id: 0,
                        nodes: vec![1, 2, 3],
                        properties: EdgeProperties::new(),
                    },
                    Way {
                        id: 0,
                        nodes: vec![4, 5, 2],
                        properties: EdgeProperties::new(),
                    }];
    let mut r = Reader {
        nodes: nodes,
        ways: ways,
    };
    r.count_nodes_uses();
    let edges = r.edges();
    assert_eq!(3, edges.len());
}

#[test]
fn test_wrong_file() {
    let r = read("i hope you have no file name like this one");
    assert!(r.is_err());
}
<|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 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", "ÿ"),
    ];

    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>don't bother running through all the string replace possibilities if there are no escape codes<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(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));
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use regex::Regex;

use rocket::request::FromParam;
use rocket::http::RawStr;

#[derive(Debug)]
pub struct IdRange(HashSet<u32>);

impl<'req> FromParam<'req> for IdRange {
    type Error = ();

    fn from_param(param: &'req RawStr) -> Result<Self, Self::Error> {

        let term_regex = Regex::new("(?P<from>[0-9]+)(?::(?P<to>[0-9]+))?").expect("invalid regex");

        let mut range = IdRange(HashSet::new());

        for term in param.split(',') {
            let caps = term_regex.captures(term).ok_or(())?;

            let from_str = caps.name("from").ok_or(())?.as_str();
            let from = from_str.parse().map_err(|_| ())?;

            if let Some(to_match) = caps.name("to") {
                let to = to_match.as_str().parse().map_err(|_| ())?;

                let (from, to) = if from <= to {
                    (from, to)
                } else {
                    (to, from)
                };

                for i in from..=to {
                    range.0.insert(i);
                }

            } else {
                range.0.insert(from);
            }
        }

        Ok(range)
    }
}
<commit_msg>Implemented IntoIterator for IdRange<commit_after>use std::collections::HashSet;
use regex::Regex;

use rocket::request::FromParam;
use rocket::http::RawStr;

#[derive(Debug, Clone)]
pub struct IdRange(HashSet<u32>);

impl<'req> FromParam<'req> for IdRange {
    type Error = ();

    fn from_param(param: &'req RawStr) -> Result<Self, Self::Error> {

        let term_regex = Regex::new("(?P<from>[0-9]+)(?::(?P<to>[0-9]+))?").expect("invalid regex");

        let mut range = IdRange(HashSet::new());

        for term in param.split(',') {
            let caps = term_regex.captures(term).ok_or(())?;

            let from_str = caps.name("from").ok_or(())?.as_str();
            let from = from_str.parse().map_err(|_| ())?;

            if let Some(to_match) = caps.name("to") {
                let to = to_match.as_str().parse().map_err(|_| ())?;

                let (from, to) = if from <= to {
                    (from, to)
                } else {
                    (to, from)
                };

                for i in from..=to {
                    range.0.insert(i);
                }

            } else {
                range.0.insert(from);
            }
        }

        Ok(range)
    }
}

impl IntoIterator for IdRange {
    type Item = u32;
    type IntoIter = ::std::collections::hash_set::IntoIter<u32>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}<|endoftext|>


<commit_before>use std::io;
use std::io::{Read, Write};
use std::fs::File;
use std::path::Path;

pub fn write_file<P: AsRef<Path>>(path: P, contents: &Vec<u8>) -> io::Result<()> {
    let mut file = try!(File::create(path));
    Ok(try!(file.write_all(contents)))
}

pub fn read_file_to_string<P: AsRef<Path>>(path: P, buf: &mut String) -> io::Result<usize> {
    let mut file = try!(File::open(path));
    let bytes = try!(file.read_to_string(buf));
    Ok(bytes)
}
<commit_msg>Add a forgotten read_file function to utils<commit_after>use std::io;
use std::io::{Read, Write};
use std::fs::File;
use std::path::Path;

pub fn write_file<P: AsRef<Path>>(path: P, contents: &Vec<u8>) -> io::Result<()> {
    let mut file = try!(File::create(path));
    Ok(try!(file.write_all(contents)))
}

pub fn read_file<P: AsRef<Path>>(path: P, buf: &mut Vec<u8>) -> io::Result<usize> {
    let mut file = try!(File::open(path));
    let bytes = try!(file.read_to_end(buf));
    Ok(bytes)
}

pub fn read_file_to_string<P: AsRef<Path>>(path: P, buf: &mut String) -> io::Result<usize> {
    let mut file = try!(File::open(path));
    let bytes = try!(file.read_to_string(buf));
    Ok(bytes)
}
<|endoftext|>


<commit_before>use rustc::lint::{Context, Lint, Level};
use syntax::ast::{DefId, Expr, Name, NodeId, Path};
use syntax::codemap::{ExpnInfo, Span};
use syntax::ptr::P;
use rustc::ast_map::Node::NodeExpr;
use rustc::middle::ty;
use std::borrow::{Cow, IntoCow};
use std::convert::From;

/// returns true if the macro that expanded the crate was outside of
/// the current crate or was a compiler plugin
pub fn in_macro(cx: &Context, opt_info: Option<&ExpnInfo>) -> bool {
    // no ExpnInfo = no macro
    opt_info.map_or(false, |info| {
        // no span for the callee = external macro
        info.callee.span.map_or(true, |span| {
            // no snippet = external macro or compiler-builtin expansion
            cx.sess().codemap().span_to_snippet(span).ok().map_or(true, |code|
                // macro doesn't start with "macro_rules"
                // = compiler plugin
                !code.starts_with("macro_rules")
            )
        })
    })
}

/// invokes in_macro with the expansion info of the given span
pub fn in_external_macro(cx: &Context, span: Span) -> bool {
    cx.sess().codemap().with_expn_info(span.expn_id,
            |info| in_macro(cx, info))
}

/// check if a DefId's path matches the given absolute type path
/// usage e.g. with
/// `match_def_path(cx, id, &["core", "option", "Option"])`
pub fn match_def_path(cx: &Context, def_id: DefId, path: &[&str]) -> bool {
    cx.tcx.with_path(def_id, |iter| iter.map(|elem| elem.name())
        .zip(path.iter()).all(|(nm, p)| &nm.as_str() == p))
}

/// match a Path against a slice of segment string literals, e.g.
/// `match_path(path, &["std", "rt", "begin_unwind"])`
pub fn match_path(path: &Path, segments: &[&str]) -> bool {
    path.segments.iter().rev().zip(segments.iter().rev()).all(
        |(a,b)| &a.identifier.name.as_str() == b)
}

/// convert a span to a code snippet if available, otherwise use default, e.g.
/// `snippet(cx, expr.span, "..")`
pub fn snippet<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
    cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or(Cow::Borrowed(default))
}

/// convert a span (from a block) to a code snippet if available, otherwise use default, e.g.
/// `snippet(cx, expr.span, "..")`
/// This trims the code of indentation, except for the first line
/// Use it for blocks or block-like things which need to be printed as such
pub fn snippet_block<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
    let snip = snippet(cx, span, default);
    trim_multiline(snip, true)
}

/// Trim indentation from a multiline string
/// with possibility of ignoring the first line
pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
    let x = s.lines().skip(ignore_first as usize)
             .map(|l| l.char_indices()
                       .find(|&(_,x)| x != ' ')
                       .unwrap_or((l.len(),' ')).0)
             .min().unwrap_or(0);
    if x > 0 {
        Cow::Owned(s.lines().enumerate().map(|(i,l)| if ignore_first && i==0 {
                                                        l
                                                     } else {
                                                        l.split_at(x).1
                                                     }).collect::<Vec<_>>()
                                       .join("\n"))
    } else {
        s
    }
}

/// get a parent expr if any – this is useful to constrain a lint
pub fn get_parent_expr<'c>(cx: &'c Context, e: &Expr) -> Option<&'c Expr> {
    let map = &cx.tcx.map;
    let node_id : NodeId = e.id;
    let parent_id : NodeId = map.get_parent_node(node_id);
    if node_id == parent_id { return None; }
    map.find(parent_id).and_then(|node|
        if let NodeExpr(parent) = node { Some(parent) } else { None } )
}

/// dereference a P<T> and return a ref on the result
pub fn de_p<T>(p: &P<T>) -> &T { &*p }

#[cfg(not(feature="structured_logging"))]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
    cx.span_lint(lint, sp, msg);
}

#[cfg(feature="structured_logging")]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
    // lint.name / lint.desc is can give details of the lint
    // cx.sess().codemap() has all these nice functions for line/column/snippet details
    // http://doc.rust-lang.org/syntax/codemap/struct.CodeMap.html#method.span_to_string
    cx.span_lint(lint, sp, msg);
}

pub fn span_help_and_lint(cx: &Context, lint: &'static Lint, span: Span,
        msg: &str, help: &str) {
    span_lint(cx, lint, span, msg);
    if cx.current_level(lint) != Level::Allow {
        cx.sess().fileline_help(span, help);
    }
}

/// return the base type for references and raw pointers
pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty {
    match ty.sty {
        ty::TyRef(_, ref tm) | ty::TyRawPtr(ref tm) => walk_ptrs_ty(tm.ty),
        _ => ty
    }
}

/// Produce a nested chain of if-lets and ifs from the patterns:
///
///     if_let_chain! {
///         [
///             Some(y) = x,
///             y.len() == 2,
///             Some(z) = y,
///         ],
///         {
///             block
///         }
///     }
///
/// becomes
///
///     if let Some(y) = x {
///         if y.len() == 2 {
///             if let Some(z) = y {
///                 block
///             }
///         }
///     }
#[macro_export]
macro_rules! if_let_chain {
    ([let $pat:pat = $expr:expr, $($tt:tt)+], $block:block) => {
        if let $pat = $expr {
           if_let_chain!{ [$($tt)+], $block }
        }
    };
    ([let $pat:pat = $expr:expr], $block:block) => {
        if let $pat = $expr {
           $block
        }
    };
    ([$expr:expr, $($tt:tt)+], $block:block) => {
        if $expr {
           if_let_chain!{ [$($tt)+], $block }
        }
    };
    ([$expr:expr], $block:block) => {
        if $expr {
           $block
        }
    };
}
<commit_msg>Handle tabs<commit_after>use rustc::lint::{Context, Lint, Level};
use syntax::ast::{DefId, Expr, Name, NodeId, Path};
use syntax::codemap::{ExpnInfo, Span};
use syntax::ptr::P;
use rustc::ast_map::Node::NodeExpr;
use rustc::middle::ty;
use std::borrow::{Cow, IntoCow};
use std::convert::From;

/// returns true if the macro that expanded the crate was outside of
/// the current crate or was a compiler plugin
pub fn in_macro(cx: &Context, opt_info: Option<&ExpnInfo>) -> bool {
    // no ExpnInfo = no macro
    opt_info.map_or(false, |info| {
        // no span for the callee = external macro
        info.callee.span.map_or(true, |span| {
            // no snippet = external macro or compiler-builtin expansion
            cx.sess().codemap().span_to_snippet(span).ok().map_or(true, |code|
                // macro doesn't start with "macro_rules"
                // = compiler plugin
                !code.starts_with("macro_rules")
            )
        })
    })
}

/// invokes in_macro with the expansion info of the given span
pub fn in_external_macro(cx: &Context, span: Span) -> bool {
    cx.sess().codemap().with_expn_info(span.expn_id,
            |info| in_macro(cx, info))
}

/// check if a DefId's path matches the given absolute type path
/// usage e.g. with
/// `match_def_path(cx, id, &["core", "option", "Option"])`
pub fn match_def_path(cx: &Context, def_id: DefId, path: &[&str]) -> bool {
    cx.tcx.with_path(def_id, |iter| iter.map(|elem| elem.name())
        .zip(path.iter()).all(|(nm, p)| &nm.as_str() == p))
}

/// match a Path against a slice of segment string literals, e.g.
/// `match_path(path, &["std", "rt", "begin_unwind"])`
pub fn match_path(path: &Path, segments: &[&str]) -> bool {
    path.segments.iter().rev().zip(segments.iter().rev()).all(
        |(a,b)| &a.identifier.name.as_str() == b)
}

/// convert a span to a code snippet if available, otherwise use default, e.g.
/// `snippet(cx, expr.span, "..")`
pub fn snippet<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
    cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or(Cow::Borrowed(default))
}

/// convert a span (from a block) to a code snippet if available, otherwise use default, e.g.
/// `snippet(cx, expr.span, "..")`
/// This trims the code of indentation, except for the first line
/// Use it for blocks or block-like things which need to be printed as such
pub fn snippet_block<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
    let snip = snippet(cx, span, default);
    trim_multiline(snip, true)
}

/// Trim indentation from a multiline string
/// with possibility of ignoring the first line
pub fn trim_multiline<'a>(s: Cow<'a, str>, ignore_first: bool) -> Cow<'a, str> {
    let s = trim_multiline_inner(s, ignore_first, ' ');
    let s = trim_multiline_inner(s, ignore_first, '\t');
    trim_multiline_inner(s, ignore_first, ' ')
}

fn trim_multiline_inner<'a>(s: Cow<'a, str>, ignore_first: bool, ch: char) -> Cow<'a, str> {
    let x = s.lines().skip(ignore_first as usize)
             .map(|l| l.char_indices()
                       .find(|&(_,x)| x != ch)
                       .unwrap_or((l.len(), ch)).0)
             .min().unwrap_or(0);
    if x > 0 {
        Cow::Owned(s.lines().enumerate().map(|(i,l)| if ignore_first && i==0 {
                                                        l
                                                     } else {
                                                        l.split_at(x).1
                                                     }).collect::<Vec<_>>()
                                       .join("\n"))
    } else {
        s
    }
}

/// get a parent expr if any – this is useful to constrain a lint
pub fn get_parent_expr<'c>(cx: &'c Context, e: &Expr) -> Option<&'c Expr> {
    let map = &cx.tcx.map;
    let node_id : NodeId = e.id;
    let parent_id : NodeId = map.get_parent_node(node_id);
    if node_id == parent_id { return None; }
    map.find(parent_id).and_then(|node|
        if let NodeExpr(parent) = node { Some(parent) } else { None } )
}

/// dereference a P<T> and return a ref on the result
pub fn de_p<T>(p: &P<T>) -> &T { &*p }

#[cfg(not(feature="structured_logging"))]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
    cx.span_lint(lint, sp, msg);
}

#[cfg(feature="structured_logging")]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
    // lint.name / lint.desc is can give details of the lint
    // cx.sess().codemap() has all these nice functions for line/column/snippet details
    // http://doc.rust-lang.org/syntax/codemap/struct.CodeMap.html#method.span_to_string
    cx.span_lint(lint, sp, msg);
}

pub fn span_help_and_lint(cx: &Context, lint: &'static Lint, span: Span,
        msg: &str, help: &str) {
    span_lint(cx, lint, span, msg);
    if cx.current_level(lint) != Level::Allow {
        cx.sess().fileline_help(span, help);
    }
}

/// return the base type for references and raw pointers
pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty {
    match ty.sty {
        ty::TyRef(_, ref tm) | ty::TyRawPtr(ref tm) => walk_ptrs_ty(tm.ty),
        _ => ty
    }
}

/// Produce a nested chain of if-lets and ifs from the patterns:
///
///     if_let_chain! {
///         [
///             Some(y) = x,
///             y.len() == 2,
///             Some(z) = y,
///         ],
///         {
///             block
///         }
///     }
///
/// becomes
///
///     if let Some(y) = x {
///         if y.len() == 2 {
///             if let Some(z) = y {
///                 block
///             }
///         }
///     }
#[macro_export]
macro_rules! if_let_chain {
    ([let $pat:pat = $expr:expr, $($tt:tt)+], $block:block) => {
        if let $pat = $expr {
           if_let_chain!{ [$($tt)+], $block }
        }
    };
    ([let $pat:pat = $expr:expr], $block:block) => {
        if let $pat = $expr {
           $block
        }
    };
    ([$expr:expr, $($tt:tt)+], $block:block) => {
        if $expr {
           if_let_chain!{ [$($tt)+], $block }
        }
    };
    ([$expr:expr], $block:block) => {
        if $expr {
           $block
        }
    };
}
<|endoftext|>


<commit_before>#![cfg(target_os = "macos")]

use CreationError;
use ContextError;
use GlAttributes;
use PixelFormat;
use PixelFormatRequirements;
use Robustness;

use objc::runtime::{BOOL, NO};

use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity};

use cocoa::base::{id, nil};
use cocoa::foundation::NSAutoreleasePool;
use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat};

use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};

use std::str::FromStr;
use std::ops::Deref;

use winit;
use winit::os::macos::WindowExt;
pub use winit::{MonitorId, NativeMonitorId, get_available_monitors, get_primary_monitor};
pub use self::headless::HeadlessContext;
pub use self::headless::PlatformSpecificHeadlessBuilderAttributes;

mod headless;
mod helpers;

pub struct Context {
    // NSOpenGLContext
    gl: IdRef,
    pixel_format: PixelFormat,
}

impl Context {

    pub fn new(
        window: &winit::Window,
        pf_reqs: &PixelFormatRequirements,
        gl_attr: &GlAttributes<&Context>,
    ) -> Result<Self, CreationError>
    {
        if gl_attr.sharing.is_some() {
            unimplemented!()
        }

        match gl_attr.robustness {
            Robustness::RobustNoResetNotification |
            Robustness::RobustLoseContextOnReset => {
                return Err(CreationError::RobustnessNotSupported);
            }
            _ => (),
        }

        let view = window.get_nsview() as id;

        let attributes = try!(helpers::build_nsattributes(pf_reqs, gl_attr));
        unsafe {
            let pixel_format = IdRef::new(NSOpenGLPixelFormat::alloc(nil)
                .initWithAttributes_(&attributes));
            let pixel_format = match pixel_format.non_nil() {
                None => return Err(CreationError::NoAvailablePixelFormat),
                Some(pf) => pf,
            };

            // TODO: Add context sharing
            let gl_context = IdRef::new(NSOpenGLContext::alloc(nil)
                .initWithFormat_shareContext_(*pixel_format, nil));
            let gl_context = match gl_context.non_nil() {
                Some(gl_context) => gl_context,
                None => return Err(CreationError::NotSupported),
            };

            let pixel_format = {
                let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 {
                    let mut value = 0;
                    NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_(
                        *pixel_format,
                        &mut value,
                        attrib,
                        NSOpenGLContext::currentVirtualScreen(*gl_context));
                    value
                };

                PixelFormat {
                    hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0,
                    color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - get_attr(appkit::NSOpenGLPFAAlphaSize)) as u8,
                    alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8,
                    depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8,
                    stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8,
                    stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0,
                    double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0,
                    multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 {
                        Some(get_attr(appkit::NSOpenGLPFASamples) as u16)
                    } else {
                        None
                    },
                    srgb: true,
                }
            };

            gl_context.setView_(view);
            let value = if gl_attr.vsync { 1 } else { 0 };
            gl_context.setValues_forParameter_(&value, appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval);

            if gl_attr.transparent {
                let mut opacity = 0;
                CGLSetParameter(gl_context.CGLContextObj() as *mut _, kCGLCPSurfaceOpacity, &mut opacity);
            }

            CGLEnable(gl_context.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);

            Ok(Context { gl: gl_context, pixel_format: pixel_format })
        }
    }

    pub fn resize(&self, _width: u32, _height: u32) {
        unsafe { self.gl.update(); }
    }

    #[inline]
    pub unsafe fn make_current(&self) -> Result<(), ContextError> {
        let _: () = msg_send![*self.gl, update];
        self.gl.makeCurrentContext();
        Ok(())
    }

    #[inline]
    pub fn is_current(&self) -> bool {
        unsafe {
            let current = NSOpenGLContext::currentContext(nil);
            if current != nil {
                let is_equal: BOOL = msg_send![current, isEqual:*self.gl];
                is_equal != NO
            } else {
                false
            }
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        let symbol_name: CFString = FromStr::from_str(addr).unwrap();
        let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
        let framework =
            unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
        let symbol = unsafe {
            CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
        };
        symbol as *const _
    }

    #[inline]
    pub fn swap_buffers(&self) -> Result<(), ContextError> {
        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            self.gl.flushBuffer();
            let _: () = msg_send![pool, release];
        }
        Ok(())
    }

    #[inline]
    pub fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    #[inline]
    pub fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

struct IdRef(id);

impl IdRef {
    fn new(i: id) -> IdRef {
        IdRef(i)
    }

    #[allow(dead_code)]
    fn retain(i: id) -> IdRef {
        if i != nil {
            let _: id = unsafe { msg_send![i, retain] };
        }
        IdRef(i)
    }

    fn non_nil(self) -> Option<IdRef> {
        if self.0 == nil { None } else { Some(self) }
    }
}

impl Drop for IdRef {
    fn drop(&mut self) {
        if self.0 != nil {
            let _: () = unsafe { msg_send![self.0, release] };
        }
    }
}

impl Deref for IdRef {
    type Target = id;
    fn deref<'a>(&'a self) -> &'a id {
        &self.0
    }
}

impl Clone for IdRef {
    fn clone(&self) -> IdRef {
        if self.0 != nil {
            let _: id = unsafe { msg_send![self.0, retain] };
        }
        IdRef(self.0)
    }
}
<commit_msg>Update macos platform to API update using WindowBuilder rather than Window directly<commit_after>#![cfg(target_os = "macos")]

use CreationError;
use ContextError;
use GlAttributes;
use PixelFormat;
use PixelFormatRequirements;
use Robustness;

use objc::runtime::{BOOL, NO};

use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity};

use cocoa::base::{id, nil};
use cocoa::foundation::NSAutoreleasePool;
use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat};

use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};

use std::str::FromStr;
use std::ops::Deref;

use winit;
use winit::os::macos::WindowExt;
pub use winit::{MonitorId, NativeMonitorId, get_available_monitors, get_primary_monitor};
pub use self::headless::HeadlessContext;
pub use self::headless::PlatformSpecificHeadlessBuilderAttributes;

mod headless;
mod helpers;

pub struct Context {
    // NSOpenGLContext
    gl: IdRef,
    pixel_format: PixelFormat,
}

impl Context {

    pub fn new(
        window_builder: winit::WindowBuilder,
        events_loop: &winit::EventsLoop,
        pf_reqs: &PixelFormatRequirements,
        gl_attr: &GlAttributes<&Context>,
    ) -> Result<(winit::Window, Self), CreationError>
    {
        let transparent = window_builder.window.transparent;
        let window = try!(window_builder.build(events_loop));

        if gl_attr.sharing.is_some() {
            unimplemented!()
        }

        match gl_attr.robustness {
            Robustness::RobustNoResetNotification |
            Robustness::RobustLoseContextOnReset => {
                return Err(CreationError::RobustnessNotSupported);
            }
            _ => (),
        }

        let view = window.get_nsview() as id;

        let attributes = try!(helpers::build_nsattributes(pf_reqs, gl_attr));
        unsafe {
            let pixel_format = IdRef::new(NSOpenGLPixelFormat::alloc(nil)
                .initWithAttributes_(&attributes));
            let pixel_format = match pixel_format.non_nil() {
                None => return Err(CreationError::NoAvailablePixelFormat),
                Some(pf) => pf,
            };

            // TODO: Add context sharing
            let gl_context = IdRef::new(NSOpenGLContext::alloc(nil)
                .initWithFormat_shareContext_(*pixel_format, nil));
            let gl_context = match gl_context.non_nil() {
                Some(gl_context) => gl_context,
                None => return Err(CreationError::NotSupported),
            };

            let pixel_format = {
                let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 {
                    let mut value = 0;
                    NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_(
                        *pixel_format,
                        &mut value,
                        attrib,
                        NSOpenGLContext::currentVirtualScreen(*gl_context));
                    value
                };

                PixelFormat {
                    hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0,
                    color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - get_attr(appkit::NSOpenGLPFAAlphaSize)) as u8,
                    alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8,
                    depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8,
                    stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8,
                    stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0,
                    double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0,
                    multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 {
                        Some(get_attr(appkit::NSOpenGLPFASamples) as u16)
                    } else {
                        None
                    },
                    srgb: true,
                }
            };

            gl_context.setView_(view);
            let value = if gl_attr.vsync { 1 } else { 0 };
            gl_context.setValues_forParameter_(&value, appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval);

            if transparent {
                let mut opacity = 0;
                CGLSetParameter(gl_context.CGLContextObj() as *mut _, kCGLCPSurfaceOpacity, &mut opacity);
            }

            CGLEnable(gl_context.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);

            let context = Context { gl: gl_context, pixel_format: pixel_format };
            Ok((window, context))
        }
    }

    pub fn resize(&self, _width: u32, _height: u32) {
        unsafe { self.gl.update(); }
    }

    #[inline]
    pub unsafe fn make_current(&self) -> Result<(), ContextError> {
        let _: () = msg_send![*self.gl, update];
        self.gl.makeCurrentContext();
        Ok(())
    }

    #[inline]
    pub fn is_current(&self) -> bool {
        unsafe {
            let current = NSOpenGLContext::currentContext(nil);
            if current != nil {
                let is_equal: BOOL = msg_send![current, isEqual:*self.gl];
                is_equal != NO
            } else {
                false
            }
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        let symbol_name: CFString = FromStr::from_str(addr).unwrap();
        let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
        let framework =
            unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
        let symbol = unsafe {
            CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
        };
        symbol as *const _
    }

    #[inline]
    pub fn swap_buffers(&self) -> Result<(), ContextError> {
        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            self.gl.flushBuffer();
            let _: () = msg_send![pool, release];
        }
        Ok(())
    }

    #[inline]
    pub fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    #[inline]
    pub fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

struct IdRef(id);

impl IdRef {
    fn new(i: id) -> IdRef {
        IdRef(i)
    }

    #[allow(dead_code)]
    fn retain(i: id) -> IdRef {
        if i != nil {
            let _: id = unsafe { msg_send![i, retain] };
        }
        IdRef(i)
    }

    fn non_nil(self) -> Option<IdRef> {
        if self.0 == nil { None } else { Some(self) }
    }
}

impl Drop for IdRef {
    fn drop(&mut self) {
        if self.0 != nil {
            let _: () = unsafe { msg_send![self.0, release] };
        }
    }
}

impl Deref for IdRef {
    type Target = id;
    fn deref<'a>(&'a self) -> &'a id {
        &self.0
    }
}

impl Clone for IdRef {
    fn clone(&self) -> IdRef {
        if self.0 != nil {
            let _: id = unsafe { msg_send![self.0, retain] };
        }
        IdRef(self.0)
    }
}
<|endoftext|>


<commit_before>use std::io::{BufReader, BufferedReader, Reader};
use std::strbuf::StrBuf;
use std::char::from_u32;
use std::num::FromStrRadix;
use ast::{TIdent, TNumber, TString, TSemicolon, TComment, TColon, TDot, TEqual, TOpenParen, TCloseParen, TComma, TOpenBlock, TCloseBlock, TOpenArray, TCloseArray, TQuestion, TNumOp, TBitOp, TCompOp, TLogOp, TArrow};
use ast::{OpAdd, OpSub, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor};
use ast::{CompEqual, CompNotEqual, CompLessThan, CompGreaterThan, CompLessThanOrEqual, CompGreaterThanOrEqual};
use ast::{LogAnd, LogOr};
use ast::{Token, TokenData};
use std::io::{IoResult, EndOfFile};
use std::char::is_whitespace;
#[deriving(Clone)]
#[deriving(Eq)]
#[deriving(Show)]
/// The type of string used
pub enum StringType {
	/// Double-quoted
	DoubleQuote,
	/// Single-quoted
	SingleQuote
}
#[deriving(Clone)]
#[deriving(Eq)]
#[deriving(Show)]
/// The type of comment used
pub enum CommentType {
	/// Multi-line comment
	MultiLineComment,
	/// Single-line comment
	SingleLineComment
}
/// The Javascript Lexer
pub struct Lexer {
	/// The list of tokens generated so far
	pub tokens : Vec<Token>,
	/// The string buffer for identities
	ident_buffer : StrBuf,
	/// The string buffer for strings
	string_buffer : StrBuf,
	/// The string buffer for comments
	comment_buffer : StrBuf,
	/// The string buffer for numbers
	num_buffer : StrBuf,
	/// The kind of string - i.e. double quote or single quote or none if it isn't in a string
	string_start : Option<StringType>,
	/// The kind of comment
	current_comment : Option<CommentType>,
	/// If the lexer is currently inside a number
	in_num : bool,
	/// If a backwards slash has just been read
	escaped : bool,
	/// The current line number
	line_number : uint,
	/// The current column number
	column_number : uint
}
impl Lexer {
	/// Creates a new lexer with empty buffers
	pub fn new() -> ~Lexer {
		return ~Lexer {
			tokens: Vec::new(),
			ident_buffer: StrBuf::with_capacity(32),
			string_buffer: StrBuf::with_capacity(256),
			num_buffer: StrBuf::with_capacity(16),
			comment_buffer: StrBuf::with_capacity(32),
			string_start: None,
			current_comment: None,
			in_num: false,
			escaped: false,
			line_number: 1,
			column_number: 0
		};
	}
	fn clear_buffer(&mut self) {
		if self.ident_buffer.len() > 0 {
			let ident = self.ident_buffer.clone().into_owned();
			self.push_token(TIdent(ident));
			self.ident_buffer.truncate(0);
		}
		if self.in_num {
			let num = from_str(self.num_buffer.clone().as_slice());
			self.push_token(TNumber(num.unwrap()));
			self.num_buffer.truncate(0);
			self.in_num = false;
		}
	}
	fn push_token(&mut self, tk:TokenData) {
		self.tokens.push(Token::new(tk, self.line_number, self.column_number))
	}
	/// Processes an input stream from a string into an array of tokens
	pub fn lex_str(&mut self, script:~str) -> IoResult<()> {
		let script_bytes:&[u8] = script.as_bytes();
		let reader = BufReader::new(script_bytes);
		let buf_reader = BufferedReader::new(reader);
		self.lex(buf_reader)
	}
	/// Processes an input stream from a BufferedReader into an array of tokens
	pub fn lex<R : Reader>(&mut self, mut reader : BufferedReader<R>) -> IoResult<()> {
		let mut iter = reader.chars().peekable();
		loop {
			let ch = match iter.next() {
				Some(Ok(ch)) => ch,
				Some(Err(ref err)) if err.kind == EndOfFile => break,
				Some(Err(err)) => return Err(err),
				None => break
			};
			self.column_number += 1;
			match ch {
				_ if self.escaped => {
					self.escaped = false;
					if ch != '\n' {
						self.string_buffer.push_char(match ch {
							'n' => '\n',
							'r' => '\r',
							't' => '\t',
							'b' => '\x08',
							'f' => '\x0c',
							'0' => '\0',
							'x' => {
								let mut nums = ~"";
								for _ in range(0, 2) {
									nums = format!("{}{}", nums, try!(iter.next().unwrap().clone()));
								}
								self.column_number += 2;
								let as_num = match FromStrRadix::from_str_radix(nums, 16) {
									Some(v) => v,
									None => 0
								};
								match from_u32(as_num) {
									Some(v) => v,
									None => fail!("Line {}, Column {}: {} is not a valid unicode scalar value", self.line_number, self.column_number, as_num)
								}
							},
							'u' => {
								let mut nums = ~"";
								for _ in range(0, 4) {
									nums = format!("{}{}", nums, try!(iter.next().unwrap().clone()));
								}
								self.column_number += 4;
								let as_num = match FromStrRadix::from_str_radix(nums, 16) {
									Some(v) => v,
									None => 0
								};
								match from_u32(as_num) {
									Some(v) => v,
									None => fail!("Line {}, Column {}: {} is not a valid unicode scalar value", self.line_number, self.column_number, as_num)
								}
							},
							'\'' if self.string_start == Some(SingleQuote) => '\'',
							'"' if self.string_start == Some(DoubleQuote) => '"',
							_ => fail!("Line {}, Column {}: Invalid escape `{}`", self.line_number, self.column_number, ch)
						});
					}
				},
				'\n' if self.current_comment == Some(SingleLineComment) => {
					let comment = self.comment_buffer.clone().into_owned();
					self.push_token(TComment(comment));
					self.comment_buffer.truncate(0);
					self.current_comment = None;
				},
				'*' if self.current_comment == Some(MultiLineComment) && try!(iter.peek().unwrap().clone()) == '/' => {
					iter.next();
					let comment = self.comment_buffer.clone().into_owned();
					self.push_token(TComment(comment));
					self.comment_buffer.truncate(0);
					self.current_comment = None;
				},
				_ if self.current_comment.is_some() => {
					self.comment_buffer.push_char(ch);
				},
				'"' if self.string_start == Some(DoubleQuote) => {
					self.string_start = None;
					let string = self.string_buffer.clone().into_owned();
					self.push_token(TString(string));
					self.string_buffer.truncate(0);
				},
				'\'' if self.string_start == Some(SingleQuote) => {
					self.string_start = None;
					let string = self.string_buffer.clone().into_owned();
					self.push_token(TString(string));
					self.string_buffer.truncate(0);
				},
				'\\' if self.string_start.is_some() => self.escaped = true,
				_ if self.string_start.is_some() => self.string_buffer.push_char(ch),
				'"' if self.string_start.is_none() => self.string_start = Some(DoubleQuote),
				'0'.. '9' if self.string_start.is_none() && self.ident_buffer.len() == 0 => {
					self.num_buffer.push_char(ch);
					self.in_num = true;
				},
				'\'' if self.string_start.is_none() => self.string_start = Some(SingleQuote),
				'.' if self.in_num && !self.num_buffer.as_slice().contains(".") => {
					self.num_buffer.push_char(ch);
				},
				';' => {
					self.clear_buffer();
					self.push_token(TSemicolon);
				},
				':' => {
					self.clear_buffer();
					self.push_token(TColon);
				},
				'.' => {
					self.clear_buffer();
					self.push_token(TDot);
				},
				'(' => {
					self.clear_buffer();
					self.push_token(TOpenParen);
				},
				')' => {
					self.clear_buffer();
					self.push_token(TCloseParen);
				},
				',' => {
					self.clear_buffer();
					self.push_token(TComma);
				},
				'{' => {
					self.clear_buffer();
					self.push_token(TOpenBlock);
				},
				'}' => {
					self.clear_buffer();
					self.push_token(TCloseBlock);
				},
				'[' => {
					self.clear_buffer();
					self.push_token(TOpenArray);
				},
				']' => {
					self.clear_buffer();
					self.push_token(TCloseArray);
				},
				'?' => {
					self.clear_buffer();
					self.push_token(TQuestion);
				},
				'/' if try!(iter.peek().unwrap().clone()) == '/' => {
					iter.next();
					self.current_comment = Some(SingleLineComment);
				},
				'/' if try!(iter.peek().unwrap().clone()) == '*' => {
					iter.next();
					self.current_comment = Some(MultiLineComment);
				},
				'/' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpDiv));
				},
				'*' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpMul));
				},
				'+' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpAdd));
				},
				'-' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpSub));
				},
				'%' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpMod));
				},
				'|' if try!(iter.peek().unwrap().clone()) == '|' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TLogOp(LogOr));
				},
				'|' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitOr));
				},
				'&' if try!(iter.peek().unwrap().clone()) == '&' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TLogOp(LogAnd));
				},
				'&' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitAnd));
				},
				'^' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitXor));
				},
				'=' if try!(iter.peek().unwrap().clone()) == '>' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TArrow);
				},
				'=' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompEqual));
				},
				'=' => {
					self.clear_buffer();
					self.push_token(TEqual);
				},
				'<' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompLessThanOrEqual));
				},
				'<' => {
					self.clear_buffer();
					self.push_token(TCompOp(CompLessThan));
				},
				'>' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompGreaterThanOrEqual));
				},
				'>' => {
					self.clear_buffer();
					self.push_token(TCompOp(CompGreaterThan));
				},
				'!' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompNotEqual));
				},
				'\n' => {
					self.clear_buffer();
					self.line_number += 1;
					self.column_number = 0;
				},
				'\r' => {
					self.clear_buffer();
					self.column_number = 0;
				},
				_ if is_whitespace(ch) => self.clear_buffer(),
				_ => self.ident_buffer.push_char(ch)
			};
		}
		self.clear_buffer();
		Ok(())
	}
}<commit_msg>Implement hexadecimal numbers in lexer<commit_after>use ast::{TIdent, TNumber, TString, TSemicolon, TComment, TColon, TDot, TEqual, TOpenParen, TCloseParen, TComma, TOpenBlock, TCloseBlock, TOpenArray, TCloseArray, TQuestion, TNumOp, TBitOp, TCompOp, TLogOp, TArrow};
use ast::{OpAdd, OpSub, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor};
use ast::{CompEqual, CompNotEqual, CompLessThan, CompGreaterThan, CompLessThanOrEqual, CompGreaterThanOrEqual};
use ast::{LogAnd, LogOr};
use ast::{Token, TokenData};
use std::io::{BufReader, BufferedReader, Reader};
use std::strbuf::StrBuf;
use std::char::from_u32;
use std::num::from_str_radix;
use std::io::{IoResult, EndOfFile};
use std::char::is_whitespace;
#[deriving(Clone)]
#[deriving(Eq)]
#[deriving(Show)]
/// The type of string used
pub enum StringType {
	/// Double-quoted
	DoubleQuote,
	/// Single-quoted
	SingleQuote
}
#[deriving(Clone)]
#[deriving(Eq)]
#[deriving(Show)]
/// The type of comment used
pub enum CommentType {
	/// Multi-line comment
	MultiLineComment,
	/// Single-line comment
	SingleLineComment
}
#[deriving(Clone, Eq, Show)]
/// The type of number used
pub enum NumberType {
	/// Decimal number
	DecimalNumber,
	/// Hexadecimal number
	HexadecimalNumber
}
/// The Javascript Lexer
pub struct Lexer {
	/// The list of tokens generated so far
	pub tokens : Vec<Token>,
	/// The string buffer for identities
	ident_buffer : StrBuf,
	/// The string buffer for strings
	string_buffer : StrBuf,
	/// The string buffer for comments
	comment_buffer : StrBuf,
	/// The string buffer for numbers
	num_buffer : StrBuf,
	/// The kind of string - i.e. double quote or single quote or none if it isn't in a string
	string_start : Option<StringType>,
	/// The kind of comment
	current_comment : Option<CommentType>,
	/// The kind of number
	current_number : Option<NumberType>,
	/// If a backwards slash has just been read
	escaped : bool,
	/// The current line number
	line_number : uint,
	/// The current column number
	column_number : uint
}
impl Lexer {
	/// Creates a new lexer with empty buffers
	pub fn new() -> ~Lexer {
		return ~Lexer {
			tokens: Vec::new(),
			ident_buffer: StrBuf::with_capacity(32),
			string_buffer: StrBuf::with_capacity(256),
			num_buffer: StrBuf::with_capacity(16),
			comment_buffer: StrBuf::with_capacity(32),
			string_start: None,
			current_comment: None,
			current_number: None,
			escaped: false,
			line_number: 1,
			column_number: 0
		};
	}
	fn clear_buffer(&mut self) {
		if self.ident_buffer.len() > 0 {
			let ident = self.ident_buffer.clone().into_owned();
			self.push_token(TIdent(ident));
			self.ident_buffer.truncate(0);
		}
		if self.current_number.is_some() {
			let num : f64 = match self.current_number {
				Some(HexadecimalNumber) => from_str_radix(self.num_buffer.as_slice(), 16).unwrap(),
				Some(DecimalNumber) => from_str(self.num_buffer.as_slice()).unwrap(),
				None => unreachable!()
			};
			self.push_token(TNumber(num));
			self.num_buffer.truncate(0);
			self.current_number = None;
		}
	}
	fn push_token(&mut self, tk:TokenData) {
		self.tokens.push(Token::new(tk, self.line_number, self.column_number))
	}
	/// Processes an input stream from a string into an array of tokens
	pub fn lex_str(&mut self, script:~str) -> IoResult<()> {
		let script_bytes:&[u8] = script.as_bytes();
		let reader = BufReader::new(script_bytes);
		let buf_reader = BufferedReader::new(reader);
		self.lex(buf_reader)
	}
	/// Processes an input stream from a BufferedReader into an array of tokens
	pub fn lex<R : Reader>(&mut self, mut reader : BufferedReader<R>) -> IoResult<()> {
		let mut iter = reader.chars().peekable();
		loop {
			let ch = match iter.next() {
				Some(Ok(ch)) => ch,
				Some(Err(ref err)) if err.kind == EndOfFile => break,
				Some(Err(err)) => return Err(err),
				None => break
			};
			self.column_number += 1;
			match ch {
				_ if self.escaped => {
					self.escaped = false;
					if ch != '\n' {
						self.string_buffer.push_char(match ch {
							'n' => '\n',
							'r' => '\r',
							't' => '\t',
							'b' => '\x08',
							'f' => '\x0c',
							'0' => '\0',
							'x' => {
								let mut nums = ~"";
								for _ in range(0, 2) {
									nums = format!("{}{}", nums, try!(iter.next().unwrap().clone()));
								}
								self.column_number += 2;
								let as_num = match from_str_radix(nums, 16) {
									Some(v) => v,
									None => 0
								};
								match from_u32(as_num) {
									Some(v) => v,
									None => fail!("Line {}, Column {}: {} is not a valid unicode scalar value", self.line_number, self.column_number, as_num)
								}
							},
							'u' => {
								let mut nums = ~"";
								for _ in range(0, 4) {
									nums = format!("{}{}", nums, try!(iter.next().unwrap().clone()));
								}
								self.column_number += 4;
								let as_num = match from_str_radix(nums, 16) {
									Some(v) => v,
									None => 0
								};
								match from_u32(as_num) {
									Some(v) => v,
									None => fail!("Line {}, Column {}: {} is not a valid unicode scalar value", self.line_number, self.column_number, as_num)
								}
							},
							'\'' if self.string_start == Some(SingleQuote) => '\'',
							'"' if self.string_start == Some(DoubleQuote) => '"',
							_ => fail!("Line {}, Column {}: Invalid escape `{}`", self.line_number, self.column_number, ch)
						});
					}
				},
				'\n' if self.current_comment == Some(SingleLineComment) => {
					let comment = self.comment_buffer.clone().into_owned();
					self.push_token(TComment(comment));
					self.comment_buffer.truncate(0);
					self.current_comment = None;
				},
				'*' if self.current_comment == Some(MultiLineComment) && try!(iter.peek().unwrap().clone()) == '/' => {
					iter.next();
					let comment = self.comment_buffer.clone().into_owned();
					self.push_token(TComment(comment));
					self.comment_buffer.truncate(0);
					self.current_comment = None;
				},
				_ if self.current_comment.is_some() => {
					self.comment_buffer.push_char(ch);
				},
				'"' if self.string_start == Some(DoubleQuote) => {
					self.string_start = None;
					let string = self.string_buffer.clone().into_owned();
					self.push_token(TString(string));
					self.string_buffer.truncate(0);
				},
				'\'' if self.string_start == Some(SingleQuote) => {
					self.string_start = None;
					let string = self.string_buffer.clone().into_owned();
					self.push_token(TString(string));
					self.string_buffer.truncate(0);
				},
				'\\' if self.string_start.is_some() => self.escaped = true,
				_ if self.string_start.is_some() => self.string_buffer.push_char(ch),
				'"' if self.string_start.is_none() => self.string_start = Some(DoubleQuote),
				'0' if try!(iter.peek().unwrap().clone()) == 'x' => {
					iter.next();
					self.current_number = Some(HexadecimalNumber);
				},
				'0'.. '9' if self.string_start.is_none() && self.ident_buffer.len() == 0 => {
					self.num_buffer.push_char(ch);
					self.current_number = Some(DecimalNumber);
				},
				'A'.. 'F' | 'a' .. 'f' if self.current_number == Some(HexadecimalNumber) => {
					self.num_buffer.push_char(ch);
				},
				'\'' if self.string_start.is_none() => self.string_start = Some(SingleQuote),
				'.' if self.current_number.is_some() && !self.num_buffer.as_slice().contains(".") => {
					self.num_buffer.push_char(ch);
				},
				';' => {
					self.clear_buffer();
					self.push_token(TSemicolon);
				},
				':' => {
					self.clear_buffer();
					self.push_token(TColon);
				},
				'.' => {
					self.clear_buffer();
					self.push_token(TDot);
				},
				'(' => {
					self.clear_buffer();
					self.push_token(TOpenParen);
				},
				')' => {
					self.clear_buffer();
					self.push_token(TCloseParen);
				},
				',' => {
					self.clear_buffer();
					self.push_token(TComma);
				},
				'{' => {
					self.clear_buffer();
					self.push_token(TOpenBlock);
				},
				'}' => {
					self.clear_buffer();
					self.push_token(TCloseBlock);
				},
				'[' => {
					self.clear_buffer();
					self.push_token(TOpenArray);
				},
				']' => {
					self.clear_buffer();
					self.push_token(TCloseArray);
				},
				'?' => {
					self.clear_buffer();
					self.push_token(TQuestion);
				},
				'/' if try!(iter.peek().unwrap().clone()) == '/' => {
					iter.next();
					self.current_comment = Some(SingleLineComment);
				},
				'/' if try!(iter.peek().unwrap().clone()) == '*' => {
					iter.next();
					self.current_comment = Some(MultiLineComment);
				},
				'/' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpDiv));
				},
				'*' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpMul));
				},
				'+' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpAdd));
				},
				'-' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpSub));
				},
				'%' => {
					self.clear_buffer();
					self.push_token(TNumOp(OpMod));
				},
				'|' if try!(iter.peek().unwrap().clone()) == '|' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TLogOp(LogOr));
				},
				'|' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitOr));
				},
				'&' if try!(iter.peek().unwrap().clone()) == '&' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TLogOp(LogAnd));
				},
				'&' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitAnd));
				},
				'^' => {
					self.clear_buffer();
					self.push_token(TBitOp(BitXor));
				},
				'=' if try!(iter.peek().unwrap().clone()) == '>' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TArrow);
				},
				'=' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompEqual));
				},
				'=' => {
					self.clear_buffer();
					self.push_token(TEqual);
				},
				'<' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompLessThanOrEqual));
				},
				'<' => {
					self.clear_buffer();
					self.push_token(TCompOp(CompLessThan));
				},
				'>' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompGreaterThanOrEqual));
				},
				'>' => {
					self.clear_buffer();
					self.push_token(TCompOp(CompGreaterThan));
				},
				'!' if try!(iter.peek().unwrap().clone()) == '=' => {
					iter.next();
					self.clear_buffer();
					self.push_token(TCompOp(CompNotEqual));
				},
				'\n' => {
					self.clear_buffer();
					self.line_number += 1;
					self.column_number = 0;
				},
				'\r' => {
					self.clear_buffer();
					self.column_number = 0;
				},
				_ if is_whitespace(ch) => self.clear_buffer(),
				_ => self.ident_buffer.push_char(ch)
			};
		}
		self.clear_buffer();
		Ok(())
	}
}<|endoftext|>