<commit_before>use std::hash::Hash;
use std::path::{PathBuf, Path};

use perlin_core::index::Index;
use perlin_core::index::vocabulary::SharedVocabulary;
use perlin_core::index::posting::{Posting, DocId};
use perlin_core::page_manager::{RamPageCache, FsPageManager};

use language::CanApply;

use field::{FieldDefinition, Field, FieldId};

type Pipeline<T, Out> = Box<Fn(DocId, FieldId) -> Box<for<'r> CanApply<&'r str, T, Output=Out>>>;

/// `DocumentIndex` takes some of the basic building blocks in `perlin_core`
/// and provides an abstraction that can be used to index and query documents
/// using fields, metadata, taxonomies etc
pub struct DocumentIndex<TContainer, TPipelineContainer> {
    // We need to overwrite perlin_core's default DocIds as some Documents might contain
    // other fields than others. This counter acts as DocumentIndex global document counter.
    doc_id_counter: DocId,
    // The base path of this index.
    base_path: PathBuf,
    index_container: TContainer,
    pipeline_container: TPipelineContainer
}

pub trait IndexContainer<T: Hash + Eq> {
    fn manage_index(&mut self, FieldDefinition, Index<T>);
}

pub trait PipelineContainer<TContainer, TOutput> {
    fn manage_pipeline(&mut self, FieldDefinition, Pipeline<TContainer, TOutput>);
    fn apply_pipeline(&mut self, &mut TContainer, FieldDefinition, DocId, &str);
}

pub trait Commitable {
    fn commit(&mut self);
}

pub trait TermIndexer<T> {
    fn index_term(&mut self, FieldId, DocId, T);
}

pub trait TermQuerier<T> {
    fn query_term(&self, &Field<T>) -> Vec<Posting>;
}

impl<TContainer: Default + Commitable, TPipelineContainer: Default> DocumentIndex<TContainer, TPipelineContainer> {
    /// Create a new index.
    pub fn new(path: &Path) -> Self {
        DocumentIndex {
            doc_id_counter: DocId::none(),
            base_path: path.to_path_buf(),
            index_container: TContainer::default(),
            pipeline_container: TPipelineContainer::default(),
        }
    }

    pub fn add_field<TTerm: Hash + Eq + Ord>(&mut self,
                                             field_def: FieldDefinition,
                                             pipeline: Pipeline<TContainer, TTerm>)
                                             -> Result<(), ()>
        where TContainer: IndexContainer<TTerm>,
              TPipelineContainer: PipelineContainer<TContainer, TTerm>
    {
        let FieldDefinition(field_id, _) = field_def;
        let page_cache = RamPageCache::new(FsPageManager::new(&self.base_path
            .join(format!("{}_pages.bin", field_id.0).to_string())));
        self.index_container.manage_index(field_def,
                                          Index::<TTerm>::new(page_cache, SharedVocabulary::new()));
        self.pipeline_container.manage_pipeline(field_def,
                                                pipeline);
        Ok(())
    }

    /// Indexes a field
    pub fn index_field<TTerm>(&mut self, doc_id: DocId, field: FieldDefinition, content: &str)
        where TPipelineContainer: PipelineContainer<TContainer, TTerm>
    {
        self.pipeline_container.apply_pipeline(&mut self.index_container,field, doc_id, content);
    }

    pub fn get_next_doc_id(&mut self) -> DocId {
        self.doc_id_counter.inc();
        self.doc_id_counter
    }

    /// Commits this index
    pub fn commit(&mut self) {
        self.index_container.commit();
    }

    /// Runs an atom_query on a certain field
    pub fn query_field<TTerm: Hash + Eq>(&self, query: &Field<TTerm>) -> Vec<DocId>
        where TContainer: TermQuerier<TTerm>
    {
        self.index_container
            .query_term(query)
            .into_iter()
            .map(|posting| posting.doc_id())
            .collect()
    }
}

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

    use rust_stemmers;

    use test_utils::create_test_dir;
    use field::{FieldDefinition, FieldType, FieldId, Field};
    use language::*;
    use language::integers::*;

    use std::collections::BTreeMap;

    use perlin_core::index::posting::{Posting, DocId};
    use perlin_core::index::Index;

    struct TestPipelineContainer {
        text_pipelines: BTreeMap<FieldId, Pipeline<TestContainer, String>>,
        num_pipelines: BTreeMap<FieldId, Pipeline<TestContainer, u64>>
    }

    impl Default for TestPipelineContainer {
        fn default() -> Self {
            TestPipelineContainer {
                text_pipelines: BTreeMap::new(),
                num_pipelines: BTreeMap::new()
            }
        }
    }

    impl PipelineContainer<TestContainer, String> for TestPipelineContainer {
        fn manage_pipeline(&mut self, def: FieldDefinition, pipe: Pipeline<TestContainer, String>) {
            self.text_pipelines.insert(def.0, pipe);
        }

        fn apply_pipeline(&mut self, cont: &mut TestContainer, def: FieldDefinition, doc_id: DocId, input: &str) {
            if let Some(pipe) = self.text_pipelines.get(&def.0) {
                pipe(doc_id, def.0).apply(input, cont);
            }
        }
    }

    impl PipelineContainer<TestContainer, u64> for TestPipelineContainer {
        fn manage_pipeline(&mut self, def: FieldDefinition, pipe: Pipeline<TestContainer, u64>) {
            self.num_pipelines.insert(def.0, pipe);
        }

        fn apply_pipeline(&mut self, cont: &mut TestContainer, def: FieldDefinition, doc_id: DocId, input: &str) {
            if let Some(pipe) = self.num_pipelines.get(&def.0) {
                pipe(doc_id, def.0).apply(input, cont);
            }
        }
    }

    struct TestContainer {
        text_fields: BTreeMap<FieldId, Index<String>>,
        num_fields: BTreeMap<FieldId, Index<u64>>,
    }

    impl IndexContainer<String> for TestContainer {
        fn manage_index(&mut self, def: FieldDefinition, index: Index<String>) {
            self.text_fields.insert(def.0, index);
        }
    }

    impl IndexContainer<u64> for TestContainer {
        fn manage_index(&mut self, def: FieldDefinition, index: Index<u64>) {
            self.num_fields.insert(def.0, index);
        }
    }

    impl TermIndexer<String> for TestContainer {
        fn index_term(&mut self, field: FieldId, doc_id: DocId, term: String) {
            if let Some(index) = self.text_fields.get_mut(&field) {
                index.index_term(term, doc_id);
            }
        }
    }

    impl TermIndexer<u64> for TestContainer {
        fn index_term(&mut self, field: FieldId, doc_id: DocId, term: u64) {
            if let Some(index) = self.num_fields.get_mut(&field) {
                index.index_term(term, doc_id);
            }
        }
    }

    impl TermQuerier<String> for TestContainer {
        fn query_term(&self, field: &Field<String>) -> Vec<Posting> {
            if let Some(index) = self.text_fields.get(&(field.0).0) {
                index.query_atom(&field.1)
            } else {
                panic!();
            }
        }
    }

    impl TermQuerier<u64> for TestContainer {
        fn query_term(&self, field: &Field<u64>) -> Vec<Posting> {
            if let Some(index) = self.num_fields.get(&(field.0).0) {
                index.query_atom(&field.1)
            } else {
                panic!();
            }
        }
    }

    impl Commitable for TestContainer {
        fn commit(&mut self) {
            self.text_fields.values_mut().map(|index| index.commit()).count();
            self.num_fields.values_mut().map(|index| index.commit()).count();
        }
    }

    impl Default for TestContainer {
        fn default() -> Self {
            TestContainer {
                text_fields: BTreeMap::new(),
                num_fields: BTreeMap::new(),
            }
        }
    }

    fn new_index(name: &str) -> DocumentIndex<TestContainer, TestPipelineContainer> {
        DocumentIndex::new(&create_test_dir(format!("perlin_index/{}", name).as_str()))
    }

    #[test]
    fn pipeline_mode() {
        let mut index = new_index("pipeline_mode");
        let text_field_def = FieldDefinition(FieldId(0), FieldType::Text);
        index.add_field::<String>(text_field_def,
                                  pipeline!( WhitespaceTokenizer
                                             > LowercaseFilter
                                             > Stemmer(rust_stemmers::Algorithm::English)));

        index.index_field::<String>(DocId(0), text_field_def, "this is a TEST");
        index.index_field::<String>(DocId(1), text_field_def, "THIS is a title");
        index.commit();
        assert_eq!(index.query_field(&Field(text_field_def, "test".to_owned())),
                   vec![DocId(0)]);
        assert_eq!(index.query_field(&Field(text_field_def, "titl".to_owned() /*Because it is stemmed!*/)),
                   vec![DocId(1)]);
        assert_eq!(index.query_field(&Field(text_field_def, "this".to_owned())),
                   vec![DocId(0), DocId(1)]);
    }

    #[test]
    fn different_pipelines() {
        let mut index = new_index("different_pipelines");
        let text_def1 = FieldDefinition(FieldId(0), FieldType::Text);
        let text_def2 = FieldDefinition(FieldId(1), FieldType::Text);
        index.add_field::<String>(text_def1,
                                  pipeline!( WhitespaceTokenizer
                                             > LowercaseFilter
                                             > Stemmer(rust_stemmers::Algorithm::English)));
        index.add_field::<String>(text_def2,
                                  pipeline!( WhitespaceTokenizer > LowercaseFilter ));
        index.index_field::<String>(DocId(0), text_def1, "THIS is a title");
        index.index_field::<String>(DocId(1), text_def1, "this is a titles");
        index.index_field::<String>(DocId(0), text_def2, "THIS is a title");
        index.index_field::<String>(DocId(1), text_def2, "this is a titles");
        index.commit();
        assert_eq!(index.query_field(&Field(text_def1, "titl".to_owned())),
                   vec![DocId(0), DocId(1)]);
        assert_eq!(index.query_field(&Field(text_def2, "title".to_owned())),
                   vec![DocId(0)]);
    }

    #[test]
    fn multityped_pipeline() {
        let mut index = new_index("multityped_pipelines");
        let text_def1 = FieldDefinition(FieldId(0), FieldType::Text);
        let num_def2 = FieldDefinition(FieldId(1), FieldType::Number);
        index.add_field::<String>(text_def1,
                                  pipeline!( WhitespaceTokenizer
                                             > NumberFilter
                                             | [FieldId(1)]
                                             > LowercaseFilter )).unwrap();
        index.add_field::<u64>(num_def2, pipeline!( ToU64 )).unwrap();
        index.index_field::<String>(DocId(0), text_def1, "These are 10 tests");
        index.index_field::<String>(DocId(1), text_def1, "this is 1 single title");
        index.index_field::<u64>(DocId(2), num_def2, "15");
        index.commit();
        assert_eq!(index.query_field(&Field(text_def1, "this".to_owned())),
                   vec![DocId(1)]);
        assert_eq!(index.query_field(&Field(text_def1, "10".to_owned())),
                   vec![]);
        assert_eq!(index.query_field(&Field(num_def2, 10)),
                   vec![DocId(0)]);
        assert_eq!(index.query_field(&Field(num_def2, 15)),
                   vec![DocId(2)]);
    }
}
<commit_msg>simplify it<commit_after>use std::hash::Hash;
use std::path::{PathBuf, Path};

use perlin_core::index::Index;
use perlin_core::index::vocabulary::SharedVocabulary;
use perlin_core::index::posting::{Posting, DocId};
use perlin_core::page_manager::{RamPageCache, FsPageManager};

use language::CanApply;

use field::{FieldDefinition, Field, FieldId};

type Pipeline<T, Out> = Box<Fn(DocId, FieldId) -> Box<for<'r> CanApply<&'r str, T, Output=Out>>>;

/// `DocumentIndex` takes some of the basic building blocks in `perlin_core`
/// and provides an abstraction that can be used to index and query documents
/// using fields, metadata, taxonomies etc
pub struct DocumentIndex<TContainer> {
    // We need to overwrite perlin_core's default DocIds as some Documents might contain
    // other fields than others. This counter acts as DocumentIndex global document counter.
    doc_id_counter: DocId,
    // The base path of this index.
    base_path: PathBuf,
    index_container: TContainer,
}

pub trait IndexContainer<T: Hash + Eq> {
    fn manage_index(&mut self, FieldDefinition, Index<T>, Pipeline<Self, T>);
    fn apply_pipeline(&mut self, FieldDefinition, DocId, &str);
}

pub trait Commitable {
    fn commit(&mut self);
}

pub trait TermIndexer<T> {
    fn index_term(&mut self, FieldId, DocId, T);
}

pub trait TermQuerier<T> {
    fn query_term(&self, &Field<T>) -> Vec<Posting>;
}

impl<TContainer: Default + Commitable> DocumentIndex<TContainer> {
    /// Create a new index.
    pub fn new(path: &Path) -> Self {
        DocumentIndex {
            doc_id_counter: DocId::none(),
            base_path: path.to_path_buf(),
            index_container: TContainer::default(),
        }
    }

    pub fn add_field<TTerm: Hash + Eq + Ord>(&mut self,
                                             field_def: FieldDefinition,
                                             pipeline: Pipeline<TContainer, TTerm>)
                                             -> Result<(), ()>
        where TContainer: IndexContainer<TTerm>
    {
        let FieldDefinition(field_id, _) = field_def;
        let page_cache = RamPageCache::new(FsPageManager::new(&self.base_path
            .join(format!("{}_pages.bin", field_id.0).to_string())));
        self.index_container.manage_index(field_def,
                                          Index::<TTerm>::new(page_cache, SharedVocabulary::new()), pipeline);
        // self.pipeline_container.manage_pipeline(field_def,
        //                                         pipeline);
        Ok(())
    }

    /// Indexes a field
    pub fn index_field<TTerm: Hash + Eq>(&mut self, doc_id: DocId, field: FieldDefinition, content: &str)
        where TContainer: IndexContainer<TTerm>
    {
        self.index_container.apply_pipeline(field, doc_id, content);
    }

    pub fn get_next_doc_id(&mut self) -> DocId {
        self.doc_id_counter.inc();
        self.doc_id_counter
    }

    /// Commits this index
    pub fn commit(&mut self) {
        self.index_container.commit();
    }

    /// Runs an atom_query on a certain field
    pub fn query_field<TTerm: Hash + Eq>(&self, query: &Field<TTerm>) -> Vec<DocId>
        where TContainer: TermQuerier<TTerm>
    {
        self.index_container
            .query_term(query)
            .into_iter()
            .map(|posting| posting.doc_id())
            .collect()
    }
}

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

    use rust_stemmers;

    use test_utils::create_test_dir;
    use field::{FieldDefinition, FieldType, FieldId, Field};
    use language::*;
    use language::integers::*;

    use std::collections::BTreeMap;

    use perlin_core::index::posting::{Posting, DocId};
    use perlin_core::index::Index;

    struct TestContainer {
        text_fields: BTreeMap<FieldId, (Index<String>, Pipeline<TestContainer, String>)>,
        num_fields: BTreeMap<FieldId, (Index<u64>, Pipeline<TestContainer, u64>)>,
    }

    impl IndexContainer<String> for TestContainer {
        fn manage_index(&mut self, def: FieldDefinition, index: Index<String>, pipe: Pipeline<TestContainer, String>) {
            self.text_fields.insert(def.0, (index, pipe));
        }

         fn apply_pipeline(&mut self, def: FieldDefinition, doc_id: DocId, input: &str) {
            let pipeline;
            if let Some(pipe) = self.text_fields.get(&def.0) {
                pipeline = pipe.1(doc_id, def.0);
            } else {
                panic!();
            }
            pipeline.apply(input, self);
        }
    }

    impl IndexContainer<u64> for TestContainer {
        fn manage_index(&mut self, def: FieldDefinition, index: Index<u64>, pipe: Pipeline<TestContainer, u64>) {
            self.num_fields.insert(def.0, (index, pipe));
        }

        fn apply_pipeline(&mut self, def: FieldDefinition, doc_id: DocId, input: &str) {
            let pipeline;
            if let Some(pipe) = self.num_fields.get(&def.0) {
                pipeline = pipe.1(doc_id, def.0);
            } else {
                panic!("Whoot");
            }
            pipeline.apply(input, self);
        }
    }

    impl TermIndexer<String> for TestContainer {
        fn index_term(&mut self, field: FieldId, doc_id: DocId, term: String) {
            if let Some(index) = self.text_fields.get_mut(&field) {
                index.0.index_term(term, doc_id);
            }
        }
    }

    impl TermIndexer<u64> for TestContainer {
        fn index_term(&mut self, field: FieldId, doc_id: DocId, term: u64) {
            if let Some(index) = self.num_fields.get_mut(&field) {
                index.0.index_term(term, doc_id);
            }
        }
    }

    impl TermQuerier<String> for TestContainer {
        fn query_term(&self, field: &Field<String>) -> Vec<Posting> {
            if let Some(index) = self.text_fields.get(&(field.0).0) {
                index.0.query_atom(&field.1)
            } else {
                panic!();
            }
        }
    }

    impl TermQuerier<u64> for TestContainer {
        fn query_term(&self, field: &Field<u64>) -> Vec<Posting> {
            if let Some(index) = self.num_fields.get(&(field.0).0) {
                index.0.query_atom(&field.1)
            } else {
                panic!();
            }
        }
    }

    impl Commitable for TestContainer {
        fn commit(&mut self) {
            self.text_fields.values_mut().map(|index| index.0.commit()).count();
            self.num_fields.values_mut().map(|index| index.0.commit()).count();
        }
    }

    impl Default for TestContainer {
        fn default() -> Self {
            TestContainer {
                text_fields: BTreeMap::new(),
                num_fields: BTreeMap::new(),
            }
        }
    }

    fn new_index(name: &str) -> DocumentIndex<TestContainer> {
        DocumentIndex::new(&create_test_dir(format!("perlin_index/{}", name).as_str()))
    }

    #[test]
    fn pipeline_mode() {
        let mut index = new_index("pipeline_mode");
        let text_field_def = FieldDefinition(FieldId(0), FieldType::Text);
        index.add_field::<String>(text_field_def,
                                  pipeline!( WhitespaceTokenizer
                                             > LowercaseFilter
                                             > Stemmer(rust_stemmers::Algorithm::English))).unwrap();

        index.index_field::<String>(DocId(0), text_field_def, "this is a TEST");
        index.index_field::<String>(DocId(1), text_field_def, "THIS is a title");
        index.commit();
        assert_eq!(index.query_field(&Field(text_field_def, "test".to_owned())),
                   vec![DocId(0)]);
        assert_eq!(index.query_field(&Field(text_field_def, "titl".to_owned() /*Because it is stemmed!*/)),
                   vec![DocId(1)]);
        assert_eq!(index.query_field(&Field(text_field_def, "this".to_owned())),
                   vec![DocId(0), DocId(1)]);
    }

    #[test]
    fn different_pipelines() {
        let mut index = new_index("different_pipelines");
        let text_def1 = FieldDefinition(FieldId(0), FieldType::Text);
        let text_def2 = FieldDefinition(FieldId(1), FieldType::Text);
        index.add_field::<String>(text_def1,
                                  pipeline!( WhitespaceTokenizer
                                             > LowercaseFilter
                                             > Stemmer(rust_stemmers::Algorithm::English))).unwrap();
        index.add_field::<String>(text_def2,
                                  pipeline!( WhitespaceTokenizer > LowercaseFilter )).unwrap();
        index.index_field::<String>(DocId(0), text_def1, "THIS is a title");
        index.index_field::<String>(DocId(1), text_def1, "this is a titles");
        index.index_field::<String>(DocId(0), text_def2, "THIS is a title");
        index.index_field::<String>(DocId(1), text_def2, "this is a titles");
        index.commit();
        assert_eq!(index.query_field(&Field(text_def1, "titl".to_owned())),
                   vec![DocId(0), DocId(1)]);
        assert_eq!(index.query_field(&Field(text_def2, "title".to_owned())),
                   vec![DocId(0)]);
    }

    #[test]
    fn multityped_pipeline() {
        let mut index = new_index("multityped_pipelines");
        let text_def1 = FieldDefinition(FieldId(0), FieldType::Text);
        let num_def2 = FieldDefinition(FieldId(1), FieldType::Number);
        index.add_field::<String>(text_def1,
                                  pipeline!( WhitespaceTokenizer
                                             > NumberFilter
                                             | [FieldId(1)]
                                             > LowercaseFilter )).unwrap();
        index.add_field::<u64>(num_def2, pipeline!( ToU64 )).unwrap();
        index.index_field::<String>(DocId(0), text_def1, "These are 10 tests");
        index.index_field::<String>(DocId(1), text_def1, "this is 1 single title");
        index.index_field::<u64>(DocId(2), num_def2, "15");
        index.commit();
        assert_eq!(index.query_field(&Field(text_def1, "this".to_owned())),
                   vec![DocId(1)]);
        assert_eq!(index.query_field(&Field(text_def1, "10".to_owned())),
                   vec![]);
        assert_eq!(index.query_field(&Field(num_def2, 10)),
                   vec![DocId(0)]);
        assert_eq!(index.query_field(&Field(num_def2, 15)),
                   vec![DocId(2)]);
    }
}
<|endoftext|>


<commit_before>use config;

use std::collections::BTreeMap;
use rustc_serialize::json::Json;
use regex::Regex;
use git2;

pub fn linkify(s: &str) -> String {
    let re = Regex::new(r"\b(?P<bugno>[1-9][0-9]{4,9})\b").unwrap();
    re.replace_all(s, "$bugno")
}

pub fn commit_header(commit: &git2::Commit) -> Result<(String, String), &'static str> {
    fn entity_replace(s: &str) -> String {
        s.replace("&", "&").replace("<", "<")
    }

    let msg = try!(commit.message().ok_or("Invalid message"));
    let mut iter = msg.split('\n');
    let header = iter.next().unwrap();
    let remainder = iter.collect::<Vec<_>>().join("\n");
    let header = linkify(&entity_replace(header));
    Ok((header, entity_replace(&remainder)))
}

pub fn get_commit_info(cfg: &config::Config, tree_name: &str, rev: &str) -> Result<String, &'static str> {
    let tree_config = try!(cfg.trees.get(tree_name).ok_or("Invalid tree"));
    let git = try!(config::get_git(tree_config));
    let commit_obj = try!(git.repo.revparse_single(rev).map_err(|_| "Bad revision"));
    let commit = try!(commit_obj.as_commit().ok_or("Bad revision"));
    let (msg, _) = try!(commit_header(&commit));

    let sig = commit.author();
    let msg = format!("{}\n
{} <{}>", msg, sig.name().unwrap(), sig.email().unwrap());

    let mut obj = BTreeMap::new();

    obj.insert("header".to_owned(), Json::String(msg));

    let parents = commit.parent_ids().collect::<Vec<_>>();
    if parents.len() == 1 {
        obj.insert("parent".to_owned(), Json::String(parents[0].to_string()));
    }

    let json = Json::Object(obj);

    Ok(json.to_string())
}
<commit_msg>Bug 1283849 - Blame should show the date when the change was done<commit_after>use config;

use std::collections::BTreeMap;
use rustc_serialize::json::Json;
use regex::Regex;
use git2;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;

pub fn linkify(s: &str) -> String {
    let re = Regex::new(r"\b(?P<bugno>[1-9][0-9]{4,9})\b").unwrap();
    re.replace_all(s, "$bugno")
}

pub fn commit_header(commit: &git2::Commit) -> Result<(String, String), &'static str> {
    fn entity_replace(s: &str) -> String {
        s.replace("&", "&").replace("<", "<")
    }

    let msg = try!(commit.message().ok_or("Invalid message"));
    let mut iter = msg.split('\n');
    let header = iter.next().unwrap();
    let remainder = iter.collect::<Vec<_>>().join("\n");
    let header = linkify(&entity_replace(header));
    Ok((header, entity_replace(&remainder)))
}

pub fn get_commit_info(cfg: &config::Config, tree_name: &str, rev: &str) -> Result<String, &'static str> {
    let tree_config = try!(cfg.trees.get(tree_name).ok_or("Invalid tree"));
    let git = try!(config::get_git(tree_config));
    let commit_obj = try!(git.repo.revparse_single(rev).map_err(|_| "Bad revision"));
    let commit = try!(commit_obj.as_commit().ok_or("Bad revision"));
    let (msg, _) = try!(commit_header(&commit));

    let naive_t = NaiveDateTime::from_timestamp(commit.time().seconds(), 0);
    let tz = FixedOffset::east(commit.time().offset_minutes() * 60);
    let t : DateTime<FixedOffset> = DateTime::from_utc(naive_t, tz);
    let t = t.to_rfc2822();

    let sig = commit.author();

    let msg = format!("{}\n
{} <{}>, {}", msg, sig.name().unwrap(), sig.email().unwrap(), t);

    let mut obj = BTreeMap::new();

    obj.insert("header".to_owned(), Json::String(msg));

    let parents = commit.parent_ids().collect::<Vec<_>>();
    if parents.len() == 1 {
        obj.insert("parent".to_owned(), Json::String(parents[0].to_string()));
    }

    obj.insert("date".to_owned(), Json::String(t));

    let json = Json::Object(obj);

    Ok(json.to_string())
}
<|endoftext|>


<commit_before>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::Number(_, _) => {
                    list.push(Expression::Number(NumberValue::from_scalar(lexeme)));
                },
                Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                debug!("Processing list item {:#?}", part);
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            debug!("Push on value stack {:#?}", nv);
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            debug!("Push on list on value stack {:#?}", list);
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(ref op) if op.token == Token::Slash && !force_slash => {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Operator(op.clone()),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);

                    },
                    Expression::Operator(op) => {
                        debug!("Push on op stack {:#?}", op);
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        debug!("Push list on value stack {:#?}", list);
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                let math_result = first.apply_math(op, second, context, force_slash);
                debug!("Math result: {:#?}", math_result);

                value_stack.push(math_result);
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
            Expression::Operator(plus()),
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<commit_msg>Ignore comments in the middle of an expression<commit_after>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::Number(_, _) => {
                    list.push(Expression::Number(NumberValue::from_scalar(lexeme)));
                },
                Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                Token::Comment(_) => {},
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                debug!("Processing list item {:#?}", part);
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            debug!("Push on value stack {:#?}", nv);
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            debug!("Push on list on value stack {:#?}", list);
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(ref op) if op.token == Token::Slash && !force_slash => {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Operator(op.clone()),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);

                    },
                    Expression::Operator(op) => {
                        debug!("Push on op stack {:#?}", op);
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        debug!("Push list on value stack {:#?}", list);
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                let math_result = first.apply_math(op, second, context, force_slash);
                debug!("Math result: {:#?}", math_result);

                value_stack.push(math_result);
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
            Expression::Operator(plus()),
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<|endoftext|>


<commit_before>// Authentication
use hyper::client::{Client};
use hyper::server::{Server, Request, Response, Listening};
use hyper::uri::RequestUri::AbsolutePath;
use regex::Regex;
use rustc_serialize::json::Json;
use std::fs;
use std::io::prelude::*;
use std::process::Command;
use std::sync::Mutex;
use std::sync::mpsc::channel;

pub type AuthToken = String;
type TempCode = String;

#[allow(dead_code)]
const AUTH_TOKEN_FILE: &'static str = "auth_token";
#[allow(dead_code)]
const SLACK_CLIENT_ID: &'static str = "2334733471.3592055147";
#[allow(dead_code)]
const SLACK_CLIENT_SECRET: &'static str = "37721a57d17018b206fb1264caa7d707";

pub fn get_oauth_token_or_panic() -> (AuthToken, Option<Listening>) {
    match maybe_existing_token(AUTH_TOKEN_FILE) {
        Some(token) => (token, None),
        None => {
            let (token, listener) = arrange_new_token();
            store_token(&token);
            (token, Some(listener))
        }
    }
}

fn maybe_existing_token(token_file: &str) -> Option<AuthToken> {
    match fs::File::open(token_file) {
        Ok(mut file) => {
            let mut s = String::new();
            file.read_to_string(&mut s).unwrap();

            if !s.is_empty() {
                Some(s)
            } else {
                None }
        },
        Err(_) => None
    }
}

fn arrange_new_token() -> (AuthToken, Listening) {
    let (temp_code, listener) = request_temp_code();
    let token = request_token(&temp_code);
    (token, listener)
}

// TODO Test
fn store_token(token: &AuthToken) {
    let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
    f.write_all(token.as_bytes()).unwrap();
}

#[allow(dead_code)] // Hard to test
fn request_temp_code() -> (TempCode, Listening) {
    Command::new("xdg-open").arg(format!("https://slack.com/oauth/authorize?scope=client&client_id={}", SLACK_CLIENT_ID)).output().unwrap();

    let (tx, rx) = channel();
    let mtx = Mutex::new(tx);

    let server = Server::http(move |req: Request, res: Response| {
        match req.uri {
            AbsolutePath(ref path) => {
                match extract_temp_code(&path) {
                    Some(tempcode) =>  {
                        mtx.lock().unwrap().send(tempcode).unwrap();
                    },
                    None => ()
                }
            },
            _ => ()
        }

        let mut res = res.start().unwrap();
        res.write_all(b"Thanks! Please return to Lax").unwrap();
        res.end().unwrap();
    });

    let mut guard = server.listen("127.0.0.1:9999").unwrap();

    let tempcode = rx.recv().unwrap();
    guard.close().unwrap();
    (tempcode, guard)
}

fn request_token(temp_code: &TempCode) -> AuthToken {
    let mut client = Client::new();
    // I thought & is sufficient to make it a slice
    let mut res = client.get(format_access_uri(temp_code).as_str()).send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    match Json::from_str(&body) {
        Ok(json) => {
            match json.find("access_token") {
                Some(j) => j.as_string().unwrap().to_string(),
                _ =>  panic!("Unexpected json in slack response\n{}", json.pretty())
            }
        },
        _ => panic!("Reponse not json")
    }
}

// TODO Needs test
fn format_access_uri(temp_code: &TempCode) -> String {
    let base = "https://slack.com/api/oauth.access";
    let query = format!("?client_id={}&client_secret={}&code={}", SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, temp_code);
    format!("{}{}", base, query)
}

fn extract_temp_code(path: &str) -> Option<TempCode> {
    let re = Regex::new(r"code=(.+?)(&|$)").unwrap();
    re.captures(path).map(|cap| cap.at(1).unwrap().to_string())
}

#[cfg(test)]
mod tests {
    use super::extract_temp_code;
    use super::maybe_existing_token;
    use std::fs;
    use std::io::Write;
    const AUTH_TOKEN_FILE: &'static str = "auth_token_test";

    #[test]
    fn test_extract_temp_code() {
        let path = "www.timonv.nl?code=blablabla";
        assert_eq!(extract_temp_code(path).unwrap(), "blablabla".to_string())
    }

    #[test]
    fn test_maybe_existing_token() {
        // None if file doesn't exist
        fs::remove_file(AUTH_TOKEN_FILE).unwrap_or(());
        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), None);

        // Some if file exists
        let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
        f.write_all(b"123").unwrap();

        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), Some("123".to_string()));

        // None if file exists but empty
        let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
        f.write_all(b"").unwrap();

        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), None);

        // Cleanup
        fs::remove_file(AUTH_TOKEN_FILE).unwrap_or(());
    }
}
<commit_msg>Rustup! New hyper api<commit_after>// Authentication
use hyper::client::{Client};
use hyper::server::{Server, Request, Response, Listening};
use hyper::uri::RequestUri::AbsolutePath;
use regex::Regex;
use rustc_serialize::json::Json;
use std::fs;
use std::io::prelude::*;
use std::process::Command;
use std::sync::Mutex;
use std::sync::mpsc::channel;

pub type AuthToken = String;
type TempCode = String;

#[allow(dead_code)]
const AUTH_TOKEN_FILE: &'static str = "auth_token";
#[allow(dead_code)]
const SLACK_CLIENT_ID: &'static str = "2334733471.3592055147";
#[allow(dead_code)]
const SLACK_CLIENT_SECRET: &'static str = "37721a57d17018b206fb1264caa7d707";

pub fn get_oauth_token_or_panic() -> (AuthToken, Option<Listening>) {
    match maybe_existing_token(AUTH_TOKEN_FILE) {
        Some(token) => (token, None),
        None => {
            let (token, listener) = arrange_new_token();
            store_token(&token);
            (token, Some(listener))
        }
    }
}

fn maybe_existing_token(token_file: &str) -> Option<AuthToken> {
    match fs::File::open(token_file) {
        Ok(mut file) => {
            let mut s = String::new();
            file.read_to_string(&mut s).unwrap();

            if !s.is_empty() {
                Some(s)
            } else {
                None }
        },
        Err(_) => None
    }
}

fn arrange_new_token() -> (AuthToken, Listening) {
    let (temp_code, listener) = request_temp_code();
    let token = request_token(&temp_code);
    (token, listener)
}

// TODO Test
fn store_token(token: &AuthToken) {
    let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
    f.write_all(token.as_bytes()).unwrap();
}

#[allow(dead_code)] // Hard to test
fn request_temp_code() -> (TempCode, Listening) {
    Command::new("xdg-open").arg(format!("https://slack.com/oauth/authorize?scope=client&client_id={}", SLACK_CLIENT_ID)).output().unwrap();

    let (tx, rx) = channel();
    let mtx = Mutex::new(tx);

    let mut guard = Server::http("127.0.0.1:9999").unwrap().handle(move |req: Request, res: Response| {
        match req.uri {
            AbsolutePath(ref path) => {
                match extract_temp_code(&path) {
                    Some(tempcode) =>  {
                        mtx.lock().unwrap().send(tempcode).unwrap();
                    },
                    None => ()
                }
            },
            _ => ()
        }

        let mut res = res.start().unwrap();
        res.write_all(b"Thanks! Please return to Lax").unwrap();
        res.end().unwrap();
    }).unwrap();

    let tempcode = rx.recv().unwrap();
    guard.close().unwrap();
    (tempcode, guard)
}

fn request_token(temp_code: &TempCode) -> AuthToken {
    let mut client = Client::new();
    // I thought & is sufficient to make it a slice
    let mut res = client.get(format_access_uri(temp_code).as_str()).send().unwrap();
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    match Json::from_str(&body) {
        Ok(json) => {
            match json.find("access_token") {
                Some(j) => j.as_string().unwrap().to_string(),
                _ =>  panic!("Unexpected json in slack response\n{}", json.pretty())
            }
        },
        _ => panic!("Reponse not json")
    }
}

// TODO Needs test
fn format_access_uri(temp_code: &TempCode) -> String {
    let base = "https://slack.com/api/oauth.access";
    let query = format!("?client_id={}&client_secret={}&code={}", SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, temp_code);
    format!("{}{}", base, query)
}

fn extract_temp_code(path: &str) -> Option<TempCode> {
    let re = Regex::new(r"code=(.+?)(&|$)").unwrap();
    re.captures(path).map(|cap| cap.at(1).unwrap().to_string())
}

#[cfg(test)]
mod tests {
    use super::extract_temp_code;
    use super::maybe_existing_token;
    use std::fs;
    use std::io::Write;
    const AUTH_TOKEN_FILE: &'static str = "auth_token_test";

    #[test]
    fn test_extract_temp_code() {
        let path = "www.timonv.nl?code=blablabla";
        assert_eq!(extract_temp_code(path).unwrap(), "blablabla".to_string())
    }

    #[test]
    fn test_maybe_existing_token() {
        // None if file doesn't exist
        fs::remove_file(AUTH_TOKEN_FILE).unwrap_or(());
        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), None);

        // Some if file exists
        let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
        f.write_all(b"123").unwrap();

        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), Some("123".to_string()));

        // None if file exists but empty
        let mut f = fs::File::create(AUTH_TOKEN_FILE).unwrap();
        f.write_all(b"").unwrap();

        assert_eq!(maybe_existing_token(AUTH_TOKEN_FILE), None);

        // Cleanup
        fs::remove_file(AUTH_TOKEN_FILE).unwrap_or(());
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
    parse::{Parse, ParseStream},
    parse_quote,
    punctuated::Punctuated,
    AngleBracketedGenericArguments, Attribute, Data, DeriveInput, Field, Fields, GenericArgument,
    GenericParam, Generics, Ident, ImplGenerics, ParenthesizedGenericArguments, PathArguments,
    Token, Type, TypeGenerics, TypePath, TypeReference, TypeSlice, Variant,
};

use crate::util::import_ruma_common;

enum StructKind {
    Struct,
    Tuple,
}

enum DataKind {
    Struct(Vec<Field>, StructKind),
    Enum(Vec<Variant>),
    Unit,
}

pub fn expand_derive_outgoing(input: DeriveInput) -> syn::Result<TokenStream> {
    let ruma_common = import_ruma_common();

    let mut derives = vec![quote! { Debug }];
    let mut derive_deserialize = true;

    derives.extend(
        input
            .attrs
            .iter()
            .filter(|attr| attr.path.is_ident("incoming_derive"))
            .map(|attr| attr.parse_args())
            .collect::<syn::Result<Vec<Meta>>>()?
            .into_iter()
            .flat_map(|meta| meta.derive_macs)
            .filter_map(|derive_mac| match derive_mac {
                DeriveMac::Regular(id) => Some(quote! { #id }),
                DeriveMac::NegativeDeserialize => {
                    derive_deserialize = false;
                    None
                }
            }),
    );
    if derive_deserialize {
        derives.push(quote! { #ruma_common::exports::serde::Deserialize });
    }

    let input_attrs =
        input.attrs.iter().filter(|attr| filter_input_attrs(attr)).collect::<Vec<_>>();

    let data = match input.data.clone() {
        Data::Union(_) => panic!("#[derive(Outgoing)] does not support Union types"),
        Data::Enum(e) => DataKind::Enum(e.variants.into_iter().collect()),
        Data::Struct(s) => match s.fields {
            Fields::Named(fs) => {
                DataKind::Struct(fs.named.into_iter().collect(), StructKind::Struct)
            }
            Fields::Unnamed(fs) => {
                DataKind::Struct(fs.unnamed.into_iter().collect(), StructKind::Tuple)
            }
            Fields::Unit => DataKind::Unit,
        },
    };

    match data {
        DataKind::Unit => Ok(impl_outgoing_with_incoming_self(&input, &ruma_common)),
        DataKind::Enum(mut vars) => {
            let mut found_lifetime = false;
            for var in &mut vars {
                for field in &mut var.fields {
                    if strip_lifetimes(&mut field.ty) {
                        found_lifetime = true;
                    }
                }
            }

            let original_ident = &input.ident;
            let (original_impl_gen, original_ty_gen, _) = input.generics.split_for_impl();

            if !found_lifetime {
                return Ok(impl_outgoing_with_incoming_self(&input, &ruma_common));
            }

            let vis = input.vis;
            let doc = format!("'Incoming' variant of [{ty}](enum.{ty}.html).", ty = &input.ident);
            let incoming_ident =
                format_ident!("Incoming{}", original_ident, span = Span::call_site());
            let mut gen_copy = input.generics.clone();
            let (impl_gen, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy);

            Ok(quote! {
                #[doc = #doc]
                #[derive( #( #derives ),* )]
                #( #input_attrs )*
                #vis enum #incoming_ident #ty_gen { #( #vars, )* }

                impl #original_impl_gen #ruma_common::Outgoing for #original_ident #original_ty_gen {
                    type Incoming = #incoming_ident #impl_gen;
                }
            })
        }
        DataKind::Struct(mut fields, struct_kind) => {
            let mut found_lifetime = false;
            for field in &mut fields {
                if !matches!(field.vis, syn::Visibility::Public(_)) {
                    return Err(syn::Error::new_spanned(field, "All fields must be marked `pub`"));
                }
                if strip_lifetimes(&mut field.ty) {
                    found_lifetime = true;
                }
            }

            let original_ident = &input.ident;
            let (original_impl_gen, original_ty_gen, _) = input.generics.split_for_impl();

            if !found_lifetime {
                return Ok(impl_outgoing_with_incoming_self(&input, &ruma_common));
            }

            let vis = input.vis;
            let doc = format!("'Incoming' variant of [{ty}](struct.{ty}.html).", ty = &input.ident);
            let incoming_ident =
                format_ident!("Incoming{}", original_ident, span = Span::call_site());
            let mut gen_copy = input.generics.clone();
            let (impl_gen, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy);

            let struct_def = match struct_kind {
                StructKind::Struct => quote! { { #(#fields,)* } },
                StructKind::Tuple => quote! { ( #(#fields,)* ); },
            };

            Ok(quote! {
                #[doc = #doc]
                #[derive( #( #derives ),* )]
                #( #input_attrs )*
                #vis struct #incoming_ident #ty_gen #struct_def

                impl #original_impl_gen #ruma_common::Outgoing for #original_ident #original_ty_gen {
                    type Incoming = #incoming_ident #impl_gen;
                }
            })
        }
    }
}

/// Keep any `serde` or `non_exhaustive` attributes found and
/// pass them to the Incoming variant.
fn filter_input_attrs(attr: &Attribute) -> bool {
    attr.path.is_ident("serde") || attr.path.is_ident("non_exhaustive")
}

fn impl_outgoing_with_incoming_self(input: &DeriveInput, ruma_common: &TokenStream) -> TokenStream {
    let ident = &input.ident;
    let (impl_gen, ty_gen, _) = input.generics.split_for_impl();

    quote! {
        impl #impl_gen #ruma_common::Outgoing for #ident #ty_gen {
            type Incoming = Self;
        }
    }
}

fn split_for_impl_lifetime_less(generics: &mut Generics) -> (ImplGenerics, TypeGenerics) {
    generics.params = generics
        .params
        .clone()
        .into_iter()
        .filter(|param| !matches!(param, GenericParam::Lifetime(_)))
        .collect();

    let (impl_gen, ty_gen, _) = generics.split_for_impl();
    (impl_gen, ty_gen)
}

fn strip_lifetimes(field_type: &mut Type) -> bool {
    match field_type {
        // T<'a> -> IncomingT
        // The IncomingT has to be declared by the user of this derive macro.
        Type::Path(TypePath { path, .. }) => {
            let mut has_lifetimes = false;
            let mut is_lifetime_generic = false;

            for seg in &mut path.segments {
                // strip generic lifetimes
                match &mut seg.arguments {
                    PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                        args, ..
                    }) => {
                        *args = args
                            .clone()
                            .into_iter()
                            .map(|mut ty| {
                                if let GenericArgument::Type(ty) = &mut ty {
                                    if strip_lifetimes(ty) {
                                        has_lifetimes = true;
                                    };
                                }
                                ty
                            })
                            .filter(|arg| {
                                if let GenericArgument::Lifetime(_) = arg {
                                    is_lifetime_generic = true;
                                    false
                                } else {
                                    true
                                }
                            })
                            .collect();
                    }
                    PathArguments::Parenthesized(ParenthesizedGenericArguments {
                        inputs, ..
                    }) => {
                        *inputs = inputs
                            .clone()
                            .into_iter()
                            .map(|mut ty| {
                                if strip_lifetimes(&mut ty) {
                                    has_lifetimes = true;
                                };
                                ty
                            })
                            .collect();
                    }
                    _ => {}
                }
            }

            // If a type has a generic lifetime parameter there must be an `Incoming` variant of
            // that type.
            if is_lifetime_generic {
                if let Some(name) = path.segments.last_mut() {
                    let incoming_ty_ident = format_ident!("Incoming{}", name.ident);
                    name.ident = incoming_ty_ident;
                }
            }

            has_lifetimes || is_lifetime_generic
        }
        Type::Reference(TypeReference { elem, .. }) => {
            let special_replacement = match &mut **elem {
                Type::Path(ty) => {
                    let path = &ty.path;
                    let last_seg = path.segments.last().unwrap();

                    if last_seg.ident == "str" {
                        // &str -> String
                        Some(parse_quote! { ::std::string::String })
                    } else if last_seg.ident == "DeviceId" || last_seg.ident == "ServerName" {
                        // The identifiers that need to be boxed `Box<T>` since they are DST's.
                        Some(parse_quote! { ::std::boxed::Box<#path> })
                    } else {
                        None
                    }
                }
                // &[T] -> Vec<T>
                Type::Slice(TypeSlice { elem, .. }) => {
                    // Recursively strip the lifetimes of the slice's elements.
                    strip_lifetimes(&mut *elem);
                    Some(parse_quote! { Vec<#elem> })
                }
                _ => None,
            };

            *field_type = match special_replacement {
                Some(ty) => ty,
                None => {
                    // Strip lifetimes of `elem`.
                    strip_lifetimes(elem);
                    // Replace reference with `elem`.
                    (**elem).clone()
                }
            };

            true
        }
        Type::Tuple(syn::TypeTuple { elems, .. }) => {
            let mut has_lifetime = false;
            for elem in elems {
                if strip_lifetimes(elem) {
                    has_lifetime = true;
                }
            }
            has_lifetime
        }
        _ => false,
    }
}

pub struct Meta {
    derive_macs: Vec<DeriveMac>,
}

impl Parse for Meta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            derive_macs: Punctuated::<_, Token![,]>::parse_terminated(input)?.into_iter().collect(),
        })
    }
}

pub enum DeriveMac {
    Regular(Ident),
    NegativeDeserialize,
}

impl Parse for DeriveMac {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(Token![!]) {
            let _: Token![!] = input.parse()?;
            let mac: Ident = input.parse()?;

            if mac != "Deserialize" {
                return Err(syn::Error::new_spanned(
                    mac,
                    "Negative incoming_derive can only be used for Deserialize",
                ));
            }

            Ok(Self::NegativeDeserialize)
        } else {
            let mac = input.parse()?;
            Ok(Self::Regular(mac))
        }
    }
}
<commit_msg>Support `&RawJsonValue` in Outgoing derive<commit_after>use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
    parse::{Parse, ParseStream},
    parse_quote,
    punctuated::Punctuated,
    AngleBracketedGenericArguments, Attribute, Data, DeriveInput, Field, Fields, GenericArgument,
    GenericParam, Generics, Ident, ImplGenerics, ParenthesizedGenericArguments, PathArguments,
    Token, Type, TypeGenerics, TypePath, TypeReference, TypeSlice, Variant,
};

use crate::util::import_ruma_common;

enum StructKind {
    Struct,
    Tuple,
}

enum DataKind {
    Struct(Vec<Field>, StructKind),
    Enum(Vec<Variant>),
    Unit,
}

pub fn expand_derive_outgoing(input: DeriveInput) -> syn::Result<TokenStream> {
    let ruma_common = import_ruma_common();

    let mut derives = vec![quote! { Debug }];
    let mut derive_deserialize = true;

    derives.extend(
        input
            .attrs
            .iter()
            .filter(|attr| attr.path.is_ident("incoming_derive"))
            .map(|attr| attr.parse_args())
            .collect::<syn::Result<Vec<Meta>>>()?
            .into_iter()
            .flat_map(|meta| meta.derive_macs)
            .filter_map(|derive_mac| match derive_mac {
                DeriveMac::Regular(id) => Some(quote! { #id }),
                DeriveMac::NegativeDeserialize => {
                    derive_deserialize = false;
                    None
                }
            }),
    );
    if derive_deserialize {
        derives.push(quote! { #ruma_common::exports::serde::Deserialize });
    }

    let input_attrs =
        input.attrs.iter().filter(|attr| filter_input_attrs(attr)).collect::<Vec<_>>();

    let data = match input.data.clone() {
        Data::Union(_) => panic!("#[derive(Outgoing)] does not support Union types"),
        Data::Enum(e) => DataKind::Enum(e.variants.into_iter().collect()),
        Data::Struct(s) => match s.fields {
            Fields::Named(fs) => {
                DataKind::Struct(fs.named.into_iter().collect(), StructKind::Struct)
            }
            Fields::Unnamed(fs) => {
                DataKind::Struct(fs.unnamed.into_iter().collect(), StructKind::Tuple)
            }
            Fields::Unit => DataKind::Unit,
        },
    };

    match data {
        DataKind::Unit => Ok(impl_outgoing_with_incoming_self(&input, &ruma_common)),
        DataKind::Enum(mut vars) => {
            let mut found_lifetime = false;
            for var in &mut vars {
                for field in &mut var.fields {
                    if strip_lifetimes(&mut field.ty) {
                        found_lifetime = true;
                    }
                }
            }

            let original_ident = &input.ident;
            let (original_impl_gen, original_ty_gen, _) = input.generics.split_for_impl();

            if !found_lifetime {
                return Ok(impl_outgoing_with_incoming_self(&input, &ruma_common));
            }

            let vis = input.vis;
            let doc = format!("'Incoming' variant of [{ty}](enum.{ty}.html).", ty = &input.ident);
            let incoming_ident =
                format_ident!("Incoming{}", original_ident, span = Span::call_site());
            let mut gen_copy = input.generics.clone();
            let (impl_gen, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy);

            Ok(quote! {
                #[doc = #doc]
                #[derive( #( #derives ),* )]
                #( #input_attrs )*
                #vis enum #incoming_ident #ty_gen { #( #vars, )* }

                impl #original_impl_gen #ruma_common::Outgoing for #original_ident #original_ty_gen {
                    type Incoming = #incoming_ident #impl_gen;
                }
            })
        }
        DataKind::Struct(mut fields, struct_kind) => {
            let mut found_lifetime = false;
            for field in &mut fields {
                if !matches!(field.vis, syn::Visibility::Public(_)) {
                    return Err(syn::Error::new_spanned(field, "All fields must be marked `pub`"));
                }
                if strip_lifetimes(&mut field.ty) {
                    found_lifetime = true;
                }
            }

            let original_ident = &input.ident;
            let (original_impl_gen, original_ty_gen, _) = input.generics.split_for_impl();

            if !found_lifetime {
                return Ok(impl_outgoing_with_incoming_self(&input, &ruma_common));
            }

            let vis = input.vis;
            let doc = format!("'Incoming' variant of [{ty}](struct.{ty}.html).", ty = &input.ident);
            let incoming_ident =
                format_ident!("Incoming{}", original_ident, span = Span::call_site());
            let mut gen_copy = input.generics.clone();
            let (impl_gen, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy);

            let struct_def = match struct_kind {
                StructKind::Struct => quote! { { #(#fields,)* } },
                StructKind::Tuple => quote! { ( #(#fields,)* ); },
            };

            Ok(quote! {
                #[doc = #doc]
                #[derive( #( #derives ),* )]
                #( #input_attrs )*
                #vis struct #incoming_ident #ty_gen #struct_def

                impl #original_impl_gen #ruma_common::Outgoing for #original_ident #original_ty_gen {
                    type Incoming = #incoming_ident #impl_gen;
                }
            })
        }
    }
}

/// Keep any `serde` or `non_exhaustive` attributes found and
/// pass them to the Incoming variant.
fn filter_input_attrs(attr: &Attribute) -> bool {
    attr.path.is_ident("serde") || attr.path.is_ident("non_exhaustive")
}

fn impl_outgoing_with_incoming_self(input: &DeriveInput, ruma_common: &TokenStream) -> TokenStream {
    let ident = &input.ident;
    let (impl_gen, ty_gen, _) = input.generics.split_for_impl();

    quote! {
        impl #impl_gen #ruma_common::Outgoing for #ident #ty_gen {
            type Incoming = Self;
        }
    }
}

fn split_for_impl_lifetime_less(generics: &mut Generics) -> (ImplGenerics, TypeGenerics) {
    generics.params = generics
        .params
        .clone()
        .into_iter()
        .filter(|param| !matches!(param, GenericParam::Lifetime(_)))
        .collect();

    let (impl_gen, ty_gen, _) = generics.split_for_impl();
    (impl_gen, ty_gen)
}

fn strip_lifetimes(field_type: &mut Type) -> bool {
    match field_type {
        // T<'a> -> IncomingT
        // The IncomingT has to be declared by the user of this derive macro.
        Type::Path(TypePath { path, .. }) => {
            let mut has_lifetimes = false;
            let mut is_lifetime_generic = false;

            for seg in &mut path.segments {
                // strip generic lifetimes
                match &mut seg.arguments {
                    PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                        args, ..
                    }) => {
                        *args = args
                            .clone()
                            .into_iter()
                            .map(|mut ty| {
                                if let GenericArgument::Type(ty) = &mut ty {
                                    if strip_lifetimes(ty) {
                                        has_lifetimes = true;
                                    };
                                }
                                ty
                            })
                            .filter(|arg| {
                                if let GenericArgument::Lifetime(_) = arg {
                                    is_lifetime_generic = true;
                                    false
                                } else {
                                    true
                                }
                            })
                            .collect();
                    }
                    PathArguments::Parenthesized(ParenthesizedGenericArguments {
                        inputs, ..
                    }) => {
                        *inputs = inputs
                            .clone()
                            .into_iter()
                            .map(|mut ty| {
                                if strip_lifetimes(&mut ty) {
                                    has_lifetimes = true;
                                };
                                ty
                            })
                            .collect();
                    }
                    _ => {}
                }
            }

            // If a type has a generic lifetime parameter there must be an `Incoming` variant of
            // that type.
            if is_lifetime_generic {
                if let Some(name) = path.segments.last_mut() {
                    let incoming_ty_ident = format_ident!("Incoming{}", name.ident);
                    name.ident = incoming_ty_ident;
                }
            }

            has_lifetimes || is_lifetime_generic
        }
        Type::Reference(TypeReference { elem, .. }) => {
            let special_replacement = match &mut **elem {
                Type::Path(ty) => {
                    let path = &ty.path;
                    let last_seg = path.segments.last().unwrap();

                    if last_seg.ident == "str" {
                        // &str -> String
                        Some(parse_quote! { ::std::string::String })
                    } else if last_seg.ident == "DeviceId"
                        || last_seg.ident == "ServerName"
                        || last_seg.ident == "RawJsonValue"
                    {
                        // The identifiers that need to be boxed `Box<T>` since they are DST's.
                        Some(parse_quote! { ::std::boxed::Box<#path> })
                    } else {
                        None
                    }
                }
                // &[T] -> Vec<T>
                Type::Slice(TypeSlice { elem, .. }) => {
                    // Recursively strip the lifetimes of the slice's elements.
                    strip_lifetimes(&mut *elem);
                    Some(parse_quote! { Vec<#elem> })
                }
                _ => None,
            };

            *field_type = match special_replacement {
                Some(ty) => ty,
                None => {
                    // Strip lifetimes of `elem`.
                    strip_lifetimes(elem);
                    // Replace reference with `elem`.
                    (**elem).clone()
                }
            };

            true
        }
        Type::Tuple(syn::TypeTuple { elems, .. }) => {
            let mut has_lifetime = false;
            for elem in elems {
                if strip_lifetimes(elem) {
                    has_lifetime = true;
                }
            }
            has_lifetime
        }
        _ => false,
    }
}

pub struct Meta {
    derive_macs: Vec<DeriveMac>,
}

impl Parse for Meta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            derive_macs: Punctuated::<_, Token![,]>::parse_terminated(input)?.into_iter().collect(),
        })
    }
}

pub enum DeriveMac {
    Regular(Ident),
    NegativeDeserialize,
}

impl Parse for DeriveMac {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(Token![!]) {
            let _: Token![!] = input.parse()?;
            let mac: Ident = input.parse()?;

            if mac != "Deserialize" {
                return Err(syn::Error::new_spanned(
                    mac,
                    "Negative incoming_derive can only be used for Deserialize",
                ));
            }

            Ok(Self::NegativeDeserialize)
        } else {
            let mac = input.parse()?;
            Ok(Self::Regular(mac))
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test, plugin, custom_attribute)]
#![plugin(rasen_plugin)]

extern crate rasen;
extern crate rasen_dsl;
extern crate test;

mod data;

use rasen_dsl::prelude::*;

use test::Bencher;
use data::*;

#[bench]
fn bench_run_basic_frag(b: &mut Bencher) {
    b.iter(|| {
        basic_frag(vec3(0.0f32, 1.0f32, 0.0f32));
    });
}

#[bench]
fn bench_run_basic_vert(b: &mut Bencher) {
    b.iter(|| {
        let a_pos = vec3(1.0f32, 2.0f32, 3.0f32);
        let a_normal = vec3(0.0f32, 1.0f32, 0.0f32);
        let a_uv = vec2(0.5f32, 0.5f32);

        let projection = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);
        let view = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);
        let model = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);

        basic_vert(
            a_pos, a_normal, a_uv,
            projection.into(),
            view.into(),
            model.into()
        )
    });
}
<commit_msg>Fix the basic_frag benchmark<commit_after>#![feature(test, plugin, custom_attribute)]
#![plugin(rasen_plugin)]

extern crate rasen;
extern crate rasen_dsl;
extern crate test;

mod data;

use rasen_dsl::prelude::*;

use test::Bencher;
use data::*;

#[bench]
fn bench_run_basic_frag(b: &mut Bencher) {
    b.iter(|| {
        basic_frag(
            vec3(0.0f32, 1.0f32, 0.0f32),
            vec2(0.0f32, 0.0f32),
            Value::Concrete(Sampler(
                Vec4(0.25f32, 0.625f32, 1.0f32, 1.0f32),
            )),
        );
    });
}

#[bench]
fn bench_run_basic_vert(b: &mut Bencher) {
    b.iter(|| {
        let a_pos = vec3(1.0f32, 2.0f32, 3.0f32);
        let a_normal = vec3(0.0f32, 1.0f32, 0.0f32);
        let a_uv = vec2(0.5f32, 0.5f32);

        let projection = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);
        let view = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);
        let model = Mat4([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);

        basic_vert(
            a_pos, a_normal, a_uv,
            projection.into(),
            view.into(),
            model.into()
        )
    });
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A base for xi plugins. Will be split out into its own crate once it's a bit more stable.

use std::io;

use serde_json::Value;

use xi_core::{ViewIdentifier, PluginPid, plugin_rpc};
use xi_rpc::{self, RpcLoop, RpcCtx, RemoteError, ReadError};

#[derive(Debug)]
pub enum Error {
    RpcError(xi_rpc::Error),
    WrongReturnType,
}

pub trait Handler {
    fn handle_notification(&mut self, ctx: PluginCtx,
                           rpc: plugin_rpc::HostNotification);
    fn handle_request(&mut self, ctx: PluginCtx, rpc: plugin_rpc::HostRequest)
                      -> Result<Value, RemoteError>;
    #[allow(unused_variables)]
    fn idle(&mut self, ctx: PluginCtx, token: usize) {}
}

pub struct PluginCtx<'a>(&'a RpcCtx);

impl<'a> PluginCtx<'a> {
    pub fn get_data(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, offset: usize,
                    max_size: usize, rev: u64) -> Result<String, Error> {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "offset": offset,
            "max_size": max_size,
            "rev": rev,
        });
        let result = self.send_rpc_request("get_data", &params);
        match result {
            Ok(Value::String(s)) => Ok(s),
            Ok(_) => Err(Error::WrongReturnType),
            Err(err) => Err(Error::RpcError(err)),
        }
    }

    pub fn add_scopes(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, scopes: &Vec<Vec<String>>) {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "scopes": scopes,
        });
        self.send_rpc_notification("add_scopes", &params);
    }

    pub fn update_spans(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, start: usize, len: usize, rev: u64, spans: &[plugin_rpc::ScopeSpan]) {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "start": start,
            "len": len,
            "rev": rev,
            "spans": spans,
        });
        self.send_rpc_notification("update_spans", &params);
    }

    fn send_rpc_notification(&self, method: &str, params: &Value) {
        self.0.get_peer().send_rpc_notification(method, params)
    }

    fn send_rpc_request(&self, method: &str, params: &Value) -> Result<Value, xi_rpc::Error> {
        self.0.get_peer().send_rpc_request(method, params)
    }

    /// Determines whether an incoming request (or notification) is pending. This
    /// is intended to reduce latency for bulk operations done in the background.
    pub fn request_is_pending(&self) -> bool {
        self.0.get_peer().request_is_pending()
    }

    /// Schedule the idle handler to be run when there are no requests pending.
    pub fn schedule_idle(&mut self, token: usize) {
        self.0.schedule_idle(token);
    }
}
struct MyHandler<'a, H: 'a>(&'a mut H);

impl<'a, H: Handler> xi_rpc::Handler for MyHandler<'a, H> {
    type Notification = plugin_rpc::HostNotification;
    type Request = plugin_rpc::HostRequest;
    fn handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification) {
        self.0.handle_notification(PluginCtx(ctx), rpc)
    }

    fn handle_request(&mut self, ctx: &RpcCtx, rpc: Self::Request)
                      -> Result<Value, RemoteError> {
        self.0.handle_request(PluginCtx(ctx), rpc)
    }

    fn idle(&mut self, ctx: &RpcCtx, token: usize) {
        self.0.idle(PluginCtx(ctx), token);
    }
}

pub fn mainloop<H: Handler>(handler: &mut H) -> Result<(), ReadError> {
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);
    let mut my_handler = MyHandler(handler);

    rpc_looper.mainloop(|| stdin.lock(), &mut my_handler)
}
<commit_msg>Add tracking of basic view info to plugin_base<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A base for xi plugins. Will be split out into its own crate once it's a bit more stable.

use std::io;
use std::path::{PathBuf, Path};

use serde_json::{self, Value};

use xi_core::{ViewIdentifier, PluginPid, plugin_rpc, SyntaxDefinition,
ConfigTable, BufferConfig};
use xi_rpc::{self, RpcLoop, RpcCtx, RemoteError, ReadError};

#[derive(Debug)]
pub enum Error {
    RpcError(xi_rpc::Error),
    WrongReturnType,
}

pub trait Handler {
    fn handle_notification(&mut self, ctx: PluginCtx,
                           rpc: plugin_rpc::HostNotification);
    fn handle_request(&mut self, ctx: PluginCtx, rpc: plugin_rpc::HostRequest)
                      -> Result<Value, RemoteError>;
    #[allow(unused_variables)]
    fn idle(&mut self, ctx: PluginCtx, token: usize) {}
}

/// A container for general view information, shared between all plugin layers.
pub struct ViewState {
    pub view_id: ViewIdentifier,
    pub syntax: SyntaxDefinition,
    config_table: ConfigTable,
    pub config: Option<BufferConfig>,
    pub path: Option<PathBuf>,
}

pub struct PluginCtx<'a> {
    inner: &'a RpcCtx,
    pub view: &'a ViewState,
    plugin_id: PluginPid,
}

impl ViewState {
    fn new(init_info: &plugin_rpc::PluginBufferInfo) -> Self {

        let &plugin_rpc::PluginBufferInfo {
            ref views, ref path, ref syntax, ref config, ..
        } = init_info;

        ViewState {
            view_id: *views.first().unwrap(),
            syntax: *syntax,
            config_table: config.clone(),
            config: serde_json::from_value(Value::Object(config.clone())).unwrap(),
            path: path.as_ref().map(PathBuf::from)
        }
    }

    fn update_config(&mut self, changes: &ConfigTable) {
        for (key, value) in changes.iter() {
            self.config_table.insert(key.to_owned(), value.to_owned());
        }
        let conf = serde_json::from_value(Value::Object(self.config_table.clone()));
        self.config = conf.unwrap();
    }

    fn update_path(&mut self, path: &Path) {
        self.path = Some(path.to_owned())
    }
}

impl<'a> PluginCtx<'a> {
    fn new(inner: &'a RpcCtx, view: &'a ViewState, plugin_id: PluginPid) -> Self {
        PluginCtx { inner, view, plugin_id }
    }

    pub fn get_data(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, offset: usize,
                    max_size: usize, rev: u64) -> Result<String, Error> {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "offset": offset,
            "max_size": max_size,
            "rev": rev,
        });
        let result = self.send_rpc_request("get_data", &params);
        match result {
            Ok(Value::String(s)) => Ok(s),
            Ok(_) => Err(Error::WrongReturnType),
            Err(err) => Err(Error::RpcError(err)),
        }
    }

    pub fn add_scopes(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, scopes: &Vec<Vec<String>>) {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "scopes": scopes,
        });
        self.send_rpc_notification("add_scopes", &params);
    }

    pub fn update_spans(&self, plugin_id: PluginPid, view_id: &ViewIdentifier, start: usize, len: usize, rev: u64, spans: &[plugin_rpc::ScopeSpan]) {
        let params = json!({
            "plugin_id": plugin_id,
            "view_id": view_id,
            "start": start,
            "len": len,
            "rev": rev,
            "spans": spans,
        });
        self.send_rpc_notification("update_spans", &params);
    }

    fn send_rpc_notification(&self, method: &str, params: &Value) {
        self.inner.get_peer().send_rpc_notification(method, params)
    }

    fn send_rpc_request(&self, method: &str, params: &Value) -> Result<Value, xi_rpc::Error> {
        self.inner.get_peer().send_rpc_request(method, params)
    }

    /// Determines whether an incoming request (or notification) is pending. This
    /// is intended to reduce latency for bulk operations done in the background.
    pub fn request_is_pending(&self) -> bool {
        self.inner.get_peer().request_is_pending()
    }

    /// Schedule the idle handler to be run when there are no requests pending.
    pub fn schedule_idle(&mut self, token: usize) {
        self.inner.schedule_idle(token);
    }
}

struct MyHandler<'a, H: 'a> {
    inner: &'a mut H,
    plugin_id: Option<PluginPid>,
    state: Option<ViewState>,
}

impl<'a, H: 'a> MyHandler<'a, H> {
    fn new(inner: &'a mut H) -> Self {
        MyHandler {
            inner: inner,
            plugin_id: None,
            state: None,
        }
    }

    fn expect_state_mut(&mut self) -> &mut ViewState {
        self.state.as_mut()
            .expect("missing state; was plugin init RPC sent?")
    }
}

impl<'a, H: Handler> xi_rpc::Handler for MyHandler<'a, H> {
    type Notification = plugin_rpc::HostNotification;
    type Request = plugin_rpc::HostRequest;
    fn handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification) {
        use self::plugin_rpc::HostNotification::*;
        match rpc {
            Initialize { ref plugin_id, ref buffer_info } => {
                assert!(self.state.is_none());
                self.state = Some(ViewState::new(buffer_info.first().as_ref().unwrap()));
                self.plugin_id = Some(*plugin_id);
            }

            ConfigChanged { ref changes, .. } =>
                self.expect_state_mut().update_config(changes),

            DidSave { ref path, .. } =>
                self.expect_state_mut().update_path(path),
            _ => (),
        }

        let plugin_ctx = PluginCtx::new(
            ctx, self.state.as_ref().unwrap(), self.plugin_id.unwrap());
        self.inner.handle_notification(plugin_ctx, rpc)
    }

    fn handle_request(&mut self, ctx: &RpcCtx, rpc: Self::Request)
                      -> Result<Value, RemoteError> {
        let plugin_ctx = PluginCtx::new(
            ctx, self.state.as_ref().unwrap(), self.plugin_id.unwrap());
        self.inner.handle_request(plugin_ctx, rpc)
    }

    fn idle(&mut self, ctx: &RpcCtx, token: usize) {
        let plugin_ctx = PluginCtx::new(
            ctx, self.state.as_ref().unwrap(), self.plugin_id.unwrap());
        self.inner.idle(plugin_ctx, token);
    }
}

pub fn mainloop<H: Handler>(handler: &mut H) -> Result<(), ReadError> {
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);
    let mut my_handler = MyHandler::new(handler);

    rpc_looper.mainloop(|| stdin.lock(), &mut my_handler)
}
<|endoftext|>