<commit_before>use std::collections::HashMap;
use std::marker::PhantomData;
use std::path::Path;

use model::Model;
use resource::ResourceManager;

/// A typed identifier.
#[derive(Debug)]
pub struct Id<T> {
  pub id: u32,
  _t: PhantomData<*const T>
}

impl<T> Id<T> {
  pub fn new(id: u32) -> Self {
    Id {
      id: id,
      _t: PhantomData
    }
  }
}

impl<T> Clone for Id<T> {
  fn clone(&self) -> Self {
    self.id.into()
  }
}

impl<T> From<u32> for Id<T> {
  fn from(id: u32) -> Self {
    Id::new(id)
  }
}

/// The scene type.
///
/// This type gathers all the required objects a scene needs to correctly handle and render all
/// visual effects.
pub struct Scene {
  /// Resource manager; used to handle scarce resources.
  res_manager: ResourceManager,
  /// List of all models used in the scene.
  models: Vec<Model>,
  /// Model cache used to resolve Id based on instance name.
  model_cache: HashMap<String, Id<Model>>,
}

impl Scene {
  pub fn new<P>(root: P) -> Self where P: AsRef<Path>{
    Scene {
      res_manager: ResourceManager::new(root),
      models: Vec::new(),
      model_cache: HashMap::new(),
    }
  }

  pub fn get_id<T>(&mut self, name: &str) -> Option<Id<T>> where T: Get {
    T::get_id(self, name)
  }

  pub fn resource_manager(&mut self) -> &mut ResourceManager {
    &mut self.res_manager
  }
}

pub trait Get: Sized {
  fn get_id(scene: &mut Scene, name: &str) -> Option<Id<Self>>;
}

impl Get for Model {
  fn get_id(scene: &mut Scene, name: &str) -> Option<Id<Self>> {
    match scene.model_cache.get(name).cloned() {
      id@Some(..) => id,
      None => {
        // cache miss; load then
        let path_str = format!("data/models/{}.obj", name);
        let path = Path::new(&path_str);

        if path.exists() {
          match Model::load(&mut scene.res_manager, path) {
            Ok(model) => {
              let model_id: Id<Model> = (scene.models.len() as u32).into();

              // add the model to the list of loaded models
              scene.models.push(model);
              // add the model to the cache
              scene.model_cache.insert(name.to_owned(), model_id.clone());

              Some(model_id)
            },
            Err(e) => {
              err!("unable to load model '{}': '{:?}'", name, e);
              None
            }
          }
        } else { // TODO: add a manifest override to look in somewhere else
          err!("model '{}' cannot be found", name);
          None
        }
      }
    }
  }
}
<commit_msg>Added Scene::get() and Deref<Target=u32> for Id<T>.<commit_after>use std::collections::HashMap;
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::Path;

use model::Model;
use resource::ResourceManager;

/// A typed identifier.
#[derive(Debug)]
pub struct Id<T> {
  pub id: u32,
  _t: PhantomData<*const T>
}

impl<T> Id<T> {
  pub fn new(id: u32) -> Self {
    Id {
      id: id,
      _t: PhantomData
    }
  }
}

impl<T> Clone for Id<T> {
  fn clone(&self) -> Self {
    self.id.into()
  }
}

impl<T> Deref for Id<T> {
  type Target = u32;

  fn deref(&self) -> &Self::Target {
    &self.id
  }
}

impl<T> From<u32> for Id<T> {
  fn from(id: u32) -> Self {
    Id::new(id)
  }
}

/// The scene type.
///
/// This type gathers all the required objects a scene needs to correctly handle and render all
/// visual effects.
pub struct Scene {
  /// Resource manager; used to handle scarce resources.
  res_manager: ResourceManager,
  /// List of all models used in the scene.
  models: Vec<Model>,
  /// Model cache used to resolve Id based on instance name.
  model_cache: HashMap<String, Id<Model>>,
}

impl Scene {
  pub fn new<P>(root: P) -> Self where P: AsRef<Path>{
    Scene {
      res_manager: ResourceManager::new(root),
      models: Vec::new(),
      model_cache: HashMap::new(),
    }
  }

  pub fn get_id<T>(&mut self, name: &str) -> Option<Id<T>> where T: Get {
    T::get_id(self, name)
  }

  pub fn get<T>(&self, id: Id<T>) -> Option<&T> where T: Get {
    T::get(self, id)
  }

  pub fn resource_manager(&mut self) -> &mut ResourceManager {
    &mut self.res_manager
  }
}

pub trait Get: Sized {
  fn get_id(scene: &mut Scene, name: &str) -> Option<Id<Self>>;
  fn get(scene: &Scene, id: Id<Self>) -> Option<&Self>;
}

impl Get for Model {
  fn get_id(scene: &mut Scene, name: &str) -> Option<Id<Self>> {
    match scene.model_cache.get(name).cloned() {
      id@Some(..) => id,
      None => {
        // cache miss; load then
        let path_str = format!("data/models/{}.obj", name);
        let path = Path::new(&path_str);

        if path.exists() {
          match Model::load(&mut scene.res_manager, path) {
            Ok(model) => {
              let model_id: Id<Model> = (scene.models.len() as u32).into();

              // add the model to the list of loaded models
              scene.models.push(model);
              // add the model to the cache
              scene.model_cache.insert(name.to_owned(), model_id.clone());

              Some(model_id)
            },
            Err(e) => {
              err!("unable to load model '{}': '{:?}'", name, e);
              None
            }
          }
        } else { // TODO: add a manifest override to look in somewhere else
          err!("model '{}' cannot be found", name);
          None
        }
      }
    }
  }

  fn get(scene: &Scene, id: Id<Self>) -> Option<&Self> {
    scene.models.get(*id as usize)
  }
}
<|endoftext|>


<commit_before>use dimensions::Dimensions;

/// A trait that indicates whether or not a widget
/// builder is positionable.
pub trait Shapeable {
    fn get_dim(&self) -> Dimensions;
    fn dim(self, dim: Dimensions) -> Self;
    fn dimensions(self, width: f64, height: f64) -> Self;
    fn width(self, width: f64) -> Self;
    fn height(self, height: f64) -> Self;
}

/// Size property.
#[derive(Copy, Clone)]
pub struct Size(pub Dimensions);

/*
impl<T> Shapeable for T
    where
        (Size, T): Pair<Data = Size, Object = T> + SetAt + GetFrom
{
    #[inline]
    fn dimensions(self, width: f64, height: f64) -> Self {
        self.set(Size([width, height]))
    }
    #[inline]
    fn dim(self, dim: ::dimensions::Dimensions) -> Self {
        self.set(Size(dim))
    }
    #[inline]
    fn width(self, width: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([width, size[1]]))
    }
    #[inline]
    fn height(self, height: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([size[0], height]))
    }
}
*/
<commit_msg>Made `Shapeable::dimensions` a provided trait method<commit_after>use dimensions::Dimensions;

/// A trait that indicates whether or not a widget
/// builder is positionable.
pub trait Shapeable: Sized {
    fn get_dim(&self) -> Dimensions;
    fn dim(self, dim: Dimensions) -> Self;
    fn dimensions(self, width: f64, height: f64) -> Self {
        self.dim([width, height])
    }
    fn width(self, width: f64) -> Self;
    fn height(self, height: f64) -> Self;
}

/// Size property.
#[derive(Copy, Clone)]
pub struct Size(pub Dimensions);

/*
impl<T> Shapeable for T
    where
        (Size, T): Pair<Data = Size, Object = T> + SetAt + GetFrom
{
    #[inline]
    fn dimensions(self, width: f64, height: f64) -> Self {
        self.set(Size([width, height]))
    }
    #[inline]
    fn dim(self, dim: ::dimensions::Dimensions) -> Self {
        self.set(Size(dim))
    }
    #[inline]
    fn width(self, width: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([width, size[1]]))
    }
    #[inline]
    fn height(self, height: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([size[0], height]))
    }
}
*/
<|endoftext|>


<commit_before>//! Solve linear problems

use ndarray::*;

use super::convert::*;
use super::error::*;
use super::layout::*;
use super::types::*;

pub use lapack_traits::{Pivot, Transpose};

pub struct Factorized<S: Data> {
    pub a: ArrayBase<S, Ix2>,
    pub ipiv: Pivot,
}

impl<A, S> Factorized<S>
where
    A: Scalar,
    S: Data<Elem = A>,
{
    pub fn solve<Sb>(&self, t: Transpose, mut rhs: ArrayBase<Sb, Ix1>) -> Result<ArrayBase<Sb, Ix1>>
    where
        Sb: DataMut<Elem = A>,
    {
        unsafe {
            A::solve(
                self.a.square_layout()?,
                t,
                self.a.as_allocated()?,
                &self.ipiv,
                rhs.as_slice_mut().unwrap(),
            )?
        };
        Ok(rhs)
    }
}

impl<A, S> Factorized<S>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    pub fn into_inverse(mut self) -> Result<ArrayBase<S, Ix2>> {
        unsafe {
            A::inv(
                self.a.square_layout()?,
                self.a.as_allocated_mut()?,
                &self.ipiv,
            )?
        };
        Ok(self.a)
    }
}

pub trait Factorize<S: Data> {
    fn factorize(&self) -> Result<Factorized<S>>;
}

pub trait FactorizeInto<S: Data> {
    fn factorize_into(self) -> Result<Factorized<S>>;
}

impl<A, S> FactorizeInto<S> for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    fn factorize_into(mut self) -> Result<Factorized<S>> {
        let ipiv = unsafe { A::lu(self.layout()?, self.as_allocated_mut()?)? };
        Ok(Factorized {
            a: self,
            ipiv: ipiv,
        })
    }
}

impl<A, Si, So> Factorize<So> for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
    So: DataOwned<Elem = A> + DataMut,
{
    fn factorize(&self) -> Result<Factorized<So>> {
        let mut a: ArrayBase<So, Ix2> = replicate(self);
        let ipiv = unsafe { A::lu(a.layout()?, a.as_allocated_mut()?)? };
        Ok(Factorized { a: a, ipiv: ipiv })
    }
}

pub trait Inverse {
    type Output;
    fn inv(&self) -> Result<Self::Output>;
}

pub trait InverseInto {
    type Output;
    fn inv_into(self) -> Result<Self::Output>;
}

impl<A, S> InverseInto for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    type Output = Self;

    fn inv_into(self) -> Result<Self::Output> {
        let f = self.factorize_into()?;
        f.into_inverse()
    }
}

impl<A, Si> Inverse for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
{
    type Output = Array2<A>;

    fn inv(&self) -> Result<Self::Output> {
        let f = self.factorize()?;
        f.into_inverse()
    }
}
<commit_msg>Specify the result Repr for `factorize()`<commit_after>//! Solve linear problems

use ndarray::*;

use super::convert::*;
use super::error::*;
use super::layout::*;
use super::types::*;

pub use lapack_traits::{Pivot, Transpose};

pub struct Factorized<S: Data> {
    pub a: ArrayBase<S, Ix2>,
    pub ipiv: Pivot,
}

impl<A, S> Factorized<S>
where
    A: Scalar,
    S: Data<Elem = A>,
{
    pub fn solve<Sb>(&self, t: Transpose, mut rhs: ArrayBase<Sb, Ix1>) -> Result<ArrayBase<Sb, Ix1>>
    where
        Sb: DataMut<Elem = A>,
    {
        unsafe {
            A::solve(
                self.a.square_layout()?,
                t,
                self.a.as_allocated()?,
                &self.ipiv,
                rhs.as_slice_mut().unwrap(),
            )?
        };
        Ok(rhs)
    }
}

impl<A, S> Factorized<S>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    pub fn into_inverse(mut self) -> Result<ArrayBase<S, Ix2>> {
        unsafe {
            A::inv(
                self.a.square_layout()?,
                self.a.as_allocated_mut()?,
                &self.ipiv,
            )?
        };
        Ok(self.a)
    }
}

pub trait Factorize<S: Data> {
    fn factorize(&self) -> Result<Factorized<S>>;
}

pub trait FactorizeInto<S: Data> {
    fn factorize_into(self) -> Result<Factorized<S>>;
}

impl<A, S> FactorizeInto<S> for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    fn factorize_into(mut self) -> Result<Factorized<S>> {
        let ipiv = unsafe { A::lu(self.layout()?, self.as_allocated_mut()?)? };
        Ok(Factorized {
            a: self,
            ipiv: ipiv,
        })
    }
}

impl<A, Si> Factorize<OwnedRepr<A>> for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
{
    fn factorize(&self) -> Result<Factorized<OwnedRepr<A>>> {
        let mut a: Array2<A> = replicate(self);
        let ipiv = unsafe { A::lu(a.layout()?, a.as_allocated_mut()?)? };
        Ok(Factorized { a: a, ipiv: ipiv })
    }
}

pub trait Inverse {
    type Output;
    fn inv(&self) -> Result<Self::Output>;
}

pub trait InverseInto {
    type Output;
    fn inv_into(self) -> Result<Self::Output>;
}

impl<A, S> InverseInto for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    type Output = Self;

    fn inv_into(self) -> Result<Self::Output> {
        let f = self.factorize_into()?;
        f.into_inverse()
    }
}

impl<A, Si> Inverse for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
{
    type Output = Array2<A>;

    fn inv(&self) -> Result<Self::Output> {
        let f = self.factorize()?;
        f.into_inverse()
    }
}
<|endoftext|>


<commit_before>use futures::future::Future;
use futures::Sink;
use futures::sync::mpsc::Sender;
use hyper;
use hyper_native_tls;
use iron::prelude::*;
use iron::status;
use serde;
use serde_json;
use tokio_core;

use args;
use bot;

/// Create a new hyper client for the given url.
fn new_client(url: &str) -> hyper::Client {
    if url.starts_with("https:") {
        let ssl = hyper_native_tls::NativeTlsClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);
        return hyper::Client::with_connector(connector);
    }

    hyper::Client::new()
}

/// Try to get json from the given url with basic token authorization.
fn get_json_with_token(url: &str, token: &str) -> Result<hyper::client::Response, hyper::Error> {
    let client = new_client(url);
    let auth = hyper::header::Authorization(hyper::header::Bearer { token: String::from(token) });
    client
        .get(url)
        .header(hyper::header::ContentType::json())
        .header(hyper::header::Accept::json())
        .header(auth)
        .send()
}

/// Try to post json to the given url with basic token authorization.
pub fn post_with_token<T>(
    url: &str,
    token: &str,
    data: &T,
) -> Result<hyper::client::Response, hyper::Error>
where
    T: serde::ser::Serialize,
{
    let client = new_client(url);
    let payload = serde_json::to_string(data).unwrap();
    let auth = hyper::header::Authorization(String::from("Bearer ") + token);
    client
        .post(url)
        .header(hyper::header::ContentType::json())
        .header(auth)
        .body(&payload)
        .send()
}

/// Try to post json to the given url with basic token authorization.
pub fn delete_with_token(url: &str, token: &str) -> Result<hyper::client::Response, hyper::Error> {
    let client = new_client(url);
    let auth = hyper::header::Authorization(String::from("Bearer ") + token);
    client
        .delete(url)
        .header(hyper::header::ContentType::json())
        .header(auth)
        .send()
}

/// Spark id of the user
pub type PersonId = String;

/// Email of the user
pub type Email = String;

/// Webhook's post request from Spark API
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Post {
    actor_id: String,
    app_id: String,
    created: String,
    created_by: String,
    data: Message,
    event: String,
    id: String,
    name: String,
    org_id: String,
    owned_by: String,
    resource: String,
    status: String,
    target_url: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Message {
    created: Option<String>,
    id: String,
    pub person_email: String,
    pub person_id: String,
    room_id: String,
    room_type: String,

    // a message contained in a post does not have text loaded
    #[serde(default)]
    text: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct PersonDetails {
    id: String,
    emails: Vec<String>,
    display_name: String,
    nick_name: Option<String>,
    org_id: String,
    created: String,
    last_activity: Option<String>,
    status: Option<String>,
    #[serde(rename = "type")]
    person_type: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Webhook {
    id: String,
    name: String,
    target_url: String,
    resource: String,
    event: String,
    org_id: String,
    created_by: String,
    app_id: String,
    owned_by: String,
    status: String,
    created: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Webhooks {
    items: Vec<Webhook>,
}

pub struct SparkClient {
    url: String,
    bot_token: String,
    pub bot_id: String,
}

impl SparkClient {
    pub fn new(args: &args::Args) -> Result<SparkClient, String> {
        let mut client = SparkClient {
            url: args.spark_url.clone(),
            bot_token: args.spark_bot_token.clone(),
            bot_id: String::new(),
        };
        client.bot_id = client.get_bot_id()?;
        debug!("Bot id: {}", client.bot_id);
        Ok(client)
    }

    pub fn reply(&self, person_id: &str, msg: &str) {
        let json = json!({
            "toPersonId": person_id,
            "markdown": msg,
        });
        let res = post_with_token(&(self.url.clone() + "/messages"), &self.bot_token, &json);
        if let Err(err) = res {
            error!("Could not reply to gerrit: {:?}", err);
        }
    }

    fn get_bot_id(&self) -> Result<String, String> {
        let resp = get_json_with_token(&(self.url.clone() + "/people/me"), &self.bot_token)
            .map_err(|err| format!("Invalid response from spark: {}", err))?;
        let details: PersonDetails = serde_json::from_reader(resp).map_err(
            |err| format!("Could not parse person details: {}", err),
        )?;
        Ok(details.id)
    }

    fn register_webhook(&self, url: &str) -> Result<(), String> {
        let json = json!({
            "name": "gerritbot",
            "targetUrl": String::from(url),
            "resource": "messages",
            "event": "created"
        });
        post_with_token(&(self.url.clone() + "/webhooks"), &self.bot_token, &json)
            .map_err(
                |err| format!("Could not register Spark's webhooks: {}", err),
            )
            .and_then(|resp| if resp.status != hyper::status::StatusCode::Ok {
                Err(
                    format!("Could not register Spark's webhook: {}", resp.status),
                )
            } else {
                Ok(())
            })
    }

    fn list_webhooks(&self) -> Result<Webhooks, String> {
        let resp = get_json_with_token(&(self.url.clone() + "/webhooks"), &self.bot_token)
            .map_err(|err| format!("Invalid response from spark: {}", err))?;
        let webhooks: Webhooks = serde_json::from_reader(resp).map_err(
            |err| format!("Could not parse webhooks list: {}", err),
        )?;
        Ok(webhooks)
    }

    fn delete_webhook(&self, id: &str) -> Result<(), String> {
        delete_with_token(&(self.url.clone() + "/webhooks/" + id), &self.bot_token)
            .map_err(|err| format!("Could not delete webhook: {}", err))
            .and_then(|resp| if resp.status !=
                hyper::status::StatusCode::NoContent
            {
                Err(format!("Could not delete webhook: {}", resp.status))
            } else {
                Ok(())
            })
    }

    pub fn replace_webhook_url(&self, url: &str) -> Result<(), String> {
        // remove all other webhooks
        let webhooks = self.list_webhooks()?;
        let to_remove = webhooks.items.into_iter().filter_map(
            |webhook| if webhook.resource == "messages" &&
                webhook.event == "created"
            {
                Some(webhook)
            } else {
                None
            },
        );
        for webhook in to_remove {
            self.delete_webhook(&webhook.id)?;
            debug!("Removed webhook from Spark: {}", webhook.target_url);
        }

        // register new webhook
        self.register_webhook(url)
    }

    fn get_message_text(&self, message_id: &str) -> Result<hyper::client::Response, hyper::Error> {
        get_json_with_token(
            &(self.url.clone() + "/messages/" + message_id),
            &self.bot_token,
        )
    }
}

impl Message {
    /// Load text from Spark for a received message
    /// Note: Spark does not send the text with the message to the registered post hook.
    pub fn load_text(&mut self, client: &SparkClient) -> Result<(), String> {
        let resp = client.get_message_text(&self.id).map_err(
            |err| format!("Invalid response from spark: {}", err),
        )?;
        let msg: Message = serde_json::from_reader(resp).map_err(|err| {
            String::from(format!("Could not parse json: {}", err))
        })?;
        self.text = msg.text;
        Ok(())
    }

    /// Convert Spark message to bot action
    pub fn into_action(self) -> bot::Action {
        match &self.text.trim().to_lowercase()[..] {
            "enable" => bot::Action::Enable(self.person_id, self.person_email),
            "disable" => bot::Action::Disable(self.person_id, self.person_email),
            "status" => bot::Action::Status(self.person_id),
            "help" => bot::Action::Help(self.person_id),
            _ => bot::Action::Unknown(self.person_id),
        }
    }
}

/// Post hook from Spark
pub fn webhook_handler(
    req: &mut Request,
    remote: &tokio_core::reactor::Remote,
    tx: Sender<Message>,
) -> IronResult<Response> {
    let new_post: Post = match serde_json::from_reader(&mut req.body) {
        Ok(post) => post,
        Err(err) => {
            error!("Could not parse post: {}", err);
            return Ok(Response::with(status::Ok));
        }
    };

    let msg = new_post.data;
    remote.spawn(move |_| tx.send(msg).map_err(|_| ()).map(|_| ()));

    Ok(Response::with(status::Ok))
}
<commit_msg>Add custom Error type to spark mod.<commit_after>use std::{error, fmt};

use futures::future::Future;
use futures::Sink;
use futures::sync::mpsc::Sender;
use hyper;
use hyper_native_tls;
use iron::prelude::*;
use iron::status;
use serde;
use serde_json;
use tokio_core;

use args;
use bot;

/// Create a new hyper client for the given url.
fn new_client(url: &str) -> hyper::Client {
    if url.starts_with("https:") {
        let ssl = hyper_native_tls::NativeTlsClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);
        return hyper::Client::with_connector(connector);
    }

    hyper::Client::new()
}

/// Try to get json from the given url with basic token authorization.
fn get_json_with_token(url: &str, token: &str) -> Result<hyper::client::Response, hyper::Error> {
    let client = new_client(url);
    let auth = hyper::header::Authorization(hyper::header::Bearer { token: String::from(token) });
    client
        .get(url)
        .header(hyper::header::ContentType::json())
        .header(hyper::header::Accept::json())
        .header(auth)
        .send()
}

/// Try to post json to the given url with basic token authorization.
pub fn post_with_token<T>(
    url: &str,
    token: &str,
    data: &T,
) -> Result<hyper::client::Response, hyper::Error>
where
    T: serde::ser::Serialize,
{
    let client = new_client(url);
    let payload = serde_json::to_string(data).unwrap();
    let auth = hyper::header::Authorization(String::from("Bearer ") + token);
    client
        .post(url)
        .header(hyper::header::ContentType::json())
        .header(auth)
        .body(&payload)
        .send()
}

/// Try to post json to the given url with basic token authorization.
pub fn delete_with_token(url: &str, token: &str) -> Result<hyper::client::Response, hyper::Error> {
    let client = new_client(url);
    let auth = hyper::header::Authorization(String::from("Bearer ") + token);
    client
        .delete(url)
        .header(hyper::header::ContentType::json())
        .header(auth)
        .send()
}

/// Spark id of the user
pub type PersonId = String;

/// Email of the user
pub type Email = String;

/// Webhook's post request from Spark API
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Post {
    actor_id: String,
    app_id: String,
    created: String,
    created_by: String,
    data: Message,
    event: String,
    id: String,
    name: String,
    org_id: String,
    owned_by: String,
    resource: String,
    status: String,
    target_url: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Message {
    created: Option<String>,
    id: String,
    pub person_email: String,
    pub person_id: String,
    room_id: String,
    room_type: String,

    // a message contained in a post does not have text loaded
    #[serde(default)]
    text: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct PersonDetails {
    id: String,
    emails: Vec<String>,
    display_name: String,
    nick_name: Option<String>,
    org_id: String,
    created: String,
    last_activity: Option<String>,
    status: Option<String>,
    #[serde(rename = "type")]
    person_type: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Webhook {
    id: String,
    name: String,
    target_url: String,
    resource: String,
    event: String,
    org_id: String,
    created_by: String,
    app_id: String,
    owned_by: String,
    status: String,
    created: String,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Webhooks {
    items: Vec<Webhook>,
}

pub struct SparkClient {
    url: String,
    bot_token: String,
    pub bot_id: String,
}

#[derive(Debug)]
pub enum Error {
    HyperError(hyper::Error),
    JsonError(serde_json::Error),
    RegisterWebhook(String),
    DeleteWebhook(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::HyperError(ref err) => fmt::Display::fmt(err, f),
            Error::JsonError(ref err) => fmt::Display::fmt(err, f),
            Error::RegisterWebhook(ref msg) => fmt::Display::fmt(msg, f),
            Error::DeleteWebhook(ref msg) => fmt::Display::fmt(msg, f),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::HyperError(ref err) => err.description(),
            Error::JsonError(ref err) => err.description(),
            Error::RegisterWebhook(ref msg) => msg,
            Error::DeleteWebhook(ref msg) => msg,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::HyperError(ref err) => err.cause(),
            Error::JsonError(ref err) => err.cause(),
            Error::RegisterWebhook(_) => None,
            Error::DeleteWebhook(_) => None,
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Self {
        Error::HyperError(err)
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Error::JsonError(err)
    }
}

impl SparkClient {
    pub fn new(args: &args::Args) -> Result<SparkClient, Error> {
        let mut client = SparkClient {
            url: args.spark_url.clone(),
            bot_token: args.spark_bot_token.clone(),
            bot_id: String::new(),
        };
        client.bot_id = client.get_bot_id()?;
        debug!("Bot id: {}", client.bot_id);
        Ok(client)
    }

    pub fn reply(&self, person_id: &str, msg: &str) {
        let json = json!({
            "toPersonId": person_id,
            "markdown": msg,
        });
        let res = post_with_token(&(self.url.clone() + "/messages"), &self.bot_token, &json);
        if let Err(err) = res {
            error!("Could not reply to gerrit: {:?}", err);
        }
    }

    fn get_bot_id(&self) -> Result<String, Error> {
        let resp = get_json_with_token(&(self.url.clone() + "/people/me"), &self.bot_token)?;
        let details: PersonDetails = serde_json::from_reader(resp)?;
        Ok(details.id)
    }

    fn register_webhook(&self, url: &str) -> Result<(), Error> {
        let json = json!({
            "name": "gerritbot",
            "targetUrl": String::from(url),
            "resource": "messages",
            "event": "created"
        });
        post_with_token(&(self.url.clone() + "/webhooks"), &self.bot_token, &json)
            .map_err(Error::from)
            .and_then(|resp| if resp.status != hyper::status::StatusCode::Ok {
                Err(Error::RegisterWebhook(
                    format!("Could not register Spark's webhook: {}", resp.status),
                ))
            } else {
                Ok(())
            })
    }

    fn list_webhooks(&self) -> Result<Webhooks, Error> {
        let resp = get_json_with_token(&(self.url.clone() + "/webhooks"), &self.bot_token)?;
        let webhooks: Webhooks = serde_json::from_reader(resp)?;
        Ok(webhooks)
    }

    fn delete_webhook(&self, id: &str) -> Result<(), Error> {
        delete_with_token(&(self.url.clone() + "/webhooks/" + id), &self.bot_token)
            .map_err(Error::from)
            .and_then(|resp| if resp.status !=
                hyper::status::StatusCode::NoContent
            {
                Err(Error::DeleteWebhook(
                    format!("Could not delete webhook: {}", resp.status),
                ))
            } else {
                Ok(())
            })
    }

    pub fn replace_webhook_url(&self, url: &str) -> Result<(), Error> {
        // remove all other webhooks
        let webhooks = self.list_webhooks()?;
        let to_remove = webhooks.items.into_iter().filter_map(
            |webhook| if webhook.resource == "messages" &&
                webhook.event == "created"
            {
                Some(webhook)
            } else {
                None
            },
        );
        for webhook in to_remove {
            self.delete_webhook(&webhook.id)?;
            debug!("Removed webhook from Spark: {}", webhook.target_url);
        }

        // register new webhook
        self.register_webhook(url)
    }

    fn get_message_text(&self, message_id: &str) -> Result<hyper::client::Response, hyper::Error> {
        get_json_with_token(
            &(self.url.clone() + "/messages/" + message_id),
            &self.bot_token,
        )
    }
}

impl Message {
    /// Load text from Spark for a received message
    /// Note: Spark does not send the text with the message to the registered post hook.
    pub fn load_text(&mut self, client: &SparkClient) -> Result<(), String> {
        let resp = client.get_message_text(&self.id).map_err(
            |err| format!("Invalid response from spark: {}", err),
        )?;
        let msg: Message = serde_json::from_reader(resp).map_err(|err| {
            String::from(format!("Could not parse json: {}", err))
        })?;
        self.text = msg.text;
        Ok(())
    }

    /// Convert Spark message to bot action
    pub fn into_action(self) -> bot::Action {
        match &self.text.trim().to_lowercase()[..] {
            "enable" => bot::Action::Enable(self.person_id, self.person_email),
            "disable" => bot::Action::Disable(self.person_id, self.person_email),
            "status" => bot::Action::Status(self.person_id),
            "help" => bot::Action::Help(self.person_id),
            _ => bot::Action::Unknown(self.person_id),
        }
    }
}

/// Post hook from Spark
pub fn webhook_handler(
    req: &mut Request,
    remote: &tokio_core::reactor::Remote,
    tx: Sender<Message>,
) -> IronResult<Response> {
    let new_post: Post = match serde_json::from_reader(&mut req.body) {
        Ok(post) => post,
        Err(err) => {
            error!("Could not parse post: {}", err);
            return Ok(Response::with(status::Ok));
        }
    };

    let msg = new_post.data;
    remote.spawn(move |_| tx.send(msg).map_err(|_| ()).map(|_| ()));

    Ok(Response::with(status::Ok))
}
<|endoftext|>


<commit_before>use BitField;

#[test]
fn test_set_reset_u8() {
    let mut field = BitField(0b11110010u8);
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..8 {
        bit_i(i);
    }
}

#[test]
fn test_set_reset_u16() {
    let mut field = BitField(0b1111001010010110u16);
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..16 {
        bit_i(i);
    }
}

#[test]
fn test_read_u32() {
    let field = BitField(0b1111111111010110u32);
    assert_eq!(field.get_bit(0), false);
    assert_eq!(field.get_bit(1), true);
    assert_eq!(field.get_bit(2), true);
    assert_eq!(field.get_bit(3), false);
    assert_eq!(field.get_bit(4), true);
    assert_eq!(field.get_bit(5), false);
    for i in 6..16 {
        assert_eq!(field.get_bit(i), true);
    }
    for i in 16..32 {
        assert_eq!(field.get_bit(i), false);
    }

    assert_eq!(field.get_range(16..32), 0);
    assert_eq!(field.get_range(6..16), 0b1111111111);
    assert_eq!(field.get_range(0..6), 0b010110);
    assert_eq!(field.get_range(0..10), 0b1111010110);
    assert_eq!(field.get_range(5..12), 0b1111110);
}

#[test]
fn test_set_reset_u32() {
    let mut field = BitField(0b1111111111010110u32);
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..32 {
        bit_i(i);
    }
}

#[test]
fn test_set_range_u32() {
    let mut field = BitField(0b1111111111010110u32);
    field.set_range(10..15, 0b00000);
    assert_eq!(field.get_range(10..15), 0b00000);
    field.set_range(10..15, 0b10101);
    assert_eq!(field.get_range(10..15), 0b10101);
    field.set_range(10..15, 0b01010);
    assert_eq!(field.get_range(10..15), 0b01010);
    field.set_range(10..15, 0b11111);
    assert_eq!(field.get_range(10..15), 0b11111);

    field.set_range(0..16, 0xdead);
    field.set_range(14..32, 0xbeaf);
    assert_eq!(field.get_range(0..16), 0xdead);
    assert_eq!(field.get_range(14..32), 0xbeaf);
}

#[test]
fn test_read_u64() {
    let field = BitField(0b1111111111010110u64 << 32);
    for i in 0..32 {
        assert_eq!(field.get_bit(i), false);
    }
    assert_eq!(field.get_bit(32), false);
    assert_eq!(field.get_bit(33), true);
    assert_eq!(field.get_bit(34), true);
    assert_eq!(field.get_bit(35), false);
    assert_eq!(field.get_bit(36), true);
    assert_eq!(field.get_bit(37), false);
    for i in 38..48 {
        assert_eq!(field.get_bit(i), true);
    }
    for i in 48..64 {
        assert_eq!(field.get_bit(i), false);
    }

    assert_eq!(field.get_range(0..32), 0);
    assert_eq!(field.get_range(48..64), 0);
    assert_eq!(field.get_range(38..48), 0b1111111111);
    assert_eq!(field.get_range(32..38), 0b010110);
    assert_eq!(field.get_range(32..42), 0b1111010110);
    assert_eq!(field.get_range(37..44), 0b1111110);
}

#[test]
fn test_set_reset_u64() {
    let mut field = BitField(0b1111111111010110u64 << 32);
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..64 {
        bit_i(i);
    }
}

#[test]
fn test_set_range_u64() {
    let mut field = BitField(0b1111111111010110u64 << 32);
    field.set_range(42..47, 0b00000);
    assert_eq!(field.get_range(42..47), 0b00000);
    field.set_range(10..15, 0b10101);
    assert_eq!(field.get_range(10..15), 0b10101);
    field.set_range(40..45, 0b01010);
    assert_eq!(field.get_range(40..45), 0b01010);
    field.set_range(40..45, 0b11111);
    assert_eq!(field.get_range(40..45), 0b11111);

    field.set_range(0..16, 0xdead);
    field.set_range(14..32, 0xbeaf);
    field.set_range(32..64, 0xcafebabe);
    assert_eq!(field.get_range(0..16), 0xdead);
    assert_eq!(field.get_range(14..32), 0xbeaf);
    assert_eq!(field.get_range(32..64), 0xcafebabe);
}
<commit_msg>Fix tests: The BitField wrapper type no longer exists<commit_after>use BitField;

#[test]
fn test_set_reset_u8() {
    let mut field = 0b11110010u8;
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..8 {
        bit_i(i);
    }
}

#[test]
fn test_set_reset_u16() {
    let mut field = 0b1111001010010110u16;
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..16 {
        bit_i(i);
    }
}

#[test]
fn test_read_u32() {
    let field = 0b1111111111010110u32;
    assert_eq!(field.get_bit(0), false);
    assert_eq!(field.get_bit(1), true);
    assert_eq!(field.get_bit(2), true);
    assert_eq!(field.get_bit(3), false);
    assert_eq!(field.get_bit(4), true);
    assert_eq!(field.get_bit(5), false);
    for i in 6..16 {
        assert_eq!(field.get_bit(i), true);
    }
    for i in 16..32 {
        assert_eq!(field.get_bit(i), false);
    }

    assert_eq!(field.get_range(16..32), 0);
    assert_eq!(field.get_range(6..16), 0b1111111111);
    assert_eq!(field.get_range(0..6), 0b010110);
    assert_eq!(field.get_range(0..10), 0b1111010110);
    assert_eq!(field.get_range(5..12), 0b1111110);
}

#[test]
fn test_set_reset_u32() {
    let mut field = 0b1111111111010110u32;
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..32 {
        bit_i(i);
    }
}

#[test]
fn test_set_range_u32() {
    let mut field = 0b1111111111010110u32;
    field.set_range(10..15, 0b00000);
    assert_eq!(field.get_range(10..15), 0b00000);
    field.set_range(10..15, 0b10101);
    assert_eq!(field.get_range(10..15), 0b10101);
    field.set_range(10..15, 0b01010);
    assert_eq!(field.get_range(10..15), 0b01010);
    field.set_range(10..15, 0b11111);
    assert_eq!(field.get_range(10..15), 0b11111);

    field.set_range(0..16, 0xdead);
    field.set_range(14..32, 0xbeaf);
    assert_eq!(field.get_range(0..16), 0xdead);
    assert_eq!(field.get_range(14..32), 0xbeaf);
}

#[test]
fn test_read_u64() {
    let field = 0b1111111111010110u64 << 32;
    for i in 0..32 {
        assert_eq!(field.get_bit(i), false);
    }
    assert_eq!(field.get_bit(32), false);
    assert_eq!(field.get_bit(33), true);
    assert_eq!(field.get_bit(34), true);
    assert_eq!(field.get_bit(35), false);
    assert_eq!(field.get_bit(36), true);
    assert_eq!(field.get_bit(37), false);
    for i in 38..48 {
        assert_eq!(field.get_bit(i), true);
    }
    for i in 48..64 {
        assert_eq!(field.get_bit(i), false);
    }

    assert_eq!(field.get_range(0..32), 0);
    assert_eq!(field.get_range(48..64), 0);
    assert_eq!(field.get_range(38..48), 0b1111111111);
    assert_eq!(field.get_range(32..38), 0b010110);
    assert_eq!(field.get_range(32..42), 0b1111010110);
    assert_eq!(field.get_range(37..44), 0b1111110);
}

#[test]
fn test_set_reset_u64() {
    let mut field = 0b1111111111010110u64 << 32;
    let mut bit_i = |i| {
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
        field.set_bit(i, false);
        assert_eq!(field.get_bit(i), false);
        field.set_bit(i, true);
        assert_eq!(field.get_bit(i), true);
    };
    for i in 0..64 {
        bit_i(i);
    }
}

#[test]
fn test_set_range_u64() {
    let mut field = 0b1111111111010110u64 << 32;
    field.set_range(42..47, 0b00000);
    assert_eq!(field.get_range(42..47), 0b00000);
    field.set_range(10..15, 0b10101);
    assert_eq!(field.get_range(10..15), 0b10101);
    field.set_range(40..45, 0b01010);
    assert_eq!(field.get_range(40..45), 0b01010);
    field.set_range(40..45, 0b11111);
    assert_eq!(field.get_range(40..45), 0b11111);

    field.set_range(0..16, 0xdead);
    field.set_range(14..32, 0xbeaf);
    field.set_range(32..64, 0xcafebabe);
    assert_eq!(field.get_range(0..16), 0xdead);
    assert_eq!(field.get_range(14..32), 0xbeaf);
    assert_eq!(field.get_range(32..64), 0xcafebabe);
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::Read;

use decoder::{decode, DecodedMap};
use errors::{Result, Error};

/// Represents a raw token
///
/// Raw tokens are used internally to represent the sourcemap
/// in a memory efficient way.  If you construct sourcemaps yourself
/// then you need to create these objects, otherwise they are invisible
/// to you as a user.
#[derive(PartialEq, Copy, Clone)]
pub struct RawToken {
    /// the destination (minified) line number
    pub dst_line: u32,
    /// the destination (minified) column number
    pub dst_col: u32,
    /// the source line number
    pub src_line: u32,
    /// the source line column
    pub src_col: u32,
    /// source identifier
    pub src_id: u32,
    /// name identifier (`!0` in case there is no associated name)
    pub name_id: u32,
}

/// Represents a token from a sourcemap
pub struct Token<'a> {
    raw: &'a RawToken,
    i: &'a SourceMap,
}

impl<'a> Token<'a> {
    /// get the destination (minified) line number
    pub fn get_dst_line(&self) -> u32 {
        self.raw.dst_line
    }

    /// get the destination (minified) column number
    pub fn get_dst_col(&self) -> u32 {
        self.raw.dst_col
    }

    /// get the destination line and column
    pub fn get_dst(&self) -> (u32, u32) {
        (self.get_dst_line(), self.get_dst_col())
    }

    /// get the source line number
    pub fn get_src_line(&self) -> u32 {
        self.raw.src_line
    }

    /// get the source column number
    pub fn get_src_col(&self) -> u32 {
        self.raw.src_col
    }

    /// get the source line and column
    pub fn get_src(&self) -> (u32, u32) {
        (self.get_src_line(), self.get_src_col())
    }

    /// get the source if it exists as string
    pub fn get_source(&self) -> &str {
        if self.raw.src_id == !0 {
            ""
        } else {
            &self.i.sources[self.raw.src_id as usize][..]
        }
    }

    /// get the source ID if it exists
    pub fn get_source_id(&self) -> Option<u32> {
        if self.raw.src_id == !0 {
            None
        } else {
            Some(self.raw.src_id)
        }
    }

    /// get the name if it exists as string
    pub fn get_name(&self) -> Option<&str> {
        if self.raw.name_id == !0 {
            None
        } else {
            Some(&self.i.names[self.raw.name_id as usize][..])
        }
    }

    /// returns `true` if a name exists, `false` otherwise
    pub fn has_name(&self) -> bool {
        self.get_name().is_some()
    }

    /// Converts the token into a debug tuple in the form
    /// `(source, src_line, src_col, name)`
    pub fn to_tuple(&self) -> (&str, u32, u32, Option<&str>) {
        (
            self.get_source(),
            self.get_src_line(),
            self.get_src_col(),
            self.get_name()
        )
    }

    /// Get the underlying raw token
    pub fn get_raw_token(&self) -> RawToken {
        *self.raw
    }
}

/// Iterates over all tokens in a sourcemap
pub struct TokenIter<'a> {
    i: &'a SourceMap,
    next_idx: u32,
}

impl<'a> Iterator for TokenIter<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Token<'a>> {
        self.i.get_token(self.next_idx).map(|tok| {
            self.next_idx += 1;
            tok
        })
    }
}

/// Iterates over all index items in a sourcemap
pub struct IndexIter<'a> {
    i: &'a SourceMap,
    next_idx: usize,
}

impl<'a> Iterator for IndexIter<'a> {
    type Item = (u32, u32, u32);

    fn next(&mut self) -> Option<(u32, u32, u32)> {
        self.i.index.get(self.next_idx).map(|idx| {
            self.next_idx += 1;
            *idx
        })
    }
}

impl<'a> fmt::Debug for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Token {}>", self)
    }
}

impl<'a> fmt::Display for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}:{}{}",
               self.get_source(),
               self.get_src_line(),
               self.get_src_col(),
               self.get_name().map(|x| format!(" name={}", x))
                   .unwrap_or("".into()))
    }
}

/// Represents a section in a sourcemap index
pub struct SourceMapSection {
    offset: (u32, u32),
    url: Option<String>,
    map: Option<Box<SourceMap>>,
}

/// Iterates over all sections in a sourcemap index
pub struct SourceMapSectionIter<'a> {
    i: &'a SourceMapIndex,
    next_idx: u32,
}

impl<'a> Iterator for SourceMapSectionIter<'a> {
    type Item = &'a SourceMapSection;

    fn next(&mut self) -> Option<&'a SourceMapSection> {
        self.i.get_section(self.next_idx).map(|sec| {
            self.next_idx += 1;
            sec
        })
    }
}

/// Represents a sourcemap index in memory
pub struct SourceMapIndex {
    version: u32,
    file: Option<String>,
    sections: Vec<SourceMapSection>,
}

/// Represents a sourcemap in memory
///
/// This is always represents a regular "non-indexed" sourcemap.  Particularly
/// in case the `from_reader` method is used an index sourcemap will be
/// rejected with an error on reading.
pub struct SourceMap {
    version: u32,
    file: Option<String>,
    tokens: Vec<RawToken>,
    index: Vec<(u32, u32, u32)>,
    names: Vec<String>,
    sources: Vec<String>,
    sources_content: Vec<Option<String>>,
}

impl SourceMap {

    /// Creates a sourcemap from a reader over a JSON stream in UTF-8
    /// format.  Optionally a "garbage header" as defined by the
    /// sourcemap draft specification is supported.  In case an indexed
    /// sourcemap is encountered an error is returned.
    ///
    /// ```rust
    /// use sourcemap::SourceMap;
    /// let input: &[_] = b"{
    ///     \"version\":3,
    ///     \"sources\":[\"coolstuff.js\"],
    ///     \"names\":[\"x\",\"alert\"],
    ///     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
    /// }";
    /// let sm = SourceMap::from_reader(input).unwrap();
    /// ```
    pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
        match try!(decode(rdr)) {
            DecodedMap::Regular(sm) => Ok(sm),
            DecodedMap::Index(_) => Err(Error::IndexedSourcemap),
        }
    }

    /// Constructs a new sourcemap from raw components.
    ///
    /// - `version`: the version is typically `3` which is the current darft version
    /// - `file`: an optional filename of the sourcemap
    /// - `tokens`: a list of raw tokens
    /// - `index`: a sorted mapping of line and column to token index
    /// - `names`: a vector of names
    /// - `sources` a vector of source filenames
    /// - `sources_content` optional source contents
    pub fn new(version: u32, file: Option<String>, tokens: Vec<RawToken>,
               index: Vec<(u32, u32, u32)>, names: Vec<String>,
               sources: Vec<String>,
               sources_content: Option<Vec<Option<String>>>) -> SourceMap {
        SourceMap {
            version: version,
            file: file,
            tokens: tokens,
            index: index,
            names: names,
            sources: sources,
            sources_content: sources_content.unwrap_or(vec![]),
        }
    }

    /// Returns the version of the sourcemap.
    pub fn get_version(&self) -> u32 {
        self.version
    }

    /// Returns the embedded filename in case there is one.
    pub fn get_file(&self) -> Option<&str> {
        self.file.as_ref().map(|x| &x[..])
    }

    /// Looks up a token by its index.
    pub fn get_token<'a>(&'a self, idx: u32) -> Option<Token<'a>> {
        self.tokens.get(idx as usize).map(|raw| {
            Token { raw: raw, i: self }
        })
    }

    /// Returns the number of tokens in the sourcemap.
    pub fn get_token_count(&self) -> u32 {
        self.tokens.len() as u32
    }

    /// Returns an iterator over the tokens.
    pub fn tokens<'a>(&'a self) -> TokenIter<'a> {
        TokenIter { i: self, next_idx: 0 }
    }

    /// Looks up the closest token to a given line and column.
    pub fn lookup_token<'a>(&'a self, line: u32, col: u32) -> Option<Token<'a>> {
        let mut low = 0;
        let mut high = self.index.len();

        while low < high {
            let mid = (low + high) / 2;
            if (line, col, 0) < self.index[mid as usize] {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        if low > 0 {
            self.get_token(self.index[low as usize].2)
        } else {
            None
        }
    }

    /// Returns the number of sources in the sourcemap.
    pub fn get_source_count(&self) -> u32 {
        self.sources.len() as u32
    }

    /// Looks up a source for a specific index.
    pub fn get_source(&self, idx: u32) -> Option<&str> {
        self.sources.get(idx as usize).map(|x| &x[..])
    }

    /// Looks up the content for a source.
    pub fn get_source_contents(&self, idx: u32) -> Option<&str> {
        self.sources_content.get(idx as usize)
            .and_then(|bucket| bucket.as_ref()).map(|x| &**x)
    }

    /// Returns the number of names in the sourcemap.
    pub fn get_name_count(&self) -> u32 {
        self.names.len() as u32
    }

    /// Looks up a name for a specific index.
    pub fn get_name(&self, idx: u32) -> Option<&str> {
        self.names.get(idx as usize).map(|x| &x[..])
    }

    /// Returns the number of items in the index
    pub fn get_index_size(&self) -> usize {
        self.index.len()
    }

    /// Returns the number of items in the index
    pub fn index_iter<'a>(&'a self) -> IndexIter<'a> {
        IndexIter { i: self, next_idx: 0 }
    }
}

impl SourceMapIndex {

    /// Creates a sourcemap index from a reader over a JSON stream in UTF-8
    /// format.  Optionally a "garbage header" as defined by the
    /// sourcemap draft specification is supported.  In case a regular
    /// sourcemap is encountered an error is returned.
    pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
        match try!(decode(rdr)) {
            DecodedMap::Regular(_) => Err(Error::RegularSourcemap),
            DecodedMap::Index(smi) => Ok(smi),
        }
    }

    /// Constructs a new sourcemap index from raw components.
    ///
    /// - `version`: the version is typically `3` which is the current darft version
    /// - `file`: an optional filename of the index
    /// - `sections`: a vector of source map index sections
    pub fn new(version: u32, file: Option<String>,
               sections: Vec<SourceMapSection>) -> SourceMapIndex {
        SourceMapIndex {
            version: version,
            file: file,
            sections: sections,
        }
    }

    /// Returns the version of the sourcemap index.
    pub fn get_version(&self) -> u32 {
        self.version
    }

    /// Returns the embedded filename in case there is one.
    pub fn get_file(&self) -> Option<&str> {
        self.file.as_ref().map(|x| &x[..])
    }

    /// Returns the number of sections in this index
    pub fn get_section_count(&self) -> u32 {
        self.sections.len() as u32
    }

    /// Looks up a single section and returns it
    pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
        self.sections.get(idx as usize)
    }

    /// Looks up a single section and returns it as a mutable ref
    pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
        self.sections.get_mut(idx as usize)
    }

    /// Iterates over all sections
    pub fn sections<'a>(&'a self) -> SourceMapSectionIter<'a> {
        SourceMapSectionIter {
            i: self,
            next_idx: 0
        }
    }

    /// Looks up the closest token to a given line and column.
    ///
    /// This requires that the referenced sourcemaps are actually loaded.
    /// If a sourcemap is encountered that is not embedded but just
    /// externally referenced it is silently skipped.
    pub fn lookup_token<'a>(&'a self, line: u32, col: u32) -> Option<Token<'a>> {
        for section in self.sections() {
            let (off_line, off_col) = section.get_offset();
            if off_line < line || off_col < col {
                continue;
            }
            if let Some(map) = section.get_sourcemap() {
                if let Some(tok) = map.lookup_token(line - off_line, col - off_col) {
                    return Some(tok);
                }
            }
        }
        None
    }
}

impl SourceMapSection {

    /// Create a new sourcemap index section
    ///
    /// - `offset`: offset as line and column
    /// - `url`: optional URL of where the sourcemap is located
    /// - `map`: an optional already resolved internal sourcemap
    pub fn new(offset: (u32, u32), url: Option<String>, map: Option<SourceMap>) -> SourceMapSection {
        SourceMapSection {
            offset: offset,
            url: url,
            map: map.map(|x| Box::new(x)),
        }
    }

    /// Returns the offset line
    pub fn get_offset_line(&self) -> u32 {
        self.offset.0
    }

    /// Returns the offset column
    pub fn get_offset_col(&self) -> u32 {
        self.offset.1
    }

    /// Returns the offset as tuple
    pub fn get_offset(&self) -> (u32, u32) {
        self.offset
    }

    /// Returns the URL of the referenced map if available
    pub fn get_url(&self) -> Option<&str> {
        self.url.as_ref().map(|x| &**x)
    }

    /// Returns a reference to the embedded sourcemap if available
    pub fn get_sourcemap(&self) -> Option<&SourceMap> {
        self.map.as_ref().map(|x| &**x)
    }

    /// Replaces the embedded sourcemap
    pub fn set_sourcemap(&mut self, sm: SourceMap) {
        self.map = Some(Box::new(sm));
    }
}
<commit_msg>Add name iter and kill some raw access that is provided by the raw token anyways.<commit_after>use std::fmt;
use std::io::Read;

use decoder::{decode, DecodedMap};
use errors::{Result, Error};

/// Represents a raw token
///
/// Raw tokens are used internally to represent the sourcemap
/// in a memory efficient way.  If you construct sourcemaps yourself
/// then you need to create these objects, otherwise they are invisible
/// to you as a user.
#[derive(PartialEq, Copy, Clone)]
pub struct RawToken {
    /// the destination (minified) line number
    pub dst_line: u32,
    /// the destination (minified) column number
    pub dst_col: u32,
    /// the source line number
    pub src_line: u32,
    /// the source line column
    pub src_col: u32,
    /// source identifier
    pub src_id: u32,
    /// name identifier (`!0` in case there is no associated name)
    pub name_id: u32,
}

/// Represents a token from a sourcemap
pub struct Token<'a> {
    raw: &'a RawToken,
    i: &'a SourceMap,
}

impl<'a> Token<'a> {
    /// get the destination (minified) line number
    pub fn get_dst_line(&self) -> u32 {
        self.raw.dst_line
    }

    /// get the destination (minified) column number
    pub fn get_dst_col(&self) -> u32 {
        self.raw.dst_col
    }

    /// get the destination line and column
    pub fn get_dst(&self) -> (u32, u32) {
        (self.get_dst_line(), self.get_dst_col())
    }

    /// get the source line number
    pub fn get_src_line(&self) -> u32 {
        self.raw.src_line
    }

    /// get the source column number
    pub fn get_src_col(&self) -> u32 {
        self.raw.src_col
    }

    /// get the source line and column
    pub fn get_src(&self) -> (u32, u32) {
        (self.get_src_line(), self.get_src_col())
    }

    /// get the source if it exists as string
    pub fn get_source(&self) -> &str {
        if self.raw.src_id == !0 {
            ""
        } else {
            &self.i.sources[self.raw.src_id as usize][..]
        }
    }

    /// get the name if it exists as string
    pub fn get_name(&self) -> Option<&str> {
        if self.raw.name_id == !0 {
            None
        } else {
            Some(&self.i.names[self.raw.name_id as usize][..])
        }
    }

    /// returns `true` if a name exists, `false` otherwise
    pub fn has_name(&self) -> bool {
        self.get_name().is_some()
    }

    /// Converts the token into a debug tuple in the form
    /// `(source, src_line, src_col, name)`
    pub fn to_tuple(&self) -> (&str, u32, u32, Option<&str>) {
        (
            self.get_source(),
            self.get_src_line(),
            self.get_src_col(),
            self.get_name()
        )
    }

    /// Get the underlying raw token
    pub fn get_raw_token(&self) -> RawToken {
        *self.raw
    }
}

/// Iterates over all tokens in a sourcemap
pub struct TokenIter<'a> {
    i: &'a SourceMap,
    next_idx: u32,
}

impl<'a> Iterator for TokenIter<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Token<'a>> {
        self.i.get_token(self.next_idx).map(|tok| {
            self.next_idx += 1;
            tok
        })
    }
}

/// Iterates over all tokens in a sourcemap
pub struct NameIter<'a> {
    i: &'a SourceMap,
    next_idx: u32,
}

impl<'a> Iterator for NameIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        self.i.get_name(self.next_idx).map(|name| {
            self.next_idx += 1;
            name
        })
    }
}

/// Iterates over all index items in a sourcemap
pub struct IndexIter<'a> {
    i: &'a SourceMap,
    next_idx: usize,
}

impl<'a> Iterator for IndexIter<'a> {
    type Item = (u32, u32, u32);

    fn next(&mut self) -> Option<(u32, u32, u32)> {
        self.i.index.get(self.next_idx).map(|idx| {
            self.next_idx += 1;
            *idx
        })
    }
}

impl<'a> fmt::Debug for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Token {}>", self)
    }
}

impl<'a> fmt::Display for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}:{}{}",
               self.get_source(),
               self.get_src_line(),
               self.get_src_col(),
               self.get_name().map(|x| format!(" name={}", x))
                   .unwrap_or("".into()))
    }
}

/// Represents a section in a sourcemap index
pub struct SourceMapSection {
    offset: (u32, u32),
    url: Option<String>,
    map: Option<Box<SourceMap>>,
}

/// Iterates over all sections in a sourcemap index
pub struct SourceMapSectionIter<'a> {
    i: &'a SourceMapIndex,
    next_idx: u32,
}

impl<'a> Iterator for SourceMapSectionIter<'a> {
    type Item = &'a SourceMapSection;

    fn next(&mut self) -> Option<&'a SourceMapSection> {
        self.i.get_section(self.next_idx).map(|sec| {
            self.next_idx += 1;
            sec
        })
    }
}

/// Represents a sourcemap index in memory
pub struct SourceMapIndex {
    version: u32,
    file: Option<String>,
    sections: Vec<SourceMapSection>,
}

/// Represents a sourcemap in memory
///
/// This is always represents a regular "non-indexed" sourcemap.  Particularly
/// in case the `from_reader` method is used an index sourcemap will be
/// rejected with an error on reading.
pub struct SourceMap {
    version: u32,
    file: Option<String>,
    tokens: Vec<RawToken>,
    index: Vec<(u32, u32, u32)>,
    names: Vec<String>,
    sources: Vec<String>,
    sources_content: Vec<Option<String>>,
}

impl SourceMap {

    /// Creates a sourcemap from a reader over a JSON stream in UTF-8
    /// format.  Optionally a "garbage header" as defined by the
    /// sourcemap draft specification is supported.  In case an indexed
    /// sourcemap is encountered an error is returned.
    ///
    /// ```rust
    /// use sourcemap::SourceMap;
    /// let input: &[_] = b"{
    ///     \"version\":3,
    ///     \"sources\":[\"coolstuff.js\"],
    ///     \"names\":[\"x\",\"alert\"],
    ///     \"mappings\":\"AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM\"
    /// }";
    /// let sm = SourceMap::from_reader(input).unwrap();
    /// ```
    pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMap> {
        match try!(decode(rdr)) {
            DecodedMap::Regular(sm) => Ok(sm),
            DecodedMap::Index(_) => Err(Error::IndexedSourcemap),
        }
    }

    /// Constructs a new sourcemap from raw components.
    ///
    /// - `version`: the version is typically `3` which is the current darft version
    /// - `file`: an optional filename of the sourcemap
    /// - `tokens`: a list of raw tokens
    /// - `index`: a sorted mapping of line and column to token index
    /// - `names`: a vector of names
    /// - `sources` a vector of source filenames
    /// - `sources_content` optional source contents
    pub fn new(version: u32, file: Option<String>, tokens: Vec<RawToken>,
               index: Vec<(u32, u32, u32)>, names: Vec<String>,
               sources: Vec<String>,
               sources_content: Option<Vec<Option<String>>>) -> SourceMap {
        SourceMap {
            version: version,
            file: file,
            tokens: tokens,
            index: index,
            names: names,
            sources: sources,
            sources_content: sources_content.unwrap_or(vec![]),
        }
    }

    /// Returns the version of the sourcemap.
    pub fn get_version(&self) -> u32 {
        self.version
    }

    /// Returns the embedded filename in case there is one.
    pub fn get_file(&self) -> Option<&str> {
        self.file.as_ref().map(|x| &x[..])
    }

    /// Looks up a token by its index.
    pub fn get_token<'a>(&'a self, idx: u32) -> Option<Token<'a>> {
        self.tokens.get(idx as usize).map(|raw| {
            Token { raw: raw, i: self }
        })
    }

    /// Returns the number of tokens in the sourcemap.
    pub fn get_token_count(&self) -> u32 {
        self.tokens.len() as u32
    }

    /// Returns an iterator over the tokens.
    pub fn tokens<'a>(&'a self) -> TokenIter<'a> {
        TokenIter { i: self, next_idx: 0 }
    }

    /// Looks up the closest token to a given line and column.
    pub fn lookup_token<'a>(&'a self, line: u32, col: u32) -> Option<Token<'a>> {
        let mut low = 0;
        let mut high = self.index.len();

        while low < high {
            let mid = (low + high) / 2;
            if (line, col, 0) < self.index[mid as usize] {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        if low > 0 {
            self.get_token(self.index[low as usize].2)
        } else {
            None
        }
    }

    /// Returns the number of sources in the sourcemap.
    pub fn get_source_count(&self) -> u32 {
        self.sources.len() as u32
    }

    /// Looks up a source for a specific index.
    pub fn get_source(&self, idx: u32) -> Option<&str> {
        self.sources.get(idx as usize).map(|x| &x[..])
    }

    /// Looks up the content for a source.
    pub fn get_source_contents(&self, idx: u32) -> Option<&str> {
        self.sources_content.get(idx as usize)
            .and_then(|bucket| bucket.as_ref()).map(|x| &**x)
    }

    /// Returns an iterator over the names.
    pub fn names<'a>(&'a self) -> NameIter<'a> {
        NameIter { i: self, next_idx: 0 }
    }

    /// Returns the number of names in the sourcemap.
    pub fn get_name_count(&self) -> u32 {
        self.names.len() as u32
    }

    /// Looks up a name for a specific index.
    pub fn get_name(&self, idx: u32) -> Option<&str> {
        self.names.get(idx as usize).map(|x| &x[..])
    }

    /// Returns the number of items in the index
    pub fn get_index_size(&self) -> usize {
        self.index.len()
    }

    /// Returns the number of items in the index
    pub fn index_iter<'a>(&'a self) -> IndexIter<'a> {
        IndexIter { i: self, next_idx: 0 }
    }
}

impl SourceMapIndex {

    /// Creates a sourcemap index from a reader over a JSON stream in UTF-8
    /// format.  Optionally a "garbage header" as defined by the
    /// sourcemap draft specification is supported.  In case a regular
    /// sourcemap is encountered an error is returned.
    pub fn from_reader<R: Read>(rdr: R) -> Result<SourceMapIndex> {
        match try!(decode(rdr)) {
            DecodedMap::Regular(_) => Err(Error::RegularSourcemap),
            DecodedMap::Index(smi) => Ok(smi),
        }
    }

    /// Constructs a new sourcemap index from raw components.
    ///
    /// - `version`: the version is typically `3` which is the current darft version
    /// - `file`: an optional filename of the index
    /// - `sections`: a vector of source map index sections
    pub fn new(version: u32, file: Option<String>,
               sections: Vec<SourceMapSection>) -> SourceMapIndex {
        SourceMapIndex {
            version: version,
            file: file,
            sections: sections,
        }
    }

    /// Returns the version of the sourcemap index.
    pub fn get_version(&self) -> u32 {
        self.version
    }

    /// Returns the embedded filename in case there is one.
    pub fn get_file(&self) -> Option<&str> {
        self.file.as_ref().map(|x| &x[..])
    }

    /// Returns the number of sections in this index
    pub fn get_section_count(&self) -> u32 {
        self.sections.len() as u32
    }

    /// Looks up a single section and returns it
    pub fn get_section(&self, idx: u32) -> Option<&SourceMapSection> {
        self.sections.get(idx as usize)
    }

    /// Looks up a single section and returns it as a mutable ref
    pub fn get_section_mut(&mut self, idx: u32) -> Option<&mut SourceMapSection> {
        self.sections.get_mut(idx as usize)
    }

    /// Iterates over all sections
    pub fn sections<'a>(&'a self) -> SourceMapSectionIter<'a> {
        SourceMapSectionIter {
            i: self,
            next_idx: 0
        }
    }

    /// Looks up the closest token to a given line and column.
    ///
    /// This requires that the referenced sourcemaps are actually loaded.
    /// If a sourcemap is encountered that is not embedded but just
    /// externally referenced it is silently skipped.
    pub fn lookup_token<'a>(&'a self, line: u32, col: u32) -> Option<Token<'a>> {
        for section in self.sections() {
            let (off_line, off_col) = section.get_offset();
            if off_line < line || off_col < col {
                continue;
            }
            if let Some(map) = section.get_sourcemap() {
                if let Some(tok) = map.lookup_token(line - off_line, col - off_col) {
                    return Some(tok);
                }
            }
        }
        None
    }
}

impl SourceMapSection {

    /// Create a new sourcemap index section
    ///
    /// - `offset`: offset as line and column
    /// - `url`: optional URL of where the sourcemap is located
    /// - `map`: an optional already resolved internal sourcemap
    pub fn new(offset: (u32, u32), url: Option<String>, map: Option<SourceMap>) -> SourceMapSection {
        SourceMapSection {
            offset: offset,
            url: url,
            map: map.map(|x| Box::new(x)),
        }
    }

    /// Returns the offset line
    pub fn get_offset_line(&self) -> u32 {
        self.offset.0
    }

    /// Returns the offset column
    pub fn get_offset_col(&self) -> u32 {
        self.offset.1
    }

    /// Returns the offset as tuple
    pub fn get_offset(&self) -> (u32, u32) {
        self.offset
    }

    /// Returns the URL of the referenced map if available
    pub fn get_url(&self) -> Option<&str> {
        self.url.as_ref().map(|x| &**x)
    }

    /// Returns a reference to the embedded sourcemap if available
    pub fn get_sourcemap(&self) -> Option<&SourceMap> {
        self.map.as_ref().map(|x| &**x)
    }

    /// Replaces the embedded sourcemap
    pub fn set_sourcemap(&mut self, sm: SourceMap) {
        self.map = Some(Box::new(sm));
    }
}
<|endoftext|>


<commit_before>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, GlobBuilder, GlobSet, GlobSetBuilder};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.config"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("handlebars", &["*.hbs"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("jinja", &["*.jinja", "*.jinja2"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("md", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("spark", &["*.spark"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<GlobSet>,
    negated: Option<GlobSet>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<GlobSet>,
        negated: Option<GlobSet>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

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

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<commit_msg>Shorten name of Handlebars type<commit_after>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, GlobBuilder, GlobSet, GlobSetBuilder};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.config"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("hbs", &["*.hbs"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("jinja", &["*.jinja", "*.jinja2"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("md", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("spark", &["*.spark"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<GlobSet>,
    negated: Option<GlobSet>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<GlobSet>,
        negated: Option<GlobSet>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

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

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<|endoftext|>