<commit_before>pub mod boolean_query;

use kite::Query;

use RocksDBIndexReader;
use search::boolean_retrieval::BooleanQueryOp;
use search::scoring::{CombinatorScorer, ScoreFunctionOp};

#[derive(Debug)]
pub struct SearchPlan {
    pub boolean_query: Vec<BooleanQueryOp>,
    pub boolean_query_is_negated: bool,
    pub score_function: Vec<ScoreFunctionOp>,
}

impl SearchPlan {
    pub fn new() -> SearchPlan {
        SearchPlan {
            boolean_query: Vec::new(),
            boolean_query_is_negated: false,
            score_function: Vec::new(),
        }
    }
}

fn plan_query_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, join_op: BooleanQueryOp, score: bool, scorer: CombinatorScorer) {
    match queries.len() {
        0 => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        1 =>  plan_query(index_reader, &mut plan, &queries[0], score),
        _ => {
            let mut query_iter = queries.iter();
            plan_query(index_reader, &mut plan, query_iter.next().unwrap(), score);

            for query in query_iter {
                plan_query(index_reader, &mut plan, query, score);
                plan.boolean_query.push(join_op.clone());
            }
        }
    }

    plan.score_function.push(ScoreFunctionOp::CombinatorScorer(queries.len() as u32, scorer));
}

pub fn plan_query(index_reader: &RocksDBIndexReader,mut plan: &mut SearchPlan, query: &Query, score: bool) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.boolean_query.push(BooleanQueryOp::PushFull);
            plan.score_function.push(ScoreFunctionOp::Literal(*score));
        }
        Query::MatchNone => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
            plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get terms
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            let mut total_terms = 0;
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
                plan.boolean_query.push(BooleanQueryOp::Or);
                plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
                total_terms += 1;
            }

            // This query must push only one score value onto the stack.
            // If we haven't pushed any score operations, Push a literal 0.0
            // If we have pushed more than one score operations, which will lead to more
            // than one score value being pushed to the stack, combine the score values
            // with a combinator operation.
            match total_terms {
                0 => plan.score_function.push(ScoreFunctionOp::Literal(0.0f64)),
                1 => {},
                _ => plan.score_function.push(ScoreFunctionOp::CombinatorScorer(total_terms, CombinatorScorer::Avg)),
            }
        }
        Query::Conjunction{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::And, score, CombinatorScorer::Avg);
        }
        Query::Disjunction{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or, score, CombinatorScorer::Avg);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or, score, CombinatorScorer::Max);
        }
        Query::Filter{ref query, ref filter} => {
            plan_query(index_reader, &mut plan, query, score);
            plan_query(index_reader, &mut plan, filter, false);
            plan.boolean_query.push(BooleanQueryOp::And);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_query(index_reader, &mut plan, query, score);
            plan_query(index_reader, &mut plan, exclude, false);
            plan.boolean_query.push(BooleanQueryOp::AndNot);
        }
    }
}
<commit_msg>Split plan_query into two separate functions<commit_after>pub mod boolean_query;

use kite::Query;

use RocksDBIndexReader;
use search::boolean_retrieval::BooleanQueryOp;
use search::scoring::{CombinatorScorer, ScoreFunctionOp};

#[derive(Debug)]
pub struct SearchPlan {
    pub boolean_query: Vec<BooleanQueryOp>,
    pub boolean_query_is_negated: bool,
    pub score_function: Vec<ScoreFunctionOp>,
}

impl SearchPlan {
    pub fn new() -> SearchPlan {
        SearchPlan {
            boolean_query: Vec::new(),
            boolean_query_is_negated: false,
            score_function: Vec::new(),
        }
    }
}

fn plan_boolean_query_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, join_op: BooleanQueryOp) {
    match queries.len() {
        0 => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
        }
        1 =>  plan_boolean_query(index_reader, &mut plan, &queries[0]),
        _ => {
            let mut query_iter = queries.iter();
            plan_boolean_query(index_reader, &mut plan, query_iter.next().unwrap());

            for query in query_iter {
                plan_boolean_query(index_reader, &mut plan, query);
                plan.boolean_query.push(join_op.clone());
            }
        }
    }
}

fn plan_boolean_query(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.boolean_query.push(BooleanQueryOp::PushFull);
        }
        Query::MatchNone => {
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.boolean_query.push(BooleanQueryOp::PushEmpty);
                    return
                }
            };

            // Get terms
            plan.boolean_query.push(BooleanQueryOp::PushEmpty);
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.boolean_query.push(BooleanQueryOp::PushTermDirectory(field_ref, term_ref));
                plan.boolean_query.push(BooleanQueryOp::Or);
            }
        }
        Query::Conjunction{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::And);
        }
        Query::Disjunction{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_boolean_query_combinator(index_reader, &mut plan, queries, BooleanQueryOp::Or);
        }
        Query::Filter{ref query, ref filter} => {
            plan_boolean_query(index_reader, &mut plan, query);
            plan_boolean_query(index_reader, &mut plan, filter);
            plan.boolean_query.push(BooleanQueryOp::And);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_boolean_query(index_reader, &mut plan, query);
            plan_boolean_query(index_reader, &mut plan, exclude);
            plan.boolean_query.push(BooleanQueryOp::AndNot);
        }
    }
}

fn plan_score_function_combinator(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, queries: &Vec<Query>, scorer: CombinatorScorer) {
    match queries.len() {
        0 => {
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        1 =>  plan_score_function(index_reader, &mut plan, &queries[0]),
        _ => {
            let mut query_iter = queries.iter();
            plan_score_function(index_reader, &mut plan, query_iter.next().unwrap());

            for query in query_iter {
                plan_score_function(index_reader, &mut plan, query);
            }
        }
    }

    plan.score_function.push(ScoreFunctionOp::CombinatorScorer(queries.len() as u32, scorer));
}

fn plan_score_function(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query) {
    match *query {
        Query::MatchAll{ref score} => {
            plan.score_function.push(ScoreFunctionOp::Literal(*score));
        }
        Query::MatchNone => {
            plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
        }
        Query::MatchTerm{ref field, ref term, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get term
            let term_bytes = term.to_bytes();
            let term_ref = match index_reader.store.term_dictionary.get(&term_bytes) {
                Some(term_ref) => term_ref,
                None => {
                    // Term doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
        }
        Query::MatchMultiTerm{ref field, ref term_selector, ref scorer} => {
            // Get field
            let field_ref = match index_reader.schema().get_field_by_name(field) {
                Some(field_ref) => field_ref,
                None => {
                    // Field doesn't exist, so will never match
                    plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
                    return
                }
            };

            // Get terms
            let mut total_terms = 0;
            for term_ref in index_reader.store.term_dictionary.select(term_selector) {
                plan.score_function.push(ScoreFunctionOp::TermScorer(field_ref, term_ref, scorer.clone()));
                total_terms += 1;
            }

            // This query must push only one score value onto the stack.
            // If we haven't pushed any score operations, Push a literal 0.0
            // If we have pushed more than one score operations, which will lead to more
            // than one score value being pushed to the stack, combine the score values
            // with a combinator operation.
            match total_terms {
                0 => plan.score_function.push(ScoreFunctionOp::Literal(0.0f64)),
                1 => {},
                _ => plan.score_function.push(ScoreFunctionOp::CombinatorScorer(total_terms, CombinatorScorer::Avg)),
            }
        }
        Query::Conjunction{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Avg);
        }
        Query::Disjunction{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Avg);
        }
        Query::DisjunctionMax{ref queries} => {
            plan_score_function_combinator(index_reader, &mut plan, queries, CombinatorScorer::Max);
        }
        Query::Filter{ref query, ref filter} => {
            plan_score_function(index_reader, &mut plan, query);
        }
        Query::Exclude{ref query, ref exclude} => {
            plan_score_function(index_reader, &mut plan, query);
        }
    }
}

pub fn plan_query(index_reader: &RocksDBIndexReader, mut plan: &mut SearchPlan, query: &Query, score: bool) {
    plan_boolean_query(index_reader, plan, query);

    if score {
        plan_score_function(index_reader, plan, query);
    } else {
        plan.score_function.push(ScoreFunctionOp::Literal(0.0f64));
    }
}
<|endoftext|>


<commit_before>use specs::*;

use amq_protocol_types::*;
use serde_json::Value;

use std::collections::BTreeMap;

/* Modified version of AMQProtocolDefinition to handle deserialization */
#[derive(Debug, Deserialize)]
pub struct _AMQProtocolDefinition {
    name:          LongString,
    #[serde(rename="major-version")]
    major_version: ShortShortUInt,
    #[serde(rename="minor-version")]
    minor_version: ShortShortUInt,
    revision:      ShortShortUInt,
    port:          LongUInt,
    copyright:     Vec<LongString>,
    domains:       Vec<_AMQPDomain>,
    constants:     Vec<_AMQPConstant>,
    classes:       Vec<_AMQPClass>,
}

impl _AMQProtocolDefinition {
    pub fn to_specs(self) -> AMQProtocolDefinition {
        let domains = self.domains.iter().fold(BTreeMap::new(), |mut domains, domain| {
            domains.insert(domain.0.clone(), domain.1.to_specs());
            domains
        });
        let classes = self.classes.iter().map(|klass| klass.to_specs(&domains)).collect();
        AMQProtocolDefinition {
            name:          self.name,
            major_version: self.major_version,
            minor_version: self.minor_version,
            revision:      self.revision,
            port:          self.port,
            copyright:     self.copyright.iter().fold(LongString::new(), |acc, cur| acc + cur),
            domains:       domains,
            constants:     self.constants.iter().filter_map(|constant| if constant.klass.is_none() { Some(constant.to_specs()) } else { None }).collect(),
            soft_errors:   self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Soft) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
            hard_errors:   self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Hard) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
            classes:       classes,
        }
    }
}

/* Defined as a two-elems array in the spec */
#[derive(Debug, Deserialize)]
struct _AMQPDomain(LongString, _AMQPType);

/* Subset of AMQPType used in specs for deserialization */
#[derive(Debug, Deserialize)]
enum _AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl _AMQPType {
    fn to_specs(&self) -> AMQPType {
        match *self {
            _AMQPType::Bit       => AMQPType::Boolean,
            _AMQPType::Octet     => AMQPType::ShortShortUInt,
            _AMQPType::Short     => AMQPType::ShortUInt,
            _AMQPType::Long      => AMQPType::LongUInt,
            _AMQPType::LongLong  => AMQPType::LongLongInt,
            _AMQPType::ShortStr  => AMQPType::LongString,
            _AMQPType::LongStr   => AMQPType::LongString,
            _AMQPType::Table     => AMQPType::FieldTable,
            _AMQPType::Timestamp => AMQPType::Timestamp,
        }
    }
}

#[derive(Debug, Deserialize)]
enum  _AMQPErrorKind {
    #[serde(rename="soft-error")]
    Soft,
    #[serde(rename="hard-error")]
    Hard,
}

#[derive(Debug, Deserialize)]
struct _AMQPConstant {
    pub name:   LongString,
    pub value:  ShortUInt,
    #[serde(rename="class")]
    pub klass: Option<_AMQPErrorKind>,
}

impl _AMQPConstant {
    fn to_specs(&self) -> AMQPConstant {
        AMQPConstant {
            name:      self.name.clone(),
            value:     self.value,
            amqp_type: if self.value > 255 { AMQPType::ShortUInt } else { AMQPType::ShortShortUInt },
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPClass {
    id:         ShortUInt,
    methods:    Vec<_AMQPMethod>,
    name:       LongString,
    properties: Option<Vec<_AMQPProperty>>,
}

impl _AMQPClass {
    fn to_specs(&self, domains: &BTreeMap<String, AMQPType>) -> AMQPClass {
        let properties     = match self.properties {
            Some(ref properties) => properties.iter().map(|prop| prop.to_specs()).collect(),
            None                 => Vec::new(),
        };
        AMQPClass {
            id:             self.id,
            methods:        self.methods.iter().map(|method| method.to_specs(domains)).collect(),
            name:           self.name.clone(),
            properties:     properties,
            has_properties: self.properties.as_ref().map(|p| !p.is_empty()).unwrap_or(false),
            is_connection:  self.id == 10,
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPMethod {
    id:          ShortUInt,
    arguments:   Vec<_AMQPArgument>,
    name:        LongString,
    synchronous: Option<Boolean>,
}

impl _AMQPMethod {
    fn to_specs(&self, domains: &BTreeMap<LongString, AMQPType>) -> AMQPMethod {
        let arguments = self.arguments_to_specs(domains);
        let has_arguments = !arguments.is_empty();
        let has_flags = arguments.iter().any(|arg| match *arg {
            AMQPArgument::Value(_) => false,
            AMQPArgument::Flags(_) => true,
        });
        AMQPMethod {
            id:            self.id,
            arguments:     arguments,
            has_arguments: has_arguments,
            has_flags:     has_flags,
            name:          self.name.clone(),
            synchronous:   self.synchronous.unwrap_or(false),
        }
    }

    fn arguments_to_specs(&self, domains: &BTreeMap<LongString, AMQPType>) -> Vec<AMQPArgument> {
        let mut arguments                            = Vec::new();
        let mut flags : Option<Vec<AMQPFlagArgument>> = None;
        for argument in &self.arguments {
            let amqp_type = argument.get_type(domains);
            if amqp_type == AMQPType::Boolean {
                let mut flgs = flags.take().unwrap_or_else(|| Vec::new());
                flgs.push(argument.to_flag_specs());
                flags = Some(flgs);
            } else {
                if let Some(flags) = flags.take() {
                    arguments.push(AMQPArgument::Flags(flags));
                }
                arguments.push(AMQPArgument::Value(argument.to_value_specs(amqp_type)));
            }
        }
        if let Some(flags) = flags.take() {
            arguments.push(AMQPArgument::Flags(flags));
        }
        arguments
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPArgument {
    #[serde(rename="type")]
    amqp_type:     Option<_AMQPType>,
    name:          LongString,
    #[serde(rename="default-value")]
    default_value: Option<Value>,
    domain:        Option<LongString>,
}

impl _AMQPArgument {
    fn to_flag_specs(&self) -> AMQPFlagArgument {
        AMQPFlagArgument {
            name:          self.name.clone(),
            default_value: self.default_value.as_ref().and_then(|v| v.as_u64()).map(|u| u != 0).unwrap_or(false),
        }
    }

    fn to_value_specs(&self, amqp_type: AMQPType) -> AMQPValueArgument {
        AMQPValueArgument {
            amqp_type:     amqp_type,
            name:          self.name.clone(),
            default_value: self.default_value.as_ref().map(From::from),
            domain:        self.domain.clone(),
        }
    }

    fn get_type(&self, domains: &BTreeMap<LongString, AMQPType>) -> AMQPType {
        match self.amqp_type {
            Some(ref amqp_type) => amqp_type.to_specs(),
            None                => {
                let domain = match self.domain {
                    Some(ref domain) => domain,
                    None             => panic!(format!("{} has no type nor domain", self.name)),
                };
                domains.get(domain).expect(&format!("No {} domain exists", domain)).clone()
            },
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPProperty {
    #[serde(rename="type")]
    amqp_type: _AMQPType,
    name:      LongString,
}

impl _AMQPProperty {
    fn to_specs(&self) -> AMQPProperty {
        AMQPProperty {
            amqp_type: self.amqp_type.to_specs(),
            name:      self.name.clone(),
        }
    }
}
<commit_msg>codegen: re-consider LongLong as LongLongUInt<commit_after>use specs::*;

use amq_protocol_types::*;
use serde_json::Value;

use std::collections::BTreeMap;

/* Modified version of AMQProtocolDefinition to handle deserialization */
#[derive(Debug, Deserialize)]
pub struct _AMQProtocolDefinition {
    name:          LongString,
    #[serde(rename="major-version")]
    major_version: ShortShortUInt,
    #[serde(rename="minor-version")]
    minor_version: ShortShortUInt,
    revision:      ShortShortUInt,
    port:          LongUInt,
    copyright:     Vec<LongString>,
    domains:       Vec<_AMQPDomain>,
    constants:     Vec<_AMQPConstant>,
    classes:       Vec<_AMQPClass>,
}

impl _AMQProtocolDefinition {
    pub fn to_specs(self) -> AMQProtocolDefinition {
        let domains = self.domains.iter().fold(BTreeMap::new(), |mut domains, domain| {
            domains.insert(domain.0.clone(), domain.1.to_specs());
            domains
        });
        let classes = self.classes.iter().map(|klass| klass.to_specs(&domains)).collect();
        AMQProtocolDefinition {
            name:          self.name,
            major_version: self.major_version,
            minor_version: self.minor_version,
            revision:      self.revision,
            port:          self.port,
            copyright:     self.copyright.iter().fold(LongString::new(), |acc, cur| acc + cur),
            domains:       domains,
            constants:     self.constants.iter().filter_map(|constant| if constant.klass.is_none() { Some(constant.to_specs()) } else { None }).collect(),
            soft_errors:   self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Soft) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
            hard_errors:   self.constants.iter().filter_map(|constant| if let Some(_AMQPErrorKind::Hard) = constant.klass { Some(constant.to_specs()) } else { None }).collect(),
            classes:       classes,
        }
    }
}

/* Defined as a two-elems array in the spec */
#[derive(Debug, Deserialize)]
struct _AMQPDomain(LongString, _AMQPType);

/* Subset of AMQPType used in specs for deserialization */
#[derive(Debug, Deserialize)]
enum _AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl _AMQPType {
    fn to_specs(&self) -> AMQPType {
        match *self {
            _AMQPType::Bit       => AMQPType::Boolean,
            _AMQPType::Octet     => AMQPType::ShortShortUInt,
            _AMQPType::Short     => AMQPType::ShortUInt,
            _AMQPType::Long      => AMQPType::LongUInt,
            _AMQPType::LongLong  => AMQPType::LongLongUInt,
            _AMQPType::ShortStr  => AMQPType::LongString,
            _AMQPType::LongStr   => AMQPType::LongString,
            _AMQPType::Table     => AMQPType::FieldTable,
            _AMQPType::Timestamp => AMQPType::Timestamp,
        }
    }
}

#[derive(Debug, Deserialize)]
enum  _AMQPErrorKind {
    #[serde(rename="soft-error")]
    Soft,
    #[serde(rename="hard-error")]
    Hard,
}

#[derive(Debug, Deserialize)]
struct _AMQPConstant {
    pub name:   LongString,
    pub value:  ShortUInt,
    #[serde(rename="class")]
    pub klass: Option<_AMQPErrorKind>,
}

impl _AMQPConstant {
    fn to_specs(&self) -> AMQPConstant {
        AMQPConstant {
            name:      self.name.clone(),
            value:     self.value,
            amqp_type: if self.value > 255 { AMQPType::ShortUInt } else { AMQPType::ShortShortUInt },
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPClass {
    id:         ShortUInt,
    methods:    Vec<_AMQPMethod>,
    name:       LongString,
    properties: Option<Vec<_AMQPProperty>>,
}

impl _AMQPClass {
    fn to_specs(&self, domains: &BTreeMap<String, AMQPType>) -> AMQPClass {
        let properties     = match self.properties {
            Some(ref properties) => properties.iter().map(|prop| prop.to_specs()).collect(),
            None                 => Vec::new(),
        };
        AMQPClass {
            id:             self.id,
            methods:        self.methods.iter().map(|method| method.to_specs(domains)).collect(),
            name:           self.name.clone(),
            properties:     properties,
            has_properties: self.properties.as_ref().map(|p| !p.is_empty()).unwrap_or(false),
            is_connection:  self.id == 10,
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPMethod {
    id:          ShortUInt,
    arguments:   Vec<_AMQPArgument>,
    name:        LongString,
    synchronous: Option<Boolean>,
}

impl _AMQPMethod {
    fn to_specs(&self, domains: &BTreeMap<LongString, AMQPType>) -> AMQPMethod {
        let arguments = self.arguments_to_specs(domains);
        let has_arguments = !arguments.is_empty();
        let has_flags = arguments.iter().any(|arg| match *arg {
            AMQPArgument::Value(_) => false,
            AMQPArgument::Flags(_) => true,
        });
        AMQPMethod {
            id:            self.id,
            arguments:     arguments,
            has_arguments: has_arguments,
            has_flags:     has_flags,
            name:          self.name.clone(),
            synchronous:   self.synchronous.unwrap_or(false),
        }
    }

    fn arguments_to_specs(&self, domains: &BTreeMap<LongString, AMQPType>) -> Vec<AMQPArgument> {
        let mut arguments                            = Vec::new();
        let mut flags : Option<Vec<AMQPFlagArgument>> = None;
        for argument in &self.arguments {
            let amqp_type = argument.get_type(domains);
            if amqp_type == AMQPType::Boolean {
                let mut flgs = flags.take().unwrap_or_else(|| Vec::new());
                flgs.push(argument.to_flag_specs());
                flags = Some(flgs);
            } else {
                if let Some(flags) = flags.take() {
                    arguments.push(AMQPArgument::Flags(flags));
                }
                arguments.push(AMQPArgument::Value(argument.to_value_specs(amqp_type)));
            }
        }
        if let Some(flags) = flags.take() {
            arguments.push(AMQPArgument::Flags(flags));
        }
        arguments
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPArgument {
    #[serde(rename="type")]
    amqp_type:     Option<_AMQPType>,
    name:          LongString,
    #[serde(rename="default-value")]
    default_value: Option<Value>,
    domain:        Option<LongString>,
}

impl _AMQPArgument {
    fn to_flag_specs(&self) -> AMQPFlagArgument {
        AMQPFlagArgument {
            name:          self.name.clone(),
            default_value: self.default_value.as_ref().and_then(|v| v.as_u64()).map(|u| u != 0).unwrap_or(false),
        }
    }

    fn to_value_specs(&self, amqp_type: AMQPType) -> AMQPValueArgument {
        AMQPValueArgument {
            amqp_type:     amqp_type,
            name:          self.name.clone(),
            default_value: self.default_value.as_ref().map(From::from),
            domain:        self.domain.clone(),
        }
    }

    fn get_type(&self, domains: &BTreeMap<LongString, AMQPType>) -> AMQPType {
        match self.amqp_type {
            Some(ref amqp_type) => amqp_type.to_specs(),
            None                => {
                let domain = match self.domain {
                    Some(ref domain) => domain,
                    None             => panic!(format!("{} has no type nor domain", self.name)),
                };
                domains.get(domain).expect(&format!("No {} domain exists", domain)).clone()
            },
        }
    }
}

#[derive(Debug, Deserialize)]
struct _AMQPProperty {
    #[serde(rename="type")]
    amqp_type: _AMQPType,
    name:      LongString,
}

impl _AMQPProperty {
    fn to_specs(&self) -> AMQPProperty {
        AMQPProperty {
            amqp_type: self.amqp_type.to_specs(),
            name:      self.name.clone(),
        }
    }
}
<|endoftext|>


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

extern crate gcc;

// Various crates need to ensure that they all use the same compiler options,
// so this crate provides a function that returns a gcc::Config object with
// all of the "must have" options already set.

pub fn gcc_config() -> gcc::Config {
    let mut config = gcc::Config::new();
    config.compiler("arm-none-eabi-gcc")
        .define("gcc", None)
        .define("USE_FREERTOS", None)
        .define("SL_PLATFORM_MULTI_THREADED", None)
        .flag("-std=c99")
        .flag("-mthumb")
        .flag("-mcpu=cortex-m4")
        .flag("-mfloat-abi=soft");
    config
}
<commit_msg>Compile C files using -Os for debug builds (#64)<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate gcc;

// Various crates need to ensure that they all use the same compiler options,
// so this crate provides a function that returns a gcc::Config object with
// all of the "must have" options already set.

pub fn gcc_config() -> gcc::Config {
    let mut config = gcc::Config::new();
    config.compiler("arm-none-eabi-gcc")
        // Always compile using -Os. For debug builds, this reduces the code
        // size by about 30K
        .opt_level_str("s")
        .define("gcc", None)
        .define("USE_FREERTOS", None)
        .define("SL_PLATFORM_MULTI_THREADED", None)
        .flag("-std=c99")
        .flag("-mthumb")
        .flag("-mcpu=cortex-m4")
        .flag("-mfloat-abi=soft");
    config
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::io::Write;
use std::path;
use std::process;

struct Project {
    root: tempfile::TempDir,
    files: Vec<(path::PathBuf, Vec<u8>)>,
}

impl Project {
    fn new() -> Project {
        Project {
            root: tempfile::tempdir().unwrap(),
            files: Vec::new(),
        }
    }

    fn add_file<N: Into<path::PathBuf>, C: Into<Vec<u8>>>(
        &mut self,
        name: N,
        content: C,
    ) -> &mut Self {
        self.files.push((name.into(), content.into()));
        self
    }

    fn bootstrap(&mut self) -> &mut Self {
        let built_root = get_built_root();
        let features = if cfg!(feature = "git2") {
            r#"["git2"]"#
        } else {
            "[]"
        };
        self.add_file(
            "Cargo.toml",
            format!(
                r#"
[package]
name = "testbox"
version = "0.0.1"
build = "build.rs"

[build-dependencies]
built = {{ path = {:?}, features = {} }}"#,
                &built_root, &features,
            ),
        )
        .add_file(
            "build.rs",
            r#"
extern crate built;
fn main() {
    built::write_built_file().expect("writing failed");
}"#,
        );
        self
    }

    /// Hold on to the tempdir, it will be removed when dropped!
    fn create(self) -> io::Result<tempfile::TempDir> {
        fs::DirBuilder::new()
            .create(&self.root.path().join("src"))
            .unwrap();
        for (name, content) in self.files {
            let fname = self.root.path().join(name);
            let mut file = fs::File::create(&fname)?;
            file.write_all(&content)?;
        }
        Ok(self.root)
    }

    fn create_and_run(self) {
        let root = self.create().expect("Creating the project failed");
        Self::run(root.as_ref())
    }

    fn run(root: &std::path::Path) {
        let cargo_result = process::Command::new("cargo")
            .current_dir(&root)
            .arg("run")
            .output()
            .expect("cargo failed");
        if !cargo_result.status.success() {
            panic!(
                "cargo failed with {}",
                String::from_utf8_lossy(&cargo_result.stderr)
            );
        }
    }

    #[cfg(feature = "git2")]
    fn init_git(&self) -> git2::Repository {
        git2::Repository::init_opts(
            &self.root,
            git2::RepositoryInitOptions::new()
                .external_template(false)
                .mkdir(false)
                .no_reinit(true)
                .mkpath(false),
        )
        .expect("git-init failed")
    }
}

/// Tries to find built's Cargo.toml, panics if it ends up in /
fn get_built_root() -> path::PathBuf {
    env::current_exe()
        .map(|path| {
            let mut path = path;
            loop {
                if path.join("Cargo.toml").exists() {
                    break;
                }
                path = path::PathBuf::from(path.parent().unwrap());
            }
            path
        })
        .unwrap()
}

#[test]
fn new_testbox() {
    let mut p = Project::new();

    let built_root = get_built_root();

    p.add_file(
        "Cargo.toml",
        format!(
            "
[package]
name = \"testbox\"
version = \"1.2.3-rc1\"
authors = [\"Joe\", \"Bob\", \"Harry:Potter\"]
build = \"build.rs\"
description = \"xobtset\"
homepage = \"localhost\"
repository = \"https://dev.example.com/sources/testbox/\"
license = \"MIT\"

[dependencies]
built = {{ path = {:?}, features=[\"git2\", \"chrono\", \"semver\"] }}

[build-dependencies]
built = {{ path = {:?}, features=[\"git2\", \"chrono\", \"semver\"] }}

[features]
default = [\"SuperAwesome\", \"MegaAwesome\"]
SuperAwesome = []
MegaAwesome = []",
            &built_root, &built_root
        ),
    );

    p.add_file(
        "build.rs",
        r#"
use std::env;
use std::path;
extern crate built;

fn main() {
    // Teleport to a CI-platform, should get detected
    env::set_var("CONTINUOUS_INTEGRATION", "1");

    let mut options = built::Options::default();
    options.set_dependencies(true);
    let src = env::var("CARGO_MANIFEST_DIR").unwrap();
    let dst = path::Path::new(&env::var("OUT_DIR").unwrap()).join("built.rs");
    built::write_built_file_with_opts(&options, src.as_ref(), &dst).unwrap();
}"#,
    );

    p.add_file(
        "src/main.rs",
        r#"
//! The testbox.

mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_VERSION, None);
    assert_eq!(built_info::GIT_DIRTY, None);
    assert_eq!(built_info::GIT_COMMIT_HASH, None);
    assert_eq!(built_info::GIT_HEAD_REF, None);
    assert!(built_info::CI_PLATFORM.is_some());
    assert_eq!(built_info::PKG_VERSION, "1.2.3-rc1");
    assert_eq!(built_info::PKG_VERSION_MAJOR, "1");
    assert_eq!(built_info::PKG_VERSION_MINOR, "2");
    assert_eq!(built_info::PKG_VERSION_PATCH, "3");
    assert_eq!(built_info::PKG_VERSION_PRE, "rc1");
    assert_eq!(built_info::PKG_AUTHORS, "Joe:Bob:Harry:Potter");
    assert_eq!(built_info::PKG_NAME, "testbox");
    assert_eq!(built_info::PKG_DESCRIPTION, "xobtset");
    assert_eq!(built_info::PKG_HOMEPAGE, "localhost");
    assert_eq!(built_info::PKG_LICENSE, "MIT");
    assert_eq!(built_info::PKG_REPOSITORY, "https://dev.example.com/sources/testbox/");
    assert!(built_info::NUM_JOBS > 0);
    assert!(built_info::OPT_LEVEL == "0");
    assert!(built_info::DEBUG);
    assert_eq!(built_info::PROFILE, "debug");
    assert_eq!(built_info::FEATURES,
               ["DEFAULT", "MEGAAWESOME", "SUPERAWESOME"]);
    assert_eq!(built_info::FEATURES_STR,
               "DEFAULT, MEGAAWESOME, SUPERAWESOME");
    assert_ne!(built_info::RUSTC_VERSION, "");
    assert_ne!(built_info::RUSTDOC_VERSION, "");
    assert_ne!(built_info::DEPENDENCIES_STR, "");
    assert_ne!(built_info::HOST, "");
    assert_ne!(built_info::TARGET, "");
    assert_ne!(built_info::RUSTC, "");
    assert_ne!(built_info::RUSTDOC, "");
    assert_ne!(built_info::CFG_TARGET_ARCH, "");
    assert_ne!(built_info::CFG_ENDIAN, "");
    assert_ne!(built_info::CFG_ENV, "");
    assert_ne!(built_info::CFG_FAMILY, "");
    assert_ne!(built_info::CFG_OS, "");
    assert_ne!(built_info::CFG_POINTER_WIDTH, "");

    assert!(built::util::parse_versions(built_info::DEPENDENCIES.iter())
        .any(|(name, ver)| name == "toml" && ver >= built::semver::Version::parse("0.1.0").unwrap()));

    assert!((built::chrono::offset::Utc::now() - built::util::strptime(built_info::BUILT_TIME_UTC)).num_days() <= 1);
}"#,
    );
    p.create_and_run();
}

#[test]
#[cfg(feature = "git2")]
fn clean_then_dirty_git() {
    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_DIRTY, Some(false));
}
"#,
    );
    let repo = p.init_git();
    let root = p.create().expect("Creating the project failed");

    let sig = git2::Signature::now("foo", "bar").unwrap();

    let mut idx = repo.index().unwrap();
    for p in &["src/main.rs", "build.rs"] {
        idx.add_path(path::Path::new(p)).unwrap();
    }
    idx.write().unwrap();
    repo.commit(
        Some("HEAD"),
        &sig,
        &sig,
        "Testing testing 1 2 3",
        &repo.find_tree(idx.write_tree().unwrap()).unwrap(),
        &[],
    )
    .unwrap();
    Project::run(root.as_ref());

    let mut f = std::fs::OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(&root.path().join("src/main.rs"))
        .unwrap();
    f.write_all(
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_DIRTY, Some(true));
}
"#
        .as_bytes(),
    )
    .unwrap();

    Project::run(root.as_ref());
}

#[test]
#[cfg(feature = "git2")]
fn empty_git() {
    // Issue #7, git can be there and still fail
    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {}
"#,
    );
    p.init_git();
    p.create_and_run();
}

#[cfg(target_os = "windows")]
#[test]
fn absolute_paths() {
    // Issue #35. Usually binaries we refer to are simply executables names but sometimes they are
    // absolute paths, containing backslashes, and everything gets sad on this devilish platform.

    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {}
"#,
    );

    let rustc_exe_buf = String::from_utf8(
        process::Command::new("where")
            .arg("rustc")
            .output()
            .expect("Unable to locate absolute path to rustc using `where`")
            .stdout,
    )
    .unwrap();
    let rustc_exe = rustc_exe_buf.split("\r\n").next().unwrap();

    // There should at least be `C:\`
    assert!(rustc_exe.contains('\\'));

    let root = p.create().expect("Creating the project failed");
    let cargo_result = process::Command::new("cargo")
        .current_dir(&root)
        .arg("run")
        .env("RUSTC", &rustc_exe)
        .output()
        .expect("cargo failed");
    if !cargo_result.status.success() {
        panic!(
            "cargo failed with {}",
            String::from_utf8_lossy(&cargo_result.stderr)
        );
    }
}
<commit_msg>Fix unneeded method<commit_after>use std::env;
use std::fs;
use std::io;
use std::io::Write;
use std::path;
use std::process;

struct Project {
    root: tempfile::TempDir,
    files: Vec<(path::PathBuf, Vec<u8>)>,
}

impl Project {
    fn new() -> Project {
        Project {
            root: tempfile::tempdir().unwrap(),
            files: Vec::new(),
        }
    }

    fn add_file<N: Into<path::PathBuf>, C: Into<Vec<u8>>>(
        &mut self,
        name: N,
        content: C,
    ) -> &mut Self {
        self.files.push((name.into(), content.into()));
        self
    }

    #[cfg(any(target_os = "windows", feature = "git2"))]
    fn bootstrap(&mut self) -> &mut Self {
        let built_root = get_built_root();
        let features = if cfg!(feature = "git2") {
            r#"["git2"]"#
        } else {
            "[]"
        };
        self.add_file(
            "Cargo.toml",
            format!(
                r#"
[package]
name = "testbox"
version = "0.0.1"
build = "build.rs"

[build-dependencies]
built = {{ path = {:?}, features = {} }}"#,
                &built_root, &features,
            ),
        )
        .add_file(
            "build.rs",
            r#"
extern crate built;
fn main() {
    built::write_built_file().expect("writing failed");
}"#,
        );
        self
    }

    /// Hold on to the tempdir, it will be removed when dropped!
    fn create(self) -> io::Result<tempfile::TempDir> {
        fs::DirBuilder::new()
            .create(&self.root.path().join("src"))
            .unwrap();
        for (name, content) in self.files {
            let fname = self.root.path().join(name);
            let mut file = fs::File::create(&fname)?;
            file.write_all(&content)?;
        }
        Ok(self.root)
    }

    fn create_and_run(self) {
        let root = self.create().expect("Creating the project failed");
        Self::run(root.as_ref())
    }

    fn run(root: &std::path::Path) {
        let cargo_result = process::Command::new("cargo")
            .current_dir(&root)
            .arg("run")
            .output()
            .expect("cargo failed");
        if !cargo_result.status.success() {
            panic!(
                "cargo failed with {}",
                String::from_utf8_lossy(&cargo_result.stderr)
            );
        }
    }

    #[cfg(feature = "git2")]
    fn init_git(&self) -> git2::Repository {
        git2::Repository::init_opts(
            &self.root,
            git2::RepositoryInitOptions::new()
                .external_template(false)
                .mkdir(false)
                .no_reinit(true)
                .mkpath(false),
        )
        .expect("git-init failed")
    }
}

/// Tries to find built's Cargo.toml, panics if it ends up in /
fn get_built_root() -> path::PathBuf {
    env::current_exe()
        .map(|path| {
            let mut path = path;
            loop {
                if path.join("Cargo.toml").exists() {
                    break;
                }
                path = path::PathBuf::from(path.parent().unwrap());
            }
            path
        })
        .unwrap()
}

#[test]
fn new_testbox() {
    let mut p = Project::new();

    let built_root = get_built_root();

    p.add_file(
        "Cargo.toml",
        format!(
            "
[package]
name = \"testbox\"
version = \"1.2.3-rc1\"
authors = [\"Joe\", \"Bob\", \"Harry:Potter\"]
build = \"build.rs\"
description = \"xobtset\"
homepage = \"localhost\"
repository = \"https://dev.example.com/sources/testbox/\"
license = \"MIT\"

[dependencies]
built = {{ path = {:?}, features=[\"git2\", \"chrono\", \"semver\"] }}

[build-dependencies]
built = {{ path = {:?}, features=[\"git2\", \"chrono\", \"semver\"] }}

[features]
default = [\"SuperAwesome\", \"MegaAwesome\"]
SuperAwesome = []
MegaAwesome = []",
            &built_root, &built_root
        ),
    );

    p.add_file(
        "build.rs",
        r#"
use std::env;
use std::path;
extern crate built;

fn main() {
    // Teleport to a CI-platform, should get detected
    env::set_var("CONTINUOUS_INTEGRATION", "1");

    let mut options = built::Options::default();
    options.set_dependencies(true);
    let src = env::var("CARGO_MANIFEST_DIR").unwrap();
    let dst = path::Path::new(&env::var("OUT_DIR").unwrap()).join("built.rs");
    built::write_built_file_with_opts(&options, src.as_ref(), &dst).unwrap();
}"#,
    );

    p.add_file(
        "src/main.rs",
        r#"
//! The testbox.

mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_VERSION, None);
    assert_eq!(built_info::GIT_DIRTY, None);
    assert_eq!(built_info::GIT_COMMIT_HASH, None);
    assert_eq!(built_info::GIT_HEAD_REF, None);
    assert!(built_info::CI_PLATFORM.is_some());
    assert_eq!(built_info::PKG_VERSION, "1.2.3-rc1");
    assert_eq!(built_info::PKG_VERSION_MAJOR, "1");
    assert_eq!(built_info::PKG_VERSION_MINOR, "2");
    assert_eq!(built_info::PKG_VERSION_PATCH, "3");
    assert_eq!(built_info::PKG_VERSION_PRE, "rc1");
    assert_eq!(built_info::PKG_AUTHORS, "Joe:Bob:Harry:Potter");
    assert_eq!(built_info::PKG_NAME, "testbox");
    assert_eq!(built_info::PKG_DESCRIPTION, "xobtset");
    assert_eq!(built_info::PKG_HOMEPAGE, "localhost");
    assert_eq!(built_info::PKG_LICENSE, "MIT");
    assert_eq!(built_info::PKG_REPOSITORY, "https://dev.example.com/sources/testbox/");
    assert!(built_info::NUM_JOBS > 0);
    assert!(built_info::OPT_LEVEL == "0");
    assert!(built_info::DEBUG);
    assert_eq!(built_info::PROFILE, "debug");
    assert_eq!(built_info::FEATURES,
               ["DEFAULT", "MEGAAWESOME", "SUPERAWESOME"]);
    assert_eq!(built_info::FEATURES_STR,
               "DEFAULT, MEGAAWESOME, SUPERAWESOME");
    assert_ne!(built_info::RUSTC_VERSION, "");
    assert_ne!(built_info::RUSTDOC_VERSION, "");
    assert_ne!(built_info::DEPENDENCIES_STR, "");
    assert_ne!(built_info::HOST, "");
    assert_ne!(built_info::TARGET, "");
    assert_ne!(built_info::RUSTC, "");
    assert_ne!(built_info::RUSTDOC, "");
    assert_ne!(built_info::CFG_TARGET_ARCH, "");
    assert_ne!(built_info::CFG_ENDIAN, "");
    assert_ne!(built_info::CFG_ENV, "");
    assert_ne!(built_info::CFG_FAMILY, "");
    assert_ne!(built_info::CFG_OS, "");
    assert_ne!(built_info::CFG_POINTER_WIDTH, "");

    assert!(built::util::parse_versions(built_info::DEPENDENCIES.iter())
        .any(|(name, ver)| name == "toml" && ver >= built::semver::Version::parse("0.1.0").unwrap()));

    assert!((built::chrono::offset::Utc::now() - built::util::strptime(built_info::BUILT_TIME_UTC)).num_days() <= 1);
}"#,
    );
    p.create_and_run();
}

#[test]
#[cfg(feature = "git2")]
fn clean_then_dirty_git() {
    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_DIRTY, Some(false));
}
"#,
    );
    let repo = p.init_git();
    let root = p.create().expect("Creating the project failed");

    let sig = git2::Signature::now("foo", "bar").unwrap();

    let mut idx = repo.index().unwrap();
    for p in &["src/main.rs", "build.rs"] {
        idx.add_path(path::Path::new(p)).unwrap();
    }
    idx.write().unwrap();
    repo.commit(
        Some("HEAD"),
        &sig,
        &sig,
        "Testing testing 1 2 3",
        &repo.find_tree(idx.write_tree().unwrap()).unwrap(),
        &[],
    )
    .unwrap();
    Project::run(root.as_ref());

    let mut f = std::fs::OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(&root.path().join("src/main.rs"))
        .unwrap();
    f.write_all(
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    assert_eq!(built_info::GIT_DIRTY, Some(true));
}
"#
        .as_bytes(),
    )
    .unwrap();

    Project::run(root.as_ref());
}

#[test]
#[cfg(feature = "git2")]
fn empty_git() {
    // Issue #7, git can be there and still fail
    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {}
"#,
    );
    p.init_git();
    p.create_and_run();
}

#[cfg(target_os = "windows")]
#[test]
fn absolute_paths() {
    // Issue #35. Usually binaries we refer to are simply executables names but sometimes they are
    // absolute paths, containing backslashes, and everything gets sad on this devilish platform.

    let mut p = Project::new();
    p.bootstrap().add_file(
        "src/main.rs",
        r#"
mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {}
"#,
    );

    let rustc_exe_buf = String::from_utf8(
        process::Command::new("where")
            .arg("rustc")
            .output()
            .expect("Unable to locate absolute path to rustc using `where`")
            .stdout,
    )
    .unwrap();
    let rustc_exe = rustc_exe_buf.split("\r\n").next().unwrap();

    // There should at least be `C:\`
    assert!(rustc_exe.contains('\\'));

    let root = p.create().expect("Creating the project failed");
    let cargo_result = process::Command::new("cargo")
        .current_dir(&root)
        .arg("run")
        .env("RUSTC", &rustc_exe)
        .output()
        .expect("cargo failed");
    if !cargo_result.status.success() {
        panic!(
            "cargo failed with {}",
            String::from_utf8_lossy(&cargo_result.stderr)
        );
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use quote::Tokens;
use syn::{Attribute, Body, Ident, MacroInput};
use syn::Body::Enum;
use syn::Lit::Str;
use syn::MetaItem::{List, NameValue, Word};
use syn::VariantData::Unit;

use string::to_dash_name;

/// Struct holding metadata information about all the variants.
struct VariantInfo {
    descriptions: Vec<String>,
    has_argument: Vec<bool>,
    hidden: Vec<bool>,
    names: Vec<String>,
}

/// Expand the required traits for the derive Commands attribute.
pub fn expand_commands_enum(mut ast: MacroInput) -> Tokens {
    let variant_info = transform_enum(&mut ast.body);
    let variant_values = variant_info.names.iter()
        .zip(&variant_info.has_argument)
        .map(|(name, &has_argument)| {
            let ident = Ident::new(name.as_ref());
            let arg_ident = Ident::new("argument");
            if has_argument {
                quote! {
                    #ident(#arg_ident.to_string())
                }
            }
            else {
                quote! {
                    #ident
                }
            }
        });
    let variant_names: Vec<_> = variant_info.names.iter()
        .map(|name| to_dash_name(&name))
        .collect();
    let metadata = variant_names.iter()
        .zip(&variant_info.hidden)
        .zip(&variant_info.descriptions)
        .map(|((name, &is_hidden), description)| {
            let name = to_dash_name(name);
            quote! {
                (#name.to_string(), ::mg_settings::CommandMetaData {
                    completion_hidden: #is_hidden,
                    help_text: #description.to_string(),
                    is_special_command: false,
                })
            }
        });
    let variant_names = &variant_names;
    let variant_has_argument = &variant_info.has_argument;
    let name = ast.ident.clone();
    quote! {
        #ast

        impl ::mg_settings::EnumFromStr for #name {
            fn create(variant: &str, argument: &str) -> ::std::result::Result<#name, String> {
                match variant {
                    #(#variant_names => Ok(#variant_values),)*
                    _ => Err(format!("unknown command {}", variant)),
                }
            }

            fn has_argument(variant: &str) -> ::std::result::Result<bool, String> {
                match variant {
                    #(#variant_names => Ok(#variant_has_argument),)*
                    _ => Err(format!("unknown command {}", variant)),
                }
            }
        }

        impl ::mg_settings::EnumMetaData for #name {
            fn get_metadata() -> ::std::collections::HashMap<String, ::mg_settings::CommandMetaData> {
                let mut vec = vec![#(#metadata),*];
                let iter = vec.drain(..);
                iter.collect()
            }
        }
    }
}

/// Remove the attributes from the variants and return the metadata gathered from the attributes.
fn transform_enum(item: &mut Body) -> VariantInfo {
    let mut descriptions = vec![];
    let mut has_argument = vec![];
    let mut hidden_variants = vec![];
    let mut variant_names = vec![];
    if let Enum(ref mut variants) = *item {
        for variant in variants {
            has_argument.push(variant.data != Unit);
            variant_names.push(variant.ident.to_string());
            let mut help = String::new();
            let mut hidden = false;
            if !variant.attrs.is_empty() {
                for attribute in &variant.attrs {
                    if let &Attribute { value: List(ref ident, ref args), .. } = attribute {
                        match ident.as_ref() {
                            "completion" => {
                                if let Word(ref arg_ident) = args[0] {
                                    if arg_ident == "hidden" {
                                        hidden = true;
                                    }
                                }
                            },
                            "help" => {
                                if let NameValue(ref arg_ident, ref value) = args[0] {
                                    if arg_ident == "text" {
                                        if let &Str(ref description, _) = value {
                                            help = description.clone();
                                        }
                                    }
                                }
                            },
                            _ => (),
                        }
                    }
                }
                variant.attrs.clear();
            }
            descriptions.push(help);
            hidden_variants.push(hidden);
        }
    }
    VariantInfo {
        descriptions: descriptions,
        has_argument: has_argument,
        hidden: hidden_variants,
        names: variant_names,
    }
}
<commit_msg>Fixed to use qualified variant names.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use quote::Tokens;
use syn::{Attribute, Body, Ident, MacroInput};
use syn::Body::Enum;
use syn::Lit::Str;
use syn::MetaItem::{List, NameValue, Word};
use syn::VariantData::Unit;

use string::to_dash_name;

/// Struct holding metadata information about all the variants.
struct VariantInfo {
    descriptions: Vec<String>,
    has_argument: Vec<bool>,
    hidden: Vec<bool>,
    names: Vec<String>,
}

/// Expand the required traits for the derive Commands attribute.
pub fn expand_commands_enum(mut ast: MacroInput) -> Tokens {
    let variant_info = transform_enum(&mut ast.body);
    let name = ast.ident.clone();
    let variant_values = variant_info.names.iter()
        .zip(&variant_info.has_argument)
        .map(|(variant_name, &has_argument)| {
            let ident = Ident::new(variant_name.as_ref());
            let arg_ident = Ident::new("argument");
            if has_argument {
                quote! {
                    #name::#ident(#arg_ident.to_string())
                }
            }
            else {
                quote! {
                    #name::#ident
                }
            }
        });
    let variant_names: Vec<_> = variant_info.names.iter()
        .map(|name| to_dash_name(&name))
        .collect();
    let metadata = variant_names.iter()
        .zip(&variant_info.hidden)
        .zip(&variant_info.descriptions)
        .map(|((name, &is_hidden), description)| {
            let name = to_dash_name(name);
            quote! {
                (#name.to_string(), ::mg_settings::CommandMetaData {
                    completion_hidden: #is_hidden,
                    help_text: #description.to_string(),
                    is_special_command: false,
                })
            }
        });
    let variant_names = &variant_names;
    let variant_has_argument = &variant_info.has_argument;
    quote! {
        #ast

        impl ::mg_settings::EnumFromStr for #name {
            fn create(variant: &str, argument: &str) -> ::std::result::Result<#name, String> {
                match variant {
                    #(#variant_names => Ok(#variant_values),)*
                    _ => Err(format!("unknown command {}", variant)),
                }
            }

            fn has_argument(variant: &str) -> ::std::result::Result<bool, String> {
                match variant {
                    #(#variant_names => Ok(#variant_has_argument),)*
                    _ => Err(format!("unknown command {}", variant)),
                }
            }
        }

        impl ::mg_settings::EnumMetaData for #name {
            fn get_metadata() -> ::std::collections::HashMap<String, ::mg_settings::CommandMetaData> {
                let mut vec = vec![#(#metadata),*];
                let iter = vec.drain(..);
                iter.collect()
            }
        }
    }
}

/// Remove the attributes from the variants and return the metadata gathered from the attributes.
fn transform_enum(item: &mut Body) -> VariantInfo {
    let mut descriptions = vec![];
    let mut has_argument = vec![];
    let mut hidden_variants = vec![];
    let mut variant_names = vec![];
    if let Enum(ref mut variants) = *item {
        for variant in variants {
            has_argument.push(variant.data != Unit);
            variant_names.push(variant.ident.to_string());
            let mut help = String::new();
            let mut hidden = false;
            if !variant.attrs.is_empty() {
                for attribute in &variant.attrs {
                    if let &Attribute { value: List(ref ident, ref args), .. } = attribute {
                        match ident.as_ref() {
                            "completion" => {
                                if let Word(ref arg_ident) = args[0] {
                                    if arg_ident == "hidden" {
                                        hidden = true;
                                    }
                                }
                            },
                            "help" => {
                                if let NameValue(ref arg_ident, ref value) = args[0] {
                                    if arg_ident == "text" {
                                        if let &Str(ref description, _) = value {
                                            help = description.clone();
                                        }
                                    }
                                }
                            },
                            _ => (),
                        }
                    }
                }
                variant.attrs.clear();
            }
            descriptions.push(help);
            hidden_variants.push(hidden);
        }
    }
    VariantInfo {
        descriptions: descriptions,
        has_argument: has_argument,
        hidden: hidden_variants,
        names: variant_names,
    }
}
<|endoftext|>


<commit_before>extern crate tar = "tar";

#[test]
fn test_data_len() {
    let data = tar::read("/Users/matuzak/workspace/rust-tar/tests/tar_test.tar");
    assert_eq!(data.len(), 13);
}

#[test]
fn test_get_type() {
    assert_eq!(tar::get_type("0"), "File");
    assert_eq!(tar::get_type("1"), "Link");
}

#[test]
fn test_get_mode() {
    assert_eq!(tar::get_mode("suid"), 4000);
}
<commit_msg>adding test<commit_after>extern crate tar = "tar";

#[test]
fn test_data_len() {
    let data = tar::read("/Users/matuzak/workspace/rust-tar/tests/tar_test.tar");
    assert_eq!(data.len(), 13);
}

#[test]
fn test_get_type() {
    assert_eq!(tar::get_type("0"), "File");
    assert_eq!(tar::get_type("1"), "Link");
    assert_eq!(tar::get_type("5"), "Directory");
}

#[test]
fn test_get_mode() {
    assert_eq!(tar::get_mode("suid"), 4000);
}
<|endoftext|>


<commit_before>extern crate cargotest;
extern crate kernel32;
extern crate libc;
extern crate winapi;

use std::net::TcpListener;
use std::io::{self, Read};
use std::process::{Stdio, Child};

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows suport for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use kernel32;
    use winapi;
    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = kernel32::GetCurrentProcess();
        let mut ret = 0;
        let r = kernel32::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert!(r != 0);
        if ret == winapi::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = kernel32::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = kernel32::AssignProcessToJobObject(job, me);
        kernel32::CloseHandle(job);
        r != 0
    }
}

#[test]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr));
    p.build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<commit_msg>Protect against spurious failure in ctrl_c test<commit_after>extern crate cargotest;
extern crate kernel32;
extern crate libc;
extern crate winapi;

use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Stdio, Child};
use std::thread;
use std::time::Duration;

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows suport for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use kernel32;
    use winapi;
    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = kernel32::GetCurrentProcess();
        let mut ret = 0;
        let r = kernel32::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert!(r != 0);
        if ret == winapi::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = kernel32::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = kernel32::AssignProcessToJobObject(job, me);
        kernel32::CloseHandle(job);
        r != 0
    }
}

#[test]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr));
    p.build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }

    // Ok so what we just did was spawn cargo that spawned a build script, then
    // we killed cargo in hopes of it killing the build script as well. If all
    // went well the build script is now dead. On Windows, however, this is
    // enforced with job objects which means that it may actually be in the
    // *process* of being torn down at this point.
    //
    // Now on Windows we can't completely remove a file until all handles to it
    // have been closed. Including those that represent running processes. So if
    // we were to return here then there may still be an open reference to some
    // file in the build directory. What we want to actually do is wait for the
    // build script to *complete* exit. Take care of that by blowing away the
    // build directory here, and panicking if we eventually spin too long
    // without being able to.
    for i in 0..10 {
        match fs::remove_dir_all(&p.root().join("target")) {
            Ok(()) => return,
            Err(e) => println!("attempt {}: {}", i, e),
        }
        thread::sleep(Duration::from_millis(100));
    }

    panic!("couldn't remove build directory after a few tries, seems like \
            we won't be able to!");
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<|endoftext|>


<commit_before>//! This module has various option flags and configs we can pass to ISPC,
//! located here for convience and clutter reduction.

/// Different math libraries that ISPC can use for computations.
pub enum MathLib {
    /// Use ispc's built-in math functions (the default).
    ISPCDefault,
    /// Use high-performance but lower-accuracy math functions.
    Fast,
    /// Use the Intel(r) SVML math libraries.
    SVML,
    /// Use the system's math library (**may be quite slow**).
    System,
}

impl ToString for MathLib {
    fn to_string(&self) -> String {
        match *self {
            MathLib::ISPCDefault => String::from("--math-lib=default"),
            MathLib::Fast => String::from("--math-lib=fast"),
            MathLib::SVML => String::from("--math-lib=svml"),
            MathLib::System => String::from("--math-lib=system"),
        }
    }
}

/// Select the target CPU architecture
pub enum Architecture {
    Arm,
    Aarch64,
    X86,
    X64,
}

impl ToString for Architecture {
    fn to_string(&self) -> String {
        match *self {
            Architecture::Arm => String::from("--arch=arm"),
            Architecture::Aarch64 => String::from("--arch=aarch64"),
            Architecture::X86 => String::from("--arch=x86"),
            Architecture::X64 => String::from("--arch=x86_64"),
        }
    }
}

/// Select 32 or 64 bit addressing to be used by ISPC. Note: 32-bit
/// addressing calculations are done by default, even on 64 bit target
/// architectures.
pub enum Addressing {
    /// Select 32 bit addressing calculations.
    A32,
    /// Select 64 bit addressing calculations.
    A64,
}

impl ToString for Addressing {
    fn to_string(&self) -> String {
        match *self {
            Addressing::A32 => String::from("--addressing=32"),
            Addressing::A64 => String::from("--addressing=64"),
        }
    }
}

/// ISPC target CPU ISA options, if none is set ISPC will
/// target the machine being compile on.
#[derive(Eq, PartialEq)]
pub enum CPU {
    Generic,
    /// Synonym for Atom target
    Bonnell,
    Core2,
    Penryn,
    /// Synonym for corei7 target
    Nehalem,
    /// Synonym for btver2
    Ps4,
    /// Synonym for corei7-avx
    SandyBridge,
    /// Synonym for core-avx-i target
    IvyBridge,
    /// Synonym for core-avx2 target
    Haswell,
    Broadwell,
    Knl,
    Skx,
    Icl,
    /// Synonym for slm target
    Silvermont,
    CoretexA15,
    CoretexA9,
    CoretexA35,
    CoretexA53,
    CoretexA57,
}

impl ToString for CPU {
    fn to_string(&self) -> String {
        match *self {
            CPU::Generic => String::from("--cpu=generic"),
            CPU::Bonnell => String::from("--cpu=bonnell"),
            CPU::Core2 => String::from("--cpu=core2"),
            CPU::Penryn => String::from("--cpu=penryn"),
            CPU::Nehalem => String::from("--cpu=nehalem"),
            CPU::Ps4 => String::from("--cpu=ps4"),
            CPU::SandyBridge => String::from("--cpu=sandybridge"),
            CPU::IvyBridge => String::from("--cpu=ivybridge"),
            CPU::Haswell => String::from("--cpu=haswell"),
            CPU::Broadwell => String::from("--cpu=broadwell"),
            CPU::Knl => String::from("--cpu=knl"),
            CPU::Skx => String::from("--cpu=skx"),
            CPU::Icl => String::from("--cpu=icl"),
            CPU::Silvermont => String::from("--cpu=silvermont"),
            CPU::CoretexA15 => String::from("--cpu=cortex-a15"),
            CPU::CoretexA9 => String::from("--cpu=cortex-a9"),
            CPU::CoretexA35 => String::from("--cpu=cortex-a35"),
            CPU::CoretexA53 => String::from("--cpu=cortex-a53"),
            CPU::CoretexA57 => String::from("--cpu=cortex-a57"),
        }
    }
}

/// ISPC optimization options.
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub enum OptimizationOpt {
    /// Remove assertion statements from final code.
    DisableAssertions,
    /// Disable 'fused multiply-add' instructions (on targets that support them).
    DisableFMA,
    /// Disable loop unrolling.
    DisableLoopUnroll,
    /// Faster masked vector loads on SSE (may go past end of array).
    FastMaskedVload,
    /// Perform non-IEEE-compliant optimizations of numeric expressions.
    FastMath,
    /// Always issue aligned vector load and store instructions.
    ForceAlignedMemory,
    /// Disable using zmm registers in favor of ymm on avx512skx-i32x16 (ISPC 1.13+)
    DisableZmm,
}

impl ToString for OptimizationOpt {
    fn to_string(&self) -> String {
        match *self {
            OptimizationOpt::DisableAssertions => String::from("--opt=disable-assertions"),
            OptimizationOpt::DisableFMA => String::from("--opt=disable-fma"),
            OptimizationOpt::DisableLoopUnroll => String::from("--opt=disable-loop-unroll"),
            OptimizationOpt::FastMaskedVload => String::from("--opt=fast-masked-vload"),
            OptimizationOpt::FastMath => String::from("--opt=fast-math"),
            OptimizationOpt::ForceAlignedMemory => String::from("--opt=force-aligned-memory"),
            OptimizationOpt::DisableZmm => String::from("--opt=disable-zmm"),
        }
    }
}

/// Target instruction sets and vector widths available to specialize for. The
/// default if none is set will be the host CPU's ISA and vector width.
pub enum TargetISA {
    Host,
    SSE2i32x4,
    SSE2i32x8,
    SSE4i32x4,
    SSE4i32x8,
    SSE4i16x8,
    SSE4i8x16,
    AVX1i32x4,
    AVX1i32x8,
    AVX1i32x16,
    AVX1i64x4,
    AVX2i32x8,
    AVX2i32x16,
    AVX2i64x4,
    AVX512KNLi32x16,
    AVX512SKXi32x16,
    AVX512SKXi32x8,
    Neoni8x16,
    Neoni16x8,
    Neoni32x4,
    Neoni32x8,
}

impl TargetISA {
    pub fn lib_suffix(&self) -> String {
        match *self {
            TargetISA::Host => String::from("host"),
            TargetISA::SSE2i32x4 => String::from("sse2"),
            TargetISA::SSE2i32x8 => String::from("sse2"),
            TargetISA::SSE4i32x4 => String::from("sse4"),
            TargetISA::SSE4i32x8 => String::from("sse4"),
            TargetISA::SSE4i16x8 => String::from("sse4"),
            TargetISA::SSE4i8x16 => String::from("sse4"),
            TargetISA::AVX1i32x4 => String::from("avx"),
            TargetISA::AVX1i32x8 => String::from("avx"),
            TargetISA::AVX1i32x16 => String::from("avx"),
            TargetISA::AVX1i64x4 => String::from("avx"),
            TargetISA::AVX2i32x8 => String::from("avx2"),
            TargetISA::AVX2i32x16 => String::from("avx2"),
            TargetISA::AVX2i64x4 => String::from("avx2"),
            TargetISA::AVX512KNLi32x16 => String::from("avx512knl"),
            TargetISA::AVX512SKXi32x16 => String::from("avx512skx"),
            TargetISA::AVX512SKXi32x8 => String::from("avx512skx"),
            TargetISA::Neoni8x16 => String::from("neon"),
            TargetISA::Neoni16x8 => String::from("neon"),
            TargetISA::Neoni32x4 => String::from("neon"),
            TargetISA::Neoni32x8 => String::from("neon"),
        }
    }
}

impl ToString for TargetISA {
    fn to_string(&self) -> String {
        match *self {
            TargetISA::Host => String::from("host"),
            TargetISA::SSE2i32x4 => String::from("sse2-i32x4"),
            TargetISA::SSE2i32x8 => String::from("sse2-i32x8"),
            TargetISA::SSE4i32x4 => String::from("sse4-i32x4"),
            TargetISA::SSE4i32x8 => String::from("sse4-i32x8"),
            TargetISA::SSE4i16x8 => String::from("sse4-i16x8"),
            TargetISA::SSE4i8x16 => String::from("sse4-i8x16"),
            TargetISA::AVX1i32x4 => String::from("avx1-i32x4"),
            TargetISA::AVX1i32x8 => String::from("avx1-i32x8"),
            TargetISA::AVX1i32x16 => String::from("avx1-i32x16"),
            TargetISA::AVX1i64x4 => String::from("avx1-i64x4"),
            TargetISA::AVX2i32x8 => String::from("avx2-i32x8"),
            TargetISA::AVX2i32x16 => String::from("avx2-i32x16"),
            TargetISA::AVX2i64x4 => String::from("avx2-i64x4"),
            TargetISA::AVX512KNLi32x16 => String::from("avx512knl-i32x16"),
            TargetISA::AVX512SKXi32x16 => String::from("avx512skx-i32x16"),
            TargetISA::AVX512SKXi32x8 => String::from("avx512skx-i32x8"),
            TargetISA::Neoni8x16 => String::from("neon-i8x16"),
            TargetISA::Neoni16x8 => String::from("neon-i16x8"),
            TargetISA::Neoni32x4 => String::from("neon-i32x4"),
            TargetISA::Neoni32x8 => String::from("neon-i32x8"),
        }
    }
}

/// Target instruction sets and vector widths available to specialize for. The
/// default if none is set will be the host CPU's ISA and vector width.
pub enum TargetOS {
    Windows,
    Ps4,
    Linux,
    Macos,
    Android,
    Ios,
}

impl TargetOS {
    pub fn lib_suffix(&self) -> String {
        match *self {
            TargetOS::Windows => String::from("windows"),
            TargetOS::Ps4 => String::from("ps4"),
            TargetOS::Linux => String::from("linux"),
            TargetOS::Macos => String::from("macos"),
            TargetOS::Android => String::from("android"),
            TargetOS::Ios => String::from("ios"),
        }
    }
}

impl ToString for TargetOS {
    fn to_string(&self) -> String {
        match *self {
            TargetOS::Windows => String::from("--target-os=windows"),
            TargetOS::Ps4 => String::from("--target-os=ps4"),
            TargetOS::Linux => String::from("--target-os=linux"),
            TargetOS::Macos => String::from("--target-os=macos"),
            TargetOS::Android => String::from("--target-os=android"),
            TargetOS::Ios => String::from("--target-os=ios"),
        }
    }
}
<commit_msg>add Clone, Copy and PartialEq for enum TargetISA<commit_after>//! This module has various option flags and configs we can pass to ISPC,
//! located here for convience and clutter reduction.

/// Different math libraries that ISPC can use for computations.
pub enum MathLib {
    /// Use ispc's built-in math functions (the default).
    ISPCDefault,
    /// Use high-performance but lower-accuracy math functions.
    Fast,
    /// Use the Intel(r) SVML math libraries.
    SVML,
    /// Use the system's math library (**may be quite slow**).
    System,
}

impl ToString for MathLib {
    fn to_string(&self) -> String {
        match *self {
            MathLib::ISPCDefault => String::from("--math-lib=default"),
            MathLib::Fast => String::from("--math-lib=fast"),
            MathLib::SVML => String::from("--math-lib=svml"),
            MathLib::System => String::from("--math-lib=system"),
        }
    }
}

/// Select the target CPU architecture
pub enum Architecture {
    Arm,
    Aarch64,
    X86,
    X64,
}

impl ToString for Architecture {
    fn to_string(&self) -> String {
        match *self {
            Architecture::Arm => String::from("--arch=arm"),
            Architecture::Aarch64 => String::from("--arch=aarch64"),
            Architecture::X86 => String::from("--arch=x86"),
            Architecture::X64 => String::from("--arch=x86_64"),
        }
    }
}

/// Select 32 or 64 bit addressing to be used by ISPC. Note: 32-bit
/// addressing calculations are done by default, even on 64 bit target
/// architectures.
pub enum Addressing {
    /// Select 32 bit addressing calculations.
    A32,
    /// Select 64 bit addressing calculations.
    A64,
}

impl ToString for Addressing {
    fn to_string(&self) -> String {
        match *self {
            Addressing::A32 => String::from("--addressing=32"),
            Addressing::A64 => String::from("--addressing=64"),
        }
    }
}

/// ISPC target CPU ISA options, if none is set ISPC will
/// target the machine being compile on.
#[derive(Eq, PartialEq)]
pub enum CPU {
    Generic,
    /// Synonym for Atom target
    Bonnell,
    Core2,
    Penryn,
    /// Synonym for corei7 target
    Nehalem,
    /// Synonym for btver2
    Ps4,
    /// Synonym for corei7-avx
    SandyBridge,
    /// Synonym for core-avx-i target
    IvyBridge,
    /// Synonym for core-avx2 target
    Haswell,
    Broadwell,
    Knl,
    Skx,
    Icl,
    /// Synonym for slm target
    Silvermont,
    CoretexA15,
    CoretexA9,
    CoretexA35,
    CoretexA53,
    CoretexA57,
}

impl ToString for CPU {
    fn to_string(&self) -> String {
        match *self {
            CPU::Generic => String::from("--cpu=generic"),
            CPU::Bonnell => String::from("--cpu=bonnell"),
            CPU::Core2 => String::from("--cpu=core2"),
            CPU::Penryn => String::from("--cpu=penryn"),
            CPU::Nehalem => String::from("--cpu=nehalem"),
            CPU::Ps4 => String::from("--cpu=ps4"),
            CPU::SandyBridge => String::from("--cpu=sandybridge"),
            CPU::IvyBridge => String::from("--cpu=ivybridge"),
            CPU::Haswell => String::from("--cpu=haswell"),
            CPU::Broadwell => String::from("--cpu=broadwell"),
            CPU::Knl => String::from("--cpu=knl"),
            CPU::Skx => String::from("--cpu=skx"),
            CPU::Icl => String::from("--cpu=icl"),
            CPU::Silvermont => String::from("--cpu=silvermont"),
            CPU::CoretexA15 => String::from("--cpu=cortex-a15"),
            CPU::CoretexA9 => String::from("--cpu=cortex-a9"),
            CPU::CoretexA35 => String::from("--cpu=cortex-a35"),
            CPU::CoretexA53 => String::from("--cpu=cortex-a53"),
            CPU::CoretexA57 => String::from("--cpu=cortex-a57"),
        }
    }
}

/// ISPC optimization options.
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub enum OptimizationOpt {
    /// Remove assertion statements from final code.
    DisableAssertions,
    /// Disable 'fused multiply-add' instructions (on targets that support them).
    DisableFMA,
    /// Disable loop unrolling.
    DisableLoopUnroll,
    /// Faster masked vector loads on SSE (may go past end of array).
    FastMaskedVload,
    /// Perform non-IEEE-compliant optimizations of numeric expressions.
    FastMath,
    /// Always issue aligned vector load and store instructions.
    ForceAlignedMemory,
    /// Disable using zmm registers in favor of ymm on avx512skx-i32x16 (ISPC 1.13+)
    DisableZmm,
}

impl ToString for OptimizationOpt {
    fn to_string(&self) -> String {
        match *self {
            OptimizationOpt::DisableAssertions => String::from("--opt=disable-assertions"),
            OptimizationOpt::DisableFMA => String::from("--opt=disable-fma"),
            OptimizationOpt::DisableLoopUnroll => String::from("--opt=disable-loop-unroll"),
            OptimizationOpt::FastMaskedVload => String::from("--opt=fast-masked-vload"),
            OptimizationOpt::FastMath => String::from("--opt=fast-math"),
            OptimizationOpt::ForceAlignedMemory => String::from("--opt=force-aligned-memory"),
            OptimizationOpt::DisableZmm => String::from("--opt=disable-zmm"),
        }
    }
}

/// Target instruction sets and vector widths available to specialize for. The
/// default if none is set will be the host CPU's ISA and vector width.
#[derive(Clone, Copy, PartialEq)]
pub enum TargetISA {
    Host,
    SSE2i32x4,
    SSE2i32x8,
    SSE4i32x4,
    SSE4i32x8,
    SSE4i16x8,
    SSE4i8x16,
    AVX1i32x4,
    AVX1i32x8,
    AVX1i32x16,
    AVX1i64x4,
    AVX2i32x8,
    AVX2i32x16,
    AVX2i64x4,
    AVX512KNLi32x16,
    AVX512SKXi32x16,
    AVX512SKXi32x8,
    Neoni8x16,
    Neoni16x8,
    Neoni32x4,
    Neoni32x8,
}

impl TargetISA {
    pub fn lib_suffix(&self) -> String {
        match *self {
            TargetISA::Host => String::from("host"),
            TargetISA::SSE2i32x4 => String::from("sse2"),
            TargetISA::SSE2i32x8 => String::from("sse2"),
            TargetISA::SSE4i32x4 => String::from("sse4"),
            TargetISA::SSE4i32x8 => String::from("sse4"),
            TargetISA::SSE4i16x8 => String::from("sse4"),
            TargetISA::SSE4i8x16 => String::from("sse4"),
            TargetISA::AVX1i32x4 => String::from("avx"),
            TargetISA::AVX1i32x8 => String::from("avx"),
            TargetISA::AVX1i32x16 => String::from("avx"),
            TargetISA::AVX1i64x4 => String::from("avx"),
            TargetISA::AVX2i32x8 => String::from("avx2"),
            TargetISA::AVX2i32x16 => String::from("avx2"),
            TargetISA::AVX2i64x4 => String::from("avx2"),
            TargetISA::AVX512KNLi32x16 => String::from("avx512knl"),
            TargetISA::AVX512SKXi32x16 => String::from("avx512skx"),
            TargetISA::AVX512SKXi32x8 => String::from("avx512skx"),
            TargetISA::Neoni8x16 => String::from("neon"),
            TargetISA::Neoni16x8 => String::from("neon"),
            TargetISA::Neoni32x4 => String::from("neon"),
            TargetISA::Neoni32x8 => String::from("neon"),
        }
    }
}

impl ToString for TargetISA {
    fn to_string(&self) -> String {
        match *self {
            TargetISA::Host => String::from("host"),
            TargetISA::SSE2i32x4 => String::from("sse2-i32x4"),
            TargetISA::SSE2i32x8 => String::from("sse2-i32x8"),
            TargetISA::SSE4i32x4 => String::from("sse4-i32x4"),
            TargetISA::SSE4i32x8 => String::from("sse4-i32x8"),
            TargetISA::SSE4i16x8 => String::from("sse4-i16x8"),
            TargetISA::SSE4i8x16 => String::from("sse4-i8x16"),
            TargetISA::AVX1i32x4 => String::from("avx1-i32x4"),
            TargetISA::AVX1i32x8 => String::from("avx1-i32x8"),
            TargetISA::AVX1i32x16 => String::from("avx1-i32x16"),
            TargetISA::AVX1i64x4 => String::from("avx1-i64x4"),
            TargetISA::AVX2i32x8 => String::from("avx2-i32x8"),
            TargetISA::AVX2i32x16 => String::from("avx2-i32x16"),
            TargetISA::AVX2i64x4 => String::from("avx2-i64x4"),
            TargetISA::AVX512KNLi32x16 => String::from("avx512knl-i32x16"),
            TargetISA::AVX512SKXi32x16 => String::from("avx512skx-i32x16"),
            TargetISA::AVX512SKXi32x8 => String::from("avx512skx-i32x8"),
            TargetISA::Neoni8x16 => String::from("neon-i8x16"),
            TargetISA::Neoni16x8 => String::from("neon-i16x8"),
            TargetISA::Neoni32x4 => String::from("neon-i32x4"),
            TargetISA::Neoni32x8 => String::from("neon-i32x8"),
        }
    }
}

/// Target instruction sets and vector widths available to specialize for. The
/// default if none is set will be the host CPU's ISA and vector width.
pub enum TargetOS {
    Windows,
    Ps4,
    Linux,
    Macos,
    Android,
    Ios,
}

impl TargetOS {
    pub fn lib_suffix(&self) -> String {
        match *self {
            TargetOS::Windows => String::from("windows"),
            TargetOS::Ps4 => String::from("ps4"),
            TargetOS::Linux => String::from("linux"),
            TargetOS::Macos => String::from("macos"),
            TargetOS::Android => String::from("android"),
            TargetOS::Ios => String::from("ios"),
        }
    }
}

impl ToString for TargetOS {
    fn to_string(&self) -> String {
        match *self {
            TargetOS::Windows => String::from("--target-os=windows"),
            TargetOS::Ps4 => String::from("--target-os=ps4"),
            TargetOS::Linux => String::from("--target-os=linux"),
            TargetOS::Macos => String::from("--target-os=macos"),
            TargetOS::Android => String::from("--target-os=android"),
            TargetOS::Ios => String::from("--target-os=ios"),
        }
    }
}
<|endoftext|>