<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use super::objects::{DocType, EsId, Admin};
use chrono;
use regex;
use hyper;
use hyper::status::StatusCode;
use rs_es::error::EsError;
use rs_es;
use rs_es::EsResponse;
use serde;
use serde_json;
use rs_es::operations::search::ScanResult;

use super::objects::{AliasOperations, AliasOperation, AliasParameter};
use rs_es::units::{Duration};
use std::collections::BTreeMap;

// Rubber is an wrapper around elasticsearch API
pub struct Rubber {
    es_client: rs_es::Client,
    // some operation are not implemented in rs_es, we need to use a raw http client
    http_client: hyper::client::Client,
}

/// return the index associated to the given type and dataset
/// this will be an alias over another real index
fn get_main_index(doc_type: &str, dataset: &str) -> String {
    format!("munin_{}_{}", doc_type, dataset)
}

impl Rubber {
    // build a rubber with a connection string (http://host:port/)
    pub fn new(cnx: &str) -> Rubber {
        let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
        let cap = re.captures(cnx).unwrap();
        let host = cap.name("host").unwrap();
        let port = cap.name("port").unwrap().parse::<u32>().unwrap();
        info!("elastic search host {:?} port {:?}", host, port);

        Rubber {
            es_client: rs_es::Client::new(&host, port),
            http_client: hyper::client::Client::new(),
        }
    }

    pub fn get(&self, path: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a get on {}", path);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .get(&url)
                              .send());
        rs_es::do_req(result)
    }
    fn put(&self, path: &str, body: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a put on {} with {}", path, body);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .put(&url)
                              .body(body)
                              .send());
        rs_es::do_req(result)
    }
    fn post(&self, path: &str, body: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a post on {} with {}", path, body);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .post(&url)
                              .body(body)
                              .send());
        rs_es::do_req(result)
    }

    pub fn make_index(&self, doc_type: &str, dataset: &str) -> Result<String, String> {
        let current_time = chrono::UTC::now().format("%Y%m%d_%H%M%S_%f");
        let index_name = format!("munin_{}_{}_{}", doc_type, dataset, current_time);
        info!("creating index {}", index_name);
        self.create_index(&index_name.to_string()).map(|_| index_name)
    }

    fn create_index(&self, name: &String) -> Result<(), String> {
        debug!("creating index");
        // Note: in rs_es it can be done with MappingOperation but for the moment I think
        // storing the mapping in json is more convenient
        let analysis = include_str!("../../../json/settings.json");
        self.put(name, &analysis)
            .map_err(|e| {
                info!("Error while creating new index {}", name);
                e.to_string()
            })
            .and_then(|res| {
                if res.status == StatusCode::Ok {
                    Ok(())
                } else {
                    Err(format!("cannot create index: {:?}", res))
                }
            })
    }

    fn get_last_index(&self, doc_type: &str, dataset: &str) -> Result<Vec<String>, String> {
        debug!("get last index: {base_index}/_aliases",
               base_index = get_main_index(doc_type, dataset));
        self.get(&format!("{base_index}/_aliases",
                          base_index = get_main_index(doc_type, dataset)))
            .map_err(|e| e.to_string())
            .and_then(|res| {
                match res.status {
                    StatusCode::Ok => {
                        let value: serde_json::Value = try!(res.read_response()
                                                               .map_err(|e| e.to_string()));
                        Ok(value.as_object()
                                .and_then(|aliases| Some(aliases.keys().cloned().collect()))
                                .unwrap_or_else(|| {
                                    info!("no previous index to delete for type {} and dataset {}",
                                          doc_type,
                                          dataset);
                                    vec![]
                                }))
                    }
                    StatusCode::NotFound => {
                        info!("impossible to find alias {}, no last index to remove",
                              get_main_index(doc_type, dataset));
                        Ok(vec![])
                    }
                    _ => Err(format!("invalid elasticsearch response: {:?}", res)),
                }

            })
    }

    /// publish the index as the new index for this doc_type and this dataset
    /// move the index alias of the doc_type and the dataset to point to this indexes
    /// and remove the old index
    pub fn publish_index(&mut self,
                         doc_type: &str,
                         dataset: &str,
                         index: String)
                         -> Result<(), String> {
        debug!("publishing index");
        let last_indexes = try!(self.get_last_index(doc_type, dataset));
        let main_index = get_main_index(doc_type, dataset);
        try!(self.alias(&main_index, &vec![index.clone()], &last_indexes));
        try!(self.alias("munin", &vec![main_index.to_string()], &vec![]));
        for i in last_indexes {
            try!(self.delete_index(&i));
        }
        Ok(())
    }

    pub fn is_existing_index(&self, name: &String) -> Result<bool, String> {
        self.get(&name)
            .map_err(|e| e.to_string())
            .map(|res| res.status == StatusCode::Ok)
    }

    /// add a list of new indexes to the alias
    /// remove a list of indexes from the alias
    fn alias(&self, alias: &str, add: &Vec<String>, remove: &Vec<String>) -> Result<(), String> {
        info!("for {}, adding alias {:?}, removing {:?}",
              alias,
              add,
              remove);
        let add_operations = add.iter().map(|x| {
            AliasOperation {
                remove: None,
                add: Some(AliasParameter {
                    index: x.clone(),
                    alias: alias.to_string(),
                }),
            }
        });
        let remove_operations = remove.iter().map(|x| {
            AliasOperation {
                add: None,
                remove: Some(AliasParameter {
                    index: x.clone(),
                    alias: alias.to_string(),
                }),
            }
        });
        let operations = AliasOperations {
            actions: add_operations.chain(remove_operations).collect(),
        };
        let json = serde_json::to_string(&operations).unwrap();
        self.post("_aliases", &json)
            .map_err(|e| e.to_string())
            .and_then(|res| {
                if res.status == StatusCode::Ok {
                    Ok(())
                } else {
                    error!("failed to change aliases for {}, es response: {:?}",
                           alias,
                           res);
                    Err(format!("failed to post aliases for {}: {:?}", alias, res).to_string())
                }
            })
    }

    pub fn delete_index(&mut self, index: &String) -> Result<(), String> {
        debug!("deleting index {}", &index);
        let res = self.es_client
                      .delete_index(&index)
                      .map(|res| res.acknowledged)
                      .unwrap_or(false);
        if !res {
            Err(format!("Error deleting index {}", &index).into())
        } else {
            Ok(())
        }
    }

    pub fn bulk_index<T, I>(&mut self,
                            index: &String,
                            iter: I)
                            -> Result<usize, rs_es::error::EsError>
        where T: serde::Serialize + DocType + EsId,
              I: Iterator<Item = T>
    {
        use rs_es::operations::bulk::Action;
        let mut chunk = Vec::new();
        let mut nb = 0;
        let chunk_size = 1000;
        //fold is used for creating the action and optionally set the id of the object
        let mut actions = iter.map(|v| v.es_id()
                                        .into_iter()
                                        .fold(Action::index(v), |action, id| action.with_id(id)));
        loop {
            let chunk = actions.by_ref().take(chunk_size).collect::<Vec<_>>();
            nb += chunk.len();
            try!(self.es_client
                     .bulk(&chunk)
                     .with_index(&index)
                     .with_doc_type(T::doc_type())
                     .send());

            if chunk.len() < chunk_size {
                break;
            }
        }

        Ok(nb)
    }

    /// add all the element of 'iter' into elasticsearch
    ///
    /// To have zero downtime:
    /// first all the elements are added in a temporary index and when all has been indexed
    /// the index is published and the old index is removed
    pub fn index<T, I>(&mut self, doc_type: &str, dataset: &str, iter: I) -> Result<u32, String>
        where T: serde::Serialize + DocType,
              I: Iterator<Item = T>
    {
        // TODO better error handling
        let index = try!(self.make_index(doc_type, dataset));
        let nb_elements = try!(self.bulk_index(&index, iter).map_err(|e| e.to_string()));
        try!(self.publish_index(doc_type, dataset, index));
        Ok(nb_elements)
    }

	fn make_admin(&self, value: Option<Box<serde_json::Value>>) -> Option<Admin> {
	    value.and_then(|v| {
	    		serde_json::from_value::<Admin>(*v).ok().and_then(|o| Some(o))
	    })
	}

    pub fn get_admins(&mut self) -> Result<BTreeMap<String, Admin>, rs_es::error::EsError> {
        debug!("Get Admins.");
        let mut result: BTreeMap<String, Admin> = BTreeMap::new();
        let scroll = Duration::minutes(1);
        let mut scan: ScanResult<serde_json::Value> =
        match self.es_client
                     .search_query()
                     .with_size(10000)
                     .with_types(&[&"admin"])
                     .scan(&scroll) {
                        Ok(scan) => scan,
                        Err(e) => {
							info!("Scan error: {:?}", e);
							return Err(e);
                        }
                     };
        loop {
			let page = match scan.scroll(&mut self.es_client, &scroll) {
				Ok(page) => page,
				Err(e) => {
					info!("scroll error: {:?}", e);
					try!(scan.close(&mut self.es_client));
					return Err(e);
				}
			};
	        if page.hits.hits.len() == 0 {
				break;
	        }
	        let tmp: Vec<Admin> = page.hits
	             .hits
	             .into_iter()
	             .filter_map(|hit| self.make_admin(hit.source))
	             .collect();
	        for ad in tmp {
				result.insert(ad.id.to_string(), ad);
	        }
		}
		try!(scan.close(&mut self.es_client));

		Ok(result)
    }
}

#[test]
pub fn test_valid_url() {
    Rubber::new("http://localhost:9200");
    Rubber::new("localhost:9200");
}

#[test]
#[should_panic]
pub fn test_invalid_url() {
    Rubber::new("http://bob");
}

#[test]
#[should_panic]
pub fn test_invalid_url_no_port() {
    Rubber::new("localhost");
}
<commit_msg>correction<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use super::objects::{DocType, EsId, Admin};
use chrono;
use regex;
use hyper;
use hyper::status::StatusCode;
use rs_es::error::EsError;
use rs_es;
use rs_es::EsResponse;
use serde;
use serde_json;
use rs_es::operations::search::ScanResult;

use super::objects::{AliasOperations, AliasOperation, AliasParameter};
use rs_es::units::{Duration};
use std::collections::BTreeMap;

// Rubber is an wrapper around elasticsearch API
pub struct Rubber {
    es_client: rs_es::Client,
    // some operation are not implemented in rs_es, we need to use a raw http client
    http_client: hyper::client::Client,
}

/// return the index associated to the given type and dataset
/// this will be an alias over another real index
fn get_main_index(doc_type: &str, dataset: &str) -> String {
    format!("munin_{}_{}", doc_type, dataset)
}

impl Rubber {
    // build a rubber with a connection string (http://host:port/)
    pub fn new(cnx: &str) -> Rubber {
        let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
        let cap = re.captures(cnx).unwrap();
        let host = cap.name("host").unwrap();
        let port = cap.name("port").unwrap().parse::<u32>().unwrap();
        info!("elastic search host {:?} port {:?}", host, port);

        Rubber {
            es_client: rs_es::Client::new(&host, port),
            http_client: hyper::client::Client::new(),
        }
    }

    pub fn get(&self, path: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a get on {}", path);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .get(&url)
                              .send());
        rs_es::do_req(result)
    }
    fn put(&self, path: &str, body: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a put on {} with {}", path, body);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .put(&url)
                              .body(body)
                              .send());
        rs_es::do_req(result)
    }
    fn post(&self, path: &str, body: &str) -> Result<hyper::client::response::Response, EsError> {
        // Note: a bit duplicate on rs_es because some ES operations are not implemented
        debug!("doing a post on {} with {}", path, body);
        let url = self.es_client.full_url(path);
        let result = try!(self.http_client
                              .post(&url)
                              .body(body)
                              .send());
        rs_es::do_req(result)
    }

    pub fn make_index(&self, doc_type: &str, dataset: &str) -> Result<String, String> {
        let current_time = chrono::UTC::now().format("%Y%m%d_%H%M%S_%f");
        let index_name = format!("munin_{}_{}_{}", doc_type, dataset, current_time);
        info!("creating index {}", index_name);
        self.create_index(&index_name.to_string()).map(|_| index_name)
    }

    fn create_index(&self, name: &String) -> Result<(), String> {
        debug!("creating index");
        // Note: in rs_es it can be done with MappingOperation but for the moment I think
        // storing the mapping in json is more convenient
        let analysis = include_str!("../../../json/settings.json");
        self.put(name, &analysis)
            .map_err(|e| {
                info!("Error while creating new index {}", name);
                e.to_string()
            })
            .and_then(|res| {
                if res.status == StatusCode::Ok {
                    Ok(())
                } else {
                    Err(format!("cannot create index: {:?}", res))
                }
            })
    }

    fn get_last_index(&self, doc_type: &str, dataset: &str) -> Result<Vec<String>, String> {
        debug!("get last index: {base_index}/_aliases",
               base_index = get_main_index(doc_type, dataset));
        self.get(&format!("{base_index}/_aliases",
                          base_index = get_main_index(doc_type, dataset)))
            .map_err(|e| e.to_string())
            .and_then(|res| {
                match res.status {
                    StatusCode::Ok => {
                        let value: serde_json::Value = try!(res.read_response()
                                                               .map_err(|e| e.to_string()));
                        Ok(value.as_object()
                                .and_then(|aliases| Some(aliases.keys().cloned().collect()))
                                .unwrap_or_else(|| {
                                    info!("no previous index to delete for type {} and dataset {}",
                                          doc_type,
                                          dataset);
                                    vec![]
                                }))
                    }
                    StatusCode::NotFound => {
                        info!("impossible to find alias {}, no last index to remove",
                              get_main_index(doc_type, dataset));
                        Ok(vec![])
                    }
                    _ => Err(format!("invalid elasticsearch response: {:?}", res)),
                }

            })
    }

    /// publish the index as the new index for this doc_type and this dataset
    /// move the index alias of the doc_type and the dataset to point to this indexes
    /// and remove the old index
    pub fn publish_index(&mut self,
                         doc_type: &str,
                         dataset: &str,
                         index: String)
                         -> Result<(), String> {
        debug!("publishing index");
        let last_indexes = try!(self.get_last_index(doc_type, dataset));
        let main_index = get_main_index(doc_type, dataset);
        try!(self.alias(&main_index, &vec![index.clone()], &last_indexes));
        try!(self.alias("munin", &vec![main_index.to_string()], &vec![]));
        for i in last_indexes {
            try!(self.delete_index(&i));
        }
        Ok(())
    }

    pub fn is_existing_index(&self, name: &String) -> Result<bool, String> {
        self.get(&name)
            .map_err(|e| e.to_string())
            .map(|res| res.status == StatusCode::Ok)
    }

    /// add a list of new indexes to the alias
    /// remove a list of indexes from the alias
    fn alias(&self, alias: &str, add: &Vec<String>, remove: &Vec<String>) -> Result<(), String> {
        info!("for {}, adding alias {:?}, removing {:?}",
              alias,
              add,
              remove);
        let add_operations = add.iter().map(|x| {
            AliasOperation {
                remove: None,
                add: Some(AliasParameter {
                    index: x.clone(),
                    alias: alias.to_string(),
                }),
            }
        });
        let remove_operations = remove.iter().map(|x| {
            AliasOperation {
                add: None,
                remove: Some(AliasParameter {
                    index: x.clone(),
                    alias: alias.to_string(),
                }),
            }
        });
        let operations = AliasOperations {
            actions: add_operations.chain(remove_operations).collect(),
        };
        let json = serde_json::to_string(&operations).unwrap();
        self.post("_aliases", &json)
            .map_err(|e| e.to_string())
            .and_then(|res| {
                if res.status == StatusCode::Ok {
                    Ok(())
                } else {
                    error!("failed to change aliases for {}, es response: {:?}",
                           alias,
                           res);
                    Err(format!("failed to post aliases for {}: {:?}", alias, res).to_string())
                }
            })
    }

    pub fn delete_index(&mut self, index: &String) -> Result<(), String> {
        debug!("deleting index {}", &index);
        let res = self.es_client
                      .delete_index(&index)
                      .map(|res| res.acknowledged)
                      .unwrap_or(false);
        if !res {
            Err(format!("Error deleting index {}", &index).into())
        } else {
            Ok(())
        }
    }

    pub fn bulk_index<T, I>(&mut self,
                            index: &String,
                            iter: I)
                            -> Result<usize, rs_es::error::EsError>
        where T: serde::Serialize + DocType + EsId,
              I: Iterator<Item = T>
    {
        use rs_es::operations::bulk::Action;
        let mut chunk = Vec::new();
        let mut nb = 0;
        let chunk_size = 1000;
        //fold is used for creating the action and optionally set the id of the object
        let mut actions = iter.map(|v| v.es_id()
                                        .into_iter()
                                        .fold(Action::index(v), |action, id| action.with_id(id)));
        loop {
            let chunk = actions.by_ref().take(chunk_size).collect::<Vec<_>>();
            nb += chunk.len();
            try!(self.es_client
                     .bulk(&chunk)
                     .with_index(&index)
                     .with_doc_type(T::doc_type())
                     .send());

            if chunk.len() < chunk_size {
                break;
            }
        }

        Ok(nb)
    }

    /// add all the element of 'iter' into elasticsearch
    ///
    /// To have zero downtime:
    /// first all the elements are added in a temporary index and when all has been indexed
    /// the index is published and the old index is removed
    pub fn index<T, I>(&mut self, doc_type: &str, dataset: &str, iter: I) -> Result<u32, String>
        where T: serde::Serialize + DocType,
              I: Iterator<Item = T>
    {
        // TODO better error handling
        let index = try!(self.make_index(doc_type, dataset));
        let nb_elements = try!(self.bulk_index(&index, iter).map_err(|e| e.to_string()));
        try!(self.publish_index(doc_type, dataset, index));
        Ok(nb_elements)
    }

	fn make_admin(&self, value: Option<Box<serde_json::Value>>) -> Option<Admin> {
	    value.and_then(|v| {
	    		serde_json::from_value::<Admin>(*v).ok().and_then(|o| Some(o))
	    })
	}

    pub fn get_admins(&mut self) -> Result<BTreeMap<String, Admin>, rs_es::error::EsError> {
        debug!("Get Admins.");
        let mut result: BTreeMap<String, Admin> = BTreeMap::new();
        let scroll = Duration::minutes(1);
        let mut scan: ScanResult<serde_json::Value> =
        match self.es_client
                     .search_query()
                     .with_size(10000)
                     .with_types(&[&"admin"])
                     .scan(&scroll) {
                        Ok(scan) => scan,
                        Err(e) => {
                            info!("Scan error: {:?}", e);
                            return Err(e);
                        }
                     };
        loop {
			let page = match scan.scroll(&mut self.es_client, &scroll) {
				Ok(page) => page,
				Err(e) => {
					info!("scroll error: {:?}", e);
					try!(scan.close(&mut self.es_client));
					return Err(e);
				}
			};
	        if page.hits.hits.len() == 0 {
				break;
	        }
	        let tmp: Vec<Admin> = page.hits
	             .hits
	             .into_iter()
	             .filter_map(|hit| self.make_admin(hit.source))
	             .collect();
	        for ad in tmp {
				result.insert(ad.id.to_string(), ad);
	        }
		}
		try!(scan.close(&mut self.es_client));

		Ok(result)
    }
}

#[test]
pub fn test_valid_url() {
    Rubber::new("http://localhost:9200");
    Rubber::new("localhost:9200");
}

#[test]
#[should_panic]
pub fn test_invalid_url() {
    Rubber::new("http://bob");
}

#[test]
#[should_panic]
pub fn test_invalid_url_no_port() {
    Rubber::new("localhost");
}
<|endoftext|>


<commit_before>use uuid::Uuid;

use super::{Conditions};
use dispatcher::request::InternalRequest;

pub use self::linear::LinearContext;
pub use self::map::MapContext;

pub mod base;
pub mod event;
pub mod map;

pub enum Context {
    Linear(LinearContext),
    Map(MapContext)
}

impl From<Context> for Box<self::event::EventHandler<InternalRequest>> {
    fn from(context: Context) -> Box<self::event::EventHandler<InternalRequest>> {
        match context {
            Context::Linear(context) => Box::new(context),
            Context::Map(context) => Box::new(context),
        }
    }
}

pub mod linear {
    use uuid::Uuid;
    use std::rc::Rc;

    use context;
    use Conditions;
    use context::event::{EventHandler};
    use message::{Message};
    use state::State;
    use TimerEvent;
    use dispatcher::request::{InternalRequest, Request};
    use context::base::BaseContext;

    pub struct LinearContext {
        base: BaseContext,
        state: State
    }

    impl LinearContext {
        pub fn new(uuid: Uuid, conditions: Conditions) -> LinearContext {
            LinearContext {
                base: BaseContext::new(uuid, conditions),
                state: State::new()
            }
        }

        pub fn on_event(&mut self, event: InternalRequest) {
            if let Request::Timer(event) = event {
                self.on_timer(&event);
            }
        }

        pub fn on_timer(&mut self, event: &TimerEvent) {
            self.base.on_timer(event, &mut self.state)
        }

        pub fn on_message(&mut self, event: Rc<Message>) {
            self.base.on_message(event, &mut self.state);
        }

        pub fn is_open(&self) -> bool {
            self.state.is_open()
        }

        pub fn patterns(&self) -> &[String] {
            &self.base.conditions().patterns
        }
    }

    impl From<BaseContext> for LinearContext {
        fn from(context: BaseContext) -> LinearContext {
            LinearContext {
                base: context,
                state: State::new()
            }
        }
    }

    impl EventHandler<InternalRequest> for LinearContext {
        fn handlers(&self) -> &[String] {
            self.patterns()
        }
        fn handle_event(&mut self, event: InternalRequest) {
            self.on_event(event);
        }
    }

    impl From<LinearContext> for Box<context::event::EventHandler<InternalRequest>> {
        fn from(context: LinearContext) -> Box<context::event::EventHandler<InternalRequest>> {
            Box::new(context)
        }
    }
}

#[cfg(test)]
mod test {
    use uuid::Uuid;
    use std::rc::Rc;

    use message;
    use TimerEvent;
    use context::LinearContext;
    use conditions::Builder;

    #[test]
    fn test_given_close_condition_with_timeout_when_the_timeout_expires_then_the_condition_is_met() {
        let timeout = 100;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        assert_false!(context.is_open());
        context.on_message(event);
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(50));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(49));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_max_size_when_the_max_size_reached_then_the_condition_is_met() {
        let timeout = 100;
        let max_size = 3;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).max_size(max_size).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_renew_timeout_when_the_timeout_expires_without_renewing_messages_then_the_condition_is_met() {
        let timeout = 100;
        let renew_timeout = 10;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).renew_timeout(renew_timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(8));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_renew_timeout_when_the_timeout_expires_with_renewing_messages_then_the_context_is_not_closed() {
        let timeout = 100;
        let renew_timeout = 10;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).renew_timeout(renew_timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        assert_false!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(8));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
    }
}
<commit_msg>Context: remove unused imports<commit_after>use dispatcher::request::InternalRequest;

pub use self::linear::LinearContext;
pub use self::map::MapContext;

pub mod base;
pub mod event;
pub mod map;

pub enum Context {
    Linear(LinearContext),
    Map(MapContext)
}

impl From<Context> for Box<self::event::EventHandler<InternalRequest>> {
    fn from(context: Context) -> Box<self::event::EventHandler<InternalRequest>> {
        match context {
            Context::Linear(context) => Box::new(context),
            Context::Map(context) => Box::new(context),
        }
    }
}

pub mod linear {
    use uuid::Uuid;
    use std::rc::Rc;

    use context;
    use Conditions;
    use context::event::{EventHandler};
    use message::{Message};
    use state::State;
    use TimerEvent;
    use dispatcher::request::{InternalRequest, Request};
    use context::base::BaseContext;

    pub struct LinearContext {
        base: BaseContext,
        state: State
    }

    impl LinearContext {
        pub fn new(uuid: Uuid, conditions: Conditions) -> LinearContext {
            LinearContext {
                base: BaseContext::new(uuid, conditions),
                state: State::new()
            }
        }

        pub fn on_event(&mut self, event: InternalRequest) {
            if let Request::Timer(event) = event {
                self.on_timer(&event);
            }
        }

        pub fn on_timer(&mut self, event: &TimerEvent) {
            self.base.on_timer(event, &mut self.state)
        }

        pub fn on_message(&mut self, event: Rc<Message>) {
            self.base.on_message(event, &mut self.state);
        }

        pub fn is_open(&self) -> bool {
            self.state.is_open()
        }

        pub fn patterns(&self) -> &[String] {
            &self.base.conditions().patterns
        }
    }

    impl From<BaseContext> for LinearContext {
        fn from(context: BaseContext) -> LinearContext {
            LinearContext {
                base: context,
                state: State::new()
            }
        }
    }

    impl EventHandler<InternalRequest> for LinearContext {
        fn handlers(&self) -> &[String] {
            self.patterns()
        }
        fn handle_event(&mut self, event: InternalRequest) {
            self.on_event(event);
        }
    }

    impl From<LinearContext> for Box<context::event::EventHandler<InternalRequest>> {
        fn from(context: LinearContext) -> Box<context::event::EventHandler<InternalRequest>> {
            Box::new(context)
        }
    }
}

#[cfg(test)]
mod test {
    use uuid::Uuid;
    use std::rc::Rc;

    use message;
    use TimerEvent;
    use context::LinearContext;
    use conditions::Builder;

    #[test]
    fn test_given_close_condition_with_timeout_when_the_timeout_expires_then_the_condition_is_met() {
        let timeout = 100;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        assert_false!(context.is_open());
        context.on_message(event);
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(50));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(49));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_max_size_when_the_max_size_reached_then_the_condition_is_met() {
        let timeout = 100;
        let max_size = 3;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).max_size(max_size).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_renew_timeout_when_the_timeout_expires_without_renewing_messages_then_the_condition_is_met() {
        let timeout = 100;
        let renew_timeout = 10;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).renew_timeout(renew_timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(8));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_false!(context.is_open());
    }

    #[test]
    fn test_given_close_condition_with_renew_timeout_when_the_timeout_expires_with_renewing_messages_then_the_context_is_not_closed() {
        let timeout = 100;
        let renew_timeout = 10;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id.clone(),
        ];
        let mut context = LinearContext::new(Uuid::new_v4(), Builder::new(timeout).renew_timeout(renew_timeout).patterns(patterns).build());
        let msg1 = message::Builder::new(&msg_id).build();
        let event = Rc::new(msg1);
        assert_false!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(8));
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
        context.on_message(event.clone());
        assert_true!(context.is_open());
        context.on_timer(&mut TimerEvent(1));
        assert_true!(context.is_open());
    }
}
<|endoftext|>


<commit_before>use super::*;

pub(super) fn mod_contents(p: &mut Parser) {
    attributes::inner_attributes(p);
    while !p.at(EOF) {
        item(p);
    }
}

fn item(p: &mut Parser){
    let attrs_start = p.mark();
    attributes::outer_attributes(p);
    visibility(p);
    let la = p.raw_lookahead(1);
    let item_start = p.mark();
    match p.current() {
        EXTERN_KW if la == CRATE_KW => extern_crate_item(p),
        MOD_KW => mod_item(p),
        USE_KW => use_item(p),
        STRUCT_KW => struct_item(p),
        FN_KW => fn_item(p),
        err_token => {
            p.start(ERROR);
            let message = if err_token == SEMI {
                //TODO: if the item is incomplete, this messsage is misleading
                "expected item, found `;`\n\
                consider removing this semicolon"
            } else {
                "expected item"
            };
            p.error()
                .message(message)
                .emit();
            p.bump();
            p.finish();
            return;
        }
    };
    p.forward_parent(attrs_start, item_start);
}

fn struct_item(p: &mut Parser) {
    p.start(STRUCT_ITEM);

    assert!(p.at(STRUCT_KW));
    p.bump();

    struct_inner(p);
    p.finish();

    fn struct_inner(p: &mut Parser) {
        if !p.expect(IDENT) {
            p.finish();
            return
        }
        generic_parameters(p);
        match p.current() {
            WHERE_KW => {
                where_clause(p);
                match p.current() {
                    SEMI => {
                        p.bump();
                        return
                    }
                    L_CURLY => named_fields(p),
                    _ => { //TODO: special case `(` error message
                        p.error()
                            .message("expected `;` or `{`")
                            .emit();
                        return
                    }
                }
            }
            SEMI => {
                p.bump();
                return
            }
            L_CURLY => named_fields(p),
            L_PAREN => {
                tuple_fields(p);
                p.expect(SEMI);
            },
            _ => {
                p.error()
                    .message("expected `;`, `{`, or `(`")
                    .emit();
                return
            }
        }
    }
}

fn named_fields(p: &mut Parser) {
    p.curly_block(|p| comma_list(p, EOF, |p| {
        named_field(p);
        true
    }));

    fn named_field(p: &mut Parser) {
        node(p, NAMED_FIELD, |p| {
            visibility(p);
            p.expect(IDENT) && p.expect(COLON) && {
                types::type_ref(p);
                true
            };
        })
    }
}

fn tuple_fields(p: &mut Parser) {
    if !p.expect(L_PAREN) {
        return
    }
    comma_list(p, R_PAREN, |p| {
        tuple_field(p);
        true
    });
    p.expect(R_PAREN);

    fn tuple_field(p: &mut Parser) {
        node(p, POS_FIELD, |p| {
            visibility(p);
            types::type_ref(p);
        })
    }
}

fn generic_parameters(_: &mut Parser) {
}

fn where_clause(_: &mut Parser) {
}

fn extern_crate_item(p: &mut Parser) {
    p.start(EXTERN_CRATE_ITEM);

    assert!(p.at(EXTERN_KW));
    p.bump();

    assert!(p.at(CRATE_KW));
    p.bump();

    p.expect(IDENT) && alias(p) && p.expect(SEMI);
    p.finish();
}

fn mod_item(p: &mut Parser) {
    p.start(MOD_ITEM);

    assert!(p.at(MOD_KW));
    p.bump();

    if p.expect(IDENT) && !p.eat(SEMI) {
        p.curly_block(mod_contents);
    }
    p.finish()
}

pub(super) fn is_use_tree_start(kind: SyntaxKind) -> bool {
    kind == STAR || kind == L_CURLY
}

fn use_item(p: &mut Parser) {
    p.start(USE_ITEM);

    assert!(p.at(USE_KW));
    p.bump();
    use_tree(p);
    p.expect(SEMI);
    p.finish();

    fn use_tree(p: &mut Parser) -> bool{
        if node_if(p, STAR, USE_TREE, |_| ()) {
            return true
        }
        if node_if(p, [COLONCOLON, STAR], USE_TREE, |_| ()) {
           return true
        }
        if [COLONCOLON, L_CURLY].is_ahead(p) || L_CURLY.is_ahead(p) {
            node(p, USE_TREE, |p| {
                p.eat(COLONCOLON);
                p.curly_block(|p| {
                    comma_list(p, EOF, use_tree);
                });
            });
            return true;
        }
        if paths::is_path_start(p) {
            node(p, USE_TREE, |p| {
                paths::use_path(p);
                match p.current() {
                    AS_KW => {
                        alias(p);
                    }
                    COLONCOLON => {
                        p.bump();
                        match p.current() {
                            STAR => {
                                p.bump();
                            }
                            L_CURLY => {
                                p.curly_block(|p| {
                                    comma_list(p, EOF, use_tree);
                                });
                            }
                            _ => {
                                // is this unreachable?
                                p.error()
                                    .message("expected `{` or `*`")
                                    .emit();
                            }
                        }
                    }
                    _ => (),
                }
            });
            return true;
        }
        false
    }
}

fn fn_item(p: &mut Parser) {
    p.start(FN_ITEM);

    assert!(p.at(FN_KW));
    p.bump();

    p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN)
        && p.curly_block(|_| ());
    p.finish();
}

<commit_msg>Drop more high-order stuff<commit_after>use super::*;

pub(super) fn mod_contents(p: &mut Parser) {
    attributes::inner_attributes(p);
    while !p.at(EOF) {
        item(p);
    }
}

fn item(p: &mut Parser){
    let attrs_start = p.mark();
    attributes::outer_attributes(p);
    visibility(p);
    let la = p.raw_lookahead(1);
    let item_start = p.mark();
    match p.current() {
        EXTERN_KW if la == CRATE_KW => extern_crate_item(p),
        MOD_KW => mod_item(p),
        USE_KW => use_item(p),
        STRUCT_KW => struct_item(p),
        FN_KW => fn_item(p),
        err_token => {
            p.start(ERROR);
            let message = if err_token == SEMI {
                //TODO: if the item is incomplete, this message is misleading
                "expected item, found `;`\n\
                consider removing this semicolon"
            } else {
                "expected item"
            };
            p.error()
                .message(message)
                .emit();
            p.bump();
            p.finish();
            return;
        }
    };
    p.forward_parent(attrs_start, item_start);
}

fn struct_item(p: &mut Parser) {
    p.start(STRUCT_ITEM);

    assert!(p.at(STRUCT_KW));
    p.bump();

    struct_inner(p);
    p.finish();

    fn struct_inner(p: &mut Parser) {
        if !p.expect(IDENT) {
            p.finish();
            return
        }
        generic_parameters(p);
        match p.current() {
            WHERE_KW => {
                where_clause(p);
                match p.current() {
                    SEMI => {
                        p.bump();
                        return
                    }
                    L_CURLY => named_fields(p),
                    _ => { //TODO: special case `(` error message
                        p.error()
                            .message("expected `;` or `{`")
                            .emit();
                        return
                    }
                }
            }
            SEMI => {
                p.bump();
                return
            }
            L_CURLY => named_fields(p),
            L_PAREN => {
                tuple_fields(p);
                p.expect(SEMI);
            },
            _ => {
                p.error()
                    .message("expected `;`, `{`, or `(`")
                    .emit();
                return
            }
        }
    }
}

fn named_fields(p: &mut Parser) {
    p.curly_block(|p| comma_list(p, EOF, |p| {
        named_field(p);
        true
    }));

    fn named_field(p: &mut Parser) {
        p.start(NAMED_FIELD);
        visibility(p);
        if p.expect(IDENT) && p.expect(COLON) {
            types::type_ref(p);
        };
        p.finish()
    }
}

fn tuple_fields(p: &mut Parser) {
    if !p.expect(L_PAREN) {
        return
    }
    comma_list(p, R_PAREN, |p| {
        tuple_field(p);
        true
    });
    p.expect(R_PAREN);

    fn tuple_field(p: &mut Parser) {
        p.start(POS_FIELD);
        visibility(p);
        types::type_ref(p);
        p.finish();
    }
}

fn generic_parameters(_: &mut Parser) {
}

fn where_clause(_: &mut Parser) {
}

fn extern_crate_item(p: &mut Parser) {
    p.start(EXTERN_CRATE_ITEM);

    assert!(p.at(EXTERN_KW));
    p.bump();

    assert!(p.at(CRATE_KW));
    p.bump();

    p.expect(IDENT) && alias(p) && p.expect(SEMI);
    p.finish();
}

fn mod_item(p: &mut Parser) {
    p.start(MOD_ITEM);

    assert!(p.at(MOD_KW));
    p.bump();

    if p.expect(IDENT) && !p.eat(SEMI) {
        p.curly_block(mod_contents);
    }
    p.finish()
}

pub(super) fn is_use_tree_start(kind: SyntaxKind) -> bool {
    kind == STAR || kind == L_CURLY
}

fn use_item(p: &mut Parser) {
    p.start(USE_ITEM);

    assert!(p.at(USE_KW));
    p.bump();
    use_tree(p);
    p.expect(SEMI);
    p.finish();

    fn use_tree(p: &mut Parser) -> bool{
        if node_if(p, STAR, USE_TREE, |_| ()) {
            return true
        }
        if node_if(p, [COLONCOLON, STAR], USE_TREE, |_| ()) {
           return true
        }
        if [COLONCOLON, L_CURLY].is_ahead(p) || L_CURLY.is_ahead(p) {
            node(p, USE_TREE, |p| {
                p.eat(COLONCOLON);
                p.curly_block(|p| {
                    comma_list(p, EOF, use_tree);
                });
            });
            return true;
        }
        if paths::is_path_start(p) {
            node(p, USE_TREE, |p| {
                paths::use_path(p);
                match p.current() {
                    AS_KW => {
                        alias(p);
                    }
                    COLONCOLON => {
                        p.bump();
                        match p.current() {
                            STAR => {
                                p.bump();
                            }
                            L_CURLY => {
                                p.curly_block(|p| {
                                    comma_list(p, EOF, use_tree);
                                });
                            }
                            _ => {
                                // is this unreachable?
                                p.error()
                                    .message("expected `{` or `*`")
                                    .emit();
                            }
                        }
                    }
                    _ => (),
                }
            });
            return true;
        }
        false
    }
}

fn fn_item(p: &mut Parser) {
    p.start(FN_ITEM);

    assert!(p.at(FN_KW));
    p.bump();

    p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN)
        && p.curly_block(|_| ());
    p.finish();
}

<|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/. */

//! The `ByteString` struct.

use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops;
use std::str;
use std::str::FromStr;

/// Encapsulates the IDL `ByteString` type.
#[derive(JSTraceable, Clone, Eq, PartialEq, HeapSizeOf)]
pub struct ByteString(Vec<u8>);

impl ByteString {
    /// Creates a new `ByteString`.
    pub fn new(value: Vec<u8>) -> ByteString {
        ByteString(value)
    }

    /// Returns `self` as a string, if it encodes valid UTF-8, and `None`
    /// otherwise.
    pub fn as_str(&self) -> Option<&str> {
        let ByteString(ref vec) = *self;
        str::from_utf8(&vec).ok()
    }

    /// Returns ownership of the underlying Vec<u8> and copies an empty
    /// vec in its place
    pub fn bytes(&mut self) -> Vec<u8> {
        mem::replace(&mut self.0, Vec::new())
    }

    /// Returns the length.
    pub fn len(&self) -> usize {
        let ByteString(ref vector) = *self;
        vector.len()
    }

    /// Compare `self` to `other`, matching A–Z and a–z as equal.
    pub fn eq_ignore_case(&self, other: &ByteString) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }

    /// Returns `self` with A–Z replaced by a–z.
    pub fn to_lower(&self) -> ByteString {
        ByteString::new(self.0.to_ascii_lowercase())
    }

    /// Returns whether `self` is a `token`, as defined by
    /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
    pub fn is_token(&self) -> bool {
        let ByteString(ref vec) = *self;
        is_token(vec)
    }

    /// Returns whether `self` is a `field-value`, as defined by
    /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-32).
    pub fn is_field_value(&self) -> bool {
        // Classifications of characters necessary for the [CRLF] (SP|HT) rule
        #[derive(PartialEq)]
        enum PreviousCharacter {
            Other,
            CR,
            LF,
            SPHT, // SP or HT
        }
        let ByteString(ref vec) = *self;
        let mut prev = PreviousCharacter::Other; // The previous character
        vec.iter().all(|&x| {
            // http://tools.ietf.org/html/rfc2616#section-2.2
            match x {
                13  => { // CR
                    if prev == PreviousCharacter::Other || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::CR;
                        true
                    } else {
                        false
                    }
                },
                10 => { // LF
                    if prev == PreviousCharacter::CR {
                        prev = PreviousCharacter::LF;
                        true
                    } else {
                        false
                    }
                },
                32 => { // SP
                    if prev == PreviousCharacter::LF || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::SPHT;
                        true
                    } else if prev == PreviousCharacter::Other {
                        // Counts as an Other here, since it's not preceded by a CRLF
                        // SP is not a CTL, so it can be used anywhere
                        // though if used immediately after a CR the CR is invalid
                        // We don't change prev since it's already Other
                        true
                    } else {
                        false
                    }
                },
                9 => { // HT
                    if prev == PreviousCharacter::LF || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::SPHT;
                        true
                    } else {
                        false
                    }
                },
                0...31 | 127 => false, // CTLs
                x if x > 127 => false, // non ASCII
                _ if prev == PreviousCharacter::Other || prev == PreviousCharacter::SPHT => {
                    prev = PreviousCharacter::Other;
                    true
                },
                _ => false // Previous character was a CR/LF but not part of the [CRLF] (SP|HT) rule
            }
        })
    }
}

impl Into<Vec<u8>> for ByteString {
    fn into(self) -> Vec<u8> {
        self.0
    }
}

impl Hash for ByteString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let ByteString(ref vec) = *self;
        vec.hash(state);
    }
}

impl FromStr for ByteString {
    type Err = ();
    fn from_str(s: &str) -> Result<ByteString, ()> {
        Ok(ByteString::new(s.to_owned().into_bytes()))
    }
}

impl ops::Deref for ByteString {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &self.0
    }
}

/// A string that is constructed from a UCS-2 buffer by replacing invalid code
/// points with the replacement character.
pub struct USVString(pub String);

/// Returns whether `s` is a `token`, as defined by
/// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
pub fn is_token(s: &[u8]) -> bool {
    if s.is_empty() {
        return false; // A token must be at least a single character
    }
    s.iter().all(|&x| {
        // http://tools.ietf.org/html/rfc2616#section-2.2
        match x {
            0...31 | 127 => false, // CTLs
            40 |
            41 |
            60 |
            62 |
            64 |
            44 |
            59 |
            58 |
            92 |
            34 |
            47 |
            91 |
            93 |
            63 |
            61 |
            123 |
            125 |
            32 => false, // separators
            x if x > 127 => false, // non-CHARs
            _ => true,
        }
    })
}
<commit_msg>Convert unwrapping of ByteString to self.0<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/. */

//! The `ByteString` struct.

use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops;
use std::str;
use std::str::FromStr;

/// Encapsulates the IDL `ByteString` type.
#[derive(JSTraceable, Clone, Eq, PartialEq, HeapSizeOf)]
pub struct ByteString(Vec<u8>);

impl ByteString {
    /// Creates a new `ByteString`.
    pub fn new(value: Vec<u8>) -> ByteString {
        ByteString(value)
    }

    /// Returns `self` as a string, if it encodes valid UTF-8, and `None`
    /// otherwise.
    pub fn as_str(&self) -> Option<&str> {
        str::from_utf8(&self.0).ok()
    }

    /// Returns ownership of the underlying Vec<u8> and copies an empty
    /// vec in its place
    pub fn bytes(&mut self) -> Vec<u8> {
        mem::replace(&mut self.0, Vec::new())
    }

    /// Returns the length.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Compare `self` to `other`, matching A–Z and a–z as equal.
    pub fn eq_ignore_case(&self, other: &ByteString) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }

    /// Returns `self` with A–Z replaced by a–z.
    pub fn to_lower(&self) -> ByteString {
        ByteString::new(self.0.to_ascii_lowercase())
    }

    /// Returns whether `self` is a `token`, as defined by
    /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
    pub fn is_token(&self) -> bool {
        is_token(&self.0)
    }

    /// Returns whether `self` is a `field-value`, as defined by
    /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-32).
    pub fn is_field_value(&self) -> bool {
        // Classifications of characters necessary for the [CRLF] (SP|HT) rule
        #[derive(PartialEq)]
        enum PreviousCharacter {
            Other,
            CR,
            LF,
            SPHT, // SP or HT
        }
        let mut prev = PreviousCharacter::Other; // The previous character
        self.0.iter().all(|&x| {
            // http://tools.ietf.org/html/rfc2616#section-2.2
            match x {
                13  => { // CR
                    if prev == PreviousCharacter::Other || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::CR;
                        true
                    } else {
                        false
                    }
                },
                10 => { // LF
                    if prev == PreviousCharacter::CR {
                        prev = PreviousCharacter::LF;
                        true
                    } else {
                        false
                    }
                },
                32 => { // SP
                    if prev == PreviousCharacter::LF || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::SPHT;
                        true
                    } else if prev == PreviousCharacter::Other {
                        // Counts as an Other here, since it's not preceded by a CRLF
                        // SP is not a CTL, so it can be used anywhere
                        // though if used immediately after a CR the CR is invalid
                        // We don't change prev since it's already Other
                        true
                    } else {
                        false
                    }
                },
                9 => { // HT
                    if prev == PreviousCharacter::LF || prev == PreviousCharacter::SPHT {
                        prev = PreviousCharacter::SPHT;
                        true
                    } else {
                        false
                    }
                },
                0...31 | 127 => false, // CTLs
                x if x > 127 => false, // non ASCII
                _ if prev == PreviousCharacter::Other || prev == PreviousCharacter::SPHT => {
                    prev = PreviousCharacter::Other;
                    true
                },
                _ => false // Previous character was a CR/LF but not part of the [CRLF] (SP|HT) rule
            }
        })
    }
}

impl Into<Vec<u8>> for ByteString {
    fn into(self) -> Vec<u8> {
        self.0
    }
}

impl Hash for ByteString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let ByteString(ref vec) = *self;
        vec.hash(state);
    }
}

impl FromStr for ByteString {
    type Err = ();
    fn from_str(s: &str) -> Result<ByteString, ()> {
        Ok(ByteString::new(s.to_owned().into_bytes()))
    }
}

impl ops::Deref for ByteString {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &self.0
    }
}

/// A string that is constructed from a UCS-2 buffer by replacing invalid code
/// points with the replacement character.
pub struct USVString(pub String);

/// Returns whether `s` is a `token`, as defined by
/// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
pub fn is_token(s: &[u8]) -> bool {
    if s.is_empty() {
        return false; // A token must be at least a single character
    }
    s.iter().all(|&x| {
        // http://tools.ietf.org/html/rfc2616#section-2.2
        match x {
            0...31 | 127 => false, // CTLs
            40 |
            41 |
            60 |
            62 |
            64 |
            44 |
            59 |
            58 |
            92 |
            34 |
            47 |
            91 |
            93 |
            63 |
            61 |
            123 |
            125 |
            32 => false, // separators
            x if x > 127 => false, // non-CHARs
            _ => true,
        }
    })
}
<|endoftext|>


<commit_before>use crate::{
    builders::CrateBuilder,
    util::{MockAnonymousUser, RequestHelper, TestApp, TestDatabase},
};
use http::StatusCode;
use std::time::Duration;

#[test]
fn download_crate_with_broken_networking_primary_database() {
    let (app, anon, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: false })
        .with_token();
    app.db(|conn| {
        CrateBuilder::new("crate_name", owner.as_model().user_id)
            .version("1.0.0")
            .expect_build(conn)
    });

    // When the database connection is healthy downloads are redirected with the proper
    // capitalization, and missing crates or versions return a 404.

    assert_checked_redirects(&anon);

    // After networking breaks, preventing new database connections, the download endpoint should
    // do an unconditional redirect to the CDN, without checking whether the crate exists or what
    // the exact capitalization of crate name is.

    app.primary_db_chaosproxy().break_networking();
    assert_unconditional_redirects(&anon);

    // After restoring the network and waiting for the database pool to get healthy again redirects
    // should be checked again.

    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    assert_checked_redirects(&anon);
}

fn assert_checked_redirects(anon: &MockAnonymousUser) {
    anon.get::<()>("/api/v1/crates/crate_name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/Crate-Name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/crate_name/2.0.0/download")
        .assert_not_found();

    anon.get::<()>("/api/v1/crates/awesome-project/1.0.0/download")
        .assert_not_found();
}

fn assert_unconditional_redirects(anon: &MockAnonymousUser) {
    anon.get::<()>("/api/v1/crates/crate_name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/Crate-Name/1.0.0/download")
        .assert_redirect_ends_with("/Crate-Name/Crate-Name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/crate_name/2.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-2.0.0.crate");

    anon.get::<()>("/api/v1/crates/awesome-project/1.0.0/download")
        .assert_redirect_ends_with("/awesome-project/awesome-project-1.0.0.crate");
}

#[test]
fn http_error_with_unhealthy_database() {
    let (app, anon) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: false })
        .empty();

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::OK);

    app.primary_db_chaosproxy().break_networking();

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::OK);
}

#[test]
fn fallback_to_replica_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: true })
        .with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();

    // When the primary database is down, requests are forwarded to the replica database
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), 200);

    // restore primary database connection
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");
}

#[test]
fn restored_replica_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: true })
        .with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();
    app.replica_db_chaosproxy().break_networking();

    // When both primary and replica database are down, the request returns an error
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    // Once the replica database is restored, it should serve as a fallback again
    app.replica_db_chaosproxy().restore_networking();
    app.as_inner()
        .read_only_replica_database
        .as_ref()
        .expect("no replica database configured")
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::OK);

    // restore connection
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");
}

#[test]
fn restored_primary_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init().with_database(TestDatabase::SlowRealPool{ replica: true }).with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();
    app.replica_db_chaosproxy().break_networking();

    // When both primary and replica database are down, the request returns an error
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    // Once the replica database is restored, it should serve as a fallback again
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::OK);
}
<commit_msg>Fix code formating<commit_after>use crate::{
    builders::CrateBuilder,
    util::{MockAnonymousUser, RequestHelper, TestApp, TestDatabase},
};
use http::StatusCode;
use std::time::Duration;

#[test]
fn download_crate_with_broken_networking_primary_database() {
    let (app, anon, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: false })
        .with_token();
    app.db(|conn| {
        CrateBuilder::new("crate_name", owner.as_model().user_id)
            .version("1.0.0")
            .expect_build(conn)
    });

    // When the database connection is healthy downloads are redirected with the proper
    // capitalization, and missing crates or versions return a 404.

    assert_checked_redirects(&anon);

    // After networking breaks, preventing new database connections, the download endpoint should
    // do an unconditional redirect to the CDN, without checking whether the crate exists or what
    // the exact capitalization of crate name is.

    app.primary_db_chaosproxy().break_networking();
    assert_unconditional_redirects(&anon);

    // After restoring the network and waiting for the database pool to get healthy again redirects
    // should be checked again.

    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    assert_checked_redirects(&anon);
}

fn assert_checked_redirects(anon: &MockAnonymousUser) {
    anon.get::<()>("/api/v1/crates/crate_name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/Crate-Name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/crate_name/2.0.0/download")
        .assert_not_found();

    anon.get::<()>("/api/v1/crates/awesome-project/1.0.0/download")
        .assert_not_found();
}

fn assert_unconditional_redirects(anon: &MockAnonymousUser) {
    anon.get::<()>("/api/v1/crates/crate_name/1.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/Crate-Name/1.0.0/download")
        .assert_redirect_ends_with("/Crate-Name/Crate-Name-1.0.0.crate");

    anon.get::<()>("/api/v1/crates/crate_name/2.0.0/download")
        .assert_redirect_ends_with("/crate_name/crate_name-2.0.0.crate");

    anon.get::<()>("/api/v1/crates/awesome-project/1.0.0/download")
        .assert_redirect_ends_with("/awesome-project/awesome-project-1.0.0.crate");
}

#[test]
fn http_error_with_unhealthy_database() {
    let (app, anon) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: false })
        .empty();

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::OK);

    app.primary_db_chaosproxy().break_networking();

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = anon.get::<()>("/api/v1/summary");
    assert_eq!(response.status(), StatusCode::OK);
}

#[test]
fn fallback_to_replica_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: true })
        .with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();

    // When the primary database is down, requests are forwarded to the replica database
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), 200);

    // restore primary database connection
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");
}

#[test]
fn restored_replica_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: true })
        .with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();
    app.replica_db_chaosproxy().break_networking();

    // When both primary and replica database are down, the request returns an error
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    // Once the replica database is restored, it should serve as a fallback again
    app.replica_db_chaosproxy().restore_networking();
    app.as_inner()
        .read_only_replica_database
        .as_ref()
        .expect("no replica database configured")
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::OK);

    // restore connection
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");
}

#[test]
fn restored_primary_returns_user_info() {
    const URL: &str = "/api/v1/users/foo";

    let (app, _, owner) = TestApp::init()
        .with_database(TestDatabase::SlowRealPool { replica: true })
        .with_user();
    app.db_new_user("foo");
    app.primary_db_chaosproxy().break_networking();
    app.replica_db_chaosproxy().break_networking();

    // When both primary and replica database are down, the request returns an error
    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);

    // Once the replica database is restored, it should serve as a fallback again
    app.primary_db_chaosproxy().restore_networking();
    app.as_inner()
        .primary_database
        .wait_until_healthy(Duration::from_millis(500))
        .expect("the database did not return healthy");

    let response = owner.get::<()>(URL);
    assert_eq!(response.status(), StatusCode::OK);
}
<|endoftext|>