<commit_before>#![feature(proc_macro)]

extern crate postgres;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::fs;
use std::fs::File;
use std::io::{Write, BufWriter};

mod pombase {
    use std::rc::Rc;
    use std::collections::HashMap;
    use postgres::Connection;

    use pombase::db::*;

    pub struct Raw {
        pub organisms: Vec<Rc<Organism>>,
        pub cvs: Vec<Rc<Cv>>,
        pub dbs: Vec<Rc<Db>>,
        pub dbxrefs: Vec<Rc<Dbxref>>,
        pub cvterms: Vec<Rc<Cvterm>>,
        pub cvtermpaths: Vec<Rc<Cvtermpath>>,
        pub cvterm_relationships: Vec<Rc<CvtermRelationship>>,
        pub publications: Vec<Rc<Publication>>,
        pub features: Vec<Rc<Feature>>,
        pub feature_cvterms: Vec<Rc<FeatureCvterm>>,
        pub feature_relationships: Vec<Rc<FeatureRelationship>>,
    }

    impl Raw {
        pub fn new(conn: &Connection) -> Raw {
            let mut ret = Raw {
                organisms: vec![],
                cvs: vec![], dbs: vec![], dbxrefs: vec![], cvterms: vec![],
                cvtermpaths: vec![], cvterm_relationships: vec![],
                publications: vec![], features: vec![], feature_cvterms: vec![],
                feature_relationships: vec![],
            };

            let mut organism_map: HashMap<i32, Rc<Organism>> = HashMap::new();
            let mut cv_map: HashMap<i32, Rc<Cv>> = HashMap::new();
            let mut db_map: HashMap<i32, Rc<Db>> = HashMap::new();
            let mut dbxref_map: HashMap<i32, Rc<Dbxref>> = HashMap::new();
            let mut cvterm_map: HashMap<i32, Rc<Cvterm>> = HashMap::new();
            let mut feature_map: HashMap<i32, Rc<Feature>> = HashMap::new();
            let mut feature_cvterm_map: HashMap<i32, Rc<FeatureCvterm>> = HashMap::new();
            let mut feature_relationship_map: HashMap<i32, Rc<FeatureRelationship>> = HashMap::new();
            let mut publication_map: HashMap<i32, Rc<Publication>> = HashMap::new();

            for row in &conn.query("SELECT organism_id, genus, species, abbreviation, common_name FROM organism", &[]).unwrap() {
                let organism = Organism {
                    genus: row.get(1),
                    species: row.get(2),
                    abbreviation: row.get(3),
                    common_name: row.get(4),
                };
                let rc_organism = Rc::new(organism);
                ret.organisms.push(rc_organism.clone());
                organism_map.insert(row.get(0), rc_organism);
            }

            for row in &conn.query("SELECT cv_id, name FROM cv", &[]).unwrap() {
                let cv = Cv {
                    name: row.get(1),
                };
                let rc_cv = Rc::new(cv);
                ret.cvs.push(rc_cv.clone());
                cv_map.insert(row.get(0), rc_cv);
            }

            for row in &conn.query("SELECT db_id, name FROM db", &[]).unwrap() {
                let db_id = row.get(0);
                let db = Db {
                    name: row.get(1),
                };
                let rc_db = Rc::new(db);
                ret.dbs.push(rc_db.clone());
                db_map.insert(db_id, rc_db);
            }

            for row in &conn.query("SELECT dbxref_id, db_id, accession FROM dbxref", &[]).unwrap() {
                let dbxref_id: i32 = row.get(0);
                let db_id: i32 = row.get(1);
                let dbxref = Dbxref {
                    db: db_map.get(&db_id).unwrap().clone(),
                    accession: row.get(2),
                };
                let rc_dbxref = Rc::new(dbxref);
                ret.dbxrefs.push(rc_dbxref.clone());
                dbxref_map.insert(dbxref_id, rc_dbxref);
            }

            for row in &conn.query("SELECT cvterm_id, cv_id, dbxref_id, name FROM cvterm", &[]).unwrap() {
                let cvterm_id: i32 = row.get(0);
                let cv_id: i32 = row.get(1);
                let dbxref_id: i32 = row.get(2);
                let cvterm = Cvterm {
                    cv: cv_map.get(&cv_id).unwrap().clone(),
                    dbxref: dbxref_map.get(&dbxref_id).unwrap().clone(),
                    name: row.get(3),
                };
                let rc_cvterm = Rc::new(cvterm);
                ret.cvterms.push(rc_cvterm.clone());
                cvterm_map.insert(cvterm_id, rc_cvterm);
            }

            for row in &conn.query("SELECT pub_id, uniquename, type_id, title FROM pub", &[]).unwrap() {
                let pub_id: i32 = row.get(0);
                let uniquename: String = row.get(1);
                let type_id: i32 = row.get(2);
                let title: Option<String> = row.get(3);
                let publication = Publication {
                    uniquename: uniquename,
                    pub_type: cvterm_map.get(&type_id).unwrap().clone(),
                    title: title
                };
                let rc_publication = Rc::new(publication);
                ret.publications.push(rc_publication.clone());
                publication_map.insert(pub_id, rc_publication);
            }

            for row in &conn.query("SELECT feature_id, uniquename, name, type_id, organism_id FROM feature", &[]).unwrap() {
                let feature_id = row.get(0);
                let type_id: i32 = row.get(3);
                let organism_id: i32 = row.get(4);
                let feature = Feature {
                    uniquename: row.get(1),
                    name: row.get(2),
                    feat_type: cvterm_map.get(&type_id).unwrap().clone(),
                    organism: organism_map.get(&organism_id).unwrap().clone(),
                };
                let rc_feature = Rc::new(feature);
                ret.features.push(rc_feature.clone());
                feature_map.insert(feature_id, rc_feature);
            }

            for row in &conn.query("SELECT feature_cvterm_id, feature_id, cvterm_id, pub_id, is_not FROM feature_cvterm", &[]).unwrap() {
                let feature_cvterm_id = row.get(0);
                let feature_id = row.get(1);
                let cvterm_id: i32 = row.get(2);
                let pub_id: i32 = row.get(3);
                let is_not: bool = row.get(4);
                let feature_cvterm = FeatureCvterm {
                    feature: feature_map.get(&feature_id).unwrap().clone(),
                    cvterm: cvterm_map.get(&cvterm_id).unwrap().clone(),
                    publication: publication_map.get(&pub_id).unwrap().clone(),
                    is_not: is_not,
                };
                let rc_feature_cvterm = Rc::new(feature_cvterm);
                ret.feature_cvterms.push(rc_feature_cvterm.clone());
                feature_cvterm_map.insert(feature_cvterm_id, rc_feature_cvterm);
            }

            for row in &conn.query("SELECT feature_relationship_id, object_id, subject_id, type_id FROM feature_relationship", &[]).unwrap() {
                let feature_relationship_id = row.get(0);
                let subject_id = row.get(1);
                let object_id: i32 = row.get(2);
                let type_id: i32 = row.get(3);
                let feature_relationship = FeatureRelationship {
                    subject: feature_map.get(&subject_id).unwrap().clone(),
                    object: feature_map.get(&object_id).unwrap().clone(),
                    rel_type: cvterm_map.get(&type_id).unwrap().clone(),
                };
                let rc_feature_relationship = Rc::new(feature_relationship);
                ret.feature_relationships.push(rc_feature_relationship.clone());
                feature_relationship_map.insert(feature_relationship_id, rc_feature_relationship);
            }

            ret
        }
    }

    pub mod db {
        use std::rc::Rc;

        pub struct Organism {
            pub genus: String,
            pub species: String,
            pub abbreviation: String,
            pub common_name: String,
        }
        pub struct Cv {
            pub name: String,
        }
        pub struct Db {
            pub name: String,
        }
        pub struct Dbxref {
            pub accession: String,
            pub db: Rc<Db>,
        }
        pub struct Cvterm {
            pub name: String,
            pub cv: Rc<Cv>,
            pub dbxref: Rc<Dbxref>,
        }
        pub struct Publication {
            pub uniquename: String,
            pub pub_type: Rc<Cvterm>,
            pub title: Option<String>,
        }
        pub struct CvtermRelationship {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub reltype: Rc<Cvterm>,
        }
        pub struct Cvtermpath {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub rel_type: Rc<Cvterm>,
            pub pathdistance: Option<i32>,
        }
        pub struct Feature {
            pub uniquename: String,
            pub name: Option<String>,
            pub feat_type: Rc<Cvterm>,
            pub organism: Rc<Organism>,
        }
        pub struct FeatureCvterm {
            pub feature: Rc<Feature>,
            pub cvterm: Rc<Cvterm>,
            pub publication: Rc<Publication>,
            pub is_not: bool,
        }
        pub struct FeatureRelationship {
            pub subject: Rc<Feature>,
            pub object: Rc<Feature>,
            pub rel_type: Rc<Cvterm>,
        }
    }

    pub mod web {
        pub mod data {
            use std::collections::HashMap;

            type GeneUniquename = String;
            type GeneName = String;

            #[derive(Serialize)]
            pub struct GeneShort {
                pub uniquename: GeneUniquename,
                pub name: Option<GeneName>,
            }

            type TermName = String;
            type TermId = String;
            type TermDef = String;

            #[derive(Serialize)]
            pub struct TermShort {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
            }

            type Evidence = String;

            #[derive(Serialize)]
            pub struct FeatureAnnotation {
                pub term: TermShort,
                pub evidence: Evidence,
            }

            type TypeName = String;
            pub type TypeFeatureAnnotationMap =
                HashMap<TypeName, Vec<FeatureAnnotation>>;

            #[derive(Serialize)]
            pub struct GeneDetails {
                pub uniquename: GeneUniquename,
                pub name: Option<String>,
                pub annotations: TypeFeatureAnnotationMap,
            }

            type UniquenameGeneMap =
                HashMap<GeneUniquename, Vec<GeneDetails>>;

            #[derive(Serialize)]
            pub struct TermAnnotation {
                pub gene: GeneShort,
                pub evidence: Evidence,
            }

            #[derive(Serialize)]
            pub struct TermDetails {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
                pub annotations: Vec<TermAnnotation>,
            }
        }
    }
}

use pombase::Raw;
use pombase::web::data::*;

fn get_web_data(raw: &Raw, organism_genus_species: &String) -> (Vec<GeneDetails>, Vec<TermDetails>) {
    let mut genes: Vec<GeneDetails> = vec![];
    let terms: Vec<TermDetails> = vec![];

    for feat in raw.features.iter().filter(|&f| {
        let feature_org_genus_species = String::new() +
            &f.organism.genus + "_" + &f.organism.species;
        f.feat_type.name == "gene" &&
            feature_org_genus_species == *organism_genus_species
    } ) {
        genes.push(GeneDetails {
            uniquename: feat.uniquename.clone(),
            name: feat.name.clone(),
            annotations: TypeFeatureAnnotationMap::new(),
        });
    }

    (genes, terms)
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.reqopt("c", "connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.reqopt("d", "output-directory",
                "Destination directory for JSON output", "DIR");
    opts.reqopt("O", "organism",
                "Only output genes from this organism (eg. 'Schizosaccharomyces_pombe')", "GENUS_SPECIES");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let connection_string = matches.opt_str("c").unwrap();
    let output_dir = matches.opt_str("d").unwrap();
    let organism_genus_species = matches.opt_str("O").unwrap();

    let subdirs = vec!["gene", "term"];

    for subdir in &subdirs {
        let dir = String::new() + &output_dir + "/" + subdir;
        fs::create_dir_all(&dir).unwrap_or_else(|why| {
            println!("Creating output directory failed: {:?}", why.kind());
        });
    }

    let conn = Connection::connect(connection_string.as_str(), TlsMode::None).unwrap();

    let raw = Raw::new(&conn);

    let (genes, terms) = get_web_data(&raw, &organism_genus_species);

    for gene in &genes {
        let s = serde_json::to_string(&gene).unwrap();
        let file_name = String::new() + &output_dir + "/gene/" + &gene.uniquename + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    for term in &terms {
        let s = serde_json::to_string(&term).unwrap();
        let file_name = String::new() + &output_dir + "/term/" + &term.termid + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    println!("write {} genes", genes.len());
    println!("write {} terms", terms.len());
}
<commit_msg>Tidy status messages<commit_after>#![feature(proc_macro)]

extern crate postgres;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::fs;
use std::fs::File;
use std::io::{Write, BufWriter};

mod pombase {
    use std::rc::Rc;
    use std::collections::HashMap;
    use postgres::Connection;

    use pombase::db::*;

    pub struct Raw {
        pub organisms: Vec<Rc<Organism>>,
        pub cvs: Vec<Rc<Cv>>,
        pub dbs: Vec<Rc<Db>>,
        pub dbxrefs: Vec<Rc<Dbxref>>,
        pub cvterms: Vec<Rc<Cvterm>>,
        pub cvtermpaths: Vec<Rc<Cvtermpath>>,
        pub cvterm_relationships: Vec<Rc<CvtermRelationship>>,
        pub publications: Vec<Rc<Publication>>,
        pub features: Vec<Rc<Feature>>,
        pub feature_cvterms: Vec<Rc<FeatureCvterm>>,
        pub feature_relationships: Vec<Rc<FeatureRelationship>>,
    }

    impl Raw {
        pub fn new(conn: &Connection) -> Raw {
            let mut ret = Raw {
                organisms: vec![],
                cvs: vec![], dbs: vec![], dbxrefs: vec![], cvterms: vec![],
                cvtermpaths: vec![], cvterm_relationships: vec![],
                publications: vec![], features: vec![], feature_cvterms: vec![],
                feature_relationships: vec![],
            };

            let mut organism_map: HashMap<i32, Rc<Organism>> = HashMap::new();
            let mut cv_map: HashMap<i32, Rc<Cv>> = HashMap::new();
            let mut db_map: HashMap<i32, Rc<Db>> = HashMap::new();
            let mut dbxref_map: HashMap<i32, Rc<Dbxref>> = HashMap::new();
            let mut cvterm_map: HashMap<i32, Rc<Cvterm>> = HashMap::new();
            let mut feature_map: HashMap<i32, Rc<Feature>> = HashMap::new();
            let mut feature_cvterm_map: HashMap<i32, Rc<FeatureCvterm>> = HashMap::new();
            let mut feature_relationship_map: HashMap<i32, Rc<FeatureRelationship>> = HashMap::new();
            let mut publication_map: HashMap<i32, Rc<Publication>> = HashMap::new();

            for row in &conn.query("SELECT organism_id, genus, species, abbreviation, common_name FROM organism", &[]).unwrap() {
                let organism = Organism {
                    genus: row.get(1),
                    species: row.get(2),
                    abbreviation: row.get(3),
                    common_name: row.get(4),
                };
                let rc_organism = Rc::new(organism);
                ret.organisms.push(rc_organism.clone());
                organism_map.insert(row.get(0), rc_organism);
            }

            for row in &conn.query("SELECT cv_id, name FROM cv", &[]).unwrap() {
                let cv = Cv {
                    name: row.get(1),
                };
                let rc_cv = Rc::new(cv);
                ret.cvs.push(rc_cv.clone());
                cv_map.insert(row.get(0), rc_cv);
            }

            for row in &conn.query("SELECT db_id, name FROM db", &[]).unwrap() {
                let db_id = row.get(0);
                let db = Db {
                    name: row.get(1),
                };
                let rc_db = Rc::new(db);
                ret.dbs.push(rc_db.clone());
                db_map.insert(db_id, rc_db);
            }

            for row in &conn.query("SELECT dbxref_id, db_id, accession FROM dbxref", &[]).unwrap() {
                let dbxref_id: i32 = row.get(0);
                let db_id: i32 = row.get(1);
                let dbxref = Dbxref {
                    db: db_map.get(&db_id).unwrap().clone(),
                    accession: row.get(2),
                };
                let rc_dbxref = Rc::new(dbxref);
                ret.dbxrefs.push(rc_dbxref.clone());
                dbxref_map.insert(dbxref_id, rc_dbxref);
            }

            for row in &conn.query("SELECT cvterm_id, cv_id, dbxref_id, name FROM cvterm", &[]).unwrap() {
                let cvterm_id: i32 = row.get(0);
                let cv_id: i32 = row.get(1);
                let dbxref_id: i32 = row.get(2);
                let cvterm = Cvterm {
                    cv: cv_map.get(&cv_id).unwrap().clone(),
                    dbxref: dbxref_map.get(&dbxref_id).unwrap().clone(),
                    name: row.get(3),
                };
                let rc_cvterm = Rc::new(cvterm);
                ret.cvterms.push(rc_cvterm.clone());
                cvterm_map.insert(cvterm_id, rc_cvterm);
            }

            for row in &conn.query("SELECT pub_id, uniquename, type_id, title FROM pub", &[]).unwrap() {
                let pub_id: i32 = row.get(0);
                let uniquename: String = row.get(1);
                let type_id: i32 = row.get(2);
                let title: Option<String> = row.get(3);
                let publication = Publication {
                    uniquename: uniquename,
                    pub_type: cvterm_map.get(&type_id).unwrap().clone(),
                    title: title
                };
                let rc_publication = Rc::new(publication);
                ret.publications.push(rc_publication.clone());
                publication_map.insert(pub_id, rc_publication);
            }

            for row in &conn.query("SELECT feature_id, uniquename, name, type_id, organism_id FROM feature", &[]).unwrap() {
                let feature_id = row.get(0);
                let type_id: i32 = row.get(3);
                let organism_id: i32 = row.get(4);
                let feature = Feature {
                    uniquename: row.get(1),
                    name: row.get(2),
                    feat_type: cvterm_map.get(&type_id).unwrap().clone(),
                    organism: organism_map.get(&organism_id).unwrap().clone(),
                };
                let rc_feature = Rc::new(feature);
                ret.features.push(rc_feature.clone());
                feature_map.insert(feature_id, rc_feature);
            }

            for row in &conn.query("SELECT feature_cvterm_id, feature_id, cvterm_id, pub_id, is_not FROM feature_cvterm", &[]).unwrap() {
                let feature_cvterm_id = row.get(0);
                let feature_id = row.get(1);
                let cvterm_id: i32 = row.get(2);
                let pub_id: i32 = row.get(3);
                let is_not: bool = row.get(4);
                let feature_cvterm = FeatureCvterm {
                    feature: feature_map.get(&feature_id).unwrap().clone(),
                    cvterm: cvterm_map.get(&cvterm_id).unwrap().clone(),
                    publication: publication_map.get(&pub_id).unwrap().clone(),
                    is_not: is_not,
                };
                let rc_feature_cvterm = Rc::new(feature_cvterm);
                ret.feature_cvterms.push(rc_feature_cvterm.clone());
                feature_cvterm_map.insert(feature_cvterm_id, rc_feature_cvterm);
            }

            for row in &conn.query("SELECT feature_relationship_id, object_id, subject_id, type_id FROM feature_relationship", &[]).unwrap() {
                let feature_relationship_id = row.get(0);
                let subject_id = row.get(1);
                let object_id: i32 = row.get(2);
                let type_id: i32 = row.get(3);
                let feature_relationship = FeatureRelationship {
                    subject: feature_map.get(&subject_id).unwrap().clone(),
                    object: feature_map.get(&object_id).unwrap().clone(),
                    rel_type: cvterm_map.get(&type_id).unwrap().clone(),
                };
                let rc_feature_relationship = Rc::new(feature_relationship);
                ret.feature_relationships.push(rc_feature_relationship.clone());
                feature_relationship_map.insert(feature_relationship_id, rc_feature_relationship);
            }

            ret
        }
    }

    pub mod db {
        use std::rc::Rc;

        pub struct Organism {
            pub genus: String,
            pub species: String,
            pub abbreviation: String,
            pub common_name: String,
        }
        pub struct Cv {
            pub name: String,
        }
        pub struct Db {
            pub name: String,
        }
        pub struct Dbxref {
            pub accession: String,
            pub db: Rc<Db>,
        }
        pub struct Cvterm {
            pub name: String,
            pub cv: Rc<Cv>,
            pub dbxref: Rc<Dbxref>,
        }
        pub struct Publication {
            pub uniquename: String,
            pub pub_type: Rc<Cvterm>,
            pub title: Option<String>,
        }
        pub struct CvtermRelationship {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub reltype: Rc<Cvterm>,
        }
        pub struct Cvtermpath {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub rel_type: Rc<Cvterm>,
            pub pathdistance: Option<i32>,
        }
        pub struct Feature {
            pub uniquename: String,
            pub name: Option<String>,
            pub feat_type: Rc<Cvterm>,
            pub organism: Rc<Organism>,
        }
        pub struct FeatureCvterm {
            pub feature: Rc<Feature>,
            pub cvterm: Rc<Cvterm>,
            pub publication: Rc<Publication>,
            pub is_not: bool,
        }
        pub struct FeatureRelationship {
            pub subject: Rc<Feature>,
            pub object: Rc<Feature>,
            pub rel_type: Rc<Cvterm>,
        }
    }

    pub mod web {
        pub mod data {
            use std::collections::HashMap;

            type GeneUniquename = String;
            type GeneName = String;

            #[derive(Serialize)]
            pub struct GeneShort {
                pub uniquename: GeneUniquename,
                pub name: Option<GeneName>,
            }

            type TermName = String;
            type TermId = String;
            type TermDef = String;

            #[derive(Serialize)]
            pub struct TermShort {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
            }

            type Evidence = String;

            #[derive(Serialize)]
            pub struct FeatureAnnotation {
                pub term: TermShort,
                pub evidence: Evidence,
            }

            type TypeName = String;
            pub type TypeFeatureAnnotationMap =
                HashMap<TypeName, Vec<FeatureAnnotation>>;

            #[derive(Serialize)]
            pub struct GeneDetails {
                pub uniquename: GeneUniquename,
                pub name: Option<String>,
                pub annotations: TypeFeatureAnnotationMap,
            }

            type UniquenameGeneMap =
                HashMap<GeneUniquename, Vec<GeneDetails>>;

            #[derive(Serialize)]
            pub struct TermAnnotation {
                pub gene: GeneShort,
                pub evidence: Evidence,
            }

            #[derive(Serialize)]
            pub struct TermDetails {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
                pub annotations: Vec<TermAnnotation>,
            }
        }
    }
}

use pombase::Raw;
use pombase::web::data::*;

fn get_web_data(raw: &Raw, organism_genus_species: &String) -> (Vec<GeneDetails>, Vec<TermDetails>) {
    let mut genes: Vec<GeneDetails> = vec![];
    let terms: Vec<TermDetails> = vec![];

    for feat in raw.features.iter().filter(|&f| {
        let feature_org_genus_species = String::new() +
            &f.organism.genus + "_" + &f.organism.species;
        f.feat_type.name == "gene" &&
            feature_org_genus_species == *organism_genus_species
    } ) {
        genes.push(GeneDetails {
            uniquename: feat.uniquename.clone(),
            name: feat.name.clone(),
            annotations: TypeFeatureAnnotationMap::new(),
        });
    }

    (genes, terms)
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.reqopt("c", "connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.reqopt("d", "output-directory",
                "Destination directory for JSON output", "DIR");
    opts.reqopt("O", "organism",
                "Only output genes from this organism (eg. 'Schizosaccharomyces_pombe')", "GENUS_SPECIES");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let connection_string = matches.opt_str("c").unwrap();
    let output_dir = matches.opt_str("d").unwrap();
    let organism_genus_species = matches.opt_str("O").unwrap();

    let subdirs = vec!["gene", "term"];

    for subdir in &subdirs {
        let dir = String::new() + &output_dir + "/" + subdir;
        fs::create_dir_all(&dir).unwrap_or_else(|why| {
            println!("Creating output directory failed: {:?}", why.kind());
        });
    }

    let conn = Connection::connect(connection_string.as_str(), TlsMode::None).unwrap();

    let raw = Raw::new(&conn);

    let (genes, terms) = get_web_data(&raw, &organism_genus_species);

    for gene in &genes {
        let s = serde_json::to_string(&gene).unwrap();
        let file_name = String::new() + &output_dir + "/gene/" + &gene.uniquename + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    for term in &terms {
        let s = serde_json::to_string(&term).unwrap();
        let file_name = String::new() + &output_dir + "/term/" + &term.termid + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    println!("wrote {} genes", genes.len());
    println!("wrote {} terms", terms.len());
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    logger::stdout("Creating GitHub release");
    github::release(&config, &tag_name, &tag_message)
        .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<commit_msg>refactor: move generation and display of changelog into functions<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    logger::stdout("Creating GitHub release");
    github::release(&config, &tag_name, &tag_message)
        .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        let changelog = generate_changelog(repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables, unused_imports)]

#[macro_use]
extern crate ecs;
use ecs::*;

extern crate shader_version;
extern crate input;
extern crate event;
extern crate graphics;
extern crate sdl2_window;
extern crate window;
extern crate opengl_graphics;
extern crate quack;

use window::WindowSettings;

use std::collections::HashMap;
use std::default::Default;
use std::rand::Rng;
use event::{Event, ReleaseEvent, UpdateEvent, PressEvent, RenderEvent};
use quack::Set;

use opengl_graphics::{
    Gl,
};
use sdl2_window::Sdl2Window;

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;
static WINDOW_PADDING: i32 = 20;

//fn draw_system(event: &Event,
//              gl: &mut Gl,
//              positions: &mut Components<Position>,
//              shapes: &mut Components<Shape>,
//              colors: &mut Components<Color>) {
//    use graphics::*;
//    if let Some(args) = event.update_args() {
//        let w = args.width as f64;
//        let h = args.height as f64;
//
//        let pad = WINDOW_PADDING;
//        gl.viewport(pad, pad, w as i32 - 2 * pad, h as i32 - 2 * pad);
//
//        gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
//            graphics::clear([0.2, 0.2, 0.0, 1.0], gl);
//            let r = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
//            r.draw([0.0, 0.0, WINDOW_W, WINDOW_H], &c, gl);
//        });
//
////        c.rgb(0.7, 0.7, 0.7).rect(0.0, 0.0, WINDOW_W, WINDOW_H).border_radius(1.0).draw(gl);
////
////        for (eid, pos) in positions.iter_mut() {
////          let mut shape = Point;
////          let mut border = None;
////          if shapes.contains_key(eid) {
////            shape = shapes.get_mut(eid).varient;
////            border = shapes.get_mut(eid).border;
////          }
////          let (r, g, b) = if colors.contains_key(eid) {
////              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
////          } else {
////              (1.0, 1.0, 1.0)
////          };
////          let drawing = c.rgb(r, g, b);
////          match (shape, border) {
////              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
////              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),
////
////              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
////              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),
////
////              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
////              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
////          };
////        }
//    }
//}

//fn shimmer_system(shimmers: &mut Components<Shimmer>,
//                 colors: &mut Components<Color>) {
//    for (eid, _) in shimmers.iter_mut() {
//        if !colors.contains_key(eid) {
//            continue;
//        }
//        let color = colors.get_mut(eid);
//        let ref mut rng = rand::thread_rng();
//        color.r = rng.gen_range(0.5, 1.0);
//        color.g = rng.gen_range(0.5, 1.0);
//        color.b = rng.gen_range(0.5, 1.0);
//    }
//}
//
//fn move_system(event: &Event,
//               to_delete: &mut Vec<u32>,
//              positions: &mut Components<Position>,
//              shapes: &mut Components<Shape>,
//              clamps: &mut Components<WindowClamp>,
//              velocities: &mut Components<Velocity>) {
//    if let Some(args) = event.update_args {
//        let dt = args.dt;
////
////        for (eid, position) in positions.iter_mut() {
////            if !velocities.contains_key(eid) {
////                continue;
////            }
////
////            // If we have both a position and a velocity, integrate.
////            let velocity = velocities.get_mut(eid);
////            position.x += velocity.x * dt;
////            position.y += velocity.y * dt;
////
////            if !clamps.contains_key(eid) {
////              continue;
////            }
////            let (w, h) = match shapes.find(eid) {
////                Some(&s) =>
////                  match s.varient {
////                    Circle(r) => (r, r),
////                    Square(w,h) => (w, h),
////                    Point => (0.0, 0.0)
////                  },
////                None => (0.0, 0.0)
////            };
////
////            let clamp = clamps.get_mut(eid);
////            let velocity_mult = match clamp.varient {
////              Bounce => -1.0,
////              Stop => 0.0,
////              _ => 1.0
////            };
////            match clamp.varient {
////              Bounce | Stop => {
////                if position.x + w > WINDOW_W {
////                  position.x = WINDOW_W - w;
////                  velocity.x *= velocity_mult;
////                } else if position.x < 0.0 {
////                  position.x = 0.0;
////                  velocity.x *= velocity_mult;
////                }
////                if position.y + h > WINDOW_H {
////                  position.y = WINDOW_H - h;
////                  velocity.y *= velocity_mult;
////                } else if position.y < 0.0 {
////                  position.y = 0.0;
////                  velocity.y *= velocity_mult;
////                }
////              },
////              Remove => {
////                if position.x > WINDOW_W
////                || position.x + w < 0.0 {
////                  to_delete.push(*eid);
////                }
////                if position.y > WINDOW_H
////                || position.y + h < 0.0 {
////                  to_delete.push(*eid);
////                }
////              }
////            }
////        }
//    }
//}
//
//fn control_system(event: &Event,
//              controllers: &mut Components<PlayerController>,
//              velocities: &mut Components<Velocity>) {
//
//    const PADDLE_V: f64 = 800.0;
//
////    for (eid, controller) in controllers.iter_mut() {
////        event.press(|button| {
////            if button == Keyboard(controller.up) {
////                velocities.get_mut(eid).y = -PADDLE_V;
////            } else if button == Keyboard(controller.down) {
////                velocities.get_mut(eid).y = PADDLE_V;
////            }
////        });
////        event.release(|button| {
////            if button == Keyboard(controller.up)
////            || button == Keyboard(controller.down) {
////                velocities.get_mut(eid).y = 0.0;
////            }
////        });
////    }
//}

#[derive(Clone, PartialEq, Debug)]
pub enum ClampVarient {
    Bounce,
    Stop,
    Remove // Acts when item leaves window.
}

#[derive(Clone, PartialEq, Debug)]
pub struct WindowClamp {
    varient: ClampVarient
}

#[derive(Clone, PartialEq, Debug)]
pub struct PlayerController {
    up: input::Button,
    down: input::Button
}

#[derive(Clone, PartialEq, Debug)]
pub struct Position {
    x: f64,
    y: f64
}

#[derive(Clone, PartialEq, Debug)]
pub struct Shimmer;

#[derive(Clone, PartialEq, Debug)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[derive(Clone, PartialEq, Debug)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { ShapeVarient::Point }
}

#[derive(Clone, PartialEq, Debug)]
pub struct Shape {
    varient: ShapeVarient,
    border: Option<f64>
}

#[derive(Clone, PartialEq, Debug)]
pub struct Velocity {
    x: f64,
    y: f64
}

components! {
    Components {
        #[hot] position: Position,
        #[hot] shape: Shape,
        #[hot] velocity: Velocity,
        #[hot] color: Color,
        #[hot] shimmer: Shimmer,
        #[hot] player_controller: PlayerController,
        #[hot] window_clamp: WindowClamp
    }
}

systems! {
    Systems<Components>;
}

fn make_ball(world: &mut World<Components, Systems>) {
    const BALL_R: f64 = 20.0;
    let shape = ShapeVarient::Circle(BALL_R);
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    let entity = world.create_entity(Box::new(
        |entity: BuildData, data: &mut Components| {
        data.position.add(&entity,
            Position{
                x: x,
                y: y
            });
        data.shimmer.add(&entity, Shimmer);
        data.shape.add(&entity,
            Shape {
                varient: shape,
                border: None
            });
        }
    ));

//    let e = world.create_entity(Box::new(
//            |entity: BuildData, data: &mut Components| {
//        data.color.add(&entity,
//            Color{
//                r: 1.0,
//                g: 0.5,
//                b: 0.2
//            });
//        data.window_clamp.add(&entity,
//            WindowClamp {
//               varient: ClampVarient::Bounce
//            });
//    }) as Box<EntityBuilder<Components>>);
}

//fn make_player(ents: &mut Entities, p1: bool) -> u32 {
//    const FROM_WALL: f64 = 20.0;
//    const PADDLE_W: f64 = 20.0;
//    const PADDLE_H: f64 = 150.0;
//    let x = if p1 {
//        FROM_WALL
//    } else {
//        WINDOW_W - FROM_WALL - PADDLE_W
//    };
//    let y = (WINDOW_H - PADDLE_H) / 2.0;
//    let e = Entity::new()
//        .with_player_controller(
//            PlayerController {
//                up: if p1 { keyboard::W } else { keyboard::I },
//                down: if p1 { keyboard::S } else { keyboard::K }
//            })
//        .with_velocity(Velocity { x: 0.0, y: 0.0 } )
//        .with_position(
//            Position{
//                x: x,
//                y: y
//            })
//        .with_shape(
//            Shape {
//                varient: Square(PADDLE_W, PADDLE_H),
//                border: None
//            })
//        .with_color(
//            Color{
//                r: 0.5,
//                g: 0.2,
//                b: 0.8
//            })
//        .with_window_clamp(
//            WindowClamp {
//               varient: Stop
//            });
//    ents.add(e)
//}

fn main() {
    let opengl = shader_version::OpenGL::_3_2;
    let mut window = Sdl2Window::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);

    let mut world = World::<Components, Systems>::new();
    make_ball(&mut world);

    for e in event::events(window) {
        use event::{ ReleaseEvent, UpdateEvent, PressEvent, RenderEvent };

        if let Some(args) = e.update_args() {
            //(args.dt as f32);
        }
        if let Some(args) = e.render_args() {
            use graphics::*;
//            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
//                graphics::clear([0.0, 0.0, 0.0, 1.0], gl);
//                let r = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
//                let off = Color([0.0, 0.0, 0.0, 1.0]);
//                let on = Color([1.0, 1.0, 1.0, 1.0]);
//
//                let w = args.width as f64 / 64.0;
//                let h = args.height as f64 / 32.0;
//
//                for (y,row) in vm.screen_rows().enumerate() {
//                    for (x,byte) in row.iter().enumerate() {
//                        let x = x as f64 * w;
//                        let y = y as f64 * h;
//                        r.set(match *byte { 0 => off, _ => on })
//                        .draw([x, y, w, h], &c, gl);
//                    }
//                }
//            });
        }
    }
}
<commit_msg>Revert "Weird lifetime, closure issue, maybe a bug"<commit_after>#![allow(unused_variables, unused_imports)]

#[macro_use]
extern crate ecs;
use ecs::*;

extern crate shader_version;
extern crate input;
extern crate event;
extern crate graphics;
extern crate sdl2_window;
extern crate window;
extern crate opengl_graphics;
extern crate quack;

use window::WindowSettings;

use std::collections::HashMap;
use std::default::Default;
use std::rand::Rng;
use event::{Event, ReleaseEvent, UpdateEvent, PressEvent, RenderEvent};
use quack::Set;

use opengl_graphics::{
    Gl,
};
use sdl2_window::Sdl2Window;

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;
static WINDOW_PADDING: i32 = 20;

//fn draw_system(event: &Event,
//              gl: &mut Gl,
//              positions: &mut Components<Position>,
//              shapes: &mut Components<Shape>,
//              colors: &mut Components<Color>) {
//    use graphics::*;
//    if let Some(args) = event.update_args() {
//        let w = args.width as f64;
//        let h = args.height as f64;
//
//        let pad = WINDOW_PADDING;
//        gl.viewport(pad, pad, w as i32 - 2 * pad, h as i32 - 2 * pad);
//
//        gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
//            graphics::clear([0.2, 0.2, 0.0, 1.0], gl);
//            let r = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
//            r.draw([0.0, 0.0, WINDOW_W, WINDOW_H], &c, gl);
//        });
//
////        c.rgb(0.7, 0.7, 0.7).rect(0.0, 0.0, WINDOW_W, WINDOW_H).border_radius(1.0).draw(gl);
////
////        for (eid, pos) in positions.iter_mut() {
////          let mut shape = Point;
////          let mut border = None;
////          if shapes.contains_key(eid) {
////            shape = shapes.get_mut(eid).varient;
////            border = shapes.get_mut(eid).border;
////          }
////          let (r, g, b) = if colors.contains_key(eid) {
////              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
////          } else {
////              (1.0, 1.0, 1.0)
////          };
////          let drawing = c.rgb(r, g, b);
////          match (shape, border) {
////              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
////              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),
////
////              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
////              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),
////
////              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
////              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
////          };
////        }
//    }
//}

//fn shimmer_system(shimmers: &mut Components<Shimmer>,
//                 colors: &mut Components<Color>) {
//    for (eid, _) in shimmers.iter_mut() {
//        if !colors.contains_key(eid) {
//            continue;
//        }
//        let color = colors.get_mut(eid);
//        let ref mut rng = rand::thread_rng();
//        color.r = rng.gen_range(0.5, 1.0);
//        color.g = rng.gen_range(0.5, 1.0);
//        color.b = rng.gen_range(0.5, 1.0);
//    }
//}
//
//fn move_system(event: &Event,
//               to_delete: &mut Vec<u32>,
//              positions: &mut Components<Position>,
//              shapes: &mut Components<Shape>,
//              clamps: &mut Components<WindowClamp>,
//              velocities: &mut Components<Velocity>) {
//    if let Some(args) = event.update_args {
//        let dt = args.dt;
////
////        for (eid, position) in positions.iter_mut() {
////            if !velocities.contains_key(eid) {
////                continue;
////            }
////
////            // If we have both a position and a velocity, integrate.
////            let velocity = velocities.get_mut(eid);
////            position.x += velocity.x * dt;
////            position.y += velocity.y * dt;
////
////            if !clamps.contains_key(eid) {
////              continue;
////            }
////            let (w, h) = match shapes.find(eid) {
////                Some(&s) =>
////                  match s.varient {
////                    Circle(r) => (r, r),
////                    Square(w,h) => (w, h),
////                    Point => (0.0, 0.0)
////                  },
////                None => (0.0, 0.0)
////            };
////
////            let clamp = clamps.get_mut(eid);
////            let velocity_mult = match clamp.varient {
////              Bounce => -1.0,
////              Stop => 0.0,
////              _ => 1.0
////            };
////            match clamp.varient {
////              Bounce | Stop => {
////                if position.x + w > WINDOW_W {
////                  position.x = WINDOW_W - w;
////                  velocity.x *= velocity_mult;
////                } else if position.x < 0.0 {
////                  position.x = 0.0;
////                  velocity.x *= velocity_mult;
////                }
////                if position.y + h > WINDOW_H {
////                  position.y = WINDOW_H - h;
////                  velocity.y *= velocity_mult;
////                } else if position.y < 0.0 {
////                  position.y = 0.0;
////                  velocity.y *= velocity_mult;
////                }
////              },
////              Remove => {
////                if position.x > WINDOW_W
////                || position.x + w < 0.0 {
////                  to_delete.push(*eid);
////                }
////                if position.y > WINDOW_H
////                || position.y + h < 0.0 {
////                  to_delete.push(*eid);
////                }
////              }
////            }
////        }
//    }
//}
//
//fn control_system(event: &Event,
//              controllers: &mut Components<PlayerController>,
//              velocities: &mut Components<Velocity>) {
//
//    const PADDLE_V: f64 = 800.0;
//
////    for (eid, controller) in controllers.iter_mut() {
////        event.press(|button| {
////            if button == Keyboard(controller.up) {
////                velocities.get_mut(eid).y = -PADDLE_V;
////            } else if button == Keyboard(controller.down) {
////                velocities.get_mut(eid).y = PADDLE_V;
////            }
////        });
////        event.release(|button| {
////            if button == Keyboard(controller.up)
////            || button == Keyboard(controller.down) {
////                velocities.get_mut(eid).y = 0.0;
////            }
////        });
////    }
//}

#[derive(Clone, PartialEq, Debug)]
pub enum ClampVarient {
    Bounce,
    Stop,
    Remove // Acts when item leaves window.
}

#[derive(Clone, PartialEq, Debug)]
pub struct WindowClamp {
    varient: ClampVarient
}

#[derive(Clone, PartialEq, Debug)]
pub struct PlayerController {
    up: input::Button,
    down: input::Button
}

#[derive(Clone, PartialEq, Debug)]
pub struct Position {
    x: f64,
    y: f64
}

#[derive(Clone, PartialEq, Debug)]
pub struct Shimmer;

#[derive(Clone, PartialEq, Debug)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[derive(Clone, PartialEq, Debug)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { ShapeVarient::Point }
}

#[derive(Clone, PartialEq, Debug)]
pub struct Shape {
    varient: ShapeVarient,
    border: Option<f64>
}

#[derive(Clone, PartialEq, Debug)]
pub struct Velocity {
    x: f64,
    y: f64
}

components! {
    Components {
        #[hot] position: Position,
        #[hot] shape: Shape,
        #[hot] velocity: Velocity,
        #[hot] color: Color,
        #[hot] shimmer: Shimmer,
        #[hot] player_controller: PlayerController,
        #[hot] window_clamp: WindowClamp
    }
}

systems! {
    Systems<Components>;
}

fn make_ball(world: &mut World<Components, Systems>) {
    const BALL_R: f64 = 20.0;
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    let entity = world.create_entity(Box::new(
        |entity: BuildData, data: &mut Components| {
        data.position.add(&entity,
            Position{
                x: x,
                y: y
            });
        data.shimmer.add(&entity, Shimmer);
        data.shape.add(&entity,
            Shape {
                varient: ShapeVarient::Circle(BALL_R)
                border: None
            });
        }
    ));

//    let e = world.create_entity(Box::new(
//            |entity: BuildData, data: &mut Components| {
//        data.color.add(&entity,
//            Color{
//                r: 1.0,
//                g: 0.5,
//                b: 0.2
//            });
//        data.window_clamp.add(&entity,
//            WindowClamp {
//               varient: ClampVarient::Bounce
//            });
//    }) as Box<EntityBuilder<Components>>);
}

//fn make_player(ents: &mut Entities, p1: bool) -> u32 {
//    const FROM_WALL: f64 = 20.0;
//    const PADDLE_W: f64 = 20.0;
//    const PADDLE_H: f64 = 150.0;
//    let x = if p1 {
//        FROM_WALL
//    } else {
//        WINDOW_W - FROM_WALL - PADDLE_W
//    };
//    let y = (WINDOW_H - PADDLE_H) / 2.0;
//    let e = Entity::new()
//        .with_player_controller(
//            PlayerController {
//                up: if p1 { keyboard::W } else { keyboard::I },
//                down: if p1 { keyboard::S } else { keyboard::K }
//            })
//        .with_velocity(Velocity { x: 0.0, y: 0.0 } )
//        .with_position(
//            Position{
//                x: x,
//                y: y
//            })
//        .with_shape(
//            Shape {
//                varient: Square(PADDLE_W, PADDLE_H),
//                border: None
//            })
//        .with_color(
//            Color{
//                r: 0.5,
//                g: 0.2,
//                b: 0.8
//            })
//        .with_window_clamp(
//            WindowClamp {
//               varient: Stop
//            });
//    ents.add(e)
//}

fn main() {
    let opengl = shader_version::OpenGL::_3_2;
    let mut window = Sdl2Window::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);

    let mut world = World::<Components, Systems>::new();
    make_ball(&mut world);

    for e in event::events(window) {
        use event::{ ReleaseEvent, UpdateEvent, PressEvent, RenderEvent };

        if let Some(args) = e.update_args() {
            //(args.dt as f32);
        }
        if let Some(args) = e.render_args() {
            use graphics::*;
//            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
//                graphics::clear([0.0, 0.0, 0.0, 1.0], gl);
//                let r = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
//                let off = Color([0.0, 0.0, 0.0, 1.0]);
//                let on = Color([1.0, 1.0, 1.0, 1.0]);
//
//                let w = args.width as f64 / 64.0;
//                let h = args.height as f64 / 32.0;
//
//                for (y,row) in vm.screen_rows().enumerate() {
//                    for (x,byte) in row.iter().enumerate() {
//                        let x = x as f64 * w;
//                        let y = y as f64 * h;
//                        r.set(match *byte { 0 => off, _ => on })
//                        .draw([x, y, w, h], &c, gl);
//                    }
//                }
//            });
        }
    }
}
<|endoftext|>


<commit_before>// Copyright © 2014, Peter Atashian

#![feature(slicing_syntax, tuple_indexing)]

extern crate core;
extern crate image;
extern crate lodepng;
extern crate serialize;

use core::slice::{Found, NotFound};
use serialize::json::{decode};
use std::cell::{Cell};
use std::cmp::{max};
use std::collections::{PriorityQueue};
use std::io::{File};
use std::iter::{AdditiveIterator};
use std::num::{Zero};

#[deriving(Show)]
struct TermColor(Pixel, u8);
impl PartialEq for TermColor {
    fn eq(&self, o: &TermColor) -> bool {
        self.0 == o.0
    }
}

#[deriving(Show)]
struct Coverage(char, f32);
impl PartialEq for Coverage {
    fn eq(&self, o: &Coverage) -> bool {
        self.1 == o.1
    }
}

#[deriving(Show, PartialEq, PartialOrd)]
struct Pixel(f32, f32, f32);
impl Pixel {
    fn gray(&self) -> f32 {
        (self.0 + self.1 + self.2) / 3.
    }
    fn abs(&self) -> Pixel {
        Pixel(self.0.abs(), self.1.abs(), self.2.abs())
    }
    fn sqsum(&self) -> f32 {
        self.0 * self.0 + self.1 * self.1 + self.2 * self.2
    }
    fn dot(&self, o: &Pixel) -> f32 {
        self.0 * o.0 + self.1 * o.1 + self.2 * o.2
    }
}
impl Add<Pixel, Pixel> for Pixel {
    fn add(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 + o.0, self.1 + o.1, self.2 + o.2)
    }
}
impl Sub<Pixel, Pixel> for Pixel {
    fn sub(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 - o.0, self.1 - o.1, self.2 - o.2)
    }
}
impl Zero for Pixel {
    fn zero() -> Pixel {
        Pixel(0., 0., 0.)
    }
    fn is_zero(&self) -> bool {
        *self == Zero::zero()
    }
}
impl Mul<Pixel, Pixel> for Pixel {
    fn mul(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 * o.0, self.1 * o.1, self.2 * o.2)
    }
}
impl Div<Pixel, Pixel> for Pixel {
    fn div(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 / o.0, self.1 / o.1, self.2 / o.2)
    }
}

#[deriving(Show)]
struct Image {
    pixels: Vec<Cell<Pixel>>,
    width: u32,
    height: u32,
}
impl Image {
    fn get(&self, x: u32, y: u32) -> Pixel {
        self.pixels[(y * self.width + x) as uint].get()
    }
    fn resize(&self, width: u32, height: u32) -> Image {
        let (rw, rh) = (self.width as f32 / (width as f32), self.height as f32 / (height as f32));
        let mut pixels = Vec::with_capacity((width * height) as uint);
        for y in range(0, height) {
            for x in range(0, width) {
                let (x1, x2) = ((x as f32 * rw) as u32, ((x + 1) as f32 * rw) as u32);
                let (y1, y2) = ((y as f32 * rh) as u32, ((y + 1) as f32 * rh) as u32);
                let (x2, y2) = (max(x2, x1 + 1), max(y2, y1 + 1));
                let c = range(x1, x2).map(|x| {
                    range(y1, y2).map(|y| {
                        self.get(x, y)
                    }).sum()
                }).sum();
                let m = 1. / (((x2 - x1) * (y2 - y1)) as f32);
                pixels.push(Cell::new(c * Pixel(m, m, m)));
            }
        }
        assert!(pixels.len() as u32 == width * height);
        Image {
            pixels: pixels,
            width: width,
            height: height,
        }
    }
}

#[deriving(Decodable, Show)]
struct Config {
    winwidth: u32,
    winheight: u32,
    charwidth: u32,
    charheight: u32,
    offsetx: u32,
    offsety: u32,
    chars: String
}
impl Config {
    fn load() -> Config {
        let mut file = File::open(&Path::new("config.json")).unwrap();
        let data = file.read_to_string().unwrap();
        decode(data.as_slice()).unwrap()
    }
}

#[deriving(Show)]
struct Ascii {
    config: Config,
    table: Vec<f32>,
    colors: Vec<TermColor>,
    chars: Vec<Coverage>,
}
impl Ascii {
    fn new() -> Ascii {
        let config = Config::load();
        Ascii {
            config: config,
            table: Vec::new(),
            colors: Vec::new(),
            chars: Vec::new(),
        }
    }
    #[allow(dead_code)]
    fn print_chars(&self) {
        for x in range(0u16, 256) {
            bg(x as u8);
            print!(" ");
        }
        println!("");
        fg(15);
        bg(0);
        println!("{}", self.config.chars);
        let lines = 256 / self.config.winwidth;
        let lines = lines + self.config.chars[].chars().count() as u32 / self.config.winwidth;
        for _ in range(lines + 5, self.config.winheight) {
            println!("");
        }
    }
    fn build_table(&mut self) {
        fn decode(x: f32) -> f32 {
            if x <= 0.04045 {
                x / 12.92
            } else {
                ((x + 0.055) / (1. + 0.055)).powf(2.4)
            }
        }
        self.table = range(0u32, 256).map(|x| decode(x as f32 * (1. / 255.))).collect();
    }
    fn load(&self, p: &Path) -> Image {
        use image::GenericImage;
        assert!(self.table.len() == 256);
        let img = match lodepng::load(p) {
            Ok(x) => x,
            Err(_) => image::open(p).unwrap().to_rgba(),
        };
        let (w, h) = img.dimensions();
        let pixels = img.into_vec();
        let pixels = pixels.iter().map(|p| {
            let (&r, &g, &b, &a) = unsafe {(
                self.table[].unsafe_get(p.0 as uint),
                self.table[].unsafe_get(p.1 as uint),
                self.table[].unsafe_get(p.2 as uint),
                self.table[].unsafe_get(p.3 as uint),
            )};
            Cell::new(Pixel(r * a, g * a, b * a))
        }).collect();
        Image {
            pixels: pixels,
            width: w,
            height: h,
        }
    }
    fn calc_colors(&mut self) {
        let img = self.load(&Path::new("colors.png"));
        self.colors = range(0u32, 256).map(|i| {
            let x = i % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i / self.config.winwidth * self.config.charheight + self.config.offsety;
            TermColor(img.get(x, y), i as u8)
        }).collect();
        let offset = (256 / self.config.winwidth + 1) * self.config.charheight
            + self.config.offsety;
        let (lo, hi) = (self.colors[0].0.gray(), self.colors[15].0.gray());
        self.chars = self.config.chars[].chars().enumerate().map(|(i, c)| {
            let x = i as u32 % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i as u32 / self.config.winwidth * self.config.charheight + offset;
            let p = range(x, x + self.config.charwidth).map(|x| {
                range(y, y + self.config.charheight).map(|y| {
                    img.get(x, y).gray()
                }).sum()
            }).sum() / (self.config.charwidth * self.config.charheight) as f32;
            Coverage(c, (p - lo) / (hi - lo))
        }).collect();
        self.colors.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
        self.colors.dedup();
        self.chars.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
        self.chars.dedup();
        self.chars.retain(|x| x.1 <= 0.5);
    }
    fn prepare(&mut self) {
        self.build_table();
        self.calc_colors();
    }
    fn fit_window(&self, img: &Image) -> Image {
        let (ww, wh) = (self.config.winwidth - 1, self.config.winheight - 3);
        let (cw, ch) = (self.config.charwidth, self.config.charheight);
        let fw = (cw * ww) as f32;
        let fh = (ch * wh) as f32;
        let ratio = fh / fw;
        let imgratio = img.height as f32 / img.width as f32;
        if ratio > imgratio {
            let h = (fw * imgratio / ch as f32) as u32;
            img.resize(ww, h)
        } else {
            let w = (fh / imgratio / cw as f32) as u32;
            img.resize(w, wh)
        }
    }
    fn closest(&self, p: &Pixel) -> (u8, u8, char, Pixel) {
        struct Entry(Pixel, u8, f32);
        impl Ord for Entry {
            fn cmp(&self, o: &Entry) -> Ordering {
                self.partial_cmp(o).unwrap()
            }
        }
        impl PartialOrd for Entry {
            fn partial_cmp(&self, o: &Entry) -> Option<Ordering> {
                self.2.partial_cmp(&o.2).map(|x| x.reverse())
            }
        }
        impl Eq for Entry {}
        impl PartialEq for Entry {
            fn eq(&self, o: &Entry) -> bool {
                self.2 == o.2
            }
        }
        let mut colors: PriorityQueue<_> = self.colors.iter().map(|c| {
            let d = (c.0 - *p).abs().gray();
            Entry(c.0, c.1, d)
        }).collect();
        let colors: Vec<_> = range(0, 10u).map(|_| colors.pop().unwrap()).collect();
        let mut bestdiff = 100f32;
        let mut best = (0, colors[0].1, ' ', p - colors[0].0);
        for c1 in colors.iter() {
            for c2 in colors.iter() {
                let (v1, v2) = (p - c1.0, c2.0 - c1.0);
                let cover = if v2.abs().gray() < 0.0001 {
                    0.
                } else {
                    v1.dot(&v2) / v2.sqsum()
                };
                if cover < 0. || cover > 0.5 { continue }
                let Coverage(ch, n) = match self.chars[].binary_search(|x| {
                    x.1.partial_cmp(&cover).unwrap()
                }) {
                    Found(x) => self.chars[x],
                    NotFound(x) => self.chars[x - 1],
                };
                let color = c1.0 * Pixel(1. - n, 1. - n, 1. - n) + c2.0 * Pixel(n, n, n);
                let diff = color - *p;
                let sdiff = c2.0 - c1.0;
                let d = diff.abs().sqsum();
                if d < bestdiff {
                    bestdiff = d;
                    best = (c2.1, c1.1, ch, diff);
                }
            }
        }
        best
    }
    fn convert(&self, p: &Path) {
        let img = self.load(p);
        let mut img = self.fit_window(&img);
        let width = img.width;
        img.pixels.reserve_additional(width as uint * 2);
        let pixels = img.pixels[];
        for (y, chunk) in pixels.chunks(img.width as uint).enumerate() {
            for (x, p) in chunk.iter().enumerate() {
                let (f, b, c, diff) = self.closest(&p.get());
                let adjust = |ox, oy, mult| {
                    let cell = unsafe { pixels.unsafe_get((y + oy) * (width as uint) + x + ox) };
                    cell.set(cell.get() + diff * Pixel(mult, mult, mult));
                };
                adjust( 1, 0, 5. / 32.);
                adjust( 2, 0, 3. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 1, 4. / 32.);
                adjust(-0, 1, 5. / 32.);
                adjust( 1, 1, 4. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 2, 2. / 32.);
                adjust( 0, 2, 3. / 32.);
                adjust( 1, 2, 2. / 32.);
                fg(f);
                bg(b);
                print!("{}", c);
            }
            bg(0);
            println!("");
        }
    }
}

fn fg(x: u8) {
    print!("\x1b[38;5;{}m", x);
}
fn bg(x: u8) {
    print!("\x1b[48;5;{}m", x);
}

fn main() {
    let mut ascii = Ascii::new();
    // ascii.print_chars();
    ascii.prepare();
    ascii.convert(&Path::new("winter_evening_by_gign_3208-d6vvbvo.png"));
    // ascii.convert(&Path::new("RainbowDashFluff.png"));
}
<commit_msg>Some improvements<commit_after>// Copyright © 2014, Peter Atashian

#![feature(slicing_syntax, tuple_indexing)]

extern crate core;
extern crate image;
extern crate lodepng;
extern crate serialize;

use core::slice::{Found, NotFound};
use serialize::json::{decode};
use std::cell::{Cell};
use std::cmp::{max};
use std::collections::{PriorityQueue};
use std::io::{File};
use std::iter::{AdditiveIterator};
use std::num::{Zero};

#[deriving(Show, Clone)]
struct TermColor(Pixel, u8);
impl PartialEq for TermColor {
    fn eq(&self, o: &TermColor) -> bool {
        self.0 == o.0
    }
}

#[deriving(Show)]
struct Coverage(char, f32);
impl PartialEq for Coverage {
    fn eq(&self, o: &Coverage) -> bool {
        self.1 == o.1
    }
}

#[deriving(Show, PartialEq, PartialOrd, Clone)]
struct Pixel(f32, f32, f32);
impl Pixel {
    fn gray(&self) -> f32 {
        (self.0 + self.1 + self.2) / 3.
    }
    fn abs(&self) -> Pixel {
        Pixel(self.0.abs(), self.1.abs(), self.2.abs())
    }
    fn sqsum(&self) -> f32 {
        self.0 * self.0 + self.1 * self.1 + self.2 * self.2
    }
    fn magnitude(&self) -> f32 {
        self.sqsum().sqrt()
    }
    fn dot(&self, o: &Pixel) -> f32 {
        self.0 * o.0 + self.1 * o.1 + self.2 * o.2
    }
}
impl Add<Pixel, Pixel> for Pixel {
    fn add(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 + o.0, self.1 + o.1, self.2 + o.2)
    }
}
impl Sub<Pixel, Pixel> for Pixel {
    fn sub(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 - o.0, self.1 - o.1, self.2 - o.2)
    }
}
impl Zero for Pixel {
    fn zero() -> Pixel {
        Pixel(0., 0., 0.)
    }
    fn is_zero(&self) -> bool {
        *self == Zero::zero()
    }
}
impl Mul<Pixel, Pixel> for Pixel {
    fn mul(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 * o.0, self.1 * o.1, self.2 * o.2)
    }
}
impl Div<Pixel, Pixel> for Pixel {
    fn div(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 / o.0, self.1 / o.1, self.2 / o.2)
    }
}

#[deriving(Show)]
struct Image {
    pixels: Vec<Cell<Pixel>>,
    width: u32,
    height: u32,
}
impl Image {
    fn get(&self, x: u32, y: u32) -> Pixel {
        self.pixels[(y * self.width + x) as uint].get()
    }
    fn resize(&self, width: u32, height: u32) -> Image {
        let (rw, rh) = (self.width as f32 / (width as f32), self.height as f32 / (height as f32));
        let mut pixels = Vec::with_capacity((width * height) as uint);
        for y in range(0, height) {
            for x in range(0, width) {
                let (x1, x2) = ((x as f32 * rw) as u32, ((x + 1) as f32 * rw) as u32);
                let (y1, y2) = ((y as f32 * rh) as u32, ((y + 1) as f32 * rh) as u32);
                let (x2, y2) = (max(x2, x1 + 1), max(y2, y1 + 1));
                let c = range(x1, x2).map(|x| {
                    range(y1, y2).map(|y| {
                        self.get(x, y)
                    }).sum()
                }).sum();
                let m = 1. / (((x2 - x1) * (y2 - y1)) as f32);
                pixels.push(Cell::new(c * Pixel(m, m, m)));
            }
        }
        assert!(pixels.len() as u32 == width * height);
        Image {
            pixels: pixels,
            width: width,
            height: height,
        }
    }
}

#[deriving(Decodable, Show)]
struct Config {
    winwidth: u32,
    winheight: u32,
    charwidth: u32,
    charheight: u32,
    offsetx: u32,
    offsety: u32,
    chars: String,
    fullcolor: bool,
}
impl Config {
    fn load() -> Config {
        let mut file = File::open(&Path::new("config.json")).unwrap();
        let data = file.read_to_string().unwrap();
        decode(data.as_slice()).unwrap()
    }
}

#[deriving(Show)]
struct Ascii {
    config: Config,
    table: Vec<f32>,
    colors: Vec<TermColor>,
    chars: Vec<Coverage>,
}
impl Ascii {
    fn new() -> Ascii {
        let config = Config::load();
        Ascii {
            config: config,
            table: Vec::new(),
            colors: Vec::new(),
            chars: Vec::new(),
        }
    }
    #[allow(dead_code)]
    fn print_chars(&self) {
        for x in range(0u16, 256) {
            bg(x as u8);
            print!(" ");
        }
        println!("");
        fg(15);
        bg(0);
        println!("{}", self.config.chars);
        let lines = 256 / self.config.winwidth;
        let lines = lines + self.config.chars[].chars().count() as u32 / self.config.winwidth;
        for _ in range(lines + 5, self.config.winheight) {
            println!("");
        }
    }
    fn build_table(&mut self) {
        fn decode(x: f32) -> f32 {
            if x <= 0.04045 {
                x / 12.92
            } else {
                ((x + 0.055) / (1. + 0.055)).powf(2.4)
            }
        }
        self.table = range(0u32, 256).map(|x| decode(x as f32 * (1. / 255.))).collect();
    }
    fn load(&self, p: &Path) -> Image {
        use image::GenericImage;
        assert!(self.table.len() == 256);
        let img = match lodepng::load(p) {
            Ok(x) => x,
            Err(_) => image::open(p).unwrap().to_rgba(),
        };
        let (w, h) = img.dimensions();
        let pixels = img.into_vec();
        let pixels = pixels.iter().map(|p| {
            let (&r, &g, &b, &a) = unsafe {(
                self.table[].unsafe_get(p.0 as uint),
                self.table[].unsafe_get(p.1 as uint),
                self.table[].unsafe_get(p.2 as uint),
                self.table[].unsafe_get(p.3 as uint),
            )};
            Cell::new(Pixel(r * a, g * a, b * a))
        }).collect();
        Image {
            pixels: pixels,
            width: w,
            height: h,
        }
    }
    fn calc_colors(&mut self) {
        let img = self.load(&Path::new("colors.png"));
        self.colors = range(0u32, if self.config.fullcolor { 256 } else { 16 }).map(|i| {
            let x = i % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i / self.config.winwidth * self.config.charheight + self.config.offsety;
            TermColor(img.get(x, y), i as u8)
        }).collect();
        let offset = (256 / self.config.winwidth + 1) * self.config.charheight
            + self.config.offsety;
        let (lo, hi) = (self.colors[0].0.gray(), self.colors[15].0.gray());
        self.chars = self.config.chars[].chars().enumerate().map(|(i, c)| {
            let x = i as u32 % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i as u32 / self.config.winwidth * self.config.charheight + offset;
            let p = range(x, x + self.config.charwidth).map(|x| {
                range(y, y + self.config.charheight).map(|y| {
                    img.get(x, y).gray()
                }).sum()
            }).sum() / (self.config.charwidth * self.config.charheight) as f32;
            Coverage(c, (p - lo) / (hi - lo))
        }).collect();
        self.colors.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
        self.colors.dedup();
        self.chars.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
        self.chars.dedup();
        self.chars.retain(|x| x.1 <= 0.5);
    }
    fn prepare(&mut self) {
        self.build_table();
        self.calc_colors();
    }
    fn fit_window(&self, img: &Image) -> Image {
        let (ww, wh) = (self.config.winwidth - 1, self.config.winheight - 3);
        let (cw, ch) = (self.config.charwidth, self.config.charheight);
        let fw = (cw * ww) as f32;
        let fh = (ch * wh) as f32;
        let ratio = fh / fw;
        let imgratio = img.height as f32 / img.width as f32;
        if ratio > imgratio {
            let h = (fw * imgratio / ch as f32) as u32;
            img.resize(ww, h)
        } else {
            let w = (fh / imgratio / cw as f32) as u32;
            img.resize(w, wh)
        }
    }
    fn get_closest(&self, p: &Pixel) -> Vec<TermColor> {
        struct Entry(Pixel, u8, f32);
        impl Ord for Entry {
            fn cmp(&self, o: &Entry) -> Ordering {
                self.partial_cmp(o).unwrap()
            }
        }
        impl PartialOrd for Entry {
            fn partial_cmp(&self, o: &Entry) -> Option<Ordering> {
                self.2.partial_cmp(&o.2).map(|x| x.reverse())
            }
        }
        impl Eq for Entry {}
        impl PartialEq for Entry {
            fn eq(&self, o: &Entry) -> bool {
                self.2 == o.2
            }
        }
        if self.config.fullcolor {
            let mut colors: PriorityQueue<_> = self.colors.iter().map(|c| {
                let d = (c.0 - *p).abs().gray();
                Entry(c.0, c.1, d)
            }).collect();
            range(0, 20u).map(|_| {
                let e = colors.pop().unwrap();
                TermColor(e.0, e.1)
            }).collect()
        } else {
            self.colors.clone()
        }
    }
    fn closest(&self, p: &Pixel) -> (u8, u8, char, Pixel) {
        let colors = self.get_closest(p);
        let mut bestdiff = 100f32;
        let mut best = (0, colors[0].1, ' ', p - colors[0].0);
        for c1 in colors.iter() {
            for c2 in colors.iter() {
                let (v1, v2) = (p - c1.0, c2.0 - c1.0);
                let cover = if v2.abs().gray() < 0.0001 {
                    0.
                } else {
                    v1.dot(&v2) / v2.sqsum()
                };
                if cover < 0. || cover > 0.5 { continue }
                let Coverage(ch, n) = match self.chars[].binary_search(|x| {
                    x.1.partial_cmp(&cover).unwrap()
                }) {
                    Found(x) => self.chars[x],
                    NotFound(x) => self.chars[x - 1],
                };
                let color = c1.0 * Pixel(1. - n, 1. - n, 1. - n) + c2.0 * Pixel(n, n, n);
                let diff = *p - color;
                let sdiff = c2.0 - c1.0;
                let d = diff.abs().magnitude() + sdiff.abs().magnitude() * 0.2;
                if d < bestdiff {
                    bestdiff = d;
                    best = (c2.1, c1.1, ch, diff);
                }
            }
        }
        best
    }
    fn convert(&self, p: &Path) {
        let img = self.load(p);
        let mut img = self.fit_window(&img);
        let width = img.width;
        img.pixels.reserve_additional(width as uint * 2);
        let pixels = img.pixels[];
        for (y, chunk) in pixels.chunks(img.width as uint).enumerate() {
            for (x, p) in chunk.iter().enumerate() {
                let (f, b, c, diff) = self.closest(&p.get());
                let adjust = |ox, oy, mult| {
                    let cell = unsafe { pixels.unsafe_get((y + oy) * (width as uint) + x + ox) };
                    cell.set(cell.get() + diff * Pixel(mult, mult, mult));
                };
                adjust( 1, 0, 5. / 32.);
                adjust( 2, 0, 3. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 1, 4. / 32.);
                adjust(-0, 1, 5. / 32.);
                adjust( 1, 1, 4. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 2, 2. / 32.);
                adjust( 0, 2, 3. / 32.);
                adjust( 1, 2, 2. / 32.);
                fg(f);
                bg(b);
                print!("{}", c);
            }
            bg(0);
            println!("");
        }
    }
}

fn fg(x: u8) {
    print!("\x1b[38;5;{}m", x);
}
fn bg(x: u8) {
    print!("\x1b[48;5;{}m", x);
}

fn main() {
    let mut ascii = Ascii::new();
    let args = std::os::args();
    if args.len() <= 1 {
        ascii.print_chars();
    } else {
        ascii.prepare();
        ascii.convert(&Path::new(args[1][]));
    }
}
<|endoftext|>