<commit_before>use writer::Writer;
use rustorm::database::DatabaseDev;
use std::fs::File;
use std::fs;
use std::io::Write;

use rustorm::table::Table;
use meta::{MetaCode, StructCode};

/// configuration for generating code

pub struct Config{
    /// the module name to be used when it is possible to unify all tables in 1 module
    /// likely the project name
    pub base_module:Option<String>,
    /// include the has_one, has_many, extension Models
    pub include_table_references:bool,
    ///use the condense name of the has_many else, use the table name referred
    pub use_condensed_name:bool,

    ///generate the is table definition for each table
    pub generate_table_meta:bool,

    /// base directory for the generated content
    pub base_dir: String,
}

impl Config{

    pub fn default()->Self{
        Config{
            base_module:Some("gen".to_string()),
            include_table_references:true,
            use_condensed_name:true,
            generate_table_meta:true,
            base_dir:"./src".to_string(),
        }
    }

    fn table_module(&self, table:&Table)->String{
        let parent = self.module(&table.schema);
        format!("{}::{}",parent,table.struct_name())
    }

    fn module_dir(&self, schema:&str)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}/{}/{}",self.base_dir, x, schema),
            None => format!("{}/{}",self.base_dir, schema)
        }
    }
    fn module_base_dir(&self)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}/{}",self.base_dir, x),
            None => format!("./{}",self.base_dir)
        }
    }

    fn module(&self, schema:&str)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}::{}", x, schema),
            None => format!("{}", schema)
        }
    }

}

///
/// retrieve all the table definition in the database
///
pub fn get_all_tables(db_dev:&mut DatabaseDev)->Vec<Table>{
    let all_tables_names = db_dev.get_all_tables();
    let mut all_table_def:Vec<Table> = Vec::new();
    for (schema, table) in all_tables_names{
        println!("Extracted {}.{}", schema,table);
        let meta = db_dev.get_table_metadata(&schema, &table);
        all_table_def.push(meta);
    }
    all_table_def
}

/// get the database schema
pub fn get_schemas(all_table:&Vec<Table>)->Vec<String>{
    let mut schema_names = Vec::new();
    for t in all_table{
        if !schema_names.contains(&t.schema){
            schema_names.push(t.schema.clone());
        }
    }
    schema_names.sort_by(|a, b| a.cmp(b));
    schema_names
}

/// get all tables with schema name
pub fn get_tables_in_schema<'a>(schema:&str, all_table:&'a Vec<Table>)->Vec<&'a Table>{
    let mut tables = Vec::new();
    for t in all_table{
        if &t.schema == schema{
            tables.push(t);//cloned the table here
        }
    }
    tables.sort_by(|a, b| a.name.cmp(&b.name));
    tables
}
/// this is the default config generation
/// FIXME need to compartmentalize each
/// 1. structs
/// 2. meta
/// mod.rs

pub fn generate_all(db_dev:&mut DatabaseDev, config:&Config){
    let all_tables:Vec<Table> = get_all_tables(db_dev);
    for table in &all_tables{
        generate_table(db_dev, config, table, &all_tables);
    }
    generate_mod_per_schema(&config, &all_tables);
    generate_mod_rs(&config, &all_tables);
}

pub fn generate_tables(db_dev:&mut DatabaseDev, only_tables:Vec<String>, config:&Config){
    let all_tables:Vec<Table> = get_all_tables(db_dev);
    let mut tables = vec![];
    for table in all_tables{
        if only_tables.contains(&table.name){
            tables.push(table);
        }
    }
    for table in &tables{
        generate_table(db_dev, config, table, &tables);
    }
    generate_mod_per_schema(&config, &tables);
    generate_mod_rs(&config, &tables);
}

/// the gernaration of tables should be placed on their respective directory
/// base_mod::schema::table.rs
fn generate_table(db_dev:&DatabaseDev, config:&Config, table:&Table, all_tables:&Vec<Table>){
    let mut w = Writer::new();
    let (struct_imports, imported_tables, struct_src) = table.struct_code(db_dev, all_tables);
    let (dao_imports, dao_src) = generate_dao_conversion_code(table, all_tables);
    let (meta_imports, meta_src) = generate_meta_code(table);
    let static_columns = generate_static_column_names(table);
    let warning = format!(" WARNING: This file is generated, derived from table {}, DO NOT EDIT", table.complete_name());
    w.inner_doc_comment(&warning);
    w.ln();
    w.ln();
    for i in struct_imports{
        w.appendln(&format!("use {};",i));
    }

    for it in imported_tables{
        if it != table{
            w.appendln(&format!("use {};", config.table_module(it)));
        }
    }
    for i in dao_imports{
        w.appendln(&format!("use {};",i));
    }
    for i in meta_imports{
        w.appendln(&format!("use {};",i));
    }
    w.ln();
    w.ln();
    w.appendln(&struct_src);
    w.ln();
    w.appendln(&dao_src);
    w.ln();
    w.append(&meta_src);
    w.ln();
    w.appendln(&static_columns);

    let module_dir = config.module_dir(&table.schema);
    match fs::create_dir_all(&module_dir){
        Ok(_) => {
            let file = format!("{}/{}.rs", &module_dir, table.name);
            save_to_file(&file, &w.src);
        },
        Err(e) => println!("There was an error creating directory {}", e)
    }
}

fn generate_mod_per_schema(config:&Config, all_tables:&Vec<Table>){
    let schemas = get_schemas(all_tables);
    for schema in schemas{
        let mut w = Writer::new();
         let module_dir = config.module_dir(&schema);
         let tables = get_tables_in_schema(&schema, all_tables);
         for table in &tables{
            w.append(&format!("pub mod {};",table.name));
            w.ln();
         }
         for table in &tables{
            //re-export structs
            w.append(&format!("pub use self::{}::{};", table.name, table.struct_name()));
            w.ln();
         }
         let mod_file = format!("{}/mod.rs", module_dir);
         save_to_file(&mod_file, &w.src);
    }
}

fn generate_mod_rs(config:&Config, all_tables:&Vec<Table>){
    let mut mod_src = Writer::new();
    let schemas = get_schemas(&all_tables);
    for schema in &schemas{
        mod_src.append("pub mod ");
        mod_src.append(schema);
        mod_src.append(";");
        mod_src.ln();
    }
    mod_src.append("use rustorm::table::Table;");
    mod_src.ln();
    mod_src.append("use rustorm::table::IsTable;");
    mod_src.ln();
    mod_src.ln();
    for table in all_tables{
        let table_mod = format!("use {};", config.table_module(table));
        mod_src.append(&table_mod);
        mod_src.ln();
    }
    let mod_file = format!("{}/mod.rs",config.module_base_dir());
    let all_table_fn = &generate_fn_get_all_tables(&all_tables);
    mod_src.append(all_table_fn);
    save_to_file(&mod_file, &mod_src.src);

}

fn generate_meta_code(table: &Table)->(Vec<String>, String){
    let mut w = Writer::new();
    let mut imports = Vec::new();
    imports.push("rustorm::table::IsTable".to_string());
    imports.push("rustorm::table::Column".to_string());
    imports.push("rustorm::table::Table".to_string());

    w.append("impl IsTable for ");
    w.append(&table.struct_name());
    w.append("{");
    w.ln();
    w.ln();
    w.tab();
    w.append("fn table()->Table{");
    w.ln();
    w.tab();
    let (table_imports, table_src) = table.meta_code();
    for imp in table_imports{
        imports.push(imp);
    }
    w.append(&table_src);
    w.ln();
    w.tab();
    w.append("}");
    w.ln();
    w.append("}");
    (imports, w.src)
}

fn generate_static_column_names(table: &Table)->String{
    let mut w = Writer::new();
    w.comment(" Generated columns for easier development of dynamic queries without sacrificing wrong spelling of column names");
    for column in &table.columns{
        w.ln();
        w.ln();
        w.appendln("#[allow(non_upper_case_globals)]");
        w.appendln("#[allow(dead_code)]");
        w.append("pub static ");
        w.append(&column.corrected_name());
        w.append(": &'static str = ");
        w.append(&format!("\"{}.{}\"", table.name, column.name));
        w.append(";");
    }
    w.src
}

/// TODO: if column names begins with the tablename_, then put this value to the column name hash map
/// example: product_name, value will be copied to name
/// test if product_name is not a column, split with `_`, then check if first splinter is the tablename, then the check if the 2nd splinter if
/// a column of this table, then set that column with the value of the original name
fn generate_dao_conversion_code(table: &Table, all_tables:&Vec<Table>)->(Vec<String>, String){
    let mut w = Writer::new();
    let mut imports = Vec::new();
    imports.push("rustorm::dao::Dao".to_string());
    imports.push("rustorm::dao::IsDao".to_string());

    w.ln();
    w.append("impl IsDao for ");
    w.append(&table.struct_name());
    w.append("{");
    w.ln_tab();
    w.append("fn from_dao(dao:&Dao)->Self{");
    w.ln_tabs(2);
    w.append(&table.struct_name());
    w.append("{");
    for c in &table.columns{
        w.ln_tabs(3);
        w.append(&c.corrected_name());
        w.append(": ");
        if c.not_null{
            w.append("dao.get");
        }else{
            w.append("dao.get_opt");
        }
        w.append("(\"");
        w.append(&c.name);
        w.append("\")");
        w.comma();
    }
    let referenced_tables = table.get_all_applicable_reference(all_tables);
    for ref_table in referenced_tables{
        let member_name = ref_table.member_name(table);
        w.ln_tabs(3);
        w.append(&member_name);
        w.append(": ");
        if ref_table.is_has_one{
            w.append("None");
        }
        if ref_table.is_ext{
            w.append("None");
        }
        if ref_table.is_has_many{
            w.append("vec![]");
        }
        w.comma();
    }
    w.ln_tabs(2);
    w.append("}");
    w.ln_tab();
    w.append("}");
    w.ln();
    w.ln_tab();
    w.append("fn to_dao(&self)->Dao{");
    w.ln_tabs(2);
    w.append("let mut dao = Dao::new();");
    for c in &table.columns{
        w.ln_tabs(2);
        if c.not_null{
            w.append("dao.set(\"");
            w.append(&c.corrected_name());
            w.append("\", &self.");
            w.append(&c.corrected_name());
            w.append(");");
        }else{
            w.append("match self.");
            w.append(&c.corrected_name());
            w.append("{");
            w.ln_tabs(3);
            w.append("Some(ref _value) => dao.set(\"");
            w.append(&c.corrected_name());
            w.append("\", _value),");
            w.ln_tabs(3);
            w.append("None => dao.set_null(\"");
            w.append(&c.corrected_name());
            w.append("\")");
            w.ln_tabs(2);
            w.append("}");
        }
    }
    w.ln_tabs(2);
    w.append("dao");
    w.ln_tab();
    w.append("}");
    w.ln();
    w.append("}");
    (imports, w.src)
}

fn generate_fn_get_all_tables(tables:&Vec<Table>)->String{
    let mut w = Writer::new();
    w.ln();
    w.ln();
    w.append("pub fn get_all_tables()->Vec<Table>{");
    w.ln();
    w.tab();
    w.append("vec![");
    for t in tables{
        w.ln();
        w.tabs(2);
        w.append(&t.struct_name());
        w.append("::table(),");
    }
    w.ln();
    w.tab();
    w.append("]");
    w.ln();
    w.append("}");
    w.src
}

fn save_to_file(filename: &str, content:&str){
   match File::create(filename){
        Err(_) => panic!("couldn't create file {}", filename),
        Ok(mut file) => {
            match file.write_all(content.as_bytes()){
                Ok(_) => {println!("Saved to {}",filename);},
                Err(_) => {println!("There was error saving to file: {}",filename)}
            };
        },
    };

}
<commit_msg>added generation for tables and columns<commit_after>use writer::Writer;
use rustorm::database::DatabaseDev;
use std::fs::File;
use std::fs;
use std::io::Write;

use rustorm::table::Table;
use meta::{MetaCode, StructCode};

/// configuration for generating code

pub struct Config{
    /// the module name to be used when it is possible to unify all tables in 1 module
    /// likely the project name
    pub base_module:Option<String>,
    /// include the has_one, has_many, extension Models
    pub include_table_references:bool,
    ///use the condense name of the has_many else, use the table name referred
    pub use_condensed_name:bool,

    ///generate the is table definition for each table
    pub generate_table_meta:bool,

    /// base directory for the generated content
    pub base_dir: String,
}

impl Config{

    pub fn default()->Self{
        Config{
            base_module:Some("gen".to_string()),
            include_table_references:true,
            use_condensed_name:true,
            generate_table_meta:true,
            base_dir:"./src".to_string(),
        }
    }

    fn table_module(&self, table:&Table)->String{
        let parent = self.module(&table.schema);
        format!("{}::{}",parent,table.struct_name())
    }

    fn module_dir(&self, schema:&str)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}/{}/{}",self.base_dir, x, schema),
            None => format!("{}/{}",self.base_dir, schema)
        }
    }
    fn module_base_dir(&self)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}/{}",self.base_dir, x),
            None => format!("./{}",self.base_dir)
        }
    }

    fn module(&self, schema:&str)->String{
        let base_module = self.base_module.clone();
        match base_module{
            Some(x) => format!("{}::{}", x, schema),
            None => format!("{}", schema)
        }
    }

}

///
/// retrieve all the table definition in the database
///
pub fn get_all_tables(db_dev:&DatabaseDev)->Vec<Table>{
    let all_tables_names = db_dev.get_all_tables();
    let mut all_table_def:Vec<Table> = Vec::new();
    for (schema, table) in all_tables_names{
        println!("Extracted {}.{}", schema,table);
        let meta = db_dev.get_table_metadata(&schema, &table);
        all_table_def.push(meta);
    }
    all_table_def
}

/// get the database schema
pub fn get_schemas(all_table:&Vec<Table>)->Vec<String>{
    let mut schema_names = Vec::new();
    for t in all_table{
        if !schema_names.contains(&t.schema){
            schema_names.push(t.schema.clone());
        }
    }
    schema_names.sort_by(|a, b| a.cmp(b));
    schema_names
}

/// get all tables with schema name
pub fn get_tables_in_schema<'a>(schema:&str, all_table:&'a Vec<Table>)->Vec<&'a Table>{
    let mut tables = Vec::new();
    for t in all_table{
        if &t.schema == schema{
            tables.push(t);//cloned the table here
        }
    }
    tables.sort_by(|a, b| a.name.cmp(&b.name));
    tables
}
/// this is the default config generation

pub fn generate_all(db_dev:&DatabaseDev, config:&Config){
    generate_tables(db_dev, vec![], config)
}

/// if only_tables is empty vec, then generate_all
pub fn generate_tables(db_dev:&DatabaseDev, only_tables:Vec<String>, config:&Config){
    let all_tables:Vec<Table> = get_all_tables(db_dev);
    let mut tables = vec![];
    if only_tables.is_empty(){
        for table in all_tables{
            tables.push(table);
        }
    }
    else{
        for table in all_tables{
            if only_tables.contains(&table.name){
                tables.push(table);
            }
        }
    }
    for table in &tables{
        generate_table(db_dev, config, table, &tables);
    }
    generate_mod_per_schema(&config, &tables);
    generate_mod_rs(&config, &tables);
    generate_mod_table_names(&config, &tables);
    generate_mod_column_names(&config, &tables);
}

/// the gernaration of tables should be placed on their respective directory
/// base_mod::schema::table.rs
fn generate_table(db_dev:&DatabaseDev, config:&Config, table:&Table, all_tables:&Vec<Table>){
    let mut w = Writer::new();
    let (struct_imports, imported_tables, struct_src) = table.struct_code(db_dev, all_tables);
    let (dao_imports, dao_src) = generate_dao_conversion_code(table, all_tables);
    let (meta_imports, meta_src) = generate_meta_code(table);
    let (json_imports, json_src) = generate_to_json_code(table);
    let static_columns = generate_static_column_names(table);
    let warning = format!(" WARNING: This file is generated, derived from table {}, DO NOT EDIT", table.complete_name());
    w.inner_doc_comment(&warning);
    w.ln();
    w.ln();
    for i in struct_imports{
        w.appendln(&format!("use {};",i));
    }

    for it in imported_tables{
        if it != table{
            w.appendln(&format!("use {};", config.table_module(it)));
        }
    }
    for i in dao_imports{
        w.appendln(&format!("use {};",i));
    }
    for i in meta_imports{
        w.appendln(&format!("use {};",i));
    }
    for i in json_imports{
        w.appendln(&format!("use {};",i));
    }
    w.ln();
    w.ln();
    w.appendln(&struct_src);
    w.ln();
    w.appendln(&dao_src);
    w.ln();
    w.appendln(&json_src);
    w.ln();
    w.append(&meta_src);
    w.ln();
    w.appendln(&static_columns);

    let module_dir = config.module_dir(&table.schema);
    match fs::create_dir_all(&module_dir){
        Ok(_) => {
            let file = format!("{}/{}.rs", &module_dir, table.name);
            save_to_file(&file, &w.src);
        },
        Err(e) => println!("There was an error creating directory {}", e)
    }
}

fn generate_mod_per_schema(config:&Config, all_tables:&Vec<Table>){
    let schemas = get_schemas(all_tables);
    for schema in schemas{
        let mut w = Writer::new();
         let module_dir = config.module_dir(&schema);
         let tables = get_tables_in_schema(&schema, all_tables);
         for table in &tables{
            w.appendln(&format!("pub mod {};",table.name));
         }
         for table in &tables{
            //re-export structs
            w.appendln(&format!("pub use self::{}::{};", table.name, table.struct_name()));
         }
         let mod_file = format!("{}/mod.rs", module_dir);
         save_to_file(&mod_file, &w.src);
    }
}

/// listing of all table names in the system
fn generate_mod_table_names(config:&Config, all_tables:&Vec<Table>){
    let mut unique_table = vec![];
    for table in all_tables{
        unique_table.push(table.name.to_string());
    }
    unique_table.sort_by(|a, b| a.cmp(b));
    unique_table.dedup();

    let mut w = Writer::new();
    for table_name in unique_table{
        w.ln();
        w.appendln("#[allow(non_upper_case_globals)]");
        w.appendln(&format!("pub const {}: &'static str = \"{}\";",table_name, table_name));
    }
    let module_dir = config.module_base_dir();
    let mod_file = format!("{}/table.rs", module_dir);
    save_to_file(&mod_file, &w.src);
}

/// listing of all column names in the system
fn generate_mod_column_names(config:&Config, all_tables:&Vec<Table>){
    let mut unique_columns = vec![];
    for table in all_tables{
        for column in &table.columns{
            unique_columns.push((column.corrected_name(), column.name.to_string()));
        }
    }
    unique_columns.sort_by(|a, b| a.cmp(b));
    unique_columns.dedup();

    let mut w = Writer::new();
    for (corrected_name, column_name) in unique_columns{
        w.ln();
        w.appendln("#[allow(non_upper_case_globals)]");
        w.appendln(&format!("pub const {}: &'static str = \"{}\";",corrected_name, column_name));
    }
    let module_dir = config.module_base_dir();
    let mod_file = format!("{}/column.rs", module_dir);
    save_to_file(&mod_file, &w.src);
}

fn generate_mod_rs(config:&Config, all_tables:&Vec<Table>){
    let mut w = Writer::new();
    let schemas = get_schemas(&all_tables);
    for schema in &schemas{
        w.append("pub mod ");
        w.append(schema);
        w.appendln(";");
    }
    w.appendln("pub mod table;");
    w.appendln("pub mod column;");
    w.appendln("use rustorm::table::Table;");
    w.appendln("use rustorm::table::IsTable;");
    for table in all_tables{
        let table_mod = format!("use {};", config.table_module(table));
        w.appendln(&table_mod);
    }
    let mod_file = format!("{}/mod.rs",config.module_base_dir());
    let all_table_fn = &generate_fn_get_all_tables(&all_tables);
    w.append(all_table_fn);
    save_to_file(&mod_file, &w.src);

}

fn generate_meta_code(table: &Table)->(Vec<String>, String){
    let mut w = Writer::new();
    let mut imports = Vec::new();
    imports.push("rustorm::table::IsTable".to_string());
    imports.push("rustorm::table::Column".to_string());
    imports.push("rustorm::table::Table".to_string());

    w.append("impl IsTable for ");
    w.append(&table.struct_name());
    w.append("{");
    w.ln();
    w.ln();
    w.tab();
    w.append("fn table()->Table{");
    w.ln();
    w.tab();
    let (table_imports, table_src) = table.meta_code();
    for imp in table_imports{
        imports.push(imp);
    }
    w.append(&table_src);
    w.ln();
    w.tab();
    w.append("}");
    w.ln();
    w.append("}");
    (imports, w.src)
}

fn generate_to_json_code(table: &Table)->(Vec<String>, String){
    let mut w = Writer::new();
    let mut imports = Vec::new();
    imports.push("rustc_serialize::json::ToJson".to_string());
    imports.push("rustc_serialize::json::Json".to_string());

    w.append("impl ToJson for ");
    w.append(&table.struct_name());
    w.append("{");
    w.ln();
    w.ln();
    w.tab();
    w.append("fn to_json(&self)->Json{");
    w.ln();
    w.tabs(2);
    w.append("self.to_dao().to_json()");
    w.ln();
    w.tab();
    w.append("}");
    w.ln();
    w.append("}");
    (imports, w.src)
}

fn generate_static_column_names(table: &Table)->String{
    let mut w = Writer::new();
    w.comment(" Generated columns for easier development of dynamic queries without sacrificing wrong spelling of column names");
    for column in &table.columns{
        w.ln();
        w.ln();
        w.appendln("#[allow(non_upper_case_globals)]");
        w.appendln("#[allow(dead_code)]");
        w.append("pub static ");
        w.append(&column.corrected_name());
        w.append(": &'static str = ");
        w.append(&format!("\"{}.{}\"", table.name, column.name));
        w.append(";");
    }
    w.src
}

/// TODO: if column names begins with the tablename_, then put this value to the column name hash map
/// example: product_name, value will be copied to name
/// test if product_name is not a column, split with `_`, then check if first splinter is the tablename, then the check if the 2nd splinter if
/// a column of this table, then set that column with the value of the original name
fn generate_dao_conversion_code(table: &Table, all_tables:&Vec<Table>)->(Vec<String>, String){
    let mut w = Writer::new();
    let mut imports = Vec::new();
    imports.push("rustorm::dao::Dao".to_string());
    imports.push("rustorm::dao::IsDao".to_string());

    w.ln();
    w.append("impl IsDao for ");
    w.append(&table.struct_name());
    w.append("{");
    w.ln_tab();
    w.append("fn from_dao(dao:&Dao)->Self{");
    w.ln_tabs(2);
    w.append(&table.struct_name());
    w.append("{");
    for c in &table.columns{
        w.ln_tabs(3);
        w.append(&c.corrected_name());
        w.append(": ");
        if c.not_null{
            w.append("dao.get");
        }else{
            w.append("dao.get_opt");
        }
        w.append("(\"");
        w.append(&c.name);
        w.append("\")");
        w.comma();
    }
    let referenced_tables = table.get_all_applicable_reference(all_tables);
    for ref_table in referenced_tables{
        let member_name = ref_table.member_name(table);
        w.ln_tabs(3);
        w.append(&member_name);
        w.append(": ");
        if ref_table.is_has_one{
            w.append("None");
        }
        if ref_table.is_ext{
            w.append("None");
        }
        if ref_table.is_has_many{
            w.append("vec![]");
        }
        w.comma();
    }
    w.ln_tabs(2);
    w.append("}");
    w.ln_tab();
    w.append("}");
    w.ln();
    w.ln_tab();
    w.append("fn to_dao(&self)->Dao{");
    w.ln_tabs(2);
    w.append("let mut dao = Dao::new();");
    for c in &table.columns{
        w.ln_tabs(2);
        if c.not_null{
            w.append("dao.set(\"");
            w.append(&c.corrected_name());
            w.append("\", &self.");
            w.append(&c.corrected_name());
            w.append(");");
        }else{
            w.append("match self.");
            w.append(&c.corrected_name());
            w.append("{");
            w.ln_tabs(3);
            w.append("Some(ref _value) => dao.set(\"");
            w.append(&c.corrected_name());
            w.append("\", _value),");
            w.ln_tabs(3);
            w.append("None => dao.set_null(\"");
            w.append(&c.corrected_name());
            w.append("\")");
            w.ln_tabs(2);
            w.append("}");
        }
    }
    w.ln_tabs(2);
    w.append("dao");
    w.ln_tab();
    w.append("}");
    w.ln();
    w.append("}");
    (imports, w.src)
}

fn generate_fn_get_all_tables(tables:&Vec<Table>)->String{
    let mut w = Writer::new();
    w.ln();
    w.ln();
    w.append("pub fn get_all_tables()->Vec<Table>{");
    w.ln();
    w.tab();
    w.append("vec![");
    for t in tables{
        w.ln();
        w.tabs(2);
        w.append(&t.struct_name());
        w.append("::table(),");
    }
    w.ln();
    w.tab();
    w.append("]");
    w.ln();
    w.append("}");
    w.src
}

fn save_to_file(filename: &str, content:&str){
   match File::create(filename){
        Err(_) => panic!("couldn't create file {}", filename),
        Ok(mut file) => {
            match file.write_all(content.as_bytes()){
                Ok(_) => {println!("Saved to {}",filename);},
                Err(_) => {println!("There was error saving to file: {}",filename)}
            };
        },
    };

}
<|endoftext|>


<commit_before>use neb::ram::schema::Field;
use neb::ram::types::{TypeId, Id, key_hash, Map, Value};
use neb::ram::cell::{Cell, WriteError};
use neb::client::{Client as NebClient};
use bifrost::rpc::RPCError;

use server::schema::{MorpheusSchema, SchemaType, SchemaContainer, SchemaError};
use graph::vertex::Vertex;

use std::sync::Arc;
use serde::Serialize;

pub mod vertex;
pub mod edge;

pub enum NewVertexError {
    SchemaNotFound,
    SchemaNotVertex,
    KeyFieldNotAvailable,
    DataNotMap,
    RPCError(RPCError),
    WriteError(WriteError),
}
pub enum RemoveVertexError {
    WriteError(WriteError),
    RPCError(RPCError)
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum CellType {
    Vertex,
    Edge(edge::EdgeType)
}

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from("next"), TypeId::Id as u32, true, false, None),
            Field::new(&String::from("list"), TypeId::Id as u32, false, true, None)
        ];
}

pub fn cell_to_vertex (cell: Cell) -> Vertex {
    Vertex {
        id: cell.header.id(),
        schema: cell.header.schema,
        data: cell.data
    }
}

fn encode_cell_key<V>(schema_id: u32, value: &V) -> Id
    where V: Serialize{
    Id::from_obj(&(schema_id, value))
}

pub struct Graph {
    schema: Arc<SchemaContainer>,
    neb_client: Arc<NebClient>
}

impl Graph {
    pub fn new_vertex_group(&self, schema: &mut MorpheusSchema) -> Result<(), SchemaError> {
        schema.schema_type = SchemaType::Vertex;
        self.schema.new_schema(schema)
    }
    pub fn new_edge_group(&self, schema: &mut MorpheusSchema, edge_type: edge::EdgeType) -> Result<(), SchemaError> {
        if edge_type == edge::EdgeType::Simple {
            return Err(SchemaError::SimpleEdgeShouldNotHaveSchema)
        }
        schema.schema_type = SchemaType::Edge(edge_type);
        self.schema.new_schema(schema)
    }
    pub fn new_vertex(&self, mut vertex: Vertex) -> Result<Vertex, NewVertexError> {
        let schema_id = vertex.schema;
        if let Some(stype) = self.schema.schema_type(schema_id) {
            if stype != SchemaType::Vertex {
                return Err(NewVertexError::SchemaNotVertex)
            }
        } else {
            return Err(NewVertexError::SchemaNotFound)
        }
        let neb_schema = match self.schema.get_neb_schema(schema_id) {
            Some(schema) => schema,
            None => return Err(NewVertexError::SchemaNotFound)
        };
        let mut data = {
            match vertex.data {
                Value::Map(mut map) => map,
                _ => return Err(NewVertexError::DataNotMap)
            }
        };
        let id = {match neb_schema.key_field {
                Some(ref keys) => {
                    let value = data.get_in_by_ids(keys.iter());
                    match value {
                        &Value::Null => return Err(NewVertexError::KeyFieldNotAvailable),
                        _ => encode_cell_key(schema_id, value)
                    }
                },
                None => {
                    if vertex.id.is_unit_id() {
                        Id::rand()
                    } else {
                        vertex.id
                    }
                }
            }
        };
        data.insert_key_id(*vertex::INBOUND_KEY_ID, Value::Id(Id::unit_id()));
        data.insert_key_id(*vertex::OUTBOUND_KEY_ID, Value::Id(Id::unit_id()));
        data.insert_key_id(*vertex::INDIRECTED_KEY_ID, Value::Id(Id::unit_id()));
        let mut cell = Cell::new(schema_id, &id, Value::Map(data));
        let header = match self.neb_client.write_cell(&cell) {
            Ok(Ok(header)) => header,
            Ok(Err(e)) => return Err(NewVertexError::WriteError(e)),
            Err(e) => return Err(NewVertexError::RPCError(e))
        };
        cell.header = header;
        Ok(cell_to_vertex(cell))
    }
    pub fn remove_vertex(&self, id: &Id) -> Result<(), RemoveVertexError> {
        match self.neb_client.remove_cell(id) {
            Err(e) => Err(RemoveVertexError::RPCError(e)),
            Ok(Err(e)) => Err(RemoveVertexError::WriteError(e)),
            Ok(Ok(())) => Ok(())
        }
    }
    pub fn remove_vertex_by_key<K>(&self, schema_id: u32, key: &K) -> Result<(), RemoveVertexError>
        where K: Serialize {
        let id = encode_cell_key(schema_id, key);
        self.remove_vertex(&id)
    }
}<commit_msg>let neb cell function decide cell id<commit_after>use neb::ram::schema::Field;
use neb::ram::types::{TypeId, Id, key_hash, Map, Value};
use neb::ram::cell::{Cell, WriteError};
use neb::client::{Client as NebClient};
use bifrost::rpc::RPCError;

use server::schema::{MorpheusSchema, SchemaType, SchemaContainer, SchemaError};
use graph::vertex::Vertex;

use std::sync::Arc;
use serde::Serialize;

pub mod vertex;
pub mod edge;

pub enum NewVertexError {
    SchemaNotFound,
    SchemaNotVertex,
    CannotGenerateCellByData,
    DataNotMap,
    RPCError(RPCError),
    WriteError(WriteError),
}
pub enum RemoveVertexError {
    WriteError(WriteError),
    RPCError(RPCError)
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum CellType {
    Vertex,
    Edge(edge::EdgeType)
}

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from("next"), TypeId::Id as u32, true, false, None),
            Field::new(&String::from("list"), TypeId::Id as u32, false, true, None)
        ];
}

pub fn cell_to_vertex (cell: Cell) -> Vertex {
    Vertex {
        id: cell.header.id(),
        schema: cell.header.schema,
        data: cell.data
    }
}

pub struct Graph {
    schema: Arc<SchemaContainer>,
    neb_client: Arc<NebClient>
}

impl Graph {
    pub fn new_vertex_group(&self, schema: &mut MorpheusSchema) -> Result<(), SchemaError> {
        schema.schema_type = SchemaType::Vertex;
        self.schema.new_schema(schema)
    }
    pub fn new_edge_group(&self, schema: &mut MorpheusSchema, edge_type: edge::EdgeType) -> Result<(), SchemaError> {
        if edge_type == edge::EdgeType::Simple {
            return Err(SchemaError::SimpleEdgeShouldNotHaveSchema)
        }
        schema.schema_type = SchemaType::Edge(edge_type);
        self.schema.new_schema(schema)
    }
    pub fn new_vertex(&self, mut vertex: Vertex) -> Result<Vertex, NewVertexError> {
        let schema_id = vertex.schema;
        if let Some(stype) = self.schema.schema_type(schema_id) {
            if stype != SchemaType::Vertex {
                return Err(NewVertexError::SchemaNotVertex)
            }
        } else {
            return Err(NewVertexError::SchemaNotFound)
        }
        let neb_schema = match self.schema.get_neb_schema(schema_id) {
            Some(schema) => schema,
            None => return Err(NewVertexError::SchemaNotFound)
        };
        let mut data = {
            match vertex.data {
                Value::Map(mut map) => map,
                _ => return Err(NewVertexError::DataNotMap)
            }
        };
        data.insert_key_id(*vertex::INBOUND_KEY_ID, Value::Id(Id::unit_id()));
        data.insert_key_id(*vertex::OUTBOUND_KEY_ID, Value::Id(Id::unit_id()));
        data.insert_key_id(*vertex::INDIRECTED_KEY_ID, Value::Id(Id::unit_id()));
        let mut cell = match Cell::new(&neb_schema, Value::Map(data)) {
            Some(cell) => cell,
            None => return Err(NewVertexError::CannotGenerateCellByData)
        };
        let header = match self.neb_client.write_cell(&cell) {
            Ok(Ok(header)) => header,
            Ok(Err(e)) => return Err(NewVertexError::WriteError(e)),
            Err(e) => return Err(NewVertexError::RPCError(e))
        };
        cell.header = header;
        Ok(cell_to_vertex(cell))
    }
    pub fn remove_vertex(&self, id: &Id) -> Result<(), RemoveVertexError> {
        match self.neb_client.remove_cell(id) {
            Err(e) => Err(RemoveVertexError::RPCError(e)),
            Ok(Err(e)) => Err(RemoveVertexError::WriteError(e)),
            Ok(Ok(())) => Ok(())
        }
    }
    pub fn remove_vertex_by_key<K>(&self, schema_id: u32, key: &K) -> Result<(), RemoveVertexError>
        where K: Serialize {
        let id = Cell::encode_cell_key(schema_id, key);
        self.remove_vertex(&id)
    }
}<|endoftext|>


<commit_before>#![feature(test)]
#![deny(warnings)]

extern crate test;

use std::net::SocketAddr;

use futures_util::future::join_all;

use hyper::client::HttpConnector;
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};

// HTTP1

#[bench]
fn http1_get(b: &mut test::Bencher) {
    opts().bench(b)
}

#[bench]
fn http1_post(b: &mut test::Bencher) {
    opts()
        .method(Method::POST)
        .request_body(b"foo bar baz quux")
        .bench(b)
}

#[bench]
fn http1_body_both_100kb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 100];
    opts()
        .method(Method::POST)
        .request_body(body)
        .response_body(body)
        .bench(b)
}

#[bench]
fn http1_body_both_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .method(Method::POST)
        .request_body(body)
        .response_body(body)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
    opts().parallel(10).bench(b)
}

#[bench]
fn http1_parallel_x10_req_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .parallel(10)
        .method(Method::POST)
        .request_body(body)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 1];
    opts().parallel(10).response_body(body).bench(b)
}

#[bench]
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts().parallel(10).response_body(body).bench(b)
}

// HTTP2

const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;

#[bench]
fn http2_get(b: &mut test::Bencher) {
    opts().http2().bench(b)
}

#[bench]
fn http2_post(b: &mut test::Bencher) {
    opts()
        .http2()
        .method(Method::POST)
        .request_body(b"foo bar baz quux")
        .bench(b)
}

#[bench]
fn http2_req_100kb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 100];
    opts()
        .http2()
        .method(Method::POST)
        .request_body(body)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
    opts().http2().parallel(10).bench(b)
}

#[bench]
fn http2_parallel_x10_req_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_adaptive_window(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .http2_adaptive_window()
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_max_window(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_res_1mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 1];
    opts()
        .http2()
        .parallel(10)
        .response_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_res_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .response_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

// ==== Benchmark Options =====

struct Opts {
    http2: bool,
    http2_stream_window: Option<u32>,
    http2_conn_window: Option<u32>,
    http2_adaptive_window: bool,
    parallel_cnt: u32,
    request_method: Method,
    request_body: Option<&'static [u8]>,
    request_chunks: usize,
    response_body: &'static [u8],
}

fn opts() -> Opts {
    Opts {
        http2: false,
        http2_stream_window: None,
        http2_conn_window: None,
        http2_adaptive_window: false,
        parallel_cnt: 1,
        request_method: Method::GET,
        request_body: None,
        request_chunks: 0,
        response_body: b"",
    }
}

impl Opts {
    fn http2(mut self) -> Self {
        self.http2 = true;
        self
    }

    fn http2_stream_window(mut self, sz: impl Into<Option<u32>>) -> Self {
        assert!(!self.http2_adaptive_window);
        self.http2_stream_window = sz.into();
        self
    }

    fn http2_conn_window(mut self, sz: impl Into<Option<u32>>) -> Self {
        assert!(!self.http2_adaptive_window);
        self.http2_conn_window = sz.into();
        self
    }

    fn http2_adaptive_window(mut self) -> Self {
        assert!(self.http2_stream_window.is_none());
        assert!(self.http2_conn_window.is_none());
        self.http2_adaptive_window = true;
        self
    }

    fn method(mut self, m: Method) -> Self {
        self.request_method = m;
        self
    }

    fn request_body(mut self, body: &'static [u8]) -> Self {
        self.request_body = Some(body);
        self
    }

    fn request_chunks(mut self, chunk: &'static [u8], cnt: usize) -> Self {
        assert!(cnt > 0);
        self.request_body = Some(chunk);
        self.request_chunks = cnt;
        self
    }

    fn response_body(mut self, body: &'static [u8]) -> Self {
        self.response_body = body;
        self
    }

    fn parallel(mut self, cnt: u32) -> Self {
        assert!(cnt > 0, "parallel count must be larger than 0");
        self.parallel_cnt = cnt;
        self
    }

    fn bench(self, b: &mut test::Bencher) {
        use std::sync::Arc;
        let _ = pretty_env_logger::try_init();
        // Create a runtime of current thread.
        let rt = Arc::new(
            tokio::runtime::Builder::new_current_thread()
                .enable_all()
                .build()
                .expect("rt build"),
        );
        let exec = rt.clone();

        let req_len = self.request_body.map(|b| b.len()).unwrap_or(0) as u64;
        let req_len = if self.request_chunks > 0 {
            req_len * self.request_chunks as u64
        } else {
            req_len
        };
        let bytes_per_iter = (req_len + self.response_body.len() as u64) * self.parallel_cnt as u64;
        b.bytes = bytes_per_iter;

        let addr = spawn_server(&rt, &self);

        let connector = HttpConnector::new();
        let client = hyper::Client::builder()
            .http2_only(self.http2)
            .http2_initial_stream_window_size(self.http2_stream_window)
            .http2_initial_connection_window_size(self.http2_conn_window)
            .http2_adaptive_window(self.http2_adaptive_window)
            .build::<_, Body>(connector);

        let url: hyper::Uri = format!("http://{}/hello", addr).parse().unwrap();

        let make_request = || {
            let chunk_cnt = self.request_chunks;
            let body = if chunk_cnt > 0 {
                let (mut tx, body) = Body::channel();
                let chunk = self
                    .request_body
                    .expect("request_chunks means request_body");
                exec.spawn(async move {
                    for _ in 0..chunk_cnt {
                        tx.send_data(chunk.into()).await.expect("send_data");
                    }
                });
                body
            } else {
                self.request_body
                    .map(Body::from)
                    .unwrap_or_else(Body::empty)
            };
            let mut req = Request::new(body);
            *req.method_mut() = self.request_method.clone();
            *req.uri_mut() = url.clone();
            req
        };

        let send_request = |req: Request<Body>| {
            let fut = client.request(req);
            async {
                let res = fut.await.expect("client wait");
                let mut body = res.into_body();
                while let Some(_chunk) = body.data().await {}
            }
        };

        if self.parallel_cnt == 1 {
            b.iter(|| {
                let req = make_request();
                rt.block_on(send_request(req));
            });
        } else {
            b.iter(|| {
                let futs = (0..self.parallel_cnt).map(|_| {
                    let req = make_request();
                    send_request(req)
                });
                // Await all spawned futures becoming completed.
                rt.block_on(join_all(futs));
            });
        }
    }
}

fn spawn_server(rt: &tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
    use hyper::service::{make_service_fn, service_fn};
    let addr = "127.0.0.1:0".parse().unwrap();

    let body = opts.response_body;
    let srv = rt.block_on(async move {
        Server::bind(&addr)
            .http2_only(opts.http2)
            .http2_initial_stream_window_size(opts.http2_stream_window)
            .http2_initial_connection_window_size(opts.http2_conn_window)
            .http2_adaptive_window(opts.http2_adaptive_window)
            .serve(make_service_fn(move |_| async move {
                Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
                    let mut req_body = req.into_body();
                    while let Some(_chunk) = req_body.data().await {}
                    Ok::<_, hyper::Error>(Response::new(Body::from(body)))
                }))
            }))
    });
    let addr = srv.local_addr();
    rt.spawn(async {
        if let Err(err) = srv.await {
            panic!("server error: {}", err);
        }
    });
    addr
}
<commit_msg>refactor(benches): make benchmark names more consistent<commit_after>#![feature(test)]
#![deny(warnings)]

extern crate test;

use std::net::SocketAddr;

use futures_util::future::join_all;

use hyper::client::HttpConnector;
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};

// HTTP1

#[bench]
fn http1_consecutive_x1_empty(b: &mut test::Bencher) {
    opts().bench(b)
}

#[bench]
fn http1_consecutive_x1_req_10b(b: &mut test::Bencher) {
    opts()
        .method(Method::POST)
        .request_body(&[b's'; 10])
        .bench(b)
}

#[bench]
fn http1_consecutive_x1_both_100kb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 100];
    opts()
        .method(Method::POST)
        .request_body(body)
        .response_body(body)
        .bench(b)
}

#[bench]
fn http1_consecutive_x1_both_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .method(Method::POST)
        .request_body(body)
        .response_body(body)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
    opts().parallel(10).bench(b)
}

#[bench]
fn http1_parallel_x10_req_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .parallel(10)
        .method(Method::POST)
        .request_body(body)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .bench(b)
}

#[bench]
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 1];
    opts().parallel(10).response_body(body).bench(b)
}

#[bench]
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts().parallel(10).response_body(body).bench(b)
}

// HTTP2

const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;

#[bench]
fn http2_consecutive_x1_empty(b: &mut test::Bencher) {
    opts().http2().bench(b)
}

#[bench]
fn http2_consecutive_x1_req_10b(b: &mut test::Bencher) {
    opts()
        .http2()
        .method(Method::POST)
        .request_body(&[b's'; 10])
        .bench(b)
}

#[bench]
fn http2_consecutive_x1_req_100kb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 100];
    opts()
        .http2()
        .method(Method::POST)
        .request_body(body)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
    opts().http2().parallel(10).bench(b)
}

#[bench]
fn http2_parallel_x10_req_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_adaptive_window(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .http2_adaptive_window()
        .bench(b)
}

#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_max_window(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .method(Method::POST)
        .request_chunks(body, 100)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_res_1mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 1];
    opts()
        .http2()
        .parallel(10)
        .response_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

#[bench]
fn http2_parallel_x10_res_10mb(b: &mut test::Bencher) {
    let body = &[b'x'; 1024 * 1024 * 10];
    opts()
        .http2()
        .parallel(10)
        .response_body(body)
        .http2_stream_window(HTTP2_MAX_WINDOW)
        .http2_conn_window(HTTP2_MAX_WINDOW)
        .bench(b)
}

// ==== Benchmark Options =====

struct Opts {
    http2: bool,
    http2_stream_window: Option<u32>,
    http2_conn_window: Option<u32>,
    http2_adaptive_window: bool,
    parallel_cnt: u32,
    request_method: Method,
    request_body: Option<&'static [u8]>,
    request_chunks: usize,
    response_body: &'static [u8],
}

fn opts() -> Opts {
    Opts {
        http2: false,
        http2_stream_window: None,
        http2_conn_window: None,
        http2_adaptive_window: false,
        parallel_cnt: 1,
        request_method: Method::GET,
        request_body: None,
        request_chunks: 0,
        response_body: b"",
    }
}

impl Opts {
    fn http2(mut self) -> Self {
        self.http2 = true;
        self
    }

    fn http2_stream_window(mut self, sz: impl Into<Option<u32>>) -> Self {
        assert!(!self.http2_adaptive_window);
        self.http2_stream_window = sz.into();
        self
    }

    fn http2_conn_window(mut self, sz: impl Into<Option<u32>>) -> Self {
        assert!(!self.http2_adaptive_window);
        self.http2_conn_window = sz.into();
        self
    }

    fn http2_adaptive_window(mut self) -> Self {
        assert!(self.http2_stream_window.is_none());
        assert!(self.http2_conn_window.is_none());
        self.http2_adaptive_window = true;
        self
    }

    fn method(mut self, m: Method) -> Self {
        self.request_method = m;
        self
    }

    fn request_body(mut self, body: &'static [u8]) -> Self {
        self.request_body = Some(body);
        self
    }

    fn request_chunks(mut self, chunk: &'static [u8], cnt: usize) -> Self {
        assert!(cnt > 0);
        self.request_body = Some(chunk);
        self.request_chunks = cnt;
        self
    }

    fn response_body(mut self, body: &'static [u8]) -> Self {
        self.response_body = body;
        self
    }

    fn parallel(mut self, cnt: u32) -> Self {
        assert!(cnt > 0, "parallel count must be larger than 0");
        self.parallel_cnt = cnt;
        self
    }

    fn bench(self, b: &mut test::Bencher) {
        use std::sync::Arc;
        let _ = pretty_env_logger::try_init();
        // Create a runtime of current thread.
        let rt = Arc::new(
            tokio::runtime::Builder::new_current_thread()
                .enable_all()
                .build()
                .expect("rt build"),
        );
        let exec = rt.clone();

        let req_len = self.request_body.map(|b| b.len()).unwrap_or(0) as u64;
        let req_len = if self.request_chunks > 0 {
            req_len * self.request_chunks as u64
        } else {
            req_len
        };
        let bytes_per_iter = (req_len + self.response_body.len() as u64) * self.parallel_cnt as u64;
        b.bytes = bytes_per_iter;

        let addr = spawn_server(&rt, &self);

        let connector = HttpConnector::new();
        let client = hyper::Client::builder()
            .http2_only(self.http2)
            .http2_initial_stream_window_size(self.http2_stream_window)
            .http2_initial_connection_window_size(self.http2_conn_window)
            .http2_adaptive_window(self.http2_adaptive_window)
            .build::<_, Body>(connector);

        let url: hyper::Uri = format!("http://{}/hello", addr).parse().unwrap();

        let make_request = || {
            let chunk_cnt = self.request_chunks;
            let body = if chunk_cnt > 0 {
                let (mut tx, body) = Body::channel();
                let chunk = self
                    .request_body
                    .expect("request_chunks means request_body");
                exec.spawn(async move {
                    for _ in 0..chunk_cnt {
                        tx.send_data(chunk.into()).await.expect("send_data");
                    }
                });
                body
            } else {
                self.request_body
                    .map(Body::from)
                    .unwrap_or_else(Body::empty)
            };
            let mut req = Request::new(body);
            *req.method_mut() = self.request_method.clone();
            *req.uri_mut() = url.clone();
            req
        };

        let send_request = |req: Request<Body>| {
            let fut = client.request(req);
            async {
                let res = fut.await.expect("client wait");
                let mut body = res.into_body();
                while let Some(_chunk) = body.data().await {}
            }
        };

        if self.parallel_cnt == 1 {
            b.iter(|| {
                let req = make_request();
                rt.block_on(send_request(req));
            });
        } else {
            b.iter(|| {
                let futs = (0..self.parallel_cnt).map(|_| {
                    let req = make_request();
                    send_request(req)
                });
                // Await all spawned futures becoming completed.
                rt.block_on(join_all(futs));
            });
        }
    }
}

fn spawn_server(rt: &tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
    use hyper::service::{make_service_fn, service_fn};
    let addr = "127.0.0.1:0".parse().unwrap();

    let body = opts.response_body;
    let srv = rt.block_on(async move {
        Server::bind(&addr)
            .http2_only(opts.http2)
            .http2_initial_stream_window_size(opts.http2_stream_window)
            .http2_initial_connection_window_size(opts.http2_conn_window)
            .http2_adaptive_window(opts.http2_adaptive_window)
            .serve(make_service_fn(move |_| async move {
                Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
                    let mut req_body = req.into_body();
                    while let Some(_chunk) = req_body.data().await {}
                    Ok::<_, hyper::Error>(Response::new(Body::from(body)))
                }))
            }))
    });
    let addr = srv.local_addr();
    rt.spawn(async {
        if let Err(err) = srv.await {
            panic!("server error: {}", err);
        }
    });
    addr
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate criterion;
extern crate bmemcached;

use std::sync::Arc;

use criterion::Criterion;

use bmemcached::MemcachedClient;

fn criterion_benchmark(c: &mut Criterion) {
    let client = Arc::new(MemcachedClient::new(vec!["127.0.0.1:11211"], 10).unwrap());
    let cli = client.clone();
    c.bench_function("set/get/delete", move |b| {
        b.iter(|| {
            let key = "benchmark test";
            cli.set(key, "abc", 10_000).unwrap();
            let returned_value: String = cli.get(key).unwrap();
            assert_eq!(returned_value, "abc");
            cli.delete(key).unwrap();
        })
    });
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
<commit_msg>chore: Arc is not needed for the benchmark<commit_after>#[macro_use]
extern crate criterion;
extern crate bmemcached;

use criterion::Criterion;

use bmemcached::MemcachedClient;

fn criterion_benchmark(c: &mut Criterion) {
    let cli = MemcachedClient::new(vec!["127.0.0.1:11211"], 10).unwrap();
    c.bench_function("set/get/delete", move |b| {
        b.iter(|| {
            let key = "benchmark test";
            cli.set(key, "abc", 10_000).unwrap();
            let returned_value: String = cli.get(key).unwrap();
            assert_eq!(returned_value, "abc");
            cli.delete(key).unwrap();
        })
    });
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;

use std::collections::HashSet;

use proc_macro::TokenStream;
use proc_macro2::Ident;
use syn::{ItemFn, Stmt, UseTree, ItemUse, Item,
          parse::{self, Parse, ParseStream},
          punctuated::Punctuated};

/// Parses a list of variable names separated by commas
///
///     a, b, c
///
/// This is how the compiler passes in arguments to our attribute -- it is
/// everything inside the delimiters after the attribute name.
///
///     #[wlroots_dehandle(a, b, c)]
///
struct Args {
    vars: HashSet<Ident>
}

impl Parse for Args {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            vars: vars.into_iter().collect(),
        })
    }
}

impl Args {
    // TODO Pop and then report error if not all used
    fn is_handle(&self, name: Ident) -> bool {
        self.vars.contains(&name)
    }
}

/// Attribute to automatically call the `run` method on handles with the
/// remaining block of code.
///
/// The name of the variable you want to use as the upgraded handle should be
/// provided as an argument to the attribute. It does not need to be the same
/// as the handle variable.
///
/// The syntax in the code should be `use $handle as $upgraded_handle`.
/// E.g the variable in the code that stores the handle should go on the
/// **left** and the variable you used in the attribute declaration should
/// go on the **right**.
///
/// # Example
///
/// ```
/// impl InputManagerHandler for InputManager {
///     #[wlroots_dehandle(compositor, keyboard, seat)]
///     fn keyboard_added(&mut self,
///                       compositor: CompositorHandle,
///                       keyboard: KeyboardHandle)
///                       -> Option<Box<Keyboard Handler>> {
///         use compositor as compositor;
///         let keyboard = keyboard;
///         use compositor as compositor;
///         use keyboard as keyboard;
///         let server: &mut ::Server = compositor.into();
///         server.keyboards.push(keyboard.weak_reference());
///         // Now that we have at least one keyboard, update the seat capabilities.
///         let seat = &server.seat.seat;
///         use seat as seat;
///         let mut capabilities = seat.capabilities();
///         capabilities.insert(Capability::Keyboard);
///         seat.set_capabilities(capabilities);
///         seat.set_keyboard(keyboard.input_device());
///         Some(Box::new(::Keyboard))
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn wlroots_dehandle(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as ItemFn);
    let args = parse_macro_input!(args as Args);
    let output = build_block(input.block.stmts.iter(), &args);
    input.block = parse_quote!({#(#output)*});
    TokenStream::from(quote!(#input))
}

fn build_block(mut input: std::slice::Iter<Stmt>, args: &Args) -> Vec<Stmt> {
    let mut output = vec![];
    let mut inner = None;
    while let Some(stmt) = input.next().cloned() {
        use {Stmt::Item, Item::Use, UseTree::Rename};
        match stmt.clone() {
            Item(Use(ItemUse { tree: Rename(use_stmt), ..})) => {
                if args.is_handle(use_stmt.ident.clone()) {
                    inner = Some((use_stmt.ident, use_stmt.rename));
                    break
                }
                output.push(stmt)
            },
            _ => output.push(stmt)
        }
    }
    if let Some((handle, dehandle)) = inner {
        let inner_output = build_block(input, args);
        let inner_block = parse_quote!(
            (#handle).run(|#dehandle|{
                #(#inner_output)*
            }).expect(concat!("Could not upgrade handle ",
                              stringify!(#handle), " to ",
                              stringify!(#dehandle)));
        );
        output.push(inner_block);
    }
    output
}
<commit_msg>Handle all syntax cases<commit_after>extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;

use std::collections::HashSet;

use proc_macro::TokenStream;
use proc_macro2::Ident;
use syn::{ItemFn, Stmt, UseTree, ItemUse, Item, Block, Expr,
          parse::{self, Parse, ParseStream},
          punctuated::Punctuated,
          fold::Fold};

/// Parses a list of variable names separated by commas
///
///     a, b, c
///
/// This is how the compiler passes in arguments to our attribute -- it is
/// everything inside the delimiters after the attribute name.
///
///     #[wlroots_dehandle(a, b, c)]
///
struct Args {
    vars: HashSet<Ident>
}

impl Parse for Args {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            vars: vars.into_iter().collect(),
        })
    }
}

impl Fold for Args {
    fn fold_block(&mut self, block: Block) -> Block {
        build_block(block.stmts.iter(), self)
    }
}

impl Args {
    fn is_handle(&self, name: Ident) -> bool {
        self.vars.contains(&name)
    }
}

/// Attribute to automatically call the `run` method on handles with the
/// remaining block of code.
///
/// The name of the variable you want to use as the upgraded handle should be
/// provided as an argument to the attribute. It does not need to be the same
/// as the handle variable.
///
/// The syntax in the code should be `use $handle as $upgraded_handle`.
/// E.g the variable in the code that stores the handle should go on the
/// **left** and the variable you used in the attribute declaration should
/// go on the **right**.
///
/// # Example
///
/// ```
/// impl InputManagerHandler for InputManager {
///     #[wlroots_dehandle(compositor, keyboard, seat)]
///     fn keyboard_added(&mut self,
///                       compositor_handle: CompositorHandle,
///                       keyboard: KeyboardHandle)
///                       -> Option<Box<Keyboard Handler>> {
///         use compositor_handle as compositor;
///         let keyboard = keyboard;
///         use compositor as compositor;
///         use keyboard as keyboard;
///         let server: &mut ::Server = compositor.into();
///         server.keyboards.push(keyboard.weak_reference());
///         // Now that we have at least one keyboard, update the seat capabilities.
///         let server_seat = &server.seat.seat;
///         use server_seat as seat;
///         let mut capabilities = seat.capabilities();
///         capabilities.insert(Capability::Keyboard);
///         seat.set_capabilities(capabilities);
///         seat.set_keyboard(keyboard.input_device());
///         Some(Box::new(::Keyboard))
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn wlroots_dehandle(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);
    let mut args = parse_macro_input!(args as Args);
    let output = args.fold_item_fn(input);
    TokenStream::from(quote!(#output))
}

fn build_block(mut input: std::slice::Iter<Stmt>, args: &Args) -> Block {
    let mut output = vec![];
    let mut inner = None;
    while let Some(stmt) = input.next().cloned() {
        use {Item::Use, UseTree::Rename};
        match stmt.clone() {
            Stmt::Item(Use(ItemUse { tree: Rename(use_stmt), ..})) => {
                if args.is_handle(use_stmt.rename.clone()) {
                    inner = Some((use_stmt.ident, use_stmt.rename));
                    break
                }
                output.push(stmt)
            },
            // Recurse into function body
            Stmt::Item(Item::Fn(mut function)) => {
                let inner_block = function.block.clone();
                *function.block = build_block(inner_block.stmts.iter(), args);
                output.push(Stmt::Item(Item::Fn(function)))
            },
            // Recurse into let call
            Stmt::Local(mut local) => {
                if let Some((_, body)) = local.init.clone() {
                    let body = build_block_expr(*body.clone(), args);
                    let body = parse_quote!(#body);
                    local.init.as_mut().unwrap().1 = body;
                    output.push(Stmt::Local(local))
                }
            },
            Stmt::Expr(expr) => {
                let body = build_block_expr(expr, args);
                output.push(parse_quote!({#body}))
            }
            Stmt::Semi(expr, _) => {
                let body = build_block_expr(expr, args);
                output.push(parse_quote!({#body;}))
            }
            _ => output.push(stmt)
        }
    }
    if let Some((handle, dehandle)) = inner {
        let inner_block = build_block(input, args);
        let handle_call = parse_quote!(
            {(#handle).run(|#dehandle|{
                #inner_block
            }).expect(concat!("Could not upgrade handle ",
                              stringify!(#handle), " to ",
                              stringify!(#dehandle)))}
        );
        output.push(handle_call);
    }
    parse_quote!({#(#output)*})
}

/// Tries to build a block from the expression.
fn build_block_expr(expr: Expr, args: &Args) -> Expr {
    match expr {
        Expr::Block(block) => {
            let block = build_block(block.block.stmts.iter(), args);
            parse_quote!(#block)
        }
        Expr::Let(mut let_expr) => {
            *let_expr.expr = build_block_expr(*let_expr.expr.clone(), args);
            Expr::Let(let_expr)
        },
        Expr::If(mut if_expr) => {
            let then_branch = if_expr.then_branch.clone();
            let then_branch = build_block_expr(parse_quote!(#then_branch), args);
            if_expr.then_branch = parse_quote!(#then_branch);
            if_expr.else_branch = match if_expr.else_branch.clone() {
                None => if_expr.else_branch,
                Some((token, else_branch)) => {
                    Some((token, Box::new( build_block_expr(*else_branch, args))))
                }
            };
            Expr::If(if_expr)
        },
        Expr::While(mut while_expr) => {
            let body = while_expr.body.clone();
            let body = build_block_expr(parse_quote!(#body), args);
            while_expr.body = parse_quote!(#body);
            Expr::While(while_expr)
        },
        Expr::ForLoop(mut for_expr) => {
            let body = for_expr.body.clone();
            let body = build_block_expr(parse_quote!(#body), args);
            for_expr.body = parse_quote!(#body);
            Expr::ForLoop(for_expr)
        },
        Expr::Loop(mut loop_expr) => {
            let body = loop_expr.body.clone();
            let body = build_block_expr(parse_quote!(#body), args);
            loop_expr.body = parse_quote!(#body);
            Expr::Loop(loop_expr)
        },
        Expr::Match(mut match_expr) => {
            for arm in &mut match_expr.arms {
                *arm.body = build_block_expr(*arm.body.clone(), args)
            }
            Expr::Match(match_expr)
        },
        Expr::Closure(mut closure_expr) => {
            *closure_expr.body = build_block_expr(*closure_expr.body.clone(),
                                                  args);
            Expr::Closure(closure_expr)
        },
        Expr::Unsafe(mut unsafe_expr) => {
            unsafe_expr.block = build_block(unsafe_expr.block.stmts.iter(),
                                            args);
            Expr::Unsafe(unsafe_expr)
        },
        Expr::Assign(mut assign_expr) => {
            *assign_expr.right = build_block_expr(*assign_expr.right.clone(),
                                                  args);
            Expr::Assign(assign_expr)
        },
        Expr::AssignOp(mut assign_expr) => {
            *assign_expr.right = build_block_expr(*assign_expr.right.clone(),
                                                  args);
            Expr::AssignOp(assign_expr)
        },
        Expr::Break(mut break_expr) => {
            match break_expr.expr {
                None => {},
                Some(ref mut expr) => {
                    **expr = build_block_expr(*expr.clone(), args);
                }
            }
            Expr::Break(break_expr)
        },
        Expr::Return(mut return_expr) => {
            match return_expr.expr {
                None => {},
                Some(ref mut expr) => {
                    **expr = build_block_expr(*expr.clone(), args);
                }
            }
            Expr::Return(return_expr)
        },
        v => {
            v
        }
    }
}
<|endoftext|>


<commit_before>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;
use spline::Spline;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}: {:?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed: {:?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              objects: Object<'a>,
              shaders: Program,
              splines: Spline<f32>);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shaders: Program);
impl_get_no_lifetime!(splines: Spline<f32>);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<commit_msg>Pretty-printing resource-related errors.<commit_after>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;
use spline::Spline;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}:\n{:#?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              objects: Object<'a>,
              shaders: Program,
              splines: Spline<f32>);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shaders: Program);
impl_get_no_lifetime!(splines: Spline<f32>);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<|endoftext|>