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

//! Implementation of ElasticSearch Mapping operation

//!
//! Please note: this will grow and become a full implementation of the ElasticSearch
//! [Indices API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html)
//! so subtle (potentially breaking) changes will be made to the API when that happens

use std::collections::HashMap;

use ::Client;
use ::error::EsError;

pub type DocType<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
pub type DocTypes<'a> = HashMap<&'a str, DocType<'a>>;
pub type Mapping<'a> = HashMap<&'a str, DocTypes<'a>>;

/// An indexing operation
pub struct MappingOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client:    &'a mut Client,

    /// The index that will be created and eventually mapped
    index:     &'b str,

    /// A map containing the doc types and their type mapping
    doc_types: &'b DocTypes<'b>
}

impl<'a, 'b> MappingOperation<'a, 'b> {
    pub fn new(client: &'a mut Client,
               index: &'b str,
               doc_types: &'b DocTypes) -> MappingOperation<'a, 'b> {
        MappingOperation {
            client:    client,
            index:     index,
            doc_types: doc_types
        }
    }

    pub fn send(&'b mut self) -> Result<MappingResult, EsError> {
        let mut mappings: Mapping = HashMap::new();
        for (type_name, mapping) in self.doc_types.into_iter() {
            let doc_type = hashmap! { "properties" => mapping.clone() };
            mappings.insert(type_name, doc_type.to_owned());
        }

        let body = hashmap! { "mappings" => mappings };

        let url    = format!("{}", self.index);
        // let (_, _) = try!(self.client.put_body_op(&url, &body));
        // Ok(MappingResult)
        unimplemented!()
    }
}

/// The result of a mapping operation
#[derive(Debug)]
pub struct MappingResult;

#[cfg(test)]
pub mod tests {
    extern crate env_logger;

    use std::collections::HashMap;

    use super::MappingOperation;

    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Author {
        pub name: String
    }

    // #[test]
    // fn test_mapping() {
    //     let index_name = "tests_test_mapping";
    //     let mut client = ::tests::make_client();

    //     // TODO - this fails in many cases (specifically on TravisCI), but we ignore the
    //     // failures anyway
    //     client.delete_index(index_name);

    //     let mapping = hashmap! { // DocTypes
    //         "post" => hashmap! { // DocType
    //             "created_at" => hashmap! {
    //                 "type" => "date",
    //                 "format" => "date_time"
    //             },

    //             "title" => hashmap! {
    //                 "type" => "string",
    //                 "index" => "not_analyzed"
    //             }
    //         },

    //         "author" => hashmap! { // DocType
    //             "name" => hashmap! {
    //                 "type" => "string",
    //             }
    //         },
    //     };

    //     let result = MappingOperation::new(&mut client, index_name, &mapping).send();
    //     assert!(result.is_ok());

    //     {
    //         let result_wrapped = client
    //             .index(index_name, "post")
    //             .with_doc(&Author { name: "Homu".to_owned() })
    //             .send();

    //         assert!(result_wrapped.is_ok());

    //         let result = result_wrapped.unwrap();
    //         assert!(result.created);
    //     }
    // }
}
<commit_msg>Index mapping operation<commit_after>/*
 * Copyright 2016 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of ElasticSearch Mapping operation

//!
//! Please note: this will grow and become a full implementation of the ElasticSearch
//! [Indices API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html)
//! so subtle (potentially breaking) changes will be made to the API when that happens

use std::collections::HashMap;

use ::Client;
use ::error::EsError;

pub type DocType<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
pub type DocTypes<'a> = HashMap<&'a str, DocType<'a>>;
pub type Mapping<'a> = HashMap<&'a str, DocTypes<'a>>;

/// An indexing operation
pub struct MappingOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client:    &'a mut Client,

    /// The index that will be created and eventually mapped
    index:     &'b str,

    /// A map containing the doc types and their type mapping
    doc_types: &'b DocTypes<'b>
}

impl<'a, 'b> MappingOperation<'a, 'b> {
    pub fn new(client: &'a mut Client,
               index: &'b str,
               doc_types: &'b DocTypes) -> MappingOperation<'a, 'b> {
        MappingOperation {
            client:    client,
            index:     index,
            doc_types: doc_types
        }
    }

    pub fn send(&'b mut self) -> Result<MappingResult, EsError> {
        let mut mappings: Mapping = HashMap::new();
        for (type_name, mapping) in self.doc_types.into_iter() {
            let doc_type = hashmap! { "properties" => mapping.clone() };
            mappings.insert(type_name, doc_type.to_owned());
        }

        let body = hashmap! { "mappings" => mappings };

        let url = format!("{}", self.index);
        // TODO - actually check the response
        let response = try!(self.client.put_body_op(&url, &body));
        Ok(MappingResult)
    }
}

/// The result of a mapping operation
#[derive(Debug)]
pub struct MappingResult;

#[cfg(test)]
pub mod tests {
    extern crate env_logger;

    use std::collections::HashMap;

    use super::MappingOperation;

    #[derive(Debug, RustcDecodable, RustcEncodable, Serialize)]
    pub struct Author {
        pub name: String
    }

    #[test]
    fn test_mapping() {
        let index_name = "tests_test_mapping";
        let mut client = ::tests::make_client();

        // TODO - this fails in many cases (specifically on TravisCI), but we ignore the
        // failures anyway
        client.delete_index(index_name);

        let mapping = hashmap! { // DocTypes
            "post" => hashmap! { // DocType
                "created_at" => hashmap! {
                    "type" => "date",
                    "format" => "date_time"
                },

                "title" => hashmap! {
                    "type" => "string",
                    "index" => "not_analyzed"
                }
            },

            "author" => hashmap! { // DocType
                "name" => hashmap! {
                    "type" => "string",
                }
            },
        };

        // TODO add appropriate functions to the `Client` struct
        let result = MappingOperation::new(&mut client, index_name, &mapping).send();
        assert!(result.is_ok());

        {
            let result_wrapped = client
                .index(index_name, "post")
                .with_doc(&Author { name: "Homu".to_owned() })
                .send();

            assert!(result_wrapped.is_ok());

            let result = result_wrapped.unwrap();
            assert!(result.created);
        }
    }
}
<|endoftext|>


<commit_before>// This is a test that the `#![feature(nll)]` opt-in overrides the
// migration mode. The intention here is to emulate the goal behavior
// that `--edition 2018` effects on borrowck (modeled here by `-Z
// borrowck=migrate`) are themselves overridden by the
// `#![feature(nll)]` opt-in.
//
// Therefore, for developer convenience, under `#[feature(nll)]` the
// NLL checks will be emitted as errors *even* in the presence of `-Z
// borrowck=migrate`.

// revisions: zflag edition
// [zflag]compile-flags: -Z borrowck=migrate
// [edition]edition:2018

#![feature(nll)]

fn main() {
    match Some(&4) {
        None => {},
        ref mut foo
            if {
                (|| { let bar = foo; bar.take() })();
                //[zflag]~^ ERROR cannot move out of `foo` in pattern guard [E0507]
                //[edition]~^^ ERROR cannot move out of `foo` in pattern guard [E0507]
                false
            } => {},
        Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
        _ => println!("Here is some supposedly unreachable code."),
    }
}
<commit_msg>Fix revision annotations in borrowck-feature-nll-overrides-migrate<commit_after>// This is a test that the `#![feature(nll)]` opt-in overrides the
// migration mode. The intention here is to emulate the goal behavior
// that `--edition 2018` effects on borrowck (modeled here by `-Z
// borrowck=migrate`) are themselves overridden by the
// `#![feature(nll)]` opt-in.
//
// Therefore, for developer convenience, under `#[feature(nll)]` the
// NLL checks will be emitted as errors *even* in the presence of `-Z
// borrowck=migrate`.

// revisions: zflag edition
//[zflag]compile-flags: -Z borrowck=migrate
//[edition]edition:2018

#![feature(nll)]

fn main() {
    match Some(&4) {
        None => {},
        ref mut foo
            if {
                (|| { let bar = foo; bar.take() })();
                //[zflag]~^ ERROR cannot move out of `foo` in pattern guard [E0507]
                //[edition]~^^ ERROR cannot move out of `foo` in pattern guard [E0507]
                false
            } => {},
        Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
        _ => println!("Here is some supposedly unreachable code."),
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro2;
extern crate syn;

use std::str::FromStr;

use proc_macro2::TokenStream;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let code = "..100u32";
    let tt = TokenStream::from_str(code).unwrap();
    let expr: Expr = syn::parse2(tt.clone()).unwrap();
    let expr_range: ExprRange = syn::parse2(tt).unwrap();
    assert_eq!(expr, Expr::Range(expr_range));
}
<commit_msg>Add snapshot test for parsing await expr<commit_after>extern crate proc_macro2;
extern crate syn;

#[macro_use]
mod macros;

use std::str::FromStr;

use proc_macro2::TokenStream;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let code = "..100u32";
    let tt = TokenStream::from_str(code).unwrap();
    let expr: Expr = syn::parse2(tt.clone()).unwrap();
    let expr_range: ExprRange = syn::parse2(tt).unwrap();
    assert_eq!(expr, Expr::Range(expr_range));
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let expr = syn::parse_str::<Expr>("fut.await").unwrap();

    snapshot!(expr, @r###"
   ⋮Expr::Await {
   ⋮    base: Expr::Path {
   ⋮        path: Path {
   ⋮            segments: [
   ⋮                PathSegment {
   ⋮                    ident: "fut",
   ⋮                    arguments: None,
   ⋮                },
   ⋮            ],
   ⋮        },
   ⋮    },
   ⋮}
    "###);
}
<|endoftext|>


<commit_before><commit_msg>remove redundant Ok(...?)<commit_after><|endoftext|>


<commit_before>use std::io::Write;
use std::io;

use regex::Regex;

use pombase_rc_string::RcString;

use crate::web::config::*;
use crate::data_types::*;

pub const GO_ASPECT_NAMES: [&str; 3] =
    ["cellular_component", "biological_process", "molecular_function"];

fn eco_evidence_from_annotation(mapping: &GoEcoMapping,
                                annotation: &OntAnnotationDetail)
                                -> String
{
    if let Some(ref go_evidence) = annotation.evidence {
        if let Some(ref reference) = annotation.reference {
            mapping.lookup_with_go_ref(go_evidence, reference)
                .unwrap_or_else(|| mapping.lookup_default(go_evidence)
                                .unwrap_or_else(|| panic!("failed to find {} for {} in GO mapping",
                                                          go_evidence, reference)))
        } else {
            mapping.lookup_default(go_evidence)
                .unwrap_or_else(|| panic!("failed to find {} in GO mapping",
                                          go_evidence))
        }
    } else {
        String::from("")
    }
}

fn get_gpad_relation_of(term_details: &TermDetails,
                        annotation_detail: &OntAnnotationDetail) -> String {
    match term_details.cv_name.as_str() {
        "molecular_function" => {
            if annotation_detail.qualifiers.iter()
                .find(|q| q.as_str() == "contributes_to").is_some()
            {
                String::from("RO:0002326")
            } else {
                String::from("RO:0002327")
            }
        },
        "biological_process" => String::from("RO:0002331"),
        "cellular_component" => {
            if term_details.interesting_parent_details.iter()
                .any(|p| p.termid == RcString::from("GO:0032991"))
            {
                String::from("BFO:0000050")
            } else {
                String::from("RO:0001025")
            }
        },
        _ => panic!("unknown cv_name in GPAD export: {}", term_details.cv_name)
    }
}

fn make_gpad_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let rel_mapping = &config.file_exports.gpad_gpi.extension_relation_mappings;
    let get_rel_termid = |ext_part: &ExtPart| {
        if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
            map_termid.clone().unwrap()
        } else {
            ext_part.rel_type_id.clone().unwrap()
        }
    };

    let get_range = |ext_part: &ExtPart| {
        let mut range_copy = ext_part.ext_range.clone();

        if let ExtRange::Gene(ref mut gene_uniquename) = range_copy {
            if !gene_uniquename.contains(":") {
                let new_uniquename =
                    RcString::from(&format!("{}:{}", config.database_name,
                                            gene_uniquename));
                *gene_uniquename = new_uniquename;
            }
        }
        range_copy
    };

    extension.iter()
        .filter(|ext_part| {
            if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
                map_termid.is_some()
            } else {
                ext_part.rel_type_id.is_some()
            }
        })
        .map(|ext_part| format!("{}({})", get_rel_termid(ext_part),
                                get_range(ext_part)))
        .collect::<Vec<_>>().join(",")
}

pub fn write_gene_product_annotation(gpad_writer: &mut dyn Write,
                                     go_eco_mappping: &GoEcoMapping, config: &Config,
                                     api_maps: &APIMaps, gene_details: &GeneDetails)
                                     -> Result<(), io::Error>
{
    let database_name = &config.database_name;
    let db_object_id = format!("{}:{}", database_name, gene_details.uniquename);

    for (cv_name, term_annotations) in &gene_details.cv_annotations {
        if !GO_ASPECT_NAMES.contains(&cv_name.as_ref()) {
            continue;
        }

        for term_annotation in term_annotations {
            let term = api_maps.terms.get(&term_annotation.term)
                .expect(&format!("failed to find term summary for {}",
                                 term_annotation.term));

            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));
                let not = if term_annotation.is_not {
                    "NOT"
                } else {
                    ""
                };

                let relation = get_gpad_relation_of(term, annotation_detail);

                let ontology_class_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let evidence_type =
                    eco_evidence_from_annotation(go_eco_mappping, annotation_detail);
                let assigned_by = &config.database_name;

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let date = annotation_detail.date.clone()
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gpad_extension_string(config, &annotation_detail.extension);
                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}\t{}\t{}\t\n",
                                   db_object_id,
                                   not, relation, ontology_class_id,
                                   reference_uniquename, evidence_type,
                                   with_or_from, date, assigned_by,
                                   annotation_extensions);
                gpad_writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

fn make_gaf_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let get_range = |ext_part: &ExtPart| {
        let mut range_copy = ext_part.ext_range.clone();

        if let ExtRange::Gene(ref mut gene_uniquename) = range_copy {
            if !gene_uniquename.contains(":") {
                let new_uniquename =
                    RcString::from(&format!("{}:{}", config.database_name,
                                            gene_uniquename));
                *gene_uniquename = new_uniquename;
            }
        }
        range_copy
    };

    extension.iter()
        .map(|ext_part| format!("{}({})", ext_part.rel_type_name,
                                get_range(ext_part)))
        .collect::<Vec<_>>().join(",")
}

pub fn write_go_annotation_format(writer: &mut dyn Write, config: &Config,
                                  api_maps: &APIMaps, gene_details: &GeneDetails,
                                  cv_name: &str)
                                  -> Result<(), io::Error>
{
    if !GO_ASPECT_NAMES.contains(&cv_name) {
        return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                  format!("unknown CV: {}", cv_name)));
    }

    let database_name = &config.database_name;
    let db_object_id = &gene_details.uniquename;
    let db_object_symbol =
        if let Some(ref gene_name) = gene_details.name {
            gene_name.as_str()
        } else {
            &db_object_id
        };
    let db_object_synonyms =
        gene_details.synonyms.iter().filter(|synonym| {
            synonym.synonym_type == "exact"
        })
        .map(|synonym| synonym.name.to_string())
        .collect::<Vec<String>>()
        .join("|");

    let single_letter_re = Regex::new(r"\w+_(?P<letter>\w)\w+").unwrap();

    let single_letter_aspect =
        if let Some(captures) = single_letter_re.captures(&cv_name) {
            captures["letter"].to_uppercase()
        } else {
            panic!("{} doesn't look like a GO aspect name", cv_name);
        };

    if let Some(term_annotations) = gene_details.cv_annotations.get(cv_name) {
        for term_annotation in term_annotations {
            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));

                let mut qualifier_parts = vec![];
                if term_annotation.is_not {
                    qualifier_parts.push("NOT");
                };

                qualifier_parts.extend(annotation_detail.qualifiers.iter()
                                       .map(|s| s.as_str()));

                let go_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let assigned_by =
                    if let Some(ref assigned_by) = annotation_detail.assigned_by {
                        assigned_by.as_str()
                    } else {
                        database_name.as_str()
                    };

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let evidence_code =
                    annotation_detail.evidence
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| "");

                let db_object_name =
                    if let Some(ref product) = gene_details.product {
                        product.as_str()
                    } else {
                        ""
                    };

                let transcript_type_str =
                    gene_details.transcripts[0].transcript_type.as_str();
                let db_object_type =
                    if transcript_type_str == "mRNA" {
                        "protein"
                    } else {
                        transcript_type_str
                    };
                let date = annotation_detail.date.clone()
                    .map(|d| d.replace("-", ""))
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gaf_extension_string(config, &annotation_detail.extension);

                let gene_product_form_id =
                    if let Some(ref gene_product_form_id) = annotation_detail.gene_product_form_id
                {
                    gene_product_form_id.as_str()
                } else {
                    ""
                };

                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\ttaxon:{}\t{}\t{}\t{}\t{}\n",
                                   database_name,
                                   db_object_id,
                                   db_object_symbol,
                                   qualifier_parts.join("|"),
                                   go_id,
                                   reference_uniquename,
                                   evidence_code,
                                   with_or_from,
                                   single_letter_aspect,
                                   db_object_name,
                                   db_object_synonyms,
                                   db_object_type,
                                   gene_details.taxonid,
                                   date,
                                   assigned_by,
                                   annotation_extensions,
                                   gene_product_form_id);
                writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

<commit_msg>Fix slow GAF creation<commit_after>use std::io;

use std::fmt::Write;

use pombase_rc_string::RcString;

use crate::web::config::*;
use crate::data_types::*;

pub const GO_ASPECT_NAMES: [&str; 3] =
    ["cellular_component", "biological_process", "molecular_function"];

fn eco_evidence_from_annotation(mapping: &GoEcoMapping,
                                annotation: &OntAnnotationDetail)
                                -> String
{
    if let Some(ref go_evidence) = annotation.evidence {
        if let Some(ref reference) = annotation.reference {
            mapping.lookup_with_go_ref(go_evidence, reference)
                .unwrap_or_else(|| mapping.lookup_default(go_evidence)
                                .unwrap_or_else(|| panic!("failed to find {} for {} in GO mapping",
                                                          go_evidence, reference)))
        } else {
            mapping.lookup_default(go_evidence)
                .unwrap_or_else(|| panic!("failed to find {} in GO mapping",
                                          go_evidence))
        }
    } else {
        String::from("")
    }
}

fn get_gpad_relation_of(term_details: &TermDetails,
                        annotation_detail: &OntAnnotationDetail) -> String {
    match term_details.cv_name.as_str() {
        "molecular_function" => {
            if annotation_detail.qualifiers.iter()
                .find(|q| q.as_str() == "contributes_to").is_some()
            {
                String::from("RO:0002326")
            } else {
                String::from("RO:0002327")
            }
        },
        "biological_process" => String::from("RO:0002331"),
        "cellular_component" => {
            if term_details.interesting_parent_details.iter()
                .any(|p| p.termid == RcString::from("GO:0032991"))
            {
                String::from("BFO:0000050")
            } else {
                String::from("RO:0001025")
            }
        },
        _ => panic!("unknown cv_name in GPAD export: {}", term_details.cv_name)
    }
}

fn make_gpad_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let rel_mapping = &config.file_exports.gpad_gpi.extension_relation_mappings;
    let get_rel_termid = |ext_part: &ExtPart| {
        if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
            map_termid.clone().unwrap()
        } else {
            ext_part.rel_type_id.clone().unwrap()
        }
    };

    let get_range = |ext_part: &ExtPart| {
        let mut range_copy = ext_part.ext_range.clone();

        if let ExtRange::Gene(ref mut gene_uniquename) = range_copy {
            if !gene_uniquename.contains(":") {
                let new_uniquename =
                    RcString::from(&format!("{}:{}", config.database_name,
                                            gene_uniquename));
                *gene_uniquename = new_uniquename;
            }
        }
        range_copy
    };

    extension.iter()
        .filter(|ext_part| {
            if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
                map_termid.is_some()
            } else {
                ext_part.rel_type_id.is_some()
            }
        })
        .map(|ext_part| format!("{}({})", get_rel_termid(ext_part),
                                get_range(ext_part)))
        .collect::<Vec<_>>().join(",")
}

pub fn write_gene_product_annotation(gpad_writer: &mut dyn io::Write,
                                     go_eco_mappping: &GoEcoMapping, config: &Config,
                                     api_maps: &APIMaps, gene_details: &GeneDetails)
                                     -> Result<(), io::Error>
{
    let database_name = &config.database_name;
    let db_object_id = format!("{}:{}", database_name, gene_details.uniquename);

    for (cv_name, term_annotations) in &gene_details.cv_annotations {
        if !GO_ASPECT_NAMES.contains(&cv_name.as_ref()) {
            continue;
        }

        for term_annotation in term_annotations {
            let term = api_maps.terms.get(&term_annotation.term)
                .expect(&format!("failed to find term summary for {}",
                                 term_annotation.term));

            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));
                let not = if term_annotation.is_not {
                    "NOT"
                } else {
                    ""
                };

                let relation = get_gpad_relation_of(term, annotation_detail);

                let ontology_class_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let evidence_type =
                    eco_evidence_from_annotation(go_eco_mappping, annotation_detail);
                let assigned_by = &config.database_name;

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let date = annotation_detail.date.clone()
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gpad_extension_string(config, &annotation_detail.extension);
                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}\t{}\t{}\t\n",
                                   db_object_id,
                                   not, relation, ontology_class_id,
                                   reference_uniquename, evidence_type,
                                   with_or_from, date, assigned_by,
                                   annotation_extensions);
                gpad_writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

fn make_gaf_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let mut ret_string = String::new();

    for (idx, ext_part) in extension.iter().enumerate() {
        ret_string += &ext_part.rel_type_name;
        ret_string.push('(');

        if let ExtRange::Gene(ref gene_uniquename) = ext_part.ext_range {
            if gene_uniquename.contains(":") {
                ret_string += gene_uniquename;
            } else {
                ret_string += &config.database_name;
                ret_string.push(':');
                ret_string += &gene_uniquename;
            }
        } else {
            write!(ret_string, "{}", &ext_part.ext_range).unwrap();
        }
        ret_string.push(')');

        if idx < extension.len() - 1 {
            ret_string.push(',');
        }
    }

    ret_string
}

pub fn write_go_annotation_format(writer: &mut dyn io::Write, config: &Config,
                                  api_maps: &APIMaps, gene_details: &GeneDetails,
                                  cv_name: &str)
                                  -> Result<(), io::Error>
{
    if !GO_ASPECT_NAMES.contains(&cv_name) {
        return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                  format!("unknown CV: {}", cv_name)));
    }

    let database_name = &config.database_name;
    let db_object_id = &gene_details.uniquename;
    let db_object_symbol =
        if let Some(ref gene_name) = gene_details.name {
            gene_name.as_str()
        } else {
            &db_object_id
        };
    let db_object_synonyms =
        gene_details.synonyms.iter().filter(|synonym| {
            synonym.synonym_type == "exact"
        })
        .map(|synonym| synonym.name.to_string())
        .collect::<Vec<String>>()
        .join("|");

    let single_letter_aspect =
        if cv_name.starts_with("b") {
            "P"
        } else {
            if cv_name.starts_with("c") {
                "C"
            } else {
                "F"
            }
        };

    if let Some(term_annotations) = gene_details.cv_annotations.get(cv_name) {
        for term_annotation in term_annotations {
            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));

                let mut qualifier_parts = vec![];
                if term_annotation.is_not {
                    qualifier_parts.push("NOT");
                };

                qualifier_parts.extend(annotation_detail.qualifiers.iter()
                                       .map(|s| s.as_str()));

                let go_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let assigned_by =
                    if let Some(ref assigned_by) = annotation_detail.assigned_by {
                        assigned_by.as_str()
                    } else {
                        database_name.as_str()
                    };

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let evidence_code =
                    annotation_detail.evidence
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| "");

                let db_object_name =
                    if let Some(ref product) = gene_details.product {
                        product.as_str()
                    } else {
                        ""
                    };

                let transcript_type_str =
                    gene_details.transcripts[0].transcript_type.as_str();
                let db_object_type =
                    if transcript_type_str == "mRNA" {
                        "protein"
                    } else {
                        transcript_type_str
                    };
                let date = annotation_detail.date.clone()
                    .map(|d| d.replace("-", ""))
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gaf_extension_string(config, &annotation_detail.extension);

                let gene_product_form_id =
                    if let Some(ref gene_product_form_id) = annotation_detail.gene_product_form_id
                {
                    gene_product_form_id.as_str()
                } else {
                    ""
                };

                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\ttaxon:{}\t{}\t{}\t{}\t{}\n",
                                   database_name,
                                   db_object_id,
                                   db_object_symbol,
                                   qualifier_parts.join("|"),
                                   go_id,
                                   reference_uniquename,
                                   evidence_code,
                                   with_or_from,
                                   single_letter_aspect,
                                   db_object_name,
                                   db_object_synonyms,
                                   db_object_type,
                                   gene_details.taxonid,
                                   date,
                                   assigned_by,
                                   annotation_extensions,
                                   gene_product_form_id);
                writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

<|endoftext|>


<commit_before><commit_msg>util/graph/visit: don't visit previously-visited nodes!<commit_after><|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use crate::formatter::enums::{FormatterFlowControl, PrefixKind};
use crate::formatter::enums_shared::FormatterTextKind;
use crate::formatter::fmt_utils_all::{show_rep_or_repe_prefix_bool, show_repne_prefix_bool, show_segment_prefix_bool};
use crate::formatter::{FormatterOptions, FormatterOutput};
use crate::{Code, Instruction, Register};
use alloc::string::String;
use alloc::vec::Vec;
use core::{cmp, mem};
use lazy_static::lazy_static;
use static_assertions::const_assert_eq;

lazy_static! {
	static ref SPACES_TABLE: Vec<String> = create_strings(' ', 20);
}
lazy_static! {
	static ref TABS_TABLE: Vec<String> = create_strings('\t', 6);
}

fn create_strings(c: char, cap: usize) -> Vec<String> {
	let mut v = Vec::with_capacity(cap);
	let mut s = String::with_capacity(cap);
	for _ in 0..cap - 1 {
		s.push(c);
		v.push(s.clone());
	}
	s.push(c);
	debug_assert_eq!(s.len(), cap);
	v.push(s);
	v
}

pub(super) fn add_tabs(output: &mut dyn FormatterOutput, mut column: u32, mut first_operand_char_index: u32, tab_size: u32) {
	const MAX_FIRST_OPERAND_CHAR_INDEX: u32 = 256;
	first_operand_char_index = cmp::min(first_operand_char_index, MAX_FIRST_OPERAND_CHAR_INDEX);

	if tab_size == 0 {
		let chars_left = if first_operand_char_index <= column { 1 } else { first_operand_char_index - column };
		add_strings(output, &*SPACES_TABLE, chars_left);
	} else {
		let end_col = if first_operand_char_index <= column { column + 1 } else { first_operand_char_index };
		let end_col_rounded_down = end_col / tab_size * tab_size;
		let added_tabs = end_col_rounded_down > column;
		if added_tabs {
			let tabs = (end_col_rounded_down - (column / tab_size * tab_size)) / tab_size;
			add_strings(output, &*TABS_TABLE, tabs);
			column = end_col_rounded_down;
		}
		if first_operand_char_index > column {
			add_strings(output, &*SPACES_TABLE, first_operand_char_index - column);
		} else if !added_tabs {
			add_strings(output, &*SPACES_TABLE, 1);
		}
	}
}

fn add_strings(output: &mut dyn FormatterOutput, strings: &[String], count: u32) {
	let mut count = count as usize;
	while count > 0 {
		let n = cmp::min(count, strings.len());
		output.write(&strings[n - 1], FormatterTextKind::Text);
		count -= n;
	}
}

#[must_use]
#[inline]
pub(super) fn is_call(kind: FormatterFlowControl) -> bool {
	kind == FormatterFlowControl::NearCall || kind == FormatterFlowControl::FarCall
}

#[must_use]
pub(super) fn get_flow_control(instruction: &Instruction) -> FormatterFlowControl {
	#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
	match instruction.code() {
		// GENERATOR-BEGIN: FormatterFlowControlSwitch
		// ⚠This was generated by GENERATOR!🦹♂
		Code::Jo_rel8_16
		| Code::Jo_rel8_32
		| Code::Jo_rel8_64
		| Code::Jno_rel8_16
		| Code::Jno_rel8_32
		| Code::Jno_rel8_64
		| Code::Jb_rel8_16
		| Code::Jb_rel8_32
		| Code::Jb_rel8_64
		| Code::Jae_rel8_16
		| Code::Jae_rel8_32
		| Code::Jae_rel8_64
		| Code::Je_rel8_16
		| Code::Je_rel8_32
		| Code::Je_rel8_64
		| Code::Jne_rel8_16
		| Code::Jne_rel8_32
		| Code::Jne_rel8_64
		| Code::Jbe_rel8_16
		| Code::Jbe_rel8_32
		| Code::Jbe_rel8_64
		| Code::Ja_rel8_16
		| Code::Ja_rel8_32
		| Code::Ja_rel8_64
		| Code::Js_rel8_16
		| Code::Js_rel8_32
		| Code::Js_rel8_64
		| Code::Jns_rel8_16
		| Code::Jns_rel8_32
		| Code::Jns_rel8_64
		| Code::Jp_rel8_16
		| Code::Jp_rel8_32
		| Code::Jp_rel8_64
		| Code::Jnp_rel8_16
		| Code::Jnp_rel8_32
		| Code::Jnp_rel8_64
		| Code::Jl_rel8_16
		| Code::Jl_rel8_32
		| Code::Jl_rel8_64
		| Code::Jge_rel8_16
		| Code::Jge_rel8_32
		| Code::Jge_rel8_64
		| Code::Jle_rel8_16
		| Code::Jle_rel8_32
		| Code::Jle_rel8_64
		| Code::Jg_rel8_16
		| Code::Jg_rel8_32
		| Code::Jg_rel8_64
		| Code::Jmp_rel8_16
		| Code::Jmp_rel8_32
		| Code::Jmp_rel8_64
		=> FormatterFlowControl::ShortBranch,
		Code::Loopne_rel8_16_CX
		| Code::Loopne_rel8_32_CX
		| Code::Loopne_rel8_16_ECX
		| Code::Loopne_rel8_32_ECX
		| Code::Loopne_rel8_64_ECX
		| Code::Loopne_rel8_16_RCX
		| Code::Loopne_rel8_64_RCX
		| Code::Loope_rel8_16_CX
		| Code::Loope_rel8_32_CX
		| Code::Loope_rel8_16_ECX
		| Code::Loope_rel8_32_ECX
		| Code::Loope_rel8_64_ECX
		| Code::Loope_rel8_16_RCX
		| Code::Loope_rel8_64_RCX
		| Code::Loop_rel8_16_CX
		| Code::Loop_rel8_32_CX
		| Code::Loop_rel8_16_ECX
		| Code::Loop_rel8_32_ECX
		| Code::Loop_rel8_64_ECX
		| Code::Loop_rel8_16_RCX
		| Code::Loop_rel8_64_RCX
		| Code::Jcxz_rel8_16
		| Code::Jcxz_rel8_32
		| Code::Jecxz_rel8_16
		| Code::Jecxz_rel8_32
		| Code::Jecxz_rel8_64
		| Code::Jrcxz_rel8_16
		| Code::Jrcxz_rel8_64
		=> FormatterFlowControl::AlwaysShortBranch,
		Code::Call_rel16
		| Code::Call_rel32_32
		| Code::Call_rel32_64
		=> FormatterFlowControl::NearCall,
		Code::Jmp_rel16
		| Code::Jmp_rel32_32
		| Code::Jmp_rel32_64
		| Code::Jo_rel16
		| Code::Jo_rel32_32
		| Code::Jo_rel32_64
		| Code::Jno_rel16
		| Code::Jno_rel32_32
		| Code::Jno_rel32_64
		| Code::Jb_rel16
		| Code::Jb_rel32_32
		| Code::Jb_rel32_64
		| Code::Jae_rel16
		| Code::Jae_rel32_32
		| Code::Jae_rel32_64
		| Code::Je_rel16
		| Code::Je_rel32_32
		| Code::Je_rel32_64
		| Code::Jne_rel16
		| Code::Jne_rel32_32
		| Code::Jne_rel32_64
		| Code::Jbe_rel16
		| Code::Jbe_rel32_32
		| Code::Jbe_rel32_64
		| Code::Ja_rel16
		| Code::Ja_rel32_32
		| Code::Ja_rel32_64
		| Code::Js_rel16
		| Code::Js_rel32_32
		| Code::Js_rel32_64
		| Code::Jns_rel16
		| Code::Jns_rel32_32
		| Code::Jns_rel32_64
		| Code::Jp_rel16
		| Code::Jp_rel32_32
		| Code::Jp_rel32_64
		| Code::Jnp_rel16
		| Code::Jnp_rel32_32
		| Code::Jnp_rel32_64
		| Code::Jl_rel16
		| Code::Jl_rel32_32
		| Code::Jl_rel32_64
		| Code::Jge_rel16
		| Code::Jge_rel32_32
		| Code::Jge_rel32_64
		| Code::Jle_rel16
		| Code::Jle_rel32_32
		| Code::Jle_rel32_64
		| Code::Jg_rel16
		| Code::Jg_rel32_32
		| Code::Jg_rel32_64
		| Code::Jmpe_disp16
		| Code::Jmpe_disp32
		=> FormatterFlowControl::NearBranch,
		Code::Call_ptr1616
		| Code::Call_ptr1632
		=> FormatterFlowControl::FarCall,
		Code::Jmp_ptr1616
		| Code::Jmp_ptr1632
		=> FormatterFlowControl::FarBranch,
		Code::Xbegin_rel16
		| Code::Xbegin_rel32
		=> FormatterFlowControl::Xbegin,
		// GENERATOR-END: FormatterFlowControlSwitch
		_ => unreachable!(),
	}
}

pub(super) fn show_rep_or_repe_prefix(code: Code, options: &FormatterOptions) -> bool {
	show_rep_or_repe_prefix_bool(code, options.show_useless_prefixes())
}

pub(super) fn show_repne_prefix(code: Code, options: &FormatterOptions) -> bool {
	show_repne_prefix_bool(code, options.show_useless_prefixes())
}

#[must_use]
#[inline]
pub(super) fn get_segment_register_prefix_kind(register: Register) -> PrefixKind {
	debug_assert!(
		register == Register::ES
			|| register == Register::CS
			|| register == Register::SS
			|| register == Register::DS
			|| register == Register::FS
			|| register == Register::GS
	);
	const_assert_eq!(PrefixKind::ES as u32 + 1, PrefixKind::CS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 2, PrefixKind::SS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 3, PrefixKind::DS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 4, PrefixKind::FS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 5, PrefixKind::GS as u32);
	// SAFETY: callers only pass in a valid segment register (ES,CS,SS,DS,FS,GS)
	unsafe { mem::transmute(((register as u32 - Register::ES as u32) + PrefixKind::ES as u32) as u8) }
}

pub(super) fn show_index_scale(instruction: &Instruction, options: &FormatterOptions) -> bool {
	options.show_useless_prefixes() || !instruction.code().ignores_index()
}

pub(super) fn show_segment_prefix(default_seg_reg: Register, instruction: &Instruction, options: &FormatterOptions) -> bool {
	show_segment_prefix_bool(default_seg_reg, instruction, options.show_useless_prefixes())
}

#[allow(unused_variables)]
pub(super) fn can_show_rounding_control(instruction: &Instruction, options: &FormatterOptions) -> bool {
	#[cfg(not(feature = "no_evex"))]
	{
		let code = instruction.code();
		if code == Code::EVEX_Vcvtsi2sd_xmm_xmm_rm32_er
			|| code == Code::EVEX_Vcvtusi2sd_xmm_xmm_rm32_er
			|| code == Code::EVEX_Vcvtdq2pd_zmm_k1z_ymmm256b32_er
			|| code == Code::EVEX_Vcvtudq2pd_zmm_k1z_ymmm256b32_er
		{
			return options.show_useless_prefixes();
		}
	}
	true
}
<commit_msg>Remove 2 lazy_static!() macros<commit_after>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use crate::formatter::enums::{FormatterFlowControl, PrefixKind};
use crate::formatter::enums_shared::FormatterTextKind;
use crate::formatter::fmt_utils_all::{show_rep_or_repe_prefix_bool, show_repne_prefix_bool, show_segment_prefix_bool};
use crate::formatter::{FormatterOptions, FormatterOutput};
use crate::{Code, Instruction, Register};
use core::{cmp, mem};
use static_assertions::const_assert_eq;

#[rustfmt::skip]
static SPACES_TABLE: [&str; 12] = [
	" ",
	"  ",
	"   ",
	"    ",
	"     ",
	"      ",
	"       ",
	"        ",
	"         ",
	"          ",
	"           ",
	"            ",
];
#[rustfmt::skip]
static TABS_TABLE: [&str; 4] = [
	"\t",
	"\t\t",
	"\t\t\t",
	"\t\t\t\t",
];

pub(super) fn add_tabs(output: &mut dyn FormatterOutput, mut column: u32, mut first_operand_char_index: u32, tab_size: u32) {
	const MAX_FIRST_OPERAND_CHAR_INDEX: u32 = 256;
	first_operand_char_index = cmp::min(first_operand_char_index, MAX_FIRST_OPERAND_CHAR_INDEX);

	if tab_size == 0 {
		let chars_left = if first_operand_char_index <= column { 1 } else { first_operand_char_index - column };
		add_strings(output, &SPACES_TABLE[..], chars_left);
	} else {
		let end_col = if first_operand_char_index <= column { column + 1 } else { first_operand_char_index };
		let end_col_rounded_down = end_col / tab_size * tab_size;
		let added_tabs = end_col_rounded_down > column;
		if added_tabs {
			let tabs = (end_col_rounded_down - (column / tab_size * tab_size)) / tab_size;
			add_strings(output, &TABS_TABLE[..], tabs);
			column = end_col_rounded_down;
		}
		if first_operand_char_index > column {
			add_strings(output, &SPACES_TABLE[..], first_operand_char_index - column);
		} else if !added_tabs {
			add_strings(output, &SPACES_TABLE[..], 1);
		}
	}
}

fn add_strings(output: &mut dyn FormatterOutput, strings: &[&str], count: u32) {
	let mut count = count as usize;
	while count > 0 {
		let n = cmp::min(count, strings.len());
		output.write(strings[n - 1], FormatterTextKind::Text);
		count -= n;
	}
}

#[must_use]
#[inline]
pub(super) fn is_call(kind: FormatterFlowControl) -> bool {
	kind == FormatterFlowControl::NearCall || kind == FormatterFlowControl::FarCall
}

#[must_use]
pub(super) fn get_flow_control(instruction: &Instruction) -> FormatterFlowControl {
	#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
	match instruction.code() {
		// GENERATOR-BEGIN: FormatterFlowControlSwitch
		// ⚠This was generated by GENERATOR!🦹♂
		Code::Jo_rel8_16
		| Code::Jo_rel8_32
		| Code::Jo_rel8_64
		| Code::Jno_rel8_16
		| Code::Jno_rel8_32
		| Code::Jno_rel8_64
		| Code::Jb_rel8_16
		| Code::Jb_rel8_32
		| Code::Jb_rel8_64
		| Code::Jae_rel8_16
		| Code::Jae_rel8_32
		| Code::Jae_rel8_64
		| Code::Je_rel8_16
		| Code::Je_rel8_32
		| Code::Je_rel8_64
		| Code::Jne_rel8_16
		| Code::Jne_rel8_32
		| Code::Jne_rel8_64
		| Code::Jbe_rel8_16
		| Code::Jbe_rel8_32
		| Code::Jbe_rel8_64
		| Code::Ja_rel8_16
		| Code::Ja_rel8_32
		| Code::Ja_rel8_64
		| Code::Js_rel8_16
		| Code::Js_rel8_32
		| Code::Js_rel8_64
		| Code::Jns_rel8_16
		| Code::Jns_rel8_32
		| Code::Jns_rel8_64
		| Code::Jp_rel8_16
		| Code::Jp_rel8_32
		| Code::Jp_rel8_64
		| Code::Jnp_rel8_16
		| Code::Jnp_rel8_32
		| Code::Jnp_rel8_64
		| Code::Jl_rel8_16
		| Code::Jl_rel8_32
		| Code::Jl_rel8_64
		| Code::Jge_rel8_16
		| Code::Jge_rel8_32
		| Code::Jge_rel8_64
		| Code::Jle_rel8_16
		| Code::Jle_rel8_32
		| Code::Jle_rel8_64
		| Code::Jg_rel8_16
		| Code::Jg_rel8_32
		| Code::Jg_rel8_64
		| Code::Jmp_rel8_16
		| Code::Jmp_rel8_32
		| Code::Jmp_rel8_64
		=> FormatterFlowControl::ShortBranch,
		Code::Loopne_rel8_16_CX
		| Code::Loopne_rel8_32_CX
		| Code::Loopne_rel8_16_ECX
		| Code::Loopne_rel8_32_ECX
		| Code::Loopne_rel8_64_ECX
		| Code::Loopne_rel8_16_RCX
		| Code::Loopne_rel8_64_RCX
		| Code::Loope_rel8_16_CX
		| Code::Loope_rel8_32_CX
		| Code::Loope_rel8_16_ECX
		| Code::Loope_rel8_32_ECX
		| Code::Loope_rel8_64_ECX
		| Code::Loope_rel8_16_RCX
		| Code::Loope_rel8_64_RCX
		| Code::Loop_rel8_16_CX
		| Code::Loop_rel8_32_CX
		| Code::Loop_rel8_16_ECX
		| Code::Loop_rel8_32_ECX
		| Code::Loop_rel8_64_ECX
		| Code::Loop_rel8_16_RCX
		| Code::Loop_rel8_64_RCX
		| Code::Jcxz_rel8_16
		| Code::Jcxz_rel8_32
		| Code::Jecxz_rel8_16
		| Code::Jecxz_rel8_32
		| Code::Jecxz_rel8_64
		| Code::Jrcxz_rel8_16
		| Code::Jrcxz_rel8_64
		=> FormatterFlowControl::AlwaysShortBranch,
		Code::Call_rel16
		| Code::Call_rel32_32
		| Code::Call_rel32_64
		=> FormatterFlowControl::NearCall,
		Code::Jmp_rel16
		| Code::Jmp_rel32_32
		| Code::Jmp_rel32_64
		| Code::Jo_rel16
		| Code::Jo_rel32_32
		| Code::Jo_rel32_64
		| Code::Jno_rel16
		| Code::Jno_rel32_32
		| Code::Jno_rel32_64
		| Code::Jb_rel16
		| Code::Jb_rel32_32
		| Code::Jb_rel32_64
		| Code::Jae_rel16
		| Code::Jae_rel32_32
		| Code::Jae_rel32_64
		| Code::Je_rel16
		| Code::Je_rel32_32
		| Code::Je_rel32_64
		| Code::Jne_rel16
		| Code::Jne_rel32_32
		| Code::Jne_rel32_64
		| Code::Jbe_rel16
		| Code::Jbe_rel32_32
		| Code::Jbe_rel32_64
		| Code::Ja_rel16
		| Code::Ja_rel32_32
		| Code::Ja_rel32_64
		| Code::Js_rel16
		| Code::Js_rel32_32
		| Code::Js_rel32_64
		| Code::Jns_rel16
		| Code::Jns_rel32_32
		| Code::Jns_rel32_64
		| Code::Jp_rel16
		| Code::Jp_rel32_32
		| Code::Jp_rel32_64
		| Code::Jnp_rel16
		| Code::Jnp_rel32_32
		| Code::Jnp_rel32_64
		| Code::Jl_rel16
		| Code::Jl_rel32_32
		| Code::Jl_rel32_64
		| Code::Jge_rel16
		| Code::Jge_rel32_32
		| Code::Jge_rel32_64
		| Code::Jle_rel16
		| Code::Jle_rel32_32
		| Code::Jle_rel32_64
		| Code::Jg_rel16
		| Code::Jg_rel32_32
		| Code::Jg_rel32_64
		| Code::Jmpe_disp16
		| Code::Jmpe_disp32
		=> FormatterFlowControl::NearBranch,
		Code::Call_ptr1616
		| Code::Call_ptr1632
		=> FormatterFlowControl::FarCall,
		Code::Jmp_ptr1616
		| Code::Jmp_ptr1632
		=> FormatterFlowControl::FarBranch,
		Code::Xbegin_rel16
		| Code::Xbegin_rel32
		=> FormatterFlowControl::Xbegin,
		// GENERATOR-END: FormatterFlowControlSwitch
		_ => unreachable!(),
	}
}

pub(super) fn show_rep_or_repe_prefix(code: Code, options: &FormatterOptions) -> bool {
	show_rep_or_repe_prefix_bool(code, options.show_useless_prefixes())
}

pub(super) fn show_repne_prefix(code: Code, options: &FormatterOptions) -> bool {
	show_repne_prefix_bool(code, options.show_useless_prefixes())
}

#[must_use]
#[inline]
pub(super) fn get_segment_register_prefix_kind(register: Register) -> PrefixKind {
	debug_assert!(
		register == Register::ES
			|| register == Register::CS
			|| register == Register::SS
			|| register == Register::DS
			|| register == Register::FS
			|| register == Register::GS
	);
	const_assert_eq!(PrefixKind::ES as u32 + 1, PrefixKind::CS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 2, PrefixKind::SS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 3, PrefixKind::DS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 4, PrefixKind::FS as u32);
	const_assert_eq!(PrefixKind::ES as u32 + 5, PrefixKind::GS as u32);
	// SAFETY: callers only pass in a valid segment register (ES,CS,SS,DS,FS,GS)
	unsafe { mem::transmute(((register as u32 - Register::ES as u32) + PrefixKind::ES as u32) as u8) }
}

pub(super) fn show_index_scale(instruction: &Instruction, options: &FormatterOptions) -> bool {
	options.show_useless_prefixes() || !instruction.code().ignores_index()
}

pub(super) fn show_segment_prefix(default_seg_reg: Register, instruction: &Instruction, options: &FormatterOptions) -> bool {
	show_segment_prefix_bool(default_seg_reg, instruction, options.show_useless_prefixes())
}

#[allow(unused_variables)]
pub(super) fn can_show_rounding_control(instruction: &Instruction, options: &FormatterOptions) -> bool {
	#[cfg(not(feature = "no_evex"))]
	{
		let code = instruction.code();
		if code == Code::EVEX_Vcvtsi2sd_xmm_xmm_rm32_er
			|| code == Code::EVEX_Vcvtusi2sd_xmm_xmm_rm32_er
			|| code == Code::EVEX_Vcvtdq2pd_zmm_k1z_ymmm256b32_er
			|| code == Code::EVEX_Vcvtudq2pd_zmm_k1z_ymmm256b32_er
		{
			return options.show_useless_prefixes();
		}
	}
	true
}
<|endoftext|>


<commit_before>//! Bazel label parsing library.
//!
//! USAGE: `label::analyze("//foo/bar:baz")
mod label_error;
use label_error::LabelError;

/// Parse and analyze given str.
///
/// TODO: validate . and .. in target name
/// TODO: validate used characters in target name
pub fn analyze(input: &'_ str) -> Result<Label<'_>> {
    let label = input;
    let (input, repository_name) = consume_repository_name(input, label)?;
    let (input, package_name) = consume_package_name(input, label)?;
    let name = consume_name(input, label)?;
    Ok(Label::new(repository_name, package_name, name))
}

#[derive(Debug, PartialEq)]
pub struct Label<'s> {
    pub repository_name: Option<&'s str>,
    pub package_name: Option<&'s str>,
    pub name: &'s str,
}

type Result<T, E = LabelError> = core::result::Result<T, E>;

impl<'s> Label<'s> {
    fn new(
        repository_name: Option<&'s str>,
        package_name: Option<&'s str>,
        name: &'s str,
    ) -> Label<'s> {
        Label {
            repository_name,
            package_name,
            name,
        }
    }

    pub fn packages(&self) -> Vec<&'s str> {
        match self.package_name {
            Some(name) => name.split('/').collect(),
            None => vec![],
        }
    }
}

fn err<'s>(label: &'s str, msg: &'s str) -> String {
    let mut err_msg = label.to_string();
    err_msg.push_str(" must be a legal label; ");
    err_msg.push_str(msg);
    err_msg
}

fn consume_repository_name<'s>(
    input: &'s str,
    label: &'s str,
) -> Result<(&'s str, Option<&'s str>)> {
    if !input.starts_with('@') {
        return Ok((input, None));
    }

    let slash_pos = input
        .find("//")
        .ok_or_else(|| err(label, "labels with repository must contain //."))?;
    let repository_name = &input[1..slash_pos];
    if repository_name.is_empty() {
        return Ok((&input[1..], None));
    }
    if !repository_name
        .chars()
        .next()
        .unwrap()
        .is_ascii_alphabetic()
    {
        return Err(LabelError(err(
            label,
            "workspace names must start with a letter.",
        )));
    }
    if !repository_name
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
    {
        return Err(LabelError(err(
            label,
            "workspace names \
                may contain only A-Z, a-z, 0-9, '-', '_', and '.'.",
        )));
    }
    Ok((&input[slash_pos..], Some(repository_name)))
}

fn consume_package_name<'s>(input: &'s str, label: &'s str) -> Result<(&'s str, Option<&'s str>)> {
    let colon_pos = input.find(':');
    let start_pos;
    let mut is_absolute = false;
    if input.starts_with("//") {
        start_pos = 2;
        is_absolute = true;
    } else {
        start_pos = 0;
        if colon_pos.is_none() {
            if input.contains("//") {
                return Err(LabelError(err(
                    label,
                    "'//' cannot appear in the middle of the label.",
                )));
            }
            return Ok((input, None));
        }
    };

    let (package_name, rest) = match colon_pos {
        Some(colon_pos) => (&input[start_pos..colon_pos], &input[colon_pos..]),
        None => (&input[start_pos..], ""),
    };
    if package_name.is_empty() {
        return Ok((rest, None));
    }
    if package_name.contains("//") {
        return Err(LabelError(err(
            label,
            "'//' cannot appear in the middle of the label.",
        )));
    }

    if !package_name.chars().all(|c| {
        c.is_ascii_alphanumeric()
            || c == '/'
            || c == '-'
            || c == '.'
            || c == ' '
            || c == '$'
            || c == '('
            || c == ')'
            || c == '_'
    }) {
        return Err(LabelError(err(
            label,
            "package names may contain only A-Z, \
        a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'.",
        )));
    }
    if package_name.ends_with('/') {
        return Err(LabelError(err(
            label,
            "package names may not end with '/'.",
        )));
    }

    if rest.is_empty() && is_absolute {
        // This label doesn't contain the target name, we have to use
        // last segment of the package name as target name.
        return Ok((
            match package_name.rfind('/') {
                Some(pos) => &package_name[pos..],
                None => package_name,
            },
            Some(package_name),
        ));
    }

    Ok((rest, Some(package_name)))
}

fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<&'s str> {
    if input.is_empty() {
        return Err(LabelError(err(label, "empty target name.")));
    }
    let name = if let Some(stripped) = input.strip_prefix(':') {
        stripped
    } else {
        input
    };
    if name.is_empty() {
        return Err(LabelError(err(label, "empty target name.")));
    }
    if name.starts_with('/') {
        return Err(LabelError(err(
            label,
            "target names may not start with '/'.",
        )));
    }

    Ok(name)
}

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

    #[test]
    fn test_new() {
        assert_eq!(
            Label::new(Some("repo"), Some("foo/bar"), "baz"),
            Label {
                repository_name: Some("repo"),
                package_name: Some("foo/bar"),
                name: "baz"
            }
        );
    }

    #[test]
    fn test_repository_name_parsing() -> Result<()> {
        assert_eq!(analyze("@repo//:foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//:foo")?.repository_name, None);
        assert_eq!(analyze("//:foo")?.repository_name, None);
        assert_eq!(analyze(":foo")?.repository_name, None);
        assert_eq!(
            analyze("@foo:bar"),
            Err(LabelError(
                "@foo:bar must be a legal label; labels with repository must contain //."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("@AZab0123456789_-.//:foo")?.repository_name,
            Some("AZab0123456789_-.")
        );
        assert_eq!(
            analyze("@42//:baz"),
            Err(LabelError(
                "@42//:baz must be a legal label; workspace names must \
            start with a letter."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("@foo#//:baz"),
            Err(LabelError(
                "@foo#//:baz must be a legal label; workspace names \
            may contain only A-Z, a-z, 0-9, '-', '_', and '.'."
                    .to_string()
            ))
        );
        Ok(())
    }
    #[test]
    fn test_package_name_parsing() -> Result<()> {
        assert_eq!(analyze("//:baz/qux")?.package_name, None);
        assert_eq!(analyze(":baz/qux")?.package_name, None);

        assert_eq!(analyze("//foo:baz/qux")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo/bar:baz/qux")?.package_name, Some("foo/bar"));
        assert_eq!(analyze("foo:baz/qux")?.package_name, Some("foo"));
        assert_eq!(analyze("foo/bar:baz/qux")?.package_name, Some("foo/bar"));

        assert_eq!(analyze("foo")?.package_name, None);
        assert_eq!(analyze("//foo")?.package_name, Some("foo"));

        assert_eq!(
            analyze("foo//bar"),
            Err(LabelError(
                "foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo//bar:baz"),
            Err(LabelError(
                "foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("//azAZ09/-. $()_:baz")?.package_name,
            Some("azAZ09/-. $()_")
        );
        assert_eq!(
            analyze("//bar#:baz"),
            Err(LabelError(
                "//bar#:baz must be a legal label; package names may contain only A-Z, \
                a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//bar/:baz"),
            Err(LabelError(
                "//bar/:baz must be a legal label; package names may not end with '/'.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_name_parsing() -> Result<()> {
        assert_eq!(analyze("//foo:baz")?.name, "baz");
        assert_eq!(analyze("//foo:baz/qux")?.name, "baz/qux");

        assert_eq!(
            analyze("//bar:"),
            Err(LabelError(
                "//bar: must be a legal label; empty target name.".to_string()
            ))
        );
        assert_eq!(analyze("//foo")?.name, "foo");

        assert_eq!(
            analyze("//bar:/foo"),
            Err(LabelError(
                "//bar:/foo must be a legal label; target names may not start with '/'."
                    .to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_packages() -> Result<()> {
        assert_eq!(analyze("@repo//:baz")?.packages(), Vec::<&str>::new());
        assert_eq!(analyze("@repo//foo:baz")?.packages(), vec!["foo"]);
        assert_eq!(
            analyze("@repo//foo/bar:baz")?.packages(),
            vec!["foo", "bar"]
        );

        Ok(())
    }
}
<commit_msg>Enhance label.rs to support implicit targets and expose absolute-ness. (#1046)<commit_after>//! Bazel label parsing library.
//!
//! USAGE: `label::analyze("//foo/bar:baz")
mod label_error;
use label_error::LabelError;

/// Parse and analyze given str.
///
/// TODO: validate . and .. in target name
/// TODO: validate used characters in target name
pub fn analyze(input: &'_ str) -> Result<Label<'_>> {
    let label = input;
    let (input, repository_name) = consume_repository_name(input, label)?;
    let (input, package_name) = consume_package_name(input, label)?;
    let name = consume_name(input, label)?;
    let name = match (package_name, name) {
        (None, None) => {
            return Err(LabelError(err(
                label,
                "labels must have a package and/or a name.",
            )))
        }
        (Some(package_name), None) => name_from_package(package_name),
        (_, Some(name)) => name,
    };
    Ok(Label::new(repository_name, package_name, name))
}

#[derive(Debug, PartialEq)]
pub struct Label<'s> {
    pub repository_name: Option<&'s str>,
    pub package_name: Option<&'s str>,
    pub name: &'s str,
}

type Result<T, E = LabelError> = core::result::Result<T, E>;

impl<'s> Label<'s> {
    fn new(
        repository_name: Option<&'s str>,
        package_name: Option<&'s str>,
        name: &'s str,
    ) -> Label<'s> {
        Label {
            repository_name,
            package_name,
            name,
        }
    }

    pub fn packages(&self) -> Vec<&'s str> {
        match self.package_name {
            Some(name) => name.split('/').collect(),
            None => vec![],
        }
    }
}

fn err<'s>(label: &'s str, msg: &'s str) -> String {
    let mut err_msg = label.to_string();
    err_msg.push_str(" must be a legal label; ");
    err_msg.push_str(msg);
    err_msg
}

fn consume_repository_name<'s>(
    input: &'s str,
    label: &'s str,
) -> Result<(&'s str, Option<&'s str>)> {
    if !input.starts_with('@') {
        return Ok((input, None));
    }

    let slash_pos = input
        .find("//")
        .ok_or_else(|| err(label, "labels with repository must contain //."))?;
    let repository_name = &input[1..slash_pos];
    if repository_name.is_empty() {
        return Ok((&input[1..], None));
    }
    if !repository_name
        .chars()
        .next()
        .unwrap()
        .is_ascii_alphabetic()
    {
        return Err(LabelError(err(
            label,
            "workspace names must start with a letter.",
        )));
    }
    if !repository_name
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
    {
        return Err(LabelError(err(
            label,
            "workspace names \
                may contain only A-Z, a-z, 0-9, '-', '_', and '.'.",
        )));
    }
    Ok((&input[slash_pos..], Some(repository_name)))
}

fn consume_package_name<'s>(input: &'s str, label: &'s str) -> Result<(&'s str, Option<&'s str>)> {
    let is_absolute = match input.rfind("//") {
        None => false,
        Some(0) => true,
        Some(_) => {
            return Err(LabelError(err(
                label,
                "'//' cannot appear in the middle of the label.",
            )));
        }
    };

    let (package_name, rest) = match (is_absolute, input.find(':')) {
        (false, colon_pos) if colon_pos.map_or(true, |pos| pos != 0) => {
            return Err(LabelError(err(
                label,
                "relative packages are not permitted.",
            )));
        }
        (_, colon_pos) => {
            let (input, colon_pos) = if is_absolute {
                (&input[2..], colon_pos.map(|cp| cp - 2))
            } else {
                (input, colon_pos)
            };
            match colon_pos {
                Some(colon_pos) => (&input[0..colon_pos], &input[colon_pos..]),
                None => (input, ""),
            }
        }
    };

    if package_name.is_empty() {
        return Ok((rest, None));
    }

    if !package_name.chars().all(|c| {
        c.is_ascii_alphanumeric()
            || c == '/'
            || c == '-'
            || c == '.'
            || c == ' '
            || c == '$'
            || c == '('
            || c == ')'
            || c == '_'
    }) {
        return Err(LabelError(err(
            label,
            "package names may contain only A-Z, \
        a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'.",
        )));
    }
    if package_name.ends_with('/') {
        return Err(LabelError(err(
            label,
            "package names may not end with '/'.",
        )));
    }

    if rest.is_empty() && is_absolute {
        // This label doesn't contain the target name, we have to use
        // last segment of the package name as target name.
        return Ok((
            match package_name.rfind('/') {
                Some(pos) => &package_name[pos..],
                None => package_name,
            },
            Some(package_name),
        ));
    }

    Ok((rest, Some(package_name)))
}

fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<Option<&'s str>> {
    if input.is_empty() {
        return Ok(None);
    }
    if input == ":" {
        return Err(LabelError(err(label, "empty target name.")));
    }
    let name = input
        .strip_prefix(':')
        .or_else(|| input.strip_prefix('/'))
        .unwrap_or(input);
    if name.starts_with('/') {
        return Err(LabelError(err(
            label,
            "target names may not start with '/'.",
        )));
    }
    Ok(Some(name))
}

fn name_from_package(package_name: &str) -> &str {
    package_name
        .rsplit_once('/')
        .map(|tup| tup.1)
        .unwrap_or(package_name)
}

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

    #[test]
    fn test_new() {
        assert_eq!(
            Label::new(Some("repo"), Some("foo/bar"), "baz"),
            Label {
                repository_name: Some("repo"),
                package_name: Some("foo/bar"),
                name: "baz",
            }
        );
    }

    #[test]
    fn test_repository_name_parsing() -> Result<()> {
        assert_eq!(analyze("@repo//:foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//:foo")?.repository_name, None);
        assert_eq!(analyze("//:foo")?.repository_name, None);
        assert_eq!(analyze(":foo")?.repository_name, None);

        assert_eq!(analyze("@repo//foo/bar")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo/bar")?.repository_name, None);
        assert_eq!(analyze("//foo/bar")?.repository_name, None);
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo")?.repository_name, None);
        assert_eq!(analyze("//foo")?.repository_name, None);
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(
            analyze("@foo:bar"),
            Err(LabelError(
                "@foo:bar must be a legal label; labels with repository must contain //."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("@AZab0123456789_-.//:foo")?.repository_name,
            Some("AZab0123456789_-.")
        );
        assert_eq!(
            analyze("@42//:baz"),
            Err(LabelError(
                "@42//:baz must be a legal label; workspace names must \
            start with a letter."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("@foo#//:baz"),
            Err(LabelError(
                "@foo#//:baz must be a legal label; workspace names \
            may contain only A-Z, a-z, 0-9, '-', '_', and '.'."
                    .to_string()
            ))
        );
        Ok(())
    }
    #[test]
    fn test_package_name_parsing() -> Result<()> {
        assert_eq!(analyze("//:baz/qux")?.package_name, None);
        assert_eq!(analyze(":baz/qux")?.package_name, None);

        assert_eq!(analyze("//foo:baz/qux")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo/bar:baz/qux")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo:baz/qux"),
            Err(LabelError(
                "foo:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo/bar:baz/qux"),
            Err(LabelError(
                "foo/bar:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );

        assert_eq!(analyze("//foo")?.package_name, Some("foo"));

        assert_eq!(
            analyze("foo//bar"),
            Err(LabelError(
                "foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar"),
            Err(LabelError(
                "//foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo//bar:baz"),
            Err(LabelError(
                "foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar:baz"),
            Err(LabelError(
                "//foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("//azAZ09/-. $()_:baz")?.package_name,
            Some("azAZ09/-. $()_")
        );
        assert_eq!(
            analyze("//bar#:baz"),
            Err(LabelError(
                "//bar#:baz must be a legal label; package names may contain only A-Z, \
                a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//bar/:baz"),
            Err(LabelError(
                "//bar/:baz must be a legal label; package names may not end with '/'.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(analyze("//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo")?.package_name, Some("foo"));
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_name_parsing() -> Result<()> {
        assert_eq!(analyze("//foo:baz")?.name, "baz");
        assert_eq!(analyze("//foo:baz/qux")?.name, "baz/qux");

        assert_eq!(
            analyze("//bar:"),
            Err(LabelError(
                "//bar: must be a legal label; empty target name.".to_string()
            ))
        );
        assert_eq!(analyze("//foo")?.name, "foo");

        assert_eq!(
            analyze("//bar:/foo"),
            Err(LabelError(
                "//bar:/foo must be a legal label; target names may not start with '/'."
                    .to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.name, "bar");
        assert_eq!(analyze("//foo/bar")?.name, "bar");
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.name, "foo");
        assert_eq!(analyze("//foo")?.name, "foo");
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_packages() -> Result<()> {
        assert_eq!(analyze("@repo//:baz")?.packages(), Vec::<&str>::new());
        assert_eq!(analyze("@repo//foo:baz")?.packages(), vec!["foo"]);
        assert_eq!(
            analyze("@repo//foo/bar:baz")?.packages(),
            vec!["foo", "bar"]
        );

        Ok(())
    }
}
<|endoftext|>


<commit_before><commit_msg>Add 400-series syscalls to musl riscv64 definitions<commit_after><|endoftext|>


<commit_before>use gfx;
use gfx_core;
use gfx::traits::FactoryExt;
use memmap::MmapViewSync;
use std::convert::TryInto;
use vec_map::VecMap;

use terrain::quadtree::{Node, NodeId};
use runtime_texture::{TextureArray, TextureFormat};

#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Priority(f32);
impl Priority {
    pub fn cutoff() -> Self {
        Priority(1.0)
    }
    pub fn none() -> Self {
        Priority(-1.0)
    }
    pub fn from_f32(value: f32) -> Self {
        assert!(value.is_finite());
        Priority(value)
    }
}
impl Eq for Priority {}
impl Ord for Priority {
    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

pub const NUM_LAYERS: usize = 4;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub(crate) enum LayerType {
    Heights = 0,
    Colors = 1,
    Normals = 2,
    Water = 3,
}
impl LayerType {
    pub fn cache_size(&self) -> u16 {
        match *self {
            LayerType::Heights => 512,
            LayerType::Colors => 256,
            LayerType::Normals => 32,
            LayerType::Water => 256,
        }
    }
    pub fn index(&self) -> usize {
        *self as usize
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct ByteRange {
    pub offset: usize,
    pub length: usize,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) enum PayloadType {
    Texture {
        /// Number of samples in each dimension, per tile.
        resolution: u32,
        /// Number of samples outside the tile on each side.
        border_size: u32,
        /// Format used by this layer.
        format: TextureFormat,
    },
    InstancedMesh {
        /// Maximum number of instances in any tile.
        max_instances: usize,
    },
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct LayerParams {
    /// What kind of layer this is. There can be at most one of each layer type in a file.
    pub layer_type: LayerType,
    /// Where each tile is located in the file.
    pub tile_locations: Vec<ByteRange>,
    /// What kind of data is stored in this layer.
    pub payload_type: PayloadType,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct NoiseParams {
    pub offset: usize,
    pub resolution: u32,
    pub format: TextureFormat,
    pub bytes: usize,
    pub wavelength: f32,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct MeshDescriptor {
    pub offset: usize,
    pub bytes: usize,
    pub num_vertices: usize,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct TextureDescriptor {
    pub offset: usize,
    pub resolution: u32,
    pub format: TextureFormat,
    pub bytes: usize,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct TileHeader {
    pub layers: Vec<LayerParams>,
    pub noise: NoiseParams,
    pub nodes: Vec<Node>,
    pub planet_mesh: MeshDescriptor,
    pub planet_mesh_texture: TextureDescriptor,
}

gfx_vertex_struct!(MeshInstance {
    position: [f32; 3] = "position",
    color: [f32; 3] = "color",
    rotation: f32 = "rotation",
    texture_layer: f32 = "textureLayer",
});

enum PayloadSet<R: gfx::Resources> {
    Texture {
        texture: TextureArray<R>,
        resolution: u16,
    },
    InstancedMesh {
        buffer: gfx::handle::Buffer<R, MeshInstance>,
        max_elements_per_slot: usize,
    },
}

pub(crate) struct TileCache<R: gfx::Resources> {
    /// Maximum number of slots in this `TileCache`.
    size: usize,
    /// Actually contents of the cache.
    slots: Vec<(Priority, NodeId)>,
    /// Which index each node is at in the cache (if any).
    reverse: VecMap<usize>,
    /// Nodes that should be added to the cache.
    missing: Vec<(Priority, NodeId)>,
    /// Smallest priority among all nodes in the cache.
    min_priority: Priority,

    /// Resolution of each tile in this cache.
    layer_params: LayerParams,

    payloads: PayloadSet<R>,

    /// Section of memory map that holds the data for this layer.
    data_file: MmapViewSync,
}
impl<R: gfx::Resources> TileCache<R> {
    pub fn new<F: gfx::Factory<R>>(
        params: LayerParams,
        data_file: MmapViewSync,
        factory: &mut F,
    ) -> Self {
        let cache_size = params.layer_type.cache_size();
        let payloads = match params.payload_type {
            PayloadType::Texture {
                resolution, format, ..
            } => PayloadSet::Texture {
                texture: TextureArray::new(format, resolution as u16, cache_size, factory),
                resolution: resolution.try_into().unwrap(),
            },
            PayloadType::InstancedMesh { max_instances } => PayloadSet::InstancedMesh {
                buffer: factory.create_vertex_buffer(&vec![
                    MeshInstance {
                        position: [0.0; 3],
                        color: [0.0; 3],
                        rotation: 0.0,
                        texture_layer: 0.0,
                    };
                    max_instances * cache_size as usize
                ]),
                max_elements_per_slot: max_instances,
            },
        };

        Self {
            size: cache_size as usize,
            slots: Vec::new(),
            reverse: VecMap::new(),
            missing: Vec::new(),
            min_priority: Priority::none(),
            layer_params: params,
            data_file,
            payloads,
        }
    }

    pub fn update_priorities(&mut self, nodes: &mut Vec<Node>) {
        for &mut (ref mut priority, id) in self.slots.iter_mut() {
            *priority = nodes[id].priority();
        }

        self.min_priority = self.slots
            .iter()
            .map(|s| s.0)
            .min()
            .unwrap_or(Priority::none());
    }

    pub fn add_missing(&mut self, element: (Priority, NodeId)) {
        if element.0 > self.min_priority || self.slots.len() < self.size {
            self.missing.push(element);
        }
    }

    pub fn load_missing<C: gfx_core::command::Buffer<R>>(
        &mut self,
        nodes: &mut Vec<Node>,
        encoder: &mut gfx::Encoder<R, C>,
    ) {
        while !self.missing.is_empty() && self.slots.len() < self.size {
            let m = self.missing.pop().unwrap();
            let index = self.slots.len();
            self.slots.push(m.clone());
            self.reverse.insert(m.1.index(), index);
            self.load(&mut nodes[m.1], index, encoder);
        }

        if !self.missing.is_empty() {
            let mut possible: Vec<_> = self.slots
                .iter()
                .cloned()
                .chain(self.missing.iter().cloned())
                .collect();
            possible.sort();

            // Anything >= to cutoff should be included.
            let cutoff = possible[possible.len() - self.size];

            let mut index = 0;
            while let Some(m) = self.missing.pop() {
                if cutoff >= m {
                    continue;
                }

                // Find the next element to evict.
                while self.slots[index] >= cutoff {
                    index += 1;
                }

                self.reverse.remove(self.slots[index].1.index());
                self.reverse.insert(m.1.index(), index);
                self.slots[index] = m.clone();
                self.load(&mut nodes[m.1], index, encoder);
                index += 1;
            }
        }
    }

    fn load<C: gfx_core::command::Buffer<R>>(
        &mut self,
        node: &mut Node,
        slot: usize,
        encoder: &mut gfx::Encoder<R, C>,
    ) {
        let tile = node.tile_indices[self.layer_params.layer_type.index()].unwrap() as usize;
        let offset = self.layer_params.tile_locations[tile].offset;
        let length = self.layer_params.tile_locations[tile].length;
        let data = unsafe { &self.data_file.as_slice()[offset..(offset + length)] };

        match self.payloads {
            PayloadSet::Texture {
                ref mut texture,
                resolution,
            } => texture.update_layer(
                slot as u16,
                resolution,
                gfx::memory::cast_slice(data),
                encoder,
            ),
            PayloadSet::InstancedMesh {
                ref buffer,
                max_elements_per_slot,
            } => encoder
                .update_buffer(
                    buffer,
                    gfx::memory::cast_slice(data),
                    max_elements_per_slot * slot,
                )
                .unwrap(),
        }
    }

    pub fn contains(&self, id: NodeId) -> bool {
        self.reverse.contains_key(id.index())
    }

    pub fn get_slot(&self, id: NodeId) -> Option<usize> {
        self.reverse.get(id.index()).cloned()
    }

    pub fn get_texture_view_f32(&self) -> Option<&gfx_core::handle::ShaderResourceView<R, f32>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::F32 { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn get_texture_view_rgba8(
        &self,
    ) -> Option<&gfx_core::handle::ShaderResourceView<R, [f32; 4]>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::RGBA8 { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn get_texture_view_srgba(
        &self,
    ) -> Option<&gfx_core::handle::ShaderResourceView<R, [f32; 4]>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::SRGBA { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn resolution(&self) -> u32 {
        match self.layer_params.payload_type {
            PayloadType::Texture { resolution, .. } => resolution,
            _ => unreachable!(),
        }
    }

    pub fn border(&self) -> u32 {
        match self.layer_params.payload_type {
            PayloadType::Texture { border_size, .. } => border_size,
            _ => unreachable!(),
        }
    }

    pub fn get_texel(&self, node: &Node, x: usize, y: usize) -> &[u8] {
        if let PayloadType::Texture {
            border_size,
            resolution,
            format,
        } = self.layer_params.payload_type
        {
            let tile = node.tile_indices[self.layer_params.layer_type.index()].unwrap() as usize;
            let offset = self.layer_params.tile_locations[tile].offset;
            let length = self.layer_params.tile_locations[tile].length;
            let tile_data = unsafe { &self.data_file.as_slice()[offset..(offset + length)] };
            let bytes_per_texel = format.bytes_per_texel();
            let border = border_size as usize;
            let index = ((x + border) + (y + border) * resolution as usize) * bytes_per_texel;
            &tile_data[index..(index + bytes_per_texel)]
        } else {
            unreachable!()
        }
    }
}
<commit_msg>Add test of MeshInstance size<commit_after>use gfx;
use gfx_core;
use gfx::traits::FactoryExt;
use memmap::MmapViewSync;
use std::convert::TryInto;
use vec_map::VecMap;

use terrain::quadtree::{Node, NodeId};
use runtime_texture::{TextureArray, TextureFormat};

#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Priority(f32);
impl Priority {
    pub fn cutoff() -> Self {
        Priority(1.0)
    }
    pub fn none() -> Self {
        Priority(-1.0)
    }
    pub fn from_f32(value: f32) -> Self {
        assert!(value.is_finite());
        Priority(value)
    }
}
impl Eq for Priority {}
impl Ord for Priority {
    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

pub const NUM_LAYERS: usize = 4;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub(crate) enum LayerType {
    Heights = 0,
    Colors = 1,
    Normals = 2,
    Water = 3,
}
impl LayerType {
    pub fn cache_size(&self) -> u16 {
        match *self {
            LayerType::Heights => 512,
            LayerType::Colors => 256,
            LayerType::Normals => 32,
            LayerType::Water => 256,
        }
    }
    pub fn index(&self) -> usize {
        *self as usize
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct ByteRange {
    pub offset: usize,
    pub length: usize,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) enum PayloadType {
    Texture {
        /// Number of samples in each dimension, per tile.
        resolution: u32,
        /// Number of samples outside the tile on each side.
        border_size: u32,
        /// Format used by this layer.
        format: TextureFormat,
    },
    InstancedMesh {
        /// Maximum number of instances in any tile.
        max_instances: usize,
    },
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct LayerParams {
    /// What kind of layer this is. There can be at most one of each layer type in a file.
    pub layer_type: LayerType,
    /// Where each tile is located in the file.
    pub tile_locations: Vec<ByteRange>,
    /// What kind of data is stored in this layer.
    pub payload_type: PayloadType,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct NoiseParams {
    pub offset: usize,
    pub resolution: u32,
    pub format: TextureFormat,
    pub bytes: usize,
    pub wavelength: f32,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct MeshDescriptor {
    pub offset: usize,
    pub bytes: usize,
    pub num_vertices: usize,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct TextureDescriptor {
    pub offset: usize,
    pub resolution: u32,
    pub format: TextureFormat,
    pub bytes: usize,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct TileHeader {
    pub layers: Vec<LayerParams>,
    pub noise: NoiseParams,
    pub nodes: Vec<Node>,
    pub planet_mesh: MeshDescriptor,
    pub planet_mesh_texture: TextureDescriptor,
}

gfx_vertex_struct!(MeshInstance {
    position: [f32; 3] = "position",
    color: [f32; 3] = "color",
    rotation: f32 = "rotation",
    texture_layer: f32 = "textureLayer",
});

enum PayloadSet<R: gfx::Resources> {
    Texture {
        texture: TextureArray<R>,
        resolution: u16,
    },
    InstancedMesh {
        buffer: gfx::handle::Buffer<R, MeshInstance>,
        max_elements_per_slot: usize,
    },
}

pub(crate) struct TileCache<R: gfx::Resources> {
    /// Maximum number of slots in this `TileCache`.
    size: usize,
    /// Actually contents of the cache.
    slots: Vec<(Priority, NodeId)>,
    /// Which index each node is at in the cache (if any).
    reverse: VecMap<usize>,
    /// Nodes that should be added to the cache.
    missing: Vec<(Priority, NodeId)>,
    /// Smallest priority among all nodes in the cache.
    min_priority: Priority,

    /// Resolution of each tile in this cache.
    layer_params: LayerParams,

    payloads: PayloadSet<R>,

    /// Section of memory map that holds the data for this layer.
    data_file: MmapViewSync,
}
impl<R: gfx::Resources> TileCache<R> {
    pub fn new<F: gfx::Factory<R>>(
        params: LayerParams,
        data_file: MmapViewSync,
        factory: &mut F,
    ) -> Self {
        let cache_size = params.layer_type.cache_size();
        let payloads = match params.payload_type {
            PayloadType::Texture {
                resolution, format, ..
            } => PayloadSet::Texture {
                texture: TextureArray::new(format, resolution as u16, cache_size, factory),
                resolution: resolution.try_into().unwrap(),
            },
            PayloadType::InstancedMesh { max_instances } => PayloadSet::InstancedMesh {
                buffer: factory.create_vertex_buffer(&vec![
                    MeshInstance {
                        position: [0.0; 3],
                        color: [0.0; 3],
                        rotation: 0.0,
                        texture_layer: 0.0,
                    };
                    max_instances * cache_size as usize
                ]),
                max_elements_per_slot: max_instances,
            },
        };

        Self {
            size: cache_size as usize,
            slots: Vec::new(),
            reverse: VecMap::new(),
            missing: Vec::new(),
            min_priority: Priority::none(),
            layer_params: params,
            data_file,
            payloads,
        }
    }

    pub fn update_priorities(&mut self, nodes: &mut Vec<Node>) {
        for &mut (ref mut priority, id) in self.slots.iter_mut() {
            *priority = nodes[id].priority();
        }

        self.min_priority = self.slots
            .iter()
            .map(|s| s.0)
            .min()
            .unwrap_or(Priority::none());
    }

    pub fn add_missing(&mut self, element: (Priority, NodeId)) {
        if element.0 > self.min_priority || self.slots.len() < self.size {
            self.missing.push(element);
        }
    }

    pub fn load_missing<C: gfx_core::command::Buffer<R>>(
        &mut self,
        nodes: &mut Vec<Node>,
        encoder: &mut gfx::Encoder<R, C>,
    ) {
        while !self.missing.is_empty() && self.slots.len() < self.size {
            let m = self.missing.pop().unwrap();
            let index = self.slots.len();
            self.slots.push(m.clone());
            self.reverse.insert(m.1.index(), index);
            self.load(&mut nodes[m.1], index, encoder);
        }

        if !self.missing.is_empty() {
            let mut possible: Vec<_> = self.slots
                .iter()
                .cloned()
                .chain(self.missing.iter().cloned())
                .collect();
            possible.sort();

            // Anything >= to cutoff should be included.
            let cutoff = possible[possible.len() - self.size];

            let mut index = 0;
            while let Some(m) = self.missing.pop() {
                if cutoff >= m {
                    continue;
                }

                // Find the next element to evict.
                while self.slots[index] >= cutoff {
                    index += 1;
                }

                self.reverse.remove(self.slots[index].1.index());
                self.reverse.insert(m.1.index(), index);
                self.slots[index] = m.clone();
                self.load(&mut nodes[m.1], index, encoder);
                index += 1;
            }
        }
    }

    fn load<C: gfx_core::command::Buffer<R>>(
        &mut self,
        node: &mut Node,
        slot: usize,
        encoder: &mut gfx::Encoder<R, C>,
    ) {
        let tile = node.tile_indices[self.layer_params.layer_type.index()].unwrap() as usize;
        let offset = self.layer_params.tile_locations[tile].offset;
        let length = self.layer_params.tile_locations[tile].length;
        let data = unsafe { &self.data_file.as_slice()[offset..(offset + length)] };

        match self.payloads {
            PayloadSet::Texture {
                ref mut texture,
                resolution,
            } => texture.update_layer(
                slot as u16,
                resolution,
                gfx::memory::cast_slice(data),
                encoder,
            ),
            PayloadSet::InstancedMesh {
                ref buffer,
                max_elements_per_slot,
            } => encoder
                .update_buffer(
                    buffer,
                    gfx::memory::cast_slice(data),
                    max_elements_per_slot * slot,
                )
                .unwrap(),
        }
    }

    pub fn contains(&self, id: NodeId) -> bool {
        self.reverse.contains_key(id.index())
    }

    pub fn get_slot(&self, id: NodeId) -> Option<usize> {
        self.reverse.get(id.index()).cloned()
    }

    pub fn get_texture_view_f32(&self) -> Option<&gfx_core::handle::ShaderResourceView<R, f32>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::F32 { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn get_texture_view_rgba8(
        &self,
    ) -> Option<&gfx_core::handle::ShaderResourceView<R, [f32; 4]>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::RGBA8 { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn get_texture_view_srgba(
        &self,
    ) -> Option<&gfx_core::handle::ShaderResourceView<R, [f32; 4]>> {
        match self.payloads {
            PayloadSet::Texture {
                texture: TextureArray::SRGBA { ref view, .. },
                ..
            } => Some(view),
            _ => None,
        }
    }

    pub fn resolution(&self) -> u32 {
        match self.layer_params.payload_type {
            PayloadType::Texture { resolution, .. } => resolution,
            _ => unreachable!(),
        }
    }

    pub fn border(&self) -> u32 {
        match self.layer_params.payload_type {
            PayloadType::Texture { border_size, .. } => border_size,
            _ => unreachable!(),
        }
    }

    pub fn get_texel(&self, node: &Node, x: usize, y: usize) -> &[u8] {
        if let PayloadType::Texture {
            border_size,
            resolution,
            format,
        } = self.layer_params.payload_type
        {
            let tile = node.tile_indices[self.layer_params.layer_type.index()].unwrap() as usize;
            let offset = self.layer_params.tile_locations[tile].offset;
            let length = self.layer_params.tile_locations[tile].length;
            let tile_data = unsafe { &self.data_file.as_slice()[offset..(offset + length)] };
            let bytes_per_texel = format.bytes_per_texel();
            let border = border_size as usize;
            let index = ((x + border) + (y + border) * resolution as usize) * bytes_per_texel;
            &tile_data[index..(index + bytes_per_texel)]
        } else {
            unreachable!()
        }
    }
}

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

    #[test]
    fn mesh_instance_size() {
        assert_eq!(mem::size_of::<MeshInstance>(), 32);
    }
}
<|endoftext|>