<commit_before>use pombase_rc_string::RcString;

use crate::web::config::Config;
use crate::web::data::{GeneDetails, ReferenceDetails, TermDetails,
                       ContainerType, GenotypeShort,
                       OntAnnotationId, AnnotationContainer, OrthologAnnotationContainer};

fn format_page(header: &str, body: &str) -> String {
    format! ("
<!DOCTYPE html>

    {}

    {}

",
             header, body)
}

fn make_gene_title(config: &Config, gene_details: &GeneDetails) -> String {
    let name_and_uniquename =
        if let Some(ref name) = gene_details.name {
            format!("{} ({})", name, gene_details.uniquename)
        } else {
            format!("{}", gene_details.uniquename)
        };

    let feature_type =
        if gene_details.feature_type.ends_with("gene") {
            String::from(gene_details.feature_type.as_str())
        } else {
            format!("{} gene", gene_details.feature_type)
        };

    if let Some(ref product) = gene_details.product {
        format!("{} - {} {} - {}", config.database_name, feature_type,
                name_and_uniquename, product)
    } else {
        format!("{} - {} {}", config.database_name, feature_type, name_and_uniquename)
    }
}

fn header(config: &Config, title: &str) -> String  {
    format!(r##"
  <meta charset="utf-8">
  <title>{}</title>
  <base href="/">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="title" content="{}">
  <meta name="author" content="{}">
  <meta name="description" content="{}">
  <meta name="google-site-verification" content="H8vhCdG7XsSbh-8f20VSipvu2PnZ22YSGZm81jE0-Pk" />
  <link rel="shortcut icon" href="/assets/favicon.ico">
  <meta name="apple-mobile-web-app-title" content="{}">
  <meta name="application-name" content="{}">
"##,
            title, title, config.database_name, title, config.database_name,
            config.database_name
    )
}

fn gene_summary(config: &Config, gene_details: &GeneDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";
    if let Some(ref name) = gene_details.name {
        summ += &format!("
Gene standard name

{}
\n",  name);
    }
    summ += &format!("
Systematic ID

{}
\n", gene_details.uniquename);

    if let Some(gene_organism) = config.organism_by_taxonid(gene_details.taxonid) {
        summ += &format!("
Organism

{}\n", gene_organism.scientific_name());

        if gene_organism.alternative_names.len() > 0 {
            summ += &format!(" ({})", gene_organism.alternative_names.join(", "));
        }
        summ += "
\n";
    }

    if gene_details.synonyms.len() > 0 {
        let synonyms: Vec<RcString> =
            gene_details.synonyms.iter().map(|s| s.name.clone()).collect();
        summ += &format!("
Synonyms

{}
\n", synonyms.join(", "));
    }

    if let Some(ref uniprot_identifier) = gene_details.uniprot_identifier {
        summ += &format!("
UniProt ID

{}
\n", uniprot_identifier);
    }

    if let Some(ref orfeome_identifier) = gene_details.orfeome_identifier {
        summ += &format!("
ORFeome ID

{}
\n", orfeome_identifier);
    }

    if let Some(ref characterisation_status) = gene_details.characterisation_status {
        summ += &format!("
Characterisation status

{}\n",
                         characterisation_status);
    }

    summ += "
\n";

    summ
}

fn genotype_display_name(genotype_short: &GenotypeShort) -> String {
    let mut bits: Vec<_> = genotype_short.display_uniquename.split("-").collect();
    bits.dedup();
    bits.join(" ")
}

fn get_annotations(ont_annotation_ids: &Vec<OntAnnotationId>,
                   container: &dyn AnnotationContainer) -> String {
    let mut ret = String::new();
    let mut references = vec![];
    let mut genes = vec![];
    let mut genotypes = vec![];

    for ont_annotation_id in ont_annotation_ids {
        if let Some(annotation_details) = container.annotation_details().get(ont_annotation_id) {
            if let Some(ref reference) = annotation_details.reference {
                references.push(reference);
            }

            if let Some(ref genotype) = annotation_details.genotype {
                genotypes.push(genotype);
            }

            for gene_uniquename in &annotation_details.genes {
                if let Some(maybe_gene_short) =
                    container.genes_by_uniquename().get(gene_uniquename.as_str()) {
                        if let Some(gene_short) = maybe_gene_short {
                            genes.push(gene_short)
                        }
                    }
            }
        }
    }

    if container.container_type() != ContainerType::Reference && references.len() > 0 {
        references.sort();
        references.dedup();
        ret += "
References:
\n
";
        for reference in references {
            ret += &format!("
{}
\n", reference, reference);
        }
        ret += "
\n";
    }

    if container.container_type() != ContainerType::Gene && genes.len() > 0 {
        genes.sort();
        genes.dedup();
        ret += "
Genes:
\n
";
        for gene in genes {
            ret += &format!("
{}
\n", gene.uniquename,
                            gene.display_name());
        }
        ret += "
\n";
    }

    if container.container_type() != ContainerType::Genotype && genotypes.len() > 0 {
        genotypes.sort();
        genotypes.dedup();
        if let Some(genotypes_by_uniquename) = container.genotypes_by_uniquename() {
            ret += "
Genotypes:
\n
";
            for uniquename in genotypes {
                if let Some(genotype_short) = genotypes_by_uniquename.get(uniquename.as_str()) {
                    ret += &format!("
{}
\n",
                                    uniquename,
                                    genotype_display_name(genotype_short));
                }
            }
            ret += "
\n";
        }
    }

    ret
}

fn annotation_section(config: &Config, container: &dyn AnnotationContainer) -> String {
    let mut annotation_html = String::new();

    let mut cv_names: Vec<RcString> = vec![];

    for cv_name in container.cv_annotations().keys() {
        cv_names.push(cv_name.clone());
    }

    let cmp_display_names =
        |cv_name_a: &RcString, cv_name_b: &RcString| {
            let cv_config_a = config.cv_config_by_name(&cv_name_a);
            let cv_display_name_a = cv_config_a.display_name;
            let cv_config_b = config.cv_config_by_name(&cv_name_b);
            let cv_display_name_b = cv_config_b.display_name;

            cv_display_name_a.cmp(&cv_display_name_b)
        };

    cv_names.sort_by(cmp_display_names);

    for cv_name in cv_names {
        let term_annotations = container.cv_annotations().get(&cv_name).unwrap();
        let cv_config = config.cv_config_by_name(&cv_name);

        if let Some(cv_display_name) = cv_config.display_name {
            annotation_html += &format!("<sect>\n
{}
\n", cv_display_name);
            for term_annotation in term_annotations {
                let term_name =
                    if let Some(term_short_opt) =
                    container.terms_by_termid().get(&term_annotation.term) {
                        if let Some(term_short) = term_short_opt {
                            String::from(term_short.name.as_str())
                        } else {
                            String::from("(unknown term name)")
                        }
                    } else {
                        String::from("(unknown term name)")
                    };
                annotation_html += &format!("<sect>
{} - \
                                             {}
\n{}</sect>\n",
                                            term_annotation.term,
                                            term_annotation.term,
                                            term_annotation.term,
                                            term_name,
                                            get_annotations(&term_annotation.annotations,
                                                            container));
            }
            annotation_html += "</sect>\n";
        }
    }

    annotation_html
}

fn orthologs(config: &Config, container: &dyn OrthologAnnotationContainer) -> String {
    let mut orth_html = String::new();

    if container.ortholog_annotations().is_empty() {
        return orth_html;
    }

    orth_html += "<sect>
Orthologs
\n
\n";

    for orth_annotation in container.ortholog_annotations().iter() {
        let maybe_orth_org = config.organism_by_taxonid(orth_annotation.ortholog_taxonid);
        let orth_org_scientific_name =
            if let Some(orth_org) = maybe_orth_org {
                orth_org.scientific_name()
            } else {
                String::from("Unknown organism")
            };
        if let Some(maybe_orth_gene_short) =
            container.genes_by_uniquename().get(&orth_annotation.ortholog_uniquename) {
                if let Some(orth_gene_short) = maybe_orth_gene_short {
                    orth_html += &format!("
{} {}
\n", orth_gene_short.display_name(),
                                          orth_org_scientific_name);
                }
            }
    }

    orth_html += "
</sect>\n";

    orth_html
}

fn gene_body(config: &Config, title: &str, gene_details: &GeneDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Gene summary
\n{}</sect>\n",
                     gene_summary(config, gene_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, gene_details));

    body += &orthologs(config, gene_details);

    body
}

pub fn render_simple_gene_page(config: &Config, gene_details: &GeneDetails) -> String  {
    let title = make_gene_title(config, gene_details);

    format_page(&header(config, &title), &gene_body(config, &title, gene_details))
}

fn make_reference_title(config: &Config, reference_details: &ReferenceDetails) -> String {
    if let Some(ref title) = reference_details.title {
        format!("{} - Reference - {} - {}", config.database_name, reference_details.uniquename, title)
    } else {
        format!("{} - Reference - {}", config.database_name, reference_details.uniquename)
    }
}

fn reference_summary(reference_details: &ReferenceDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";

    summ += &format!("
PubMed ID

{}
\n", reference_details.uniquename);

    if let Some(ref title) = reference_details.title {
        summ += &format!("
Title

{}
\n", title);
    }

    if let Some(ref authors) = reference_details.authors {
        summ += &format!("
Authors

{}
\n", authors);
    }

    if let Some(ref citation) = reference_details.citation {
        summ += &format!("
Citation

{}
\n", citation);
    }

    if let Some(ref publication_year) = reference_details.publication_year {
        summ += &format!("
Publication year

{}
\n", publication_year);
    }

    summ += "
\n";

    summ
}

fn reference_body(config: &Config, title: &str, reference_details: &ReferenceDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Reference summary
\n{}</sect>\n",
                     reference_summary(reference_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, reference_details));

    body += &orthologs(config, reference_details);

    body
}

pub fn render_simple_reference_page(config: &Config, reference_details: &ReferenceDetails) -> String  {
    let title = make_reference_title(config, reference_details);

    format_page(&header(config, &title), &reference_body(config, &title, reference_details))
}

fn make_term_title(config: &Config, term_details: &TermDetails) -> String {
    let cv_config = config.cv_config_by_name(&term_details.cv_name);
    let cv_display_name = cv_config.display_name.unwrap_or_else(|| RcString::from("DEFAULT"));

    format!("{} - Term - {} - {} - {}", config.database_name, term_details.termid,
            term_details.name, cv_display_name)
}

fn term_summary(config: &Config, term_details: &TermDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";

    summ += &format!("
Term ID

{}
\n", term_details.termid);
    summ += &format!("
Term name

{}
\n", term_details.name);

    let cv_config = config.cv_config_by_name(&term_details.cv_name);
    let cv_display_name = cv_config.display_name.unwrap_or_else(|| RcString::from("DEFAULT"));

    summ += &format!("
CV name

{}
\n", cv_display_name);

    summ += "
\n";

    summ
}

fn term_body(config: &Config, title: &str, term_details: &TermDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Term summary
\n{}</sect>\n",
                     term_summary(config, term_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, term_details));

    body
}

pub fn render_simple_term_page(config: &Config, term_details: &TermDetails) -> String  {
    let title = make_term_title(config, term_details);

    format_page(&header(config, &title), &term_body(config, &title, term_details))
}
<commit_msg>Improve title of simple gene pages<commit_after>use pombase_rc_string::RcString;

use crate::web::config::Config;
use crate::web::data::{GeneDetails, ReferenceDetails, TermDetails,
                       ContainerType, GenotypeShort,
                       OntAnnotationId, AnnotationContainer, OrthologAnnotationContainer};

fn format_page(header: &str, body: &str) -> String {
    format! ("
<!DOCTYPE html>

    {}

    {}

",
             header, body)
}

fn make_gene_title(config: &Config, gene_details: &GeneDetails) -> String {
    let name_and_uniquename =
        if let Some(ref name) = gene_details.name {
            format!("{} ({})", name, gene_details.uniquename)
        } else {
            format!("{}", gene_details.uniquename)
        };

    let feature_type =
        if gene_details.feature_type.ends_with("gene") {
            String::from(gene_details.feature_type.as_str())
        } else {
            format!("{} gene", gene_details.feature_type)
        };

    if let Some(ref product) = gene_details.product {
        format!("{} - {} - {} - {}", config.database_name, feature_type,
                name_and_uniquename, product)
    } else {
        format!("{} - {} - {}", config.database_name, feature_type, name_and_uniquename)
    }
}

fn header(config: &Config, title: &str) -> String  {
    format!(r##"
  <meta charset="utf-8">
  <title>{}</title>
  <base href="/">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="title" content="{}">
  <meta name="author" content="{}">
  <meta name="description" content="{}">
  <meta name="google-site-verification" content="H8vhCdG7XsSbh-8f20VSipvu2PnZ22YSGZm81jE0-Pk" />
  <link rel="shortcut icon" href="/assets/favicon.ico">
  <meta name="apple-mobile-web-app-title" content="{}">
  <meta name="application-name" content="{}">
"##,
            title, title, config.database_name, title, config.database_name,
            config.database_name
    )
}

fn gene_summary(config: &Config, gene_details: &GeneDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";
    if let Some(ref name) = gene_details.name {
        summ += &format!("
Gene standard name

{}
\n",  name);
    }
    summ += &format!("
Systematic ID

{}
\n", gene_details.uniquename);

    if let Some(gene_organism) = config.organism_by_taxonid(gene_details.taxonid) {
        summ += &format!("
Organism

{}\n", gene_organism.scientific_name());

        if gene_organism.alternative_names.len() > 0 {
            summ += &format!(" ({})", gene_organism.alternative_names.join(", "));
        }
        summ += "
\n";
    }

    if gene_details.synonyms.len() > 0 {
        let synonyms: Vec<RcString> =
            gene_details.synonyms.iter().map(|s| s.name.clone()).collect();
        summ += &format!("
Synonyms

{}
\n", synonyms.join(", "));
    }

    if let Some(ref uniprot_identifier) = gene_details.uniprot_identifier {
        summ += &format!("
UniProt ID

{}
\n", uniprot_identifier);
    }

    if let Some(ref orfeome_identifier) = gene_details.orfeome_identifier {
        summ += &format!("
ORFeome ID

{}
\n", orfeome_identifier);
    }

    if let Some(ref characterisation_status) = gene_details.characterisation_status {
        summ += &format!("
Characterisation status

{}\n",
                         characterisation_status);
    }

    summ += "
\n";

    summ
}

fn genotype_display_name(genotype_short: &GenotypeShort) -> String {
    let mut bits: Vec<_> = genotype_short.display_uniquename.split("-").collect();
    bits.dedup();
    bits.join(" ")
}

fn get_annotations(ont_annotation_ids: &Vec<OntAnnotationId>,
                   container: &dyn AnnotationContainer) -> String {
    let mut ret = String::new();
    let mut references = vec![];
    let mut genes = vec![];
    let mut genotypes = vec![];

    for ont_annotation_id in ont_annotation_ids {
        if let Some(annotation_details) = container.annotation_details().get(ont_annotation_id) {
            if let Some(ref reference) = annotation_details.reference {
                references.push(reference);
            }

            if let Some(ref genotype) = annotation_details.genotype {
                genotypes.push(genotype);
            }

            for gene_uniquename in &annotation_details.genes {
                if let Some(maybe_gene_short) =
                    container.genes_by_uniquename().get(gene_uniquename.as_str()) {
                        if let Some(gene_short) = maybe_gene_short {
                            genes.push(gene_short)
                        }
                    }
            }
        }
    }

    if container.container_type() != ContainerType::Reference && references.len() > 0 {
        references.sort();
        references.dedup();
        ret += "
References:
\n
";
        for reference in references {
            ret += &format!("
{}
\n", reference, reference);
        }
        ret += "
\n";
    }

    if container.container_type() != ContainerType::Gene && genes.len() > 0 {
        genes.sort();
        genes.dedup();
        ret += "
Genes:
\n
";
        for gene in genes {
            ret += &format!("
{}
\n", gene.uniquename,
                            gene.display_name());
        }
        ret += "
\n";
    }

    if container.container_type() != ContainerType::Genotype && genotypes.len() > 0 {
        genotypes.sort();
        genotypes.dedup();
        if let Some(genotypes_by_uniquename) = container.genotypes_by_uniquename() {
            ret += "
Genotypes:
\n
";
            for uniquename in genotypes {
                if let Some(genotype_short) = genotypes_by_uniquename.get(uniquename.as_str()) {
                    ret += &format!("
{}
\n",
                                    uniquename,
                                    genotype_display_name(genotype_short));
                }
            }
            ret += "
\n";
        }
    }

    ret
}

fn annotation_section(config: &Config, container: &dyn AnnotationContainer) -> String {
    let mut annotation_html = String::new();

    let mut cv_names: Vec<RcString> = vec![];

    for cv_name in container.cv_annotations().keys() {
        cv_names.push(cv_name.clone());
    }

    let cmp_display_names =
        |cv_name_a: &RcString, cv_name_b: &RcString| {
            let cv_config_a = config.cv_config_by_name(&cv_name_a);
            let cv_display_name_a = cv_config_a.display_name;
            let cv_config_b = config.cv_config_by_name(&cv_name_b);
            let cv_display_name_b = cv_config_b.display_name;

            cv_display_name_a.cmp(&cv_display_name_b)
        };

    cv_names.sort_by(cmp_display_names);

    for cv_name in cv_names {
        let term_annotations = container.cv_annotations().get(&cv_name).unwrap();
        let cv_config = config.cv_config_by_name(&cv_name);

        if let Some(cv_display_name) = cv_config.display_name {
            annotation_html += &format!("<sect>\n
{}
\n", cv_display_name);
            for term_annotation in term_annotations {
                let term_name =
                    if let Some(term_short_opt) =
                    container.terms_by_termid().get(&term_annotation.term) {
                        if let Some(term_short) = term_short_opt {
                            String::from(term_short.name.as_str())
                        } else {
                            String::from("(unknown term name)")
                        }
                    } else {
                        String::from("(unknown term name)")
                    };
                annotation_html += &format!("<sect>
{} - \
                                             {}
\n{}</sect>\n",
                                            term_annotation.term,
                                            term_annotation.term,
                                            term_annotation.term,
                                            term_name,
                                            get_annotations(&term_annotation.annotations,
                                                            container));
            }
            annotation_html += "</sect>\n";
        }
    }

    annotation_html
}

fn orthologs(config: &Config, container: &dyn OrthologAnnotationContainer) -> String {
    let mut orth_html = String::new();

    if container.ortholog_annotations().is_empty() {
        return orth_html;
    }

    orth_html += "<sect>
Orthologs
\n
\n";

    for orth_annotation in container.ortholog_annotations().iter() {
        let maybe_orth_org = config.organism_by_taxonid(orth_annotation.ortholog_taxonid);
        let orth_org_scientific_name =
            if let Some(orth_org) = maybe_orth_org {
                orth_org.scientific_name()
            } else {
                String::from("Unknown organism")
            };
        if let Some(maybe_orth_gene_short) =
            container.genes_by_uniquename().get(&orth_annotation.ortholog_uniquename) {
                if let Some(orth_gene_short) = maybe_orth_gene_short {
                    orth_html += &format!("
{} {}
\n", orth_gene_short.display_name(),
                                          orth_org_scientific_name);
                }
            }
    }

    orth_html += "
</sect>\n";

    orth_html
}

fn gene_body(config: &Config, title: &str, gene_details: &GeneDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Gene summary
\n{}</sect>\n",
                     gene_summary(config, gene_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, gene_details));

    body += &orthologs(config, gene_details);

    body
}

pub fn render_simple_gene_page(config: &Config, gene_details: &GeneDetails) -> String  {
    let title = make_gene_title(config, gene_details);

    format_page(&header(config, &title), &gene_body(config, &title, gene_details))
}

fn make_reference_title(config: &Config, reference_details: &ReferenceDetails) -> String {
    if let Some(ref title) = reference_details.title {
        format!("{} - Reference - {} - {}", config.database_name, reference_details.uniquename, title)
    } else {
        format!("{} - Reference - {}", config.database_name, reference_details.uniquename)
    }
}

fn reference_summary(reference_details: &ReferenceDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";

    summ += &format!("
PubMed ID

{}
\n", reference_details.uniquename);

    if let Some(ref title) = reference_details.title {
        summ += &format!("
Title

{}
\n", title);
    }

    if let Some(ref authors) = reference_details.authors {
        summ += &format!("
Authors

{}
\n", authors);
    }

    if let Some(ref citation) = reference_details.citation {
        summ += &format!("
Citation

{}
\n", citation);
    }

    if let Some(ref publication_year) = reference_details.publication_year {
        summ += &format!("
Publication year

{}
\n", publication_year);
    }

    summ += "
\n";

    summ
}

fn reference_body(config: &Config, title: &str, reference_details: &ReferenceDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Reference summary
\n{}</sect>\n",
                     reference_summary(reference_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, reference_details));

    body += &orthologs(config, reference_details);

    body
}

pub fn render_simple_reference_page(config: &Config, reference_details: &ReferenceDetails) -> String  {
    let title = make_reference_title(config, reference_details);

    format_page(&header(config, &title), &reference_body(config, &title, reference_details))
}

fn make_term_title(config: &Config, term_details: &TermDetails) -> String {
    let cv_config = config.cv_config_by_name(&term_details.cv_name);
    let cv_display_name = cv_config.display_name.unwrap_or_else(|| RcString::from("DEFAULT"));

    format!("{} - Term - {} - {} - {}", config.database_name, term_details.termid,
            term_details.name, cv_display_name)
}

fn term_summary(config: &Config, term_details: &TermDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";

    summ += &format!("
Term ID

{}
\n", term_details.termid);
    summ += &format!("
Term name

{}
\n", term_details.name);

    let cv_config = config.cv_config_by_name(&term_details.cv_name);
    let cv_display_name = cv_config.display_name.unwrap_or_else(|| RcString::from("DEFAULT"));

    summ += &format!("
CV name

{}
\n", cv_display_name);

    summ += "
\n";

    summ
}

fn term_body(config: &Config, title: &str, term_details: &TermDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to {}
\n
{}
</sect>\n",
                     config.database_name, config.site_description);

    body += &format!("<sect>
Term summary
\n{}</sect>\n",
                     term_summary(config, term_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     annotation_section(config, term_details));

    body
}

pub fn render_simple_term_page(config: &Config, term_details: &TermDetails) -> String  {
    let title = make_term_title(config, term_details);

    format_page(&header(config, &title), &term_body(config, &title, term_details))
}
<|endoftext|>


<commit_before>// {
//   Derived from : Bjoern Hoehrmann work
//   Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
//   See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.

pub const UTF8_ACCEPT: u32 = 0;
pub const UTF8_REJECT: u32 = 1;

static UTF8D: &'static [u8] = &[
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
];

/*
    state UTF8_ACCEPT => initial state or decoding successful
    state UTF8_REJECT => error
    state other => intermediate states need more inputs
*/
#[inline]
pub fn decode_byte(state: &mut u32, byte: u8, codep: &mut u32) -> u32 {

    let cp_type = UTF8D[byte as usize] as u32;

    *codep = if *state != UTF8_ACCEPT {
        (byte & 0x3f) as u32 | (*codep << 6)
    } else {
        (0xff >> cp_type) as u32 & (byte as u32)
    };

    *state = UTF8D[256 + (*state * 16) as usize + cp_type as usize] as u32;
    *state
}

// }

#[inline]
pub fn is_codepoint_start(byte: u8) -> bool {

    if byte < 0x80 {
        return true;
    }

    if byte >= 0xC2 && byte <= 0xDF {
        return true;
    }

    if byte >= 0xC2 && byte <= 0xDF {
        return true;
    }

    if byte >= 0xE0 && byte <= 0xEC {
        return true;
    }

    if byte >= 0xED && byte <= 0xEF {
        return true;
    }

    if byte >= 0xF0 && byte <= 0xF4 {
        return true;
    }

    false
}

// return 0 on error, or the number of written bytes
pub fn encode(codepoint: u32, out: &mut [u8; 4]) -> usize {
    if codepoint < 0x80 {
        out[0] = (codepoint & 0x7F) as u8;
        return 1;
    }

    if codepoint < 0x800 {
        out[0] = 0xC0 | (codepoint >> 6) as u8;
        out[1] = 0x80 | (codepoint & 0x3F) as u8;
        return 2;
    }

    if codepoint < 0xFFFF {
        out[0] = 0xE0 | (codepoint >> 12) as u8;
        out[1] = 0x80 | (codepoint >> 6) as u8;
        out[2] = 0x80 | (codepoint & 0x3F) as u8;
        return 3;
    }

    if codepoint < 0x10FFFF {
        out[0] = 0xF0 | (codepoint >> 18) as u8;
        out[1] = 0x80 | (codepoint >> 12) as u8;
        out[2] = 0x80 | (codepoint >> 6) as u8;
        out[3] = 0x80 | (codepoint & 0x3F) as u8;
        return 4;
    }

    0
}

// TODO: change this with temporary (cp, offset, size) until from_offset
pub fn get_previous_codepoint_start(data: &[u8], from_offset: u64) -> u64 {

    //                 cp    size   offset
    let mut cp_info: [(char, usize, u64); 8] = [('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0)];
    let mut nr_cpinfo = 0;

    // rewind upto 4 bytes
    // and decode forward / save offset
    let mut off = if from_offset > 4 { from_offset - 4 } else { 0 };
    while off != from_offset {
        let (cp, _, size) = get_codepoint(data, off);

        cp_info[nr_cpinfo] = (cp, size, off);
        nr_cpinfo += 1;

        off += size as u64;
    }

    if nr_cpinfo != 0 {
        cp_info[nr_cpinfo - 1].2
    } else {
        from_offset
    }
}

pub fn get_next_codepoint_start(data: &[u8], from_offset: u64) -> u64 {
    let (_, _, size) = get_codepoint(data, from_offset);
    from_offset + size as u64
}

pub fn get_codepoint(data: &[u8], from_offset: u64) -> (char, u64, usize) {

    let mut state = 0;
    let mut codep = 0;
    let mut size = 0;

    for off in from_offset as usize..data.len() {

        let b = data[off];

        size += 1;
        state = decode_byte(&mut state, b, &mut codep);
        match state {
            0 => {
                break;
            }
            1 => {
                // decode error : invalid sequence
                codep = 0xfffd;
                size = 1; // force restart @ next byte
                break;
            }
            _ => {}
        }
    }

    // TODO return Result<(char, usize), status> -> state != 1|0 -> need mode data
    (::core::codec::text::u32_to_char(codep), from_offset, size)
}

pub fn get_prev_codepoint(data: &[u8], from_offset: u64) -> (char, u64, usize) {
    let offset = get_previous_codepoint_start(data, from_offset);
    get_codepoint(data, offset)
}

#[test]
fn test_codec_encode() {

    let expect_cp: [u8; 4] = [0xe2, 0x82, 0xac, 0x00];
    let mut mut_cp: [u8; 4] = [0x00, 0x00, 0x00, 0x00];

    let n = encode(0x20ac as u32, &mut mut_cp);
    assert_eq!(n, 3);
    assert_eq!(mut_cp, expect_cp);

}

#[test]
fn test_codec_decode() {

    use core::codec::text::u32_to_char;

    let mut state: u32 = 0;
    let mut codep: u32 = 0;

    let sequence: [u8; 4] = [0xe2, 0x82, 0xac, 0x00];
    for b in &sequence {
        println!("decode byte '{:x}'", *b);
        state = decode_byte(&mut state, *b, &mut codep);
        match state {
            UTF8_ACCEPT => {
                break;
            }
            UTF8_REJECT => {
                println!("invalid utf8 sequence");
                break;
            }
            _ => {
                continue;
            }
        }
    }

    println!("decoded codepoint value {:x}", codep);
    let c = u32_to_char(codep);
    println!("decoded codepoint char {}", c);

    assert_eq!(codep, 0x20ac);
}

#[test]
fn test2_codec_decode() {

    let data: [u8; 27] = [0xe2, 0x82, 0xac, 0xe2, 0x82, 0x61, 0x0a, 0x82, 0xac, 0xe2, 0x82, 0x61,
                          0x0a, 0xac, 0xe2, 0x82, 0x61, 0x0a, 0xe2, 0x82, 0x61, 0x0a, 0x82, 0x61,
                          0x0a, 0x61, 0x0a];

    let mut state: u32 = 0;
    let mut codep: u32 = 0;

    for b in &data {
        println!("decode byte '{:x}'", *b);
        state = decode_byte(&mut state, *b, &mut codep);
        println!("state  '{}'", state);
        match state {
            UTF8_ACCEPT => {
                println!("decoded cp = '{:?}'", codep);
                state = 0;
                codep = 0;
            }
            UTF8_REJECT => {
                println!("invalid utf8 sequence, restart");
                state = 0;
                codep = 0;
                state = decode_byte(&mut state, *b, &mut codep);
                match state {
                    UTF8_ACCEPT => {
                        println!("decoded cp = '{:?}'", codep);
                        state = 0;
                        codep = 0;
                    }
                    UTF8_REJECT => {
                        println!("invalid utf8 sequence, restart");
                        state = 0;
                        codep = 0;
                    }
                    _ => {}

                }
            }

            _ => {
                continue;
            }
        }
    }
}
<commit_msg>fix get_previous_codepoint_start do not go beyond from_offset<commit_after>// {
//   Derived from : Bjoern Hoehrmann work
//   Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
//   See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.

pub const UTF8_ACCEPT: u32 = 0;
pub const UTF8_REJECT: u32 = 1;

static UTF8D: &'static [u8] = &[
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
];

/*
    state UTF8_ACCEPT => initial state or decoding successful
    state UTF8_REJECT => error
    state other => intermediate states need more inputs
*/
#[inline]
pub fn decode_byte(state: &mut u32, byte: u8, codep: &mut u32) -> u32 {

    let cp_type = UTF8D[byte as usize] as u32;

    *codep = if *state != UTF8_ACCEPT {
        (byte & 0x3f) as u32 | (*codep << 6)
    } else {
        (0xff >> cp_type) as u32 & (byte as u32)
    };

    *state = UTF8D[256 + (*state * 16) as usize + cp_type as usize] as u32;
    *state
}

// }

#[inline]
pub fn is_codepoint_start(byte: u8) -> bool {

    if byte < 0x80 {
        return true;
    }

    if byte >= 0xC2 && byte <= 0xDF {
        return true;
    }

    if byte >= 0xC2 && byte <= 0xDF {
        return true;
    }

    if byte >= 0xE0 && byte <= 0xEC {
        return true;
    }

    if byte >= 0xED && byte <= 0xEF {
        return true;
    }

    if byte >= 0xF0 && byte <= 0xF4 {
        return true;
    }

    false
}

// return 0 on error, or the number of written bytes
pub fn encode(codepoint: u32, out: &mut [u8; 4]) -> usize {
    if codepoint < 0x80 {
        out[0] = (codepoint & 0x7F) as u8;
        return 1;
    }

    if codepoint < 0x800 {
        out[0] = 0xC0 | (codepoint >> 6) as u8;
        out[1] = 0x80 | (codepoint & 0x3F) as u8;
        return 2;
    }

    if codepoint < 0xFFFF {
        out[0] = 0xE0 | (codepoint >> 12) as u8;
        out[1] = 0x80 | (codepoint >> 6) as u8;
        out[2] = 0x80 | (codepoint & 0x3F) as u8;
        return 3;
    }

    if codepoint < 0x10FFFF {
        out[0] = 0xF0 | (codepoint >> 18) as u8;
        out[1] = 0x80 | (codepoint >> 12) as u8;
        out[2] = 0x80 | (codepoint >> 6) as u8;
        out[3] = 0x80 | (codepoint & 0x3F) as u8;
        return 4;
    }

    0
}

// TODO: change this with temporary (cp, offset, size) until from_offset
pub fn get_previous_codepoint_start(data: &[u8], from_offset: u64) -> u64 {

    //                 cp    size   offset
    let mut cp_info: [(char, usize, u64); 8] = [('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0),
                                                ('\0', 0, 0)];
    let mut nr_cpinfo = 0;

    // rewind upto 4 bytes
    // and decode forward / save offset
    let mut off = if from_offset > 4 { from_offset - 4 } else { 0 };
    while off < from_offset {
        let (cp, _, size) = get_codepoint(data, off);

        cp_info[nr_cpinfo] = (cp, size, off);
        nr_cpinfo += 1;

        off += size as u64;
    }

    if nr_cpinfo != 0 {
        cp_info[nr_cpinfo - 1].2
    } else {
        from_offset
    }
}

pub fn get_next_codepoint_start(data: &[u8], from_offset: u64) -> u64 {
    let (_, _, size) = get_codepoint(data, from_offset);
    from_offset + size as u64
}

pub fn get_codepoint(data: &[u8], from_offset: u64) -> (char, u64, usize) {

    let mut state = 0;
    let mut codep = 0;
    let mut size = 0;

    for off in from_offset as usize..data.len() {

        let b = data[off];

        size += 1;
        state = decode_byte(&mut state, b, &mut codep);
        match state {
            0 => {
                break;
            }
            1 => {
                // decode error : invalid sequence
                codep = 0xfffd;
                size = 1; // force restart @ next byte
                break;
            }
            _ => {}
        }
    }

    // TODO return Result<(char, usize), status> -> state != 1|0 -> need mode data
    (::core::codec::text::u32_to_char(codep), from_offset, size)
}

pub fn get_prev_codepoint(data: &[u8], from_offset: u64) -> (char, u64, usize) {
    let offset = get_previous_codepoint_start(data, from_offset);
    get_codepoint(data, offset)
}

#[test]
fn test_codec_encode() {

    let expect_cp: [u8; 4] = [0xe2, 0x82, 0xac, 0x00];
    let mut mut_cp: [u8; 4] = [0x00, 0x00, 0x00, 0x00];

    let n = encode(0x20ac as u32, &mut mut_cp);
    assert_eq!(n, 3);
    assert_eq!(mut_cp, expect_cp);

}

#[test]
fn test_codec_decode() {

    use core::codec::text::u32_to_char;

    let mut state: u32 = 0;
    let mut codep: u32 = 0;

    let sequence: [u8; 4] = [0xe2, 0x82, 0xac, 0x00];
    for b in &sequence {
        println!("decode byte '{:x}'", *b);
        state = decode_byte(&mut state, *b, &mut codep);
        match state {
            UTF8_ACCEPT => {
                break;
            }
            UTF8_REJECT => {
                println!("invalid utf8 sequence");
                break;
            }
            _ => {
                continue;
            }
        }
    }

    println!("decoded codepoint value {:x}", codep);
    let c = u32_to_char(codep);
    println!("decoded codepoint char {}", c);

    assert_eq!(codep, 0x20ac);
}

#[test]
fn test2_codec_decode() {

    let data: [u8; 27] = [0xe2, 0x82, 0xac, 0xe2, 0x82, 0x61, 0x0a, 0x82, 0xac, 0xe2, 0x82, 0x61,
                          0x0a, 0xac, 0xe2, 0x82, 0x61, 0x0a, 0xe2, 0x82, 0x61, 0x0a, 0x82, 0x61,
                          0x0a, 0x61, 0x0a];

    let mut state: u32 = 0;
    let mut codep: u32 = 0;

    for b in &data {
        println!("decode byte '{:x}'", *b);
        state = decode_byte(&mut state, *b, &mut codep);
        println!("state  '{}'", state);
        match state {
            UTF8_ACCEPT => {
                println!("decoded cp = '{:?}'", codep);
                state = 0;
                codep = 0;
            }
            UTF8_REJECT => {
                println!("invalid utf8 sequence, restart");
                state = 0;
                codep = 0;
                state = decode_byte(&mut state, *b, &mut codep);
                match state {
                    UTF8_ACCEPT => {
                        println!("decoded cp = '{:?}'", codep);
                        state = 0;
                        codep = 0;
                    }
                    UTF8_REJECT => {
                        println!("invalid utf8 sequence, restart");
                        state = 0;
                        codep = 0;
                    }
                    _ => {}

                }
            }

            _ => {
                continue;
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Gfx-rs Developers.
//
// 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.

//!

use Backend;
use std::marker::PhantomData;

mod compute;
mod graphics;
mod raw;
mod renderpass;
mod transfer;

pub use self::graphics::*;
pub use self::raw::RawCommandBuffer;
pub use self::renderpass::*;
pub use self::transfer::*;

/// Thread-safe finished command buffer for submission.
pub struct Submit<B: Backend, C>(pub(crate) B::CommandBuffer, PhantomData<C>);
unsafe impl<B: Backend, C> Send for Submit<B, C> {}

impl<B: Backend, C> Submit<B, C> {
    ///
    pub unsafe fn new(buffer: CommandBuffer<B, C>) -> Self {
        Submit(buffer.raw.clone(), PhantomData)
    }
}

/// Command buffer with compute, graphics and transfer functionality.
pub struct CommandBuffer<'a, B: Backend, C> {
    pub(crate) raw: &'a mut B::CommandBuffer,
    _capability: PhantomData<C>,
}

impl<'a, B: Backend, C> CommandBuffer<'a, B, C> {
    /// Create a new typed command buffer from a raw command pool.
    pub unsafe fn new(raw: &'a mut B::CommandBuffer) -> Self {
        CommandBuffer {
            raw,
            _capability: PhantomData,
        }
    }

    /// Finish recording commands to the command buffers.
    ///
    /// The command buffer will be consumed and can't be modified further.
    /// The command pool must be reset to able to re-record commands.
    pub fn finish(mut self) -> Submit<B, C> {
        self.raw.finish();
        unsafe {
            Submit::new(self)
        }
    }
}
<commit_msg>[ll] core: Finish command buffer on drop<commit_after>// Copyright 2017 The Gfx-rs Developers.
//
// 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.

//!

use Backend;
use std::marker::PhantomData;

mod compute;
mod graphics;
mod raw;
mod renderpass;
mod transfer;

pub use self::graphics::*;
pub use self::raw::RawCommandBuffer;
pub use self::renderpass::*;
pub use self::transfer::*;

/// Thread-safe finished command buffer for submission.
pub struct Submit<B: Backend, C>(pub(crate) B::CommandBuffer, PhantomData<C>);
unsafe impl<B: Backend, C> Send for Submit<B, C> {}

impl<B: Backend, C> Submit<B, C> {
    ///
    pub unsafe fn new(buffer: CommandBuffer<B, C>) -> Self {
        Submit(buffer.raw.clone(), PhantomData)
    }
}

/// Command buffer with compute, graphics and transfer functionality.
pub struct CommandBuffer<'a, B: Backend, C> {
    pub(crate) raw: &'a mut B::CommandBuffer,
    _capability: PhantomData<C>,
}

impl<'a, B: Backend, C> CommandBuffer<'a, B, C> {
    /// Create a new typed command buffer from a raw command pool.
    pub unsafe fn new(raw: &'a mut B::CommandBuffer) -> Self {
        CommandBuffer {
            raw,
            _capability: PhantomData,
        }
    }

    /// Finish recording commands to the command buffers.
    ///
    /// The command buffer will be consumed and can't be modified further.
    /// The command pool must be reset to able to re-record commands.
    pub fn finish(self) -> Submit<B, C> {
        unsafe { Submit::new(self) }
    }
}

impl<'a, B: Backend, C> Drop for CommandBuffer<'a, B, C> {
    fn drop(&mut self) {
        self.raw.finish();
    }
}
<|endoftext|>


<commit_before><commit_msg>Fixed unit test to build with peel 0.7.0<commit_after><|endoftext|>


<commit_before>//! Implementation of `cpp_to_rust`'s build script.
//! Default generated build script uses this crate as a build dependency
//! and just calls `cpp_to_rust_build_tools::run()`.
//! If a custom build script is used, it should either
//! use the same call to execute all operations normally
//! or use `Config` type to change the build script's settings.
//!
//! See [README](https://github.com/rust-qt/ritual) of the repository root for more information.
//!
//! Crates generated by `ritual` require multiple custom build operations:
//!
//! - Apply extra build configuration passed via environment variables
//!   (`RITUAL_LIBRARY_PATH`, `RITUAL_FRAMEWORK_PATH`, `RITUAL_INCLUDE_PATH`);
//! - Select active build configuration based on current target;
//! - Build the C++ wrapper library;
//! - Generate `ffi.rs` file with actual linking attributes;
//! - Determine sizes of C++ types with stack allocation place;
//! - Print `cargo` attributes required for linking the crate.
//! It appears a common task to perform other custom operations in the build script,
//! so all the above operations are implemented as a separate crate
//! which is used as build dependency of the default generated build script.
//! If a custom build script is defined, it should use this crate's API to perform
//! the necessary build operations.

use log::info;
pub use ritual_common as common;
use ritual_common::cpp_build_config::{CppBuildConfig, CppBuildPaths, CppLibraryType};
use ritual_common::cpp_lib_builder::{BuildType, CMakeConfigData, CppLibBuilder};
use ritual_common::errors::{bail, err_msg, FancyUnwrap, Result, ResultExt};
use ritual_common::file_utils::{create_file, file_to_string, load_json, path_to_str};
use ritual_common::target::{current_target, LibraryTarget};
use ritual_common::utils::{exe_suffix, get_command_output};
use ritual_common::{env_var_names, BuildScriptData};
use std::env;
use std::io::Write;
use std::path::PathBuf;
use std::process::{self, Command};

/// Configuration of the build script.
#[derive(Debug)]
pub struct Config {
    cpp_build_paths: CppBuildPaths,
    build_script_data: BuildScriptData,
    current_cpp_library_version: Option<String>,
}

fn manifest_dir() -> Result<PathBuf> {
    let dir =
        env::var("CARGO_MANIFEST_DIR").with_context(|_| "CARGO_MANIFEST_DIR env var is missing")?;
    Ok(PathBuf::from(dir))
}
fn out_dir() -> Result<PathBuf> {
    let dir = env::var("OUT_DIR").with_context(|_| "OUT_DIR env var is missing")?;
    Ok(PathBuf::from(dir))
}

fn build_script_data() -> Result<BuildScriptData> {
    load_json(manifest_dir()?.join("build_script_data.json"))
}

impl Config {
    /// Constructs default configuration state based on
    /// information in the generated `build_script_data.json` file
    /// located at the crate root. The caller may change
    /// `CppBuildPaths` and `CppBuildConfig` values stored in this object
    /// and call `config.run()` to apply them.
    pub fn new() -> Result<Self> {
        Ok(Config {
            build_script_data: build_script_data()?,
            cpp_build_paths: CppBuildPaths::default(),
            current_cpp_library_version: None,
        })
    }

    pub fn set_current_cpp_library_version(&mut self, version: Option<String>) {
        self.current_cpp_library_version = version;
    }

    /// Returns version of the native C++ library used for generating this crate.
    /// This is the value set with `Config::set_cpp_lib_version` during generation,
    /// or `None` if the version was not set.
    pub fn known_targets(&self) -> &[LibraryTarget] {
        &self.build_script_data.known_targets
    }

    /// Returns current `CppBuildConfig` data.
    pub fn cpp_build_config(&self) -> &CppBuildConfig {
        &self.build_script_data.cpp_build_config
    }
    /// Returns mutable `CppBuildConfig` data.
    pub fn cpp_build_config_mut(&mut self) -> &mut CppBuildConfig {
        &mut self.build_script_data.cpp_build_config
    }
    /// Sets new `CppBuildConfig` data.
    pub fn set_cpp_build_config(&mut self, config: CppBuildConfig) {
        self.build_script_data.cpp_build_config = config;
    }
    /// Returns current `CppBuildPaths` data.
    pub fn cpp_build_paths(&self) -> &CppBuildPaths {
        &self.cpp_build_paths
    }
    /// Returns mutable `CppBuildPaths` data.
    pub fn cpp_build_paths_mut(&mut self) -> &mut CppBuildPaths {
        &mut self.cpp_build_paths
    }
    /// Sets new `CppBuildPaths` data.
    pub fn set_cpp_build_paths(&mut self, config: CppBuildPaths) {
        self.cpp_build_paths = config;
    }

    /// Same as `run()`, but result of the operation is returned to the caller.
    pub fn run_and_return(mut self) -> Result<()> {
        let current_target = LibraryTarget {
            target: current_target(),
            cpp_library_version: self.current_cpp_library_version.clone(),
        };

        let is_known_target = self
            .build_script_data
            .known_targets
            .contains(&current_target);

        if !is_known_target {
            println!("Current target is unknown: {}", current_target.short_text());
            println!("Known targets:");
            for target in &self.build_script_data.known_targets {
                println!("* {}", target.short_text());
            }
            // TODO: if the current version or target is unknown,
            //       emit a warning and set the closest known version instead
        }

        self.cpp_build_paths.apply_env();
        let cpp_build_config_data = self
            .build_script_data
            .cpp_build_config
            .eval(&current_target.target)?;

        let out_dir = out_dir()?;
        let c_lib_install_dir = out_dir.join("c_lib_install");
        let manifest_dir = manifest_dir()?;
        let profile = env::var("PROFILE").with_context(|_| "PROFILE env var is missing")?;
        info!("Building C++ wrapper library");

        let library_type = cpp_build_config_data
            .library_type()
            .ok_or_else(|| err_msg("library type (shared or static) is not set"))?;

        let cmake_config = CMakeConfigData {
            cpp_build_config_data: &cpp_build_config_data,
            cpp_build_paths: &self.cpp_build_paths,
            library_type: Some(library_type),
            cpp_library_version: self.current_cpp_library_version.clone(),
        };

        let cmake_vars = cmake_config.cmake_vars()?.into_iter().collect();
        CppLibBuilder {
            cmake_source_dir: manifest_dir.join("c_lib"),
            build_dir: out_dir.join("c_lib_build"),
            install_dir: Some(c_lib_install_dir.clone()),
            num_jobs: env::var("NUM_JOBS").ok().and_then(|x| x.parse().ok()),
            cmake_vars,
            build_type: match profile.as_str() {
                "debug" => BuildType::Debug,
                "release" => BuildType::Release,
                _ => bail!("unknown value of PROFILE env var: {}", profile),
            },
            capture_output: false,
            skip_cmake: false,
            skip_cmake_after_first_run: false,
        }
        .run()?;
        {
            info!("Generating ffi.rs file");
            let mut ffi_file = create_file(out_dir.join("ffi.rs"))?;
            if cpp_build_config_data.library_type() == Some(CppLibraryType::Shared) {
                writeln!(
                    ffi_file,
                    "#[link(name = \"{}\")]",
                    &self.build_script_data.cpp_wrapper_lib_name
                )?;
            } else {
                writeln!(
                    ffi_file,
                    "#[link(name = \"{}\", kind = \"static\")]",
                    &self.build_script_data.cpp_wrapper_lib_name
                )?;
            }
            write!(
                ffi_file,
                "{}",
                file_to_string(manifest_dir.join("src").join("ffi.in.rs"))?
            )?;
        }
        {
            info!("Requesting type sizes");
            let mut command =
                Command::new(c_lib_install_dir.join(format!("sized_types{}", exe_suffix())));
            let mut file = create_file(out_dir.join("sized_types.rs"))?;
            write!(file, "{}", get_command_output(&mut command)?)?;
        }

        for name in cpp_build_config_data.linked_libs() {
            println!("cargo:rustc-link-lib={}", name);
        }
        if crate::common::target::current_env() != crate::common::target::Env::Msvc {
            // TODO: make it configurable
            println!("cargo:rustc-link-lib=stdc++");
        }
        for name in cpp_build_config_data.linked_frameworks() {
            println!("cargo:rustc-link-lib=framework={}", name);
        }
        for path in self.cpp_build_paths.lib_paths() {
            println!("cargo:rustc-link-search=native={}", path_to_str(path)?);
        }
        for path in self.cpp_build_paths.framework_paths() {
            println!("cargo:rustc-link-search=framework={}", path_to_str(path)?);
        }
        println!(
            "cargo:rustc-link-search=native={}",
            path_to_str(&c_lib_install_dir)?
        );

        if let Some(version) = self.current_cpp_library_version {
            println!("cargo:rustc-cfg=cpp_lib_version={:?}", version);
        }
        println!("cargo:rustc-cfg=ritual_true");
        if env::var(env_var_names::RUSTDOC).is_ok() {
            println!("cargo:rustc-cfg=ritual_rustdoc");
        }

        for var in &[
            env_var_names::RUSTDOC,
            env_var_names::LIBRARY_PATH,
            env_var_names::FRAMEWORK_PATH,
            env_var_names::INCLUDE_PATH,
        ] {
            println!("cargo:rerun-if-env-changed={}", var);
        }
        info!("ritual build script finished.");
        Ok(())
    }

    /// Starts build script with current configuration.
    /// The build script performs the following operations:
    ///
    /// - Build the C++ wrapper library;
    /// - Generate `ffi.rs` file with actual link attributes;
    /// - Determine C++ type sizes on current platform and generate `sized_types.rs`;
    /// - Report linking information to `cargo`.
    ///
    /// This function ends the process with the appropriate error code and never
    /// returns to the caller.
    pub fn run(self) -> ! {
        self.run_and_return().fancy_unwrap();
        process::exit(0)
    }
}

/// Same as `run()`, but result of the operation is returned to the caller.
pub fn run_and_return() -> Result<()> {
    Config::new()?.run_and_return()
}

/// Runs the build script with default configuration.
/// See `Config::run` for more information.
pub fn run() -> ! {
    let config = Config::new().fancy_unwrap();
    config.run()
}
<commit_msg>use libc++ on macos<commit_after>//! Implementation of `cpp_to_rust`'s build script.
//! Default generated build script uses this crate as a build dependency
//! and just calls `cpp_to_rust_build_tools::run()`.
//! If a custom build script is used, it should either
//! use the same call to execute all operations normally
//! or use `Config` type to change the build script's settings.
//!
//! See [README](https://github.com/rust-qt/ritual) of the repository root for more information.
//!
//! Crates generated by `ritual` require multiple custom build operations:
//!
//! - Apply extra build configuration passed via environment variables
//!   (`RITUAL_LIBRARY_PATH`, `RITUAL_FRAMEWORK_PATH`, `RITUAL_INCLUDE_PATH`);
//! - Select active build configuration based on current target;
//! - Build the C++ wrapper library;
//! - Generate `ffi.rs` file with actual linking attributes;
//! - Determine sizes of C++ types with stack allocation place;
//! - Print `cargo` attributes required for linking the crate.
//! It appears a common task to perform other custom operations in the build script,
//! so all the above operations are implemented as a separate crate
//! which is used as build dependency of the default generated build script.
//! If a custom build script is defined, it should use this crate's API to perform
//! the necessary build operations.

use log::info;
pub use ritual_common as common;
use ritual_common::cpp_build_config::{CppBuildConfig, CppBuildPaths, CppLibraryType};
use ritual_common::cpp_lib_builder::{BuildType, CMakeConfigData, CppLibBuilder};
use ritual_common::errors::{bail, err_msg, FancyUnwrap, Result, ResultExt};
use ritual_common::file_utils::{create_file, file_to_string, load_json, path_to_str};
use ritual_common::target::{current_target, LibraryTarget, OS};
use ritual_common::utils::{exe_suffix, get_command_output};
use ritual_common::{env_var_names, BuildScriptData};
use std::env;
use std::io::Write;
use std::path::PathBuf;
use std::process::{self, Command};

/// Configuration of the build script.
#[derive(Debug)]
pub struct Config {
    cpp_build_paths: CppBuildPaths,
    build_script_data: BuildScriptData,
    current_cpp_library_version: Option<String>,
}

fn manifest_dir() -> Result<PathBuf> {
    let dir =
        env::var("CARGO_MANIFEST_DIR").with_context(|_| "CARGO_MANIFEST_DIR env var is missing")?;
    Ok(PathBuf::from(dir))
}
fn out_dir() -> Result<PathBuf> {
    let dir = env::var("OUT_DIR").with_context(|_| "OUT_DIR env var is missing")?;
    Ok(PathBuf::from(dir))
}

fn build_script_data() -> Result<BuildScriptData> {
    load_json(manifest_dir()?.join("build_script_data.json"))
}

impl Config {
    /// Constructs default configuration state based on
    /// information in the generated `build_script_data.json` file
    /// located at the crate root. The caller may change
    /// `CppBuildPaths` and `CppBuildConfig` values stored in this object
    /// and call `config.run()` to apply them.
    pub fn new() -> Result<Self> {
        Ok(Config {
            build_script_data: build_script_data()?,
            cpp_build_paths: CppBuildPaths::default(),
            current_cpp_library_version: None,
        })
    }

    pub fn set_current_cpp_library_version(&mut self, version: Option<String>) {
        self.current_cpp_library_version = version;
    }

    /// Returns version of the native C++ library used for generating this crate.
    /// This is the value set with `Config::set_cpp_lib_version` during generation,
    /// or `None` if the version was not set.
    pub fn known_targets(&self) -> &[LibraryTarget] {
        &self.build_script_data.known_targets
    }

    /// Returns current `CppBuildConfig` data.
    pub fn cpp_build_config(&self) -> &CppBuildConfig {
        &self.build_script_data.cpp_build_config
    }
    /// Returns mutable `CppBuildConfig` data.
    pub fn cpp_build_config_mut(&mut self) -> &mut CppBuildConfig {
        &mut self.build_script_data.cpp_build_config
    }
    /// Sets new `CppBuildConfig` data.
    pub fn set_cpp_build_config(&mut self, config: CppBuildConfig) {
        self.build_script_data.cpp_build_config = config;
    }
    /// Returns current `CppBuildPaths` data.
    pub fn cpp_build_paths(&self) -> &CppBuildPaths {
        &self.cpp_build_paths
    }
    /// Returns mutable `CppBuildPaths` data.
    pub fn cpp_build_paths_mut(&mut self) -> &mut CppBuildPaths {
        &mut self.cpp_build_paths
    }
    /// Sets new `CppBuildPaths` data.
    pub fn set_cpp_build_paths(&mut self, config: CppBuildPaths) {
        self.cpp_build_paths = config;
    }

    /// Same as `run()`, but result of the operation is returned to the caller.
    pub fn run_and_return(mut self) -> Result<()> {
        let current_target = LibraryTarget {
            target: current_target(),
            cpp_library_version: self.current_cpp_library_version.clone(),
        };

        let is_known_target = self
            .build_script_data
            .known_targets
            .contains(&current_target);

        if !is_known_target {
            println!("Current target is unknown: {}", current_target.short_text());
            println!("Known targets:");
            for target in &self.build_script_data.known_targets {
                println!("* {}", target.short_text());
            }
            // TODO: if the current version or target is unknown,
            //       emit a warning and set the closest known version instead
        }

        self.cpp_build_paths.apply_env();
        let cpp_build_config_data = self
            .build_script_data
            .cpp_build_config
            .eval(&current_target.target)?;

        let out_dir = out_dir()?;
        let c_lib_install_dir = out_dir.join("c_lib_install");
        let manifest_dir = manifest_dir()?;
        let profile = env::var("PROFILE").with_context(|_| "PROFILE env var is missing")?;
        info!("Building C++ wrapper library");

        let library_type = cpp_build_config_data
            .library_type()
            .ok_or_else(|| err_msg("library type (shared or static) is not set"))?;

        let cmake_config = CMakeConfigData {
            cpp_build_config_data: &cpp_build_config_data,
            cpp_build_paths: &self.cpp_build_paths,
            library_type: Some(library_type),
            cpp_library_version: self.current_cpp_library_version.clone(),
        };

        let cmake_vars = cmake_config.cmake_vars()?.into_iter().collect();
        CppLibBuilder {
            cmake_source_dir: manifest_dir.join("c_lib"),
            build_dir: out_dir.join("c_lib_build"),
            install_dir: Some(c_lib_install_dir.clone()),
            num_jobs: env::var("NUM_JOBS").ok().and_then(|x| x.parse().ok()),
            cmake_vars,
            build_type: match profile.as_str() {
                "debug" => BuildType::Debug,
                "release" => BuildType::Release,
                _ => bail!("unknown value of PROFILE env var: {}", profile),
            },
            capture_output: false,
            skip_cmake: false,
            skip_cmake_after_first_run: false,
        }
        .run()?;
        {
            info!("Generating ffi.rs file");
            let mut ffi_file = create_file(out_dir.join("ffi.rs"))?;
            if cpp_build_config_data.library_type() == Some(CppLibraryType::Shared) {
                writeln!(
                    ffi_file,
                    "#[link(name = \"{}\")]",
                    &self.build_script_data.cpp_wrapper_lib_name
                )?;
            } else {
                writeln!(
                    ffi_file,
                    "#[link(name = \"{}\", kind = \"static\")]",
                    &self.build_script_data.cpp_wrapper_lib_name
                )?;
            }
            write!(
                ffi_file,
                "{}",
                file_to_string(manifest_dir.join("src").join("ffi.in.rs"))?
            )?;
        }
        {
            info!("Requesting type sizes");
            let mut command =
                Command::new(c_lib_install_dir.join(format!("sized_types{}", exe_suffix())));
            let mut file = create_file(out_dir.join("sized_types.rs"))?;
            write!(file, "{}", get_command_output(&mut command)?)?;
        }

        for name in cpp_build_config_data.linked_libs() {
            println!("cargo:rustc-link-lib={}", name);
        }

        // TODO: make it configurable
        if crate::common::target::current_os() == OS::MacOS {
            println!("cargo:rustc-link-lib=c++");
        } else if crate::common::target::current_env() != crate::common::target::Env::Msvc {
            println!("cargo:rustc-link-lib=stdc++");
        }

        for name in cpp_build_config_data.linked_frameworks() {
            println!("cargo:rustc-link-lib=framework={}", name);
        }
        for path in self.cpp_build_paths.lib_paths() {
            println!("cargo:rustc-link-search=native={}", path_to_str(path)?);
        }
        for path in self.cpp_build_paths.framework_paths() {
            println!("cargo:rustc-link-search=framework={}", path_to_str(path)?);
        }
        println!(
            "cargo:rustc-link-search=native={}",
            path_to_str(&c_lib_install_dir)?
        );

        if let Some(version) = self.current_cpp_library_version {
            println!("cargo:rustc-cfg=cpp_lib_version={:?}", version);
        }
        println!("cargo:rustc-cfg=ritual_true");
        if env::var(env_var_names::RUSTDOC).is_ok() {
            println!("cargo:rustc-cfg=ritual_rustdoc");
        }

        for var in &[
            env_var_names::RUSTDOC,
            env_var_names::LIBRARY_PATH,
            env_var_names::FRAMEWORK_PATH,
            env_var_names::INCLUDE_PATH,
        ] {
            println!("cargo:rerun-if-env-changed={}", var);
        }
        info!("ritual build script finished.");
        Ok(())
    }

    /// Starts build script with current configuration.
    /// The build script performs the following operations:
    ///
    /// - Build the C++ wrapper library;
    /// - Generate `ffi.rs` file with actual link attributes;
    /// - Determine C++ type sizes on current platform and generate `sized_types.rs`;
    /// - Report linking information to `cargo`.
    ///
    /// This function ends the process with the appropriate error code and never
    /// returns to the caller.
    pub fn run(self) -> ! {
        self.run_and_return().fancy_unwrap();
        process::exit(0)
    }
}

/// Same as `run()`, but result of the operation is returned to the caller.
pub fn run_and_return() -> Result<()> {
    Config::new()?.run_and_return()
}

/// Runs the build script with default configuration.
/// See `Config::run` for more information.
pub fn run() -> ! {
    let config = Config::new().fancy_unwrap();
    config.run()
}
<|endoftext|>