<commit_before>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};
use uuid::Uuid;

use data::types::*;
use data::modules::get_module_list;
use data::helper::{get_module_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::pod::Pod;
use models::user::User;
use models::module::{NewModule,Module};
use models::research::Research;
use models::resource::Resource;

use schema::modules;
use schema::queue_entries;

use schema::pods::dsl as pods_dsl;
use schema::queues::dsl as queues_dsl;
use schema::modules::dsl as module_dsl;
use schema::resources::dsl as resources_dsl;
use schema::researches::dsl as research_dsl;

use models::queue::{QueueEntry, Queue, NewQueueEntry};
use validation::queue::{NewModuleSerializer, UpgradeModuleSerializer};

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all modules and their current levels for the pod of the current user.
#[get("/pod")]
pub fn get_modules(current_user: User, db: DB) -> APIResponse {

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .get_result::<Pod>(&*db)
        .expect("Failed to get user pod.");

    let module_result = module_dsl::modules
        .filter(module_dsl::pod_id.eq(pod.id))
        .get_results::<Module>(&*db);

    if module_result.is_ok() {
        let modules = module_result.unwrap();
        return ok().message("Module data.").data(json!(&modules));
    }
    ok().message("No module installed.")
}

/// Add a new module to pod
///
/// This endpoint:
/// - Checks if dependencies for the module are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
#[post("/pod/new", data = "<new_module_data>", format = "application/json")]
pub fn add_module(new_module_data: Result<JSON<NewModuleSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {
    match new_module_data {
        // Return specific error if invalid JSON has been sent.
        Err(error) => return bad_request().message(format!("{}", error).as_str()),
        Ok(data) => {
            // Check if the given module name maps to a module type.
            let result = ModuleTypes::from_string(&data.module_name);
            match result {
                // Early return if we don't know this module name
                Err(_) => {
                    return bad_request().message(format!("No such module type `{}`",
                                                         data.module_name)
                                                         .as_str());
                }
                Ok(module_type) => {
                    let dependency_strings = get_module_dependency_strings(&module_type);
                    let module_list = get_module_list();

                    // Get pod and queue from db
                    let pod = pods_dsl::pods
                        .filter(pods_dsl::user_id.eq(current_user.id))
                        .first::<Pod>(&*db)
                        .unwrap();

                    let existing_module;
                    // Check if there already exists a module for this position
                    // We distinguish between stationary and normal modules.

                    // Stationary modules have unique names and only exists once
                    // per pod.
                    if data.stationary {
                        existing_module = module_dsl::modules
                            .count()
                            .filter(module_dsl::pod_id.eq(pod.id))
                            .filter(module_dsl::name.eq(&data.module_name))
                            .execute(&*db)
                            .unwrap_or(0);
                    }
                    // Normal modules can exist multiple times. Thereby we just
                    // have to check if the requested position is free.
                    else {
                        existing_module = module_dsl::modules
                            .count()
                            .filter(module_dsl::pod_id.eq(pod.id))
                            .filter(module_dsl::x_pos.eq(data.position_x))
                            .filter(module_dsl::y_pos.eq(data.position_y))
                            .execute(&*db)
                            .unwrap_or(0);
                    };

                    // Early return if there already is a module.
                    if existing_module != 0 {
                        return bad_request().message("There already is a module at this position.");
                    }

                    // Get the researches the module is depending on
                    let dependencies = research_dsl::researches
                        .filter(research_dsl::name.eq_any(dependency_strings))
                        .get_results::<Research>(&*db);

                    // Check if the dependencies are fulfilled.
                    // If they're not fulfilled, return a bad request.
                    let fulfilled = dependencies_fulfilled(&module_type,
                                                           dependencies,
                                                           &module_list);
                    if !fulfilled {
                        return bad_request().message("Dependencies not fulfilled.");
                    }

                    let queue = queues_dsl::queues
                        .filter(queues_dsl::pod_id.eq(pod.id))
                        .first::<Queue>(&*db)
                        .unwrap();

                    // Query all pod resources
                    let pod_resources = resources_dsl::resources
                        .filter(resources_dsl::pod_id.eq(pod.id))
                        .get_results(&*db)
                        .expect("Failed to get user resources.");

                    // Get cost for level 1
                    let costs= &module_list
                                          .get(&module_type)
                                          .as_ref()
                                          .expect("No module in yml for this type.")
                                          .levels[0].resources;

                    if costs.is_some() {
                        if !Resource::check_resources(&costs, pod_resources, &db) {
                            return bad_request().message("Insufficient resources.");
                        }
                    }

                    // Create a new module in the
                    let new_module = NewModule {
                        name: data.module_name.clone(),
                        stationary: data.stationary,
                        x_pos: data.position_x,
                        y_pos: data.position_y,

                        pod_id: Some(pod.id),
                        base_id: None,
                    };

                    let module = diesel::insert(&new_module)
                        .into(modules::table)
                        .get_result::<Module>(&*db)
                        .expect("Failed to create module.");

                    // Create a new queue entry with the given module type.
                    let new_entry_model = NewQueueEntry {
                        queue_id: queue.id.clone(),
                        research_name: None,
                        module_name: Some(module.name),
                        module_id: Some(module.id),
                        level: 1,
                    };

                    let new_queue_entry = diesel::insert(&new_entry_model)
                        .into(queue_entries::table)
                        .get_result::<QueueEntry>(&*db)
                        .expect("Failed to create queue entry.");

                    created()
                        .message("Queue entry added.")
                        .data(json!(&new_queue_entry))
                }
            }
        }
    }
}

///// Remove module from pod
//#[delete("/pod/<entry_uuid>")]
//pub fn remove_module(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {
//
//    // Parse and check if we got a valid id
//    let result = Uuid::parse_str(entry_uuid);
//    if result.is_err() {
//        return bad_request().message("Got an invalid uuid");
//    }
//    let queue_entry_id = result.unwrap();
//
//    // Get the queue entry
//    let queue_entry_result = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::id.eq(queue_entry_id))
//        .first::<QueueEntry>(&*db);
//    if queue_entry_result.is_err() {
//        return bad_request().message("No queue entry with this id.");
//    }
//    let queue_entry = queue_entry_result.unwrap();
//
//    // Check if we got an module queue entry
//    if queue_entry.module_name.is_none() {
//        return bad_request().message("Queue entry is a model queue entry.");
//    }
//
//    // Check if there already are existing queue entries for this module.
//    // If there are entries with a higher level, we return a bad request.
//    let level = queue_entry.level;
//    let name = queue_entry.module_name.unwrap();
//    let higher_entry = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::queue_id.eq(queue_entry.queue_id))
//        .filter(queue_entries_dsl::module_name.eq(&name))
//        .filter(queue_entries_dsl::level.gt(queue_entry.level))
//        .get_result::<QueueEntry>(&*db);
//    if higher_entry.is_ok() {
//        return bad_request().message("Can't delete. There is an queue entry with a higher level for this module.");
//    }
//
//    // Get all needed info for resource manipulation
//    let module_list = get_module_list();
//
//    let pod = pods_dsl::pods
//        .filter(pods_dsl::user_id.eq(current_user.id))
//        .first::<Pod>(&*db)
//        .unwrap();
//
//    let pod_resources = resources_dsl::resources
//        .filter(resources_dsl::pod_id.eq(pod.id))
//        .get_results::<Resource>(&*db)
//        .expect("Failed to get user resources.");
//
//    // Add resources from module to pod resources
//    let all_levels = &module_list
//                          .get(&ModuleTypes::from_string(&name).unwrap())
//                          .unwrap()
//                          .levels;
//    let costs_result = &all_levels[level as usize].resources;
//
//    if let Some(ref costs) = *costs_result {
//        Resource::update_resources(costs, pod_resources, false, &db);
//    }
//
//    // Remove queue_entry from database
//    diesel::delete(queue_entries_dsl::queue_entries
//                       .filter(queue_entries_dsl::id.eq(queue_entry_id)))
//            .execute(&*db)
//            .expect("Failed to remove queue_entry.");
//
//    ok().message("Resource removed.")
//}

/// upgrade module from pod
#[post("/pod/upgrade/<entry_uuid>")]
pub fn upgrade_module(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {

    // Parse and check if we got a valid id
    let result = Uuid::parse_str(entry_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();
    let level = module.level + 1;

    // Get all needed info for resource manipulation
    let module_list = get_module_list();

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from module to pod resources
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;

    // Check if there is a next level.
    if level >= all_levels.len() as i32 {
        return bad_request().message("Already at max level");
    }

    let costs_result = &all_levels[level as usize].resources;

    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(queue.id.clone()),
        level: level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

///// Remove module upgrade from pod queue
//#[delete("/pod/upgrade/<entry_uuid>")]
//pub fn stop_module_upgrade(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {
//
//    // Parse and check if we got a valid id
//    let result = Uuid::parse_str(entry_uuid);
//    if result.is_err() {
//        return bad_request().message("Got an invalid uuid");
//    }
//    let queue_entry_id = result.unwrap();
//
//    // Get the queue entry
//    let queue_entry_result = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::id.eq(queue_entry_id))
//        .first::<QueueEntry>(&*db);
//    if queue_entry_result.is_err() {
//        return bad_request().message("No queue entry with this id.");
//    }
//    let queue_entry = queue_entry_result.unwrap();
//
//    // Check if we got an module queue entry
//    if queue_entry.module_id.is_none() {
//        return bad_request().message("Queue entry is a model queue entry.");
//    }
//
//    // Check if there already are existing queue entries for this module.
//    // If there are entries with a higher level, we return a bad request.
//    let level = queue_entry.level;
//    let name = queue_entry.module_name.unwrap();
//    let higher_entry = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::queue_id.eq(queue_entry.queue_id))
//        .filter(queue_entries_dsl::module_name.eq(&name))
//        .filter(queue_entries_dsl::level.gt(queue_entry.level))
//        .get_result::<QueueEntry>(&*db);
//    if higher_entry.is_ok() {
//        return bad_request().message("Can't delete. There is an queue entry with a higher level for this module.");
//    }
//
//    // Get all needed info for resource manipulation
//    let module_list = get_module_list();
//
//    let pod = pods_dsl::pods
//        .filter(pods_dsl::user_id.eq(current_user.id))
//        .first::<Pod>(&*db)
//        .unwrap();
//
//    let pod_resources = resources_dsl::resources
//        .filter(resources_dsl::pod_id.eq(pod.id))
//        .get_results::<Resource>(&*db)
//        .expect("Failed to get user resources.");
//
//    // Add resources from module to pod resources
//    let all_levels = &module_list
//                          .get(&ModuleTypes::from_string(&name).unwrap())
//                          .unwrap()
//                          .levels;
//    let costs_result = &all_levels[level as usize].resources;
//
//    if let Some(ref costs) = *costs_result {
//        Resource::update_resources(costs, pod_resources, false, &db);
//    }
//
//    // Remove queue_entry from database
//    diesel::delete(queue_entries_dsl::queue_entries
//                       .filter(queue_entries_dsl::id.eq(queue_entry_id)))
//            .execute(&*db)
//            .expect("Failed to remove queue_entry.");
//
//    ok().message("Resource removed.")
//}
<commit_msg>Refactor add_module function<commit_after>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};
use uuid::Uuid;

use data::types::*;
use data::modules::get_module_list;
use data::helper::{get_module_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::pod::Pod;
use models::user::User;
use models::module::{NewModule,Module};
use models::research::Research;
use models::resource::Resource;

use schema::modules;
use schema::queue_entries;

use schema::pods::dsl as pods_dsl;
use schema::queues::dsl as queues_dsl;
use schema::modules::dsl as module_dsl;
use schema::resources::dsl as resources_dsl;
use schema::researches::dsl as research_dsl;

use models::queue::{QueueEntry, Queue, NewQueueEntry};
use validation::queue::{NewModuleSerializer, UpgradeModuleSerializer};

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all modules and their current levels for the pod of the current user.
#[get("/pod")]
pub fn get_modules(current_user: User, db: DB) -> APIResponse {

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .get_result::<Pod>(&*db)
        .expect("Failed to get user pod.");

    let module_result = module_dsl::modules
        .filter(module_dsl::pod_id.eq(pod.id))
        .get_results::<Module>(&*db);

    if module_result.is_ok() {
        let modules = module_result.unwrap();
        return ok().message("Module data.").data(json!(&modules));
    }
    ok().message("No module installed.")
}

/// Add a new module to pod
///
/// This endpoint:
/// - Checks if dependencies for the module are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
#[post("/pod/new", data = "<request_data>", format = "application/json")]
pub fn add_module(request_data: Result<JSON<NewModuleSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {

    // Unwrap or return specific error if invalid JSON has been sent.
    if let Err(error) = request_data {
        return bad_request().message(format!("{}", error).as_str());
    };
    let module_data = request_data.unwrap();

    // Check if the given module name maps to a module type.
    let result = ModuleTypes::from_string(&module_data.module_name);
    if result.is_err() {
        return bad_request().message(format!("No such module type `{}`",
                                             module_data.module_name)
                                             .as_str());
    }
    let module_type = result.unwrap();
    let dependency_strings = get_module_dependency_strings(&module_type);
    let module_list = get_module_list();

    // Get pod and queue from db
    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let existing_module;
    // Check if there already exists a module for this position
    // We distinguish between stationary and normal modules.

    // Stationary modules have unique names and only exists once
    // per pod.
    if module_data.stationary {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::name.eq(&module_data.module_name))
            .execute(&*db)
            .unwrap_or(0);
    }
    // Normal modules can exist multiple times. Thereby we just
    // have to check if the requested position is free.
    else {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::x_pos.eq(module_data.position_x))
            .filter(module_dsl::y_pos.eq(module_data.position_y))
            .execute(&*db)
            .unwrap_or(0);
    };

    // Early return if there already is a module.
    if existing_module != 0 {
        return bad_request().message("There already is a module at this position.");
    }

    // Get the researches the module is depending on
    let dependencies = research_dsl::researches
        .filter(research_dsl::name.eq_any(dependency_strings))
        .get_results::<Research>(&*db);

    // Check if the dependencies are fulfilled.
    // If they're not fulfilled, return a bad request.
    let fulfilled = dependencies_fulfilled(&module_type,
                                           dependencies,
                                           &module_list);
    if !fulfilled {
        return bad_request().message("Dependencies not fulfilled.");
    }

    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    // Query all pod resources
    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results(&*db)
        .expect("Failed to get user resources.");

    // Get cost for level 1
    let costs= &module_list
                          .get(&module_type)
                          .as_ref()
                          .expect("No module in yml for this type.")
                          .levels[0].resources;

    if costs.is_some() {
        if !Resource::check_resources(&costs, pod_resources, &db) {
            return bad_request().message("Insufficient resources.");
        }
    }

    // Create a new module in the
    let new_module = NewModule {
        name: module_data.module_name.clone(),
        stationary: module_data.stationary,
        x_pos: module_data.position_x,
        y_pos: module_data.position_y,

        pod_id: Some(pod.id),
        base_id: None,
    };

    let module = diesel::insert(&new_module)
        .into(modules::table)
        .get_result::<Module>(&*db)
        .expect("Failed to create module.");

    // Create a new queue entry with the given module type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(module.id),
        level: 1,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to create queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

///// Remove module from pod
//#[delete("/pod/<entry_uuid>")]
//pub fn remove_module(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {
//
//    // Parse and check if we got a valid id
//    let result = Uuid::parse_str(entry_uuid);
//    if result.is_err() {
//        return bad_request().message("Got an invalid uuid");
//    }
//    let queue_entry_id = result.unwrap();
//
//    // Get the queue entry
//    let queue_entry_result = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::id.eq(queue_entry_id))
//        .first::<QueueEntry>(&*db);
//    if queue_entry_result.is_err() {
//        return bad_request().message("No queue entry with this id.");
//    }
//    let queue_entry = queue_entry_result.unwrap();
//
//    // Check if we got an module queue entry
//    if queue_entry.module_name.is_none() {
//        return bad_request().message("Queue entry is a model queue entry.");
//    }
//
//    // Check if there already are existing queue entries for this module.
//    // If there are entries with a higher level, we return a bad request.
//    let level = queue_entry.level;
//    let name = queue_entry.module_name.unwrap();
//    let higher_entry = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::queue_id.eq(queue_entry.queue_id))
//        .filter(queue_entries_dsl::module_name.eq(&name))
//        .filter(queue_entries_dsl::level.gt(queue_entry.level))
//        .get_result::<QueueEntry>(&*db);
//    if higher_entry.is_ok() {
//        return bad_request().message("Can't delete. There is an queue entry with a higher level for this module.");
//    }
//
//    // Get all needed info for resource manipulation
//    let module_list = get_module_list();
//
//    let pod = pods_dsl::pods
//        .filter(pods_dsl::user_id.eq(current_user.id))
//        .first::<Pod>(&*db)
//        .unwrap();
//
//    let pod_resources = resources_dsl::resources
//        .filter(resources_dsl::pod_id.eq(pod.id))
//        .get_results::<Resource>(&*db)
//        .expect("Failed to get user resources.");
//
//    // Add resources from module to pod resources
//    let all_levels = &module_list
//                          .get(&ModuleTypes::from_string(&name).unwrap())
//                          .unwrap()
//                          .levels;
//    let costs_result = &all_levels[level as usize].resources;
//
//    if let Some(ref costs) = *costs_result {
//        Resource::update_resources(costs, pod_resources, false, &db);
//    }
//
//    // Remove queue_entry from database
//    diesel::delete(queue_entries_dsl::queue_entries
//                       .filter(queue_entries_dsl::id.eq(queue_entry_id)))
//            .execute(&*db)
//            .expect("Failed to remove queue_entry.");
//
//    ok().message("Resource removed.")
//}

/// upgrade module from pod
#[post("/pod/upgrade/<entry_uuid>")]
pub fn upgrade_module(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {

    // Parse and check if we got a valid id
    let result = Uuid::parse_str(entry_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();
    let level = module.level + 1;

    // Get all needed info for resource manipulation
    let module_list = get_module_list();

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from module to pod resources
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;

    // Check if there is a next level.
    if level >= all_levels.len() as i32 {
        return bad_request().message("Already at max level");
    }

    let costs_result = &all_levels[level as usize].resources;

    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(queue.id.clone()),
        level: level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

///// Remove module upgrade from pod queue
//#[delete("/pod/upgrade/<entry_uuid>")]
//pub fn stop_module_upgrade(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {
//
//    // Parse and check if we got a valid id
//    let result = Uuid::parse_str(entry_uuid);
//    if result.is_err() {
//        return bad_request().message("Got an invalid uuid");
//    }
//    let queue_entry_id = result.unwrap();
//
//    // Get the queue entry
//    let queue_entry_result = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::id.eq(queue_entry_id))
//        .first::<QueueEntry>(&*db);
//    if queue_entry_result.is_err() {
//        return bad_request().message("No queue entry with this id.");
//    }
//    let queue_entry = queue_entry_result.unwrap();
//
//    // Check if we got an module queue entry
//    if queue_entry.module_id.is_none() {
//        return bad_request().message("Queue entry is a model queue entry.");
//    }
//
//    // Check if there already are existing queue entries for this module.
//    // If there are entries with a higher level, we return a bad request.
//    let level = queue_entry.level;
//    let name = queue_entry.module_name.unwrap();
//    let higher_entry = queue_entries_dsl::queue_entries
//        .filter(queue_entries_dsl::queue_id.eq(queue_entry.queue_id))
//        .filter(queue_entries_dsl::module_name.eq(&name))
//        .filter(queue_entries_dsl::level.gt(queue_entry.level))
//        .get_result::<QueueEntry>(&*db);
//    if higher_entry.is_ok() {
//        return bad_request().message("Can't delete. There is an queue entry with a higher level for this module.");
//    }
//
//    // Get all needed info for resource manipulation
//    let module_list = get_module_list();
//
//    let pod = pods_dsl::pods
//        .filter(pods_dsl::user_id.eq(current_user.id))
//        .first::<Pod>(&*db)
//        .unwrap();
//
//    let pod_resources = resources_dsl::resources
//        .filter(resources_dsl::pod_id.eq(pod.id))
//        .get_results::<Resource>(&*db)
//        .expect("Failed to get user resources.");
//
//    // Add resources from module to pod resources
//    let all_levels = &module_list
//                          .get(&ModuleTypes::from_string(&name).unwrap())
//                          .unwrap()
//                          .levels;
//    let costs_result = &all_levels[level as usize].resources;
//
//    if let Some(ref costs) = *costs_result {
//        Resource::update_resources(costs, pod_resources, false, &db);
//    }
//
//    // Remove queue_entry from database
//    diesel::delete(queue_entries_dsl::queue_entries
//                       .filter(queue_entries_dsl::id.eq(queue_entry_id)))
//            .execute(&*db)
//            .expect("Failed to remove queue_entry.");
//
//    ok().message("Resource removed.")
//}
<|endoftext|>


<commit_before>use std::sync::{Arc, Mutex};
use std::thread::Thread;
use std::sync::mpsc::channel;
use std::old_io::{TcpListener, TcpStream};
use std::old_io::{Acceptor, Listener};

fn main() {
	println!("Serer Start!");

	let mut q = Arc::new(Mutex::new(Vec::new()));

	let listener = TcpListener::bind("127.0.0.1:80").unwrap();
	let mut acceptor = listener.listen().unwrap();

	for stream in acceptor.incoming() {
		match stream {
			Err(e) => { panic!("Connection Failed"); }
			Ok(stream) => {
				Thread::spawn(move|| {
					// connection succeeded
					handle_client(stream) // function
				});
			}
		}
	}

	drop(acceptor);
}
<commit_msg>success multi client & print to stdout<commit_after>use std::sync::{Arc, Mutex};
use std::thread::Thread;
use std::sync::mpsc::channel;
use std::old_io::{TcpListener, TcpStream};
use std::old_io::{Acceptor, Listener};
use core::num::cast;

extern crate core;

fn main() {
	println!("Serer Start!");

	let mut q = Arc::new(Mutex::new(Vec::new()));

	let listener = TcpListener::bind("127.0.0.1:2345").unwrap();
	let mut acceptor = listener.listen().unwrap();

	for stream in acceptor.incoming() {
		let strq = match stream {
			Err(e) => { panic!("Connection Failed"); }
			Ok(stream) => {
				Thread::spawn(move|| {
					let mut stream = stream.clone();
					println!("connected");
					stream.write(String::from_str("Connected\n").as_bytes());
					let mut datas = vec!();
					loop {
						let mut buf = [0];
						let result = stream.read(&mut buf).unwrap();
						let mut datas = &mut datas;
						if buf[0] == 13 || buf[0] == 10 {
							println!("{}", String::from_utf8(datas.clone()).unwrap());
							datas.clear();
						}
						datas.push(buf[0]);
					}
				});
			}
		};
		q.lock().unwrap().push(strq);
	}

	drop(acceptor);
}
<|endoftext|>


<commit_before>//#![feature(plugin)]
//#![plugin(bindgen_plugin)]

//#[allow(dead_code, uppercase_variables, non_camel_case_types)]
//#[plugin(bindgen_plugin)]
//mod mysql_bindings {
//    bindgen!("/usr/include/mysql/mysql.h", match="mysql.h", link="mysql");
//}

//use std::env;
//use std::fs;
//use std::path::Path;
//use std::process::Command;

fn main() {
    if let Some(datastax_dir) = option_env!("CASSANDRA_SYS_LIB_PATH") {
        for p in datastax_dir.split(";") {
            println!("cargo:rustc-link-search={}", p);
        }
    }
    println!("cargo:rustc-flags=-l dylib=crypto");
    println!("cargo:rustc-flags=-l dylib=ssl");
    println!("cargo:rustc-flags=-l dylib=stdc++");
    println!("cargo:rustc-flags=-l dylib=uv");
    println!("cargo:rustc-link-search={}", "/usr/lib/");
    println!("cargo:rustc-link-search={}", "/usr/local/lib64");
    println!("cargo:rustc-link-search={}", "/usr/local/lib");
    println!("cargo:rustc-link-lib=static=cassandra_static");
}
<commit_msg>order ld includes by most-specific first, adding /usr/lib64/<commit_after>//#![feature(plugin)]
//#![plugin(bindgen_plugin)]

//#[allow(dead_code, uppercase_variables, non_camel_case_types)]
//#[plugin(bindgen_plugin)]
//mod mysql_bindings {
//    bindgen!("/usr/include/mysql/mysql.h", match="mysql.h", link="mysql");
//}

//use std::env;
//use std::fs;
//use std::path::Path;
//use std::process::Command;

fn main() {
    if let Some(datastax_dir) = option_env!("CASSANDRA_SYS_LIB_PATH") {
        for p in datastax_dir.split(";") {
            println!("cargo:rustc-link-search={}", p);
        }
    }
    println!("cargo:rustc-flags=-l dylib=crypto");
    println!("cargo:rustc-flags=-l dylib=ssl");
    println!("cargo:rustc-flags=-l dylib=stdc++");
    println!("cargo:rustc-flags=-l dylib=uv");
    println!("cargo:rustc-link-search={}", "/usr/local/lib64");
    println!("cargo:rustc-link-search={}", "/usr/local/lib");
    println!("cargo:rustc-link-search={}", "/usr/lib64/");
    println!("cargo:rustc-link-search={}", "/usr/lib/");
    println!("cargo:rustc-link-lib=static=cassandra_static");
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-i", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "src/Makefile"],
                            Some(&dir)));
    run_command_in_dir(&["make", "linux"], Some(&dir))
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(run_command_in_dir(&["wget",
                                "http://www.lua.org/ftp/lua-5.3.0.tar.gz"],
                                Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<commit_msg>Readd Windows support<commit_after>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

#[cfg(target_os="windows")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "mingw"], Some(&build_dir().join("lua-5.3.0")))
}

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-i", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "src/Makefile"],
                            Some(&dir)));
    run_command_in_dir(&["make", "linux"], Some(&dir))
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(run_command_in_dir(&["wget",
                                "http://www.lua.org/ftp/lua-5.3.0.tar.gz"],
                                Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate ndarray_linalg as linalg;

use ndarray::prelude::*;
use linalg::SquareMatrix;

#[test]
fn test_eigh() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0]]);
    let (e, vecs) = a.clone().eigh().unwrap();
    assert!(e.all_close(&arr1(&[2.0, 2.0, 5.0]), 1.0e-7));
    for (i, v) in vecs.axis_iter(Axis(1)).enumerate() {
        let av = a.dot(&v);
        let ev = v.mapv(|x| e[i] * x);
        assert!(av.all_close(&ev, 1.0e-7));
    }
}
<commit_msg>Add test for eigh<commit_after>
extern crate ndarray;
extern crate ndarray_linalg as linalg;

use ndarray::prelude::*;
use linalg::SquareMatrix;

fn assert_almost_eq(a: f64, b: f64) {
    let rel_dev = (a - b).abs() / (a.abs() + b.abs());
    if rel_dev > 1.0e-7 {
        panic!("a={:?}, b={:?} are not almost equal", a, b);
    }
}

#[test]
fn eigen_vector_manual() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0]]);
    let (e, vecs) = a.clone().eigh().unwrap();
    assert!(e.all_close(&arr1(&[2.0, 2.0, 5.0]), 1.0e-7));
    for (i, v) in vecs.axis_iter(Axis(1)).enumerate() {
        let av = a.dot(&v);
        let ev = v.mapv(|x| e[i] * x);
        assert!(av.all_close(&ev, 1.0e-7));
    }
}

#[test]
fn diagonalize() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0]]);
    let (e, vecs) = a.clone().eigh().unwrap();
    let s = vecs.t().dot(&a).dot(&vecs);
    for i in 0..3 {
        assert_almost_eq(e[i], s[(i, i)]);
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::HashMap,
    convert::TryFrom,
    fs::File,
    path::{
        Path,
        PathBuf,
    },
    str::FromStr,
};

use serde::de::DeserializeOwned;
use strum::IntoEnumIterator;
use strum_macros::{
    Display,
    EnumIter,
    EnumString,
};
use thiserror::Error;

use rnc_core::{
    grouper::Grouped::{
        self,
        Multiple,
        Optional,
        Required,
    },
    psql::JsonlIterator,
};

use super::{
    basic::Basic,
    crs::Crs,
    feedback::Feedback,
    go_annotation::GoAnnotation,
    interacting_protein::InteractingProtein,
    interacting_rna::InteractingRna,
    orf::Orf,
    precompute::Precompute,
    qa_status::QaStatus,
    r2dt::R2dt,
    raw::Raw,
    rfam_hit::RfamHit,
    so_tree,
    so_tree::SoMapping,
};

#[derive(Debug, Error)]
pub enum Error {
    #[error("Missing required file {0}")]
    MustDefineMissingFile(FileTypes),

    #[error("Not all files are in required format")]
    InvalidDataFormat,

    #[error("Missing requested SO term {0}")]
    MissingSoTrem(String),

    #[error("Cannot find expected file, from {0:?}")]
    BadFileName(PathBuf),

    #[error("Could not match path {0:?} to a possible file type")]
    UnknownFileType(PathBuf),

    #[error("Data out of sync at: {0:?}")]
    OutofSyncData(Vec<usize>),

    #[error(transparent)]
    JsonError(#[from] rnc_core::psql::Error),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

#[derive(Debug, Display, PartialEq, Eq, Hash, EnumString, EnumIter)]
#[strum(ascii_case_insensitive, serialize_all = "kebab-case")]
pub enum FileTypes {
    Base,
    Crs,
    Feedback,
    GoAnnotations,
    InteractingProteins,
    InteractingRnas,
    Orfs,
    Precompute,
    QaStatus,
    R2dt,
    RfamHits,
    SoInfo,
}

pub struct FileJoiner {
    basic: JsonlIterator<File, Grouped<Basic>>,
    crs: JsonlIterator<File, Grouped<Crs>>,
    feedback: JsonlIterator<File, Grouped<Feedback>>,
    go_annotations: JsonlIterator<File, Grouped<GoAnnotation>>,
    interacting_proteins: JsonlIterator<File, Grouped<InteractingProtein>>,
    interacting_rnas: JsonlIterator<File, Grouped<InteractingRna>>,
    orfs: JsonlIterator<File, Grouped<Orf>>,
    precompute: JsonlIterator<File, Grouped<Precompute>>,
    qa_status: JsonlIterator<File, Grouped<QaStatus>>,
    r2dt_hits: JsonlIterator<File, Grouped<R2dt>>,
    rfam_hits: JsonlIterator<File, Grouped<RfamHit>>,
    so_info: SoMapping,
}

pub struct FileJoinerBuilder {
    paths: HashMap<FileTypes, PathBuf>,
}

impl TryFrom<&Path> for FileTypes {
    type Error = Error;

    fn try_from(path: &Path) -> Result<Self, Self::Error> {
        let name = path
            .file_stem()
            .map(|s| s.to_str())
            .flatten()
            .ok_or_else(|| Error::BadFileName(PathBuf::from(path)))?;
        let name = name.replace("-", "").replace("_", "");
        Self::from_str(&name).map_err(|_| Error::UnknownFileType(PathBuf::from(path)))
    }
}

impl Default for FileJoinerBuilder {
    fn default() -> Self {
        Self {
            paths: HashMap::default(),
        }
    }
}

impl FileJoinerBuilder {
    pub fn from_files(paths: Vec<PathBuf>) -> Result<Self, Error> {
        let mut builder = Self::default();
        for path in paths {
            let file_type = FileTypes::try_from(path.as_ref())?;
            builder.file(file_type, path);
        }

        for file_type in FileTypes::iter() {
            if !builder.is_set(&file_type) {
                return Err(Error::MustDefineMissingFile(file_type));
            }
        }

        Ok(builder)
    }

    pub fn is_set(&mut self, file: &FileTypes) -> bool {
        self.paths.contains_key(file)
    }

    pub fn file(&mut self, file: FileTypes, path: PathBuf) -> &mut Self {
        self.paths.insert(file, path);
        self
    }

    fn path_for(&self, file: FileTypes) -> Result<&PathBuf, Error> {
        self.paths.get(&file).ok_or_else(|| Error::MustDefineMissingFile(file))
    }

    fn iterator_for<T: DeserializeOwned>(
        &self,
        file: FileTypes,
    ) -> Result<JsonlIterator<File, T>, Error> {
        let path = self.path_for(file)?;
        Ok(JsonlIterator::from_path(path)?)
    }

    pub fn build(&self) -> Result<FileJoiner, Error> {
        let basic = self.iterator_for(FileTypes::Base)?;
        let crs = self.iterator_for(FileTypes::Crs)?;
        let feedback = self.iterator_for(FileTypes::Feedback)?;
        let go_annotations = self.iterator_for(FileTypes::GoAnnotations)?;
        let interacting_proteins = self.iterator_for(FileTypes::InteractingProteins)?;
        let interacting_rnas = self.iterator_for(FileTypes::InteractingRnas)?;
        let orfs = self.iterator_for(FileTypes::Orfs)?;
        let precompute = self.iterator_for(FileTypes::Precompute)?;
        let qa_status = self.iterator_for(FileTypes::QaStatus)?;
        let r2dt_hits = self.iterator_for(FileTypes::R2dt)?;
        let rfam_hits = self.iterator_for(FileTypes::RfamHits)?;
        let so_info = so_tree::load(self.path_for(FileTypes::SoInfo)?)?;

        Ok(FileJoiner {
            basic,
            crs,
            feedback,
            go_annotations,
            interacting_proteins,
            interacting_rnas,
            orfs,
            precompute,
            qa_status,
            r2dt_hits,
            rfam_hits,
            so_info,
        })
    }
}

impl Iterator for FileJoiner {
    type Item = Result<Raw, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let current = (
            self.basic.next(),
            self.crs.next(),
            self.feedback.next(),
            self.go_annotations.next(),
            self.interacting_proteins.next(),
            self.interacting_rnas.next(),
            self.orfs.next(),
            self.precompute.next(),
            self.qa_status.next(),
            self.r2dt_hits.next(),
            self.rfam_hits.next(),
        );

        match current {
            (None, None, None, None, None, None, None, None, None, None, None) => None,
            (
                Some(Required {
                    id: id1,
                    data: base,
                }),
                Some(Multiple {
                    id: id2,
                    data: crs,
                }),
                Some(Multiple {
                    id: id3,
                    data: feedback,
                }),
                Some(Multiple {
                    id: id4,
                    data: go_annotations,
                }),
                Some(Multiple {
                    id: id5,
                    data: interacting_proteins,
                }),
                Some(Multiple {
                    id: id6,
                    data: interacting_rnas,
                }),
                Some(Multiple {
                    id: id7,
                    data: orfs,
                }),
                Some(Required {
                    id: id8,
                    data: precompute,
                }),
                Some(Required {
                    id: id9,
                    data: qa_status,
                }),
                Some(Optional {
                    id: id10,
                    data: r2dt,
                }),
                Some(Multiple {
                    id: id11,
                    data: rfam_hits,
                }),
            ) => {
                if id1 != id2
                    || id1 != id3
                    || id1 != id4
                    || id1 != id5
                    || id1 != id6
                    || id1 != id7
                    || id1 != id8
                    || id1 != id9
                    || id1 != id10
                    || id1 != id11
                {
                    return Some(Err(Error::OutofSyncData(vec![
                        id1, id2, id3, id4, id5, id6, id7, id8, id9, id10, id11
                    ])));
                }

                let pre_so_type = precompute.so_rna_type();
                if !self.so_info.contains_key(pre_so_type) {
                    return Some(Err(Error::MissingSoTrem(pre_so_type.to_string())));
                }
                let so_tree = self.so_info[pre_so_type].clone();

                let raw = Raw::builder()
                    .id(id1)
                    .base(base)
                    .precompute(precompute)
                    .qa_status(qa_status)
                    .crs(crs)
                    .feedback(feedback)
                    .go_annotations(go_annotations)
                    .interacting_proteins(interacting_proteins)
                    .interacting_rnas(interacting_rnas)
                    .r2dt(r2dt)
                    .rfam_hits(rfam_hits)
                    .orfs(orfs)
                    .so_tree(so_tree)
                    .build();

                return Some(Ok(raw));
            },
            _ => Some(Err(Error::InvalidDataFormat)),
        }
    }
}
<commit_msg>No need to normalize now<commit_after>use std::{
    collections::HashMap,
    convert::TryFrom,
    fs::File,
    path::{
        Path,
        PathBuf,
    },
    str::FromStr,
};

use serde::de::DeserializeOwned;
use strum::IntoEnumIterator;
use strum_macros::{
    Display,
    EnumIter,
    EnumString,
};
use thiserror::Error;

use rnc_core::{
    grouper::Grouped::{
        self,
        Multiple,
        Optional,
        Required,
    },
    psql::JsonlIterator,
};

use super::{
    basic::Basic,
    crs::Crs,
    feedback::Feedback,
    go_annotation::GoAnnotation,
    interacting_protein::InteractingProtein,
    interacting_rna::InteractingRna,
    orf::Orf,
    precompute::Precompute,
    qa_status::QaStatus,
    r2dt::R2dt,
    raw::Raw,
    rfam_hit::RfamHit,
    so_tree,
    so_tree::SoMapping,
};

#[derive(Debug, Error)]
pub enum Error {
    #[error("Missing required file {0}")]
    MustDefineMissingFile(FileTypes),

    #[error("Not all files are in required format")]
    InvalidDataFormat,

    #[error("Missing requested SO term {0}")]
    MissingSoTrem(String),

    #[error("Cannot find expected file, from {0:?}")]
    BadFileName(PathBuf),

    #[error("Could not match path {0:?} to a possible file type")]
    UnknownFileType(PathBuf),

    #[error("Data out of sync at: {0:?}")]
    OutofSyncData(Vec<usize>),

    #[error(transparent)]
    JsonError(#[from] rnc_core::psql::Error),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

#[derive(Debug, Display, PartialEq, Eq, Hash, EnumString, EnumIter)]
#[strum(ascii_case_insensitive, serialize_all = "kebab-case")]
pub enum FileTypes {
    Base,
    Crs,
    Feedback,
    GoAnnotations,
    InteractingProteins,
    InteractingRnas,
    Orfs,
    Precompute,
    QaStatus,
    R2dt,
    RfamHits,
    SoInfo,
}

pub struct FileJoiner {
    basic: JsonlIterator<File, Grouped<Basic>>,
    crs: JsonlIterator<File, Grouped<Crs>>,
    feedback: JsonlIterator<File, Grouped<Feedback>>,
    go_annotations: JsonlIterator<File, Grouped<GoAnnotation>>,
    interacting_proteins: JsonlIterator<File, Grouped<InteractingProtein>>,
    interacting_rnas: JsonlIterator<File, Grouped<InteractingRna>>,
    orfs: JsonlIterator<File, Grouped<Orf>>,
    precompute: JsonlIterator<File, Grouped<Precompute>>,
    qa_status: JsonlIterator<File, Grouped<QaStatus>>,
    r2dt_hits: JsonlIterator<File, Grouped<R2dt>>,
    rfam_hits: JsonlIterator<File, Grouped<RfamHit>>,
    so_info: SoMapping,
}

pub struct FileJoinerBuilder {
    paths: HashMap<FileTypes, PathBuf>,
}

impl TryFrom<&Path> for FileTypes {
    type Error = Error;

    fn try_from(path: &Path) -> Result<Self, Self::Error> {
        let name = path
            .file_stem()
            .map(|s| s.to_str())
            .flatten()
            .ok_or_else(|| Error::BadFileName(PathBuf::from(path)))?;
        Self::from_str(&name).map_err(|_| Error::UnknownFileType(PathBuf::from(path)))
    }
}

impl Default for FileJoinerBuilder {
    fn default() -> Self {
        Self {
            paths: HashMap::default(),
        }
    }
}

impl FileJoinerBuilder {
    pub fn from_files(paths: Vec<PathBuf>) -> Result<Self, Error> {
        let mut builder = Self::default();
        for path in paths {
            let file_type = FileTypes::try_from(path.as_ref())?;
            builder.file(file_type, path);
        }

        for file_type in FileTypes::iter() {
            if !builder.is_set(&file_type) {
                return Err(Error::MustDefineMissingFile(file_type));
            }
        }

        Ok(builder)
    }

    pub fn is_set(&mut self, file: &FileTypes) -> bool {
        self.paths.contains_key(file)
    }

    pub fn file(&mut self, file: FileTypes, path: PathBuf) -> &mut Self {
        self.paths.insert(file, path);
        self
    }

    fn path_for(&self, file: FileTypes) -> Result<&PathBuf, Error> {
        self.paths.get(&file).ok_or_else(|| Error::MustDefineMissingFile(file))
    }

    fn iterator_for<T: DeserializeOwned>(
        &self,
        file: FileTypes,
    ) -> Result<JsonlIterator<File, T>, Error> {
        let path = self.path_for(file)?;
        Ok(JsonlIterator::from_path(path)?)
    }

    pub fn build(&self) -> Result<FileJoiner, Error> {
        let basic = self.iterator_for(FileTypes::Base)?;
        let crs = self.iterator_for(FileTypes::Crs)?;
        let feedback = self.iterator_for(FileTypes::Feedback)?;
        let go_annotations = self.iterator_for(FileTypes::GoAnnotations)?;
        let interacting_proteins = self.iterator_for(FileTypes::InteractingProteins)?;
        let interacting_rnas = self.iterator_for(FileTypes::InteractingRnas)?;
        let orfs = self.iterator_for(FileTypes::Orfs)?;
        let precompute = self.iterator_for(FileTypes::Precompute)?;
        let qa_status = self.iterator_for(FileTypes::QaStatus)?;
        let r2dt_hits = self.iterator_for(FileTypes::R2dt)?;
        let rfam_hits = self.iterator_for(FileTypes::RfamHits)?;
        let so_info = so_tree::load(self.path_for(FileTypes::SoInfo)?)?;

        Ok(FileJoiner {
            basic,
            crs,
            feedback,
            go_annotations,
            interacting_proteins,
            interacting_rnas,
            orfs,
            precompute,
            qa_status,
            r2dt_hits,
            rfam_hits,
            so_info,
        })
    }
}

impl Iterator for FileJoiner {
    type Item = Result<Raw, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let current = (
            self.basic.next(),
            self.crs.next(),
            self.feedback.next(),
            self.go_annotations.next(),
            self.interacting_proteins.next(),
            self.interacting_rnas.next(),
            self.orfs.next(),
            self.precompute.next(),
            self.qa_status.next(),
            self.r2dt_hits.next(),
            self.rfam_hits.next(),
        );

        match current {
            (None, None, None, None, None, None, None, None, None, None, None) => None,
            (
                Some(Required {
                    id: id1,
                    data: base,
                }),
                Some(Multiple {
                    id: id2,
                    data: crs,
                }),
                Some(Multiple {
                    id: id3,
                    data: feedback,
                }),
                Some(Multiple {
                    id: id4,
                    data: go_annotations,
                }),
                Some(Multiple {
                    id: id5,
                    data: interacting_proteins,
                }),
                Some(Multiple {
                    id: id6,
                    data: interacting_rnas,
                }),
                Some(Multiple {
                    id: id7,
                    data: orfs,
                }),
                Some(Required {
                    id: id8,
                    data: precompute,
                }),
                Some(Required {
                    id: id9,
                    data: qa_status,
                }),
                Some(Optional {
                    id: id10,
                    data: r2dt,
                }),
                Some(Multiple {
                    id: id11,
                    data: rfam_hits,
                }),
            ) => {
                if id1 != id2
                    || id1 != id3
                    || id1 != id4
                    || id1 != id5
                    || id1 != id6
                    || id1 != id7
                    || id1 != id8
                    || id1 != id9
                    || id1 != id10
                    || id1 != id11
                {
                    return Some(Err(Error::OutofSyncData(vec![
                        id1, id2, id3, id4, id5, id6, id7, id8, id9, id10, id11
                    ])));
                }

                let pre_so_type = precompute.so_rna_type();
                if !self.so_info.contains_key(pre_so_type) {
                    return Some(Err(Error::MissingSoTrem(pre_so_type.to_string())));
                }
                let so_tree = self.so_info[pre_so_type].clone();

                let raw = Raw::builder()
                    .id(id1)
                    .base(base)
                    .precompute(precompute)
                    .qa_status(qa_status)
                    .crs(crs)
                    .feedback(feedback)
                    .go_annotations(go_annotations)
                    .interacting_proteins(interacting_proteins)
                    .interacting_rnas(interacting_rnas)
                    .r2dt(r2dt)
                    .rfam_hits(rfam_hits)
                    .orfs(orfs)
                    .so_tree(so_tree)
                    .build();

                return Some(Ok(raw));
            },
            _ => Some(Err(Error::InvalidDataFormat)),
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate macrolisp;
use macrolisp::prelude::*;

#[test]
fn main() {
    let add4 = lisp!(
        (lambda (((a i32)
                  (b i32)
                  (c i32)
                  (d i32))
                 i32)
         (+ a b c d))
    );
    let factorial_proc = lisp!(
        (lambda (((a i32))
                 i32)
         (let ((mut x   a)
                (mut acc 1))
          (while (> x 1)
           (:= acc (* acc x))
           (:= x   (- x 1)))
          acc))
    );
    lisp!(
        (defn factorial (((a i32))
                         i32)
         (if (== a 1)
             1
             (* a (factorial (- a 1)))))
    );

    lisp!(
        (defn factorial_tail (((a i32))
                              i32)
         (defn factorial_tail_helper (((a i32)
                                       (acc i32))
                                      i32)
          (if (== a 1)
              acc
              (factorial_tail_helper (- a 1) (* acc a))))
         (factorial_tail_helper a 1))
    );

    println!("1+2+3+4 = {}", lisp!( (add4 1 2 3 4) )); // TODO example with heterogeneous types
    println!("1-2-3-4 = {}", lisp!( (- 1 2 3 4) ));
    println!("5! = {}", lisp!( (factorial_proc 5) ));
    println!("6! = {}", lisp!( (factorial 6) ));
    println!("7! = {}", lisp!( (factorial_tail 7) ));
}

#[test]
fn lambdarec_tests() {
    let factorial_rec = lisp!(
        (lambda rec (((a i32))
                      i32)
         (if (== a 1)
             1
             (* a (rec (- a 1)))))
    );
    let fib = lisp!(
        (lambda rec (((a i32))
                      i32)
         (match a
          (0 1)
          (1 1)
          (n (+ (rec (- n 1))
                (rec (- n 2))))))
    );

    println!("-(8!) = {}", lisp!( (- (factorial_rec 8)) ));
    lisp!(
        (println! "fib = {} {} {} {} {} {} {} {} {} {} ..." (fib 0)
                                                            (fib 1)
                                                            (fib 2)
                                                            (fib 3)
                                                            (fib 4)
                                                            (fib 5)
                                                            (fib 6)
                                                            (fib 7)
                                                            (fib 8)
                                                            (fib 9))
    );
}

#[test]
fn lambda_tests() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda (((x i32))
                                     ())
                             (:= num (+ num x)))))
         (add_num 5))
    );
    println!("num = {}", num);
}

/* rust-lang/rust#12335

#[test] #[compile_fail]
fn lambdarec_cannot_capture() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda rec (((x i32))
                                        ())
                            (if (> x 0)
                             (do
                              (:= num (+ num 1))
                              (rec (- x 1)))
                             ()))))
         (add_num 5))
    );
    println!("num = {}", num);
}
*/

<commit_msg>actually check test results<commit_after>#[macro_use] extern crate macrolisp;
use macrolisp::prelude::*;

macro_rules! ck {
    ($left:expr, $right:expr) => {{
        assert_eq!($left, $right);
        $left
    }}
}

#[test]
fn main() {
    let add4 = lisp!(
        (lambda (((a i32)
                  (b i32)
                  (c i32)
                  (d i32))
                 i32)
         (+ a b c d))
    );
    let factorial_proc = lisp!(
        (lambda (((a i32))
                 i32)
         (let ((mut x   a)
                (mut acc 1))
          (while (> x 1)
           (:= acc (* acc x))
           (:= x   (- x 1)))
          acc))
    );
    lisp!(
        (defn factorial (((a i32))
                         i32)
         (if (== a 1)
             1
             (* a (factorial (- a 1)))))
    );

    lisp!(
        (defn factorial_tail (((a i32))
                              i32)
         (defn factorial_tail_helper (((a i32)
                                       (acc i32))
                                      i32)
          (if (== a 1)
              acc
              (factorial_tail_helper (- a 1) (* acc a))))
         (factorial_tail_helper a 1))
    );

    println!("1+2+3+4 = {}", ck!(lisp!( (add4 1 2 3 4)     ), 10)); // TODO example with heterogeneous types
    println!("1-2-3-4 = {}", ck!(lisp!( (- 1 2 3 4)        ), -8));
    println!("5! = {}",      ck!(lisp!( (factorial_proc 5) ), 120));
    println!("6! = {}",      ck!(lisp!( (factorial 6)      ), 720));
    println!("7! = {}",      ck!(lisp!( (factorial_tail 7) ), 5040));
}

#[test]
fn lambdarec_tests() {
    let factorial_rec = lisp!(
        (lambda rec (((a i32))
                      i32)
         (if (== a 1)
             1
             (* a (rec (- a 1)))))
    );
    let fib = lisp!(
        (lambda rec (((a i32))
                      i32)
         (match a
          (0 1)
          (1 1)
          (n (+ (rec (- n 1))
                (rec (- n 2))))))
    );

    println!("-(8!) = {}", ck!(lisp!( (- (factorial_rec 8)) ), -40320));
    lisp!(
        (println! "fib = {} {} {} {} {} {} {} {} {} {} ..." (ck! (fib 0) 1)
                                                            (ck! (fib 1) 1)
                                                            (ck! (fib 2) 2)
                                                            (ck! (fib 3) 3)
                                                            (ck! (fib 4) 5)
                                                            (ck! (fib 5) 8)
                                                            (ck! (fib 6) 13)
                                                            (ck! (fib 7) 21)
                                                            (ck! (fib 8) 34)
                                                            (ck! (fib 9) 55))
    );
}

#[test]
fn lambda_tests() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda (((x i32))
                                     ())
                             (:= num (+ num x)))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}

/* rust-lang/rust#12335

#[test] #[compile_fail]
fn lambdarec_cannot_capture() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda rec (((x i32))
                                        ())
                            (if (> x 0)
                             (do
                              (:= num (+ num 1))
                              (rec (- x 1)))
                             ()))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}
*/

<|endoftext|>


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

use darling::{FromDeriveInput, FromField, FromVariant};
use proc_macro2::{Span, TokenStream};
use quote::TokenStreamExt;
use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
use syn::{GenericArgument, GenericParam, Ident, Path};
use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray};
use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
use syn::{Variant, WherePredicate};
use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};

/// Given an input type which has some where clauses already, like:
///
/// struct InputType<T>
/// where
///     T: Zero,
/// {
///     ...
/// }
///
/// Add the necessary `where` clauses so that the output type of a trait
/// fulfils them.
///
/// For example:
///
///     <T as ToComputedValue>::ComputedValue: Zero,
///
/// This needs to run before adding other bounds to the type parameters.
pub fn propagate_clauses_to_output_type(
    where_clause: &mut Option<syn::WhereClause>,
    generics: &syn::Generics,
    trait_path: &Path,
    trait_output: &Ident,
) {
    let where_clause = match *where_clause {
        Some(ref mut clause) => clause,
        None => return,
    };
    let mut extra_bounds = vec![];
    for pred in &where_clause.predicates {
        let ty = match *pred {
            syn::WherePredicate::Type(ref ty) => ty,
            ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
        };

        let path = match ty.bounded_ty {
            syn::Type::Path(ref p) => &p.path,
            ref ty => panic!("Unhanded complex where type: {:?}", ty),
        };

        assert!(
            ty.lifetimes.is_none(),
            "Unhanded complex lifetime bound: {:?}",
            ty,
        );

        let ident = match path_to_ident(path) {
            Some(i) => i,
            None => panic!("Unhanded complex where type path: {:?}", path),
        };

        if generics.type_params().any(|param| param.ident == *ident) {
            extra_bounds.push(ty.clone());
        }
    }

    for bound in extra_bounds {
        let ty = bound.bounded_ty;
        let bounds = bound.bounds;
        where_clause
            .predicates
            .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
    }
}

pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
    where_clause
        .get_or_insert(parse_quote!(where))
        .predicates
        .push(pred);
}

pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, mut f: F) -> TokenStream
where
    F: FnMut(BindingInfo) -> TokenStream,
{
    let mut s = synstructure::Structure::new(input);
    s.variants_mut().iter_mut().for_each(|v| {
        v.bind_with(|_| bind_style);
    });
    s.each_variant(|variant| {
        let (mapped, mapped_fields) = value(variant, "mapped");
        let fields_pairs = variant.bindings().iter().zip(mapped_fields);
        let mut computations = quote!();
        computations.append_all(fields_pairs.map(|(field, mapped_field)| {
            let expr = f(field.clone());
            quote! { let #mapped_field = #expr; }
        }));
        computations.append_all(mapped);
        Some(computations)
    })
}

pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &Ident) -> Path {
    let segment = PathSegment {
        ident: input.ident.clone(),
        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
            args: input
                .generics
                .params
                .iter()
                .map(|arg| match arg {
                    &GenericParam::Lifetime(ref data) => {
                        GenericArgument::Lifetime(data.lifetime.clone())
                    },
                    &GenericParam::Type(ref data) => {
                        let ident = &data.ident;
                        GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
                    },
                    ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
                })
                .collect(),
            colon2_token: Default::default(),
            gt_token: Default::default(),
            lt_token: Default::default(),
        }),
    };
    segment.into()
}

pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type
where
    F: FnMut(&Ident) -> Type,
{
    match *ty {
        Type::Slice(ref inner) => Type::from(TypeSlice {
            elem: Box::new(map_type_params(&inner.elem, params, f)),
            ..inner.clone()
        }),
        Type::Array(ref inner) => {
            //ref ty, ref expr) => {
            Type::from(TypeArray {
                elem: Box::new(map_type_params(&inner.elem, params, f)),
                ..inner.clone()
            })
        },
        ref ty @ Type::Never(_) => ty.clone(),
        Type::Tuple(ref inner) => Type::from(TypeTuple {
            elems: inner
                .elems
                .iter()
                .map(|ty| map_type_params(&ty, params, f))
                .collect(),
            ..inner.clone()
        }),
        Type::Path(TypePath {
            qself: None,
            ref path,
        }) => {
            if let Some(ident) = path_to_ident(path) {
                if params.iter().any(|ref param| &param.ident == ident) {
                    return f(ident);
                }
            }
            Type::from(TypePath {
                qself: None,
                path: map_type_params_in_path(path, params, f),
            })
        },
        Type::Path(TypePath {
            ref qself,
            ref path,
        }) => Type::from(TypePath {
            qself: qself.as_ref().map(|qself| QSelf {
                ty: Box::new(map_type_params(&qself.ty, params, f)),
                position: qself.position,
                ..qself.clone()
            }),
            path: map_type_params_in_path(path, params, f),
        }),
        Type::Paren(ref inner) => Type::from(TypeParen {
            elem: Box::new(map_type_params(&inner.elem, params, f)),
            ..inner.clone()
        }),
        ref ty => panic!("type {:?} cannot be mapped yet", ty),
    }
}

fn map_type_params_in_path<F>(path: &Path, params: &[&TypeParam], f: &mut F) -> Path
where
    F: FnMut(&Ident) -> Type,
{
    Path {
        leading_colon: path.leading_colon,
        segments: path
            .segments
            .iter()
            .map(|segment| PathSegment {
                ident: segment.ident.clone(),
                arguments: match segment.arguments {
                    PathArguments::AngleBracketed(ref data) => {
                        PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                            args: data
                                .args
                                .iter()
                                .map(|arg| match arg {
                                    ty @ &GenericArgument::Lifetime(_) => ty.clone(),
                                    &GenericArgument::Type(ref data) => {
                                        GenericArgument::Type(map_type_params(data, params, f))
                                    },
                                    &GenericArgument::Binding(ref data) => {
                                        GenericArgument::Binding(Binding {
                                            ty: map_type_params(&data.ty, params, f),
                                            ..data.clone()
                                        })
                                    },
                                    ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
                                })
                                .collect(),
                            ..data.clone()
                        })
                    },
                    ref arg @ PathArguments::None => arg.clone(),
                    ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
                },
            })
            .collect(),
    }
}

fn path_to_ident(path: &Path) -> Option<&Ident> {
    match *path {
        Path {
            leading_colon: None,
            ref segments,
        } if segments.len() == 1 => {
            if segments[0].arguments.is_empty() {
                Some(&segments[0].ident)
            } else {
                None
            }
        },
        _ => None,
    }
}

pub fn parse_field_attrs<A>(field: &Field) -> A
where
    A: FromField,
{
    match A::from_field(field) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse field attributes: {}", e),
    }
}

pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
where
    A: FromDeriveInput,
{
    match A::from_derive_input(input) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse input attributes: {}", e),
    }
}

pub fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
where
    A: FromVariant,
{
    let v = Variant {
        ident: variant.ident.clone(),
        attrs: variant.attrs.to_vec(),
        fields: variant.fields.clone(),
        discriminant: variant.discriminant.clone(),
    };
    parse_variant_attrs(&v)
}

pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
    A: FromVariant,
{
    match A::from_variant(variant) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse variant attributes: {}", e),
    }
}

pub fn ref_pattern<'a>(
    variant: &'a VariantInfo,
    prefix: &str,
) -> (TokenStream, Vec<BindingInfo<'a>>) {
    let mut v = variant.clone();
    v.bind_with(|_| BindStyle::Ref);
    v.bindings_mut().iter_mut().for_each(|b| {
        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
    });
    (v.pat(), v.bindings().to_vec())
}

pub fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {
    let mut v = variant.clone();
    v.bindings_mut().iter_mut().for_each(|b| {
        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
    });
    v.bind_with(|_| BindStyle::Move);
    (v.pat(), v.bindings().to_vec())
}

/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
/// is prepended with "-".
pub fn to_css_identifier(mut camel_case: &str) -> String {
    camel_case = camel_case.trim_end_matches('_');
    let mut first = true;
    let mut result = String::with_capacity(camel_case.len());
    while let Some(segment) = split_camel_segment(&mut camel_case) {
        if first {
            match segment {
                "Moz" | "Webkit" | "Servo" => first = false,
                _ => {},
            }
        }
        if !first {
            result.push_str("-");
        }
        first = false;
        result.push_str(&segment.to_lowercase());
    }
    result
}

/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
    let index = match camel_case.chars().next() {
        None => return None,
        Some(ch) => ch.len_utf8(),
    };
    let end_position = camel_case[index..]
        .find(char::is_uppercase)
        .map_or(camel_case.len(), |pos| index + pos);
    let result = &camel_case[..end_position];
    *camel_case = &camel_case[end_position..];
    Some(result)
}
<commit_msg>Auto merge of #24690 - federicomenaquintero:master, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use darling::{FromDeriveInput, FromField, FromVariant};
use proc_macro2::{Span, TokenStream};
use quote::TokenStreamExt;
use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
use syn::{GenericArgument, GenericParam, Ident, Path};
use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray};
use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
use syn::{Variant, WherePredicate};
use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};

/// Given an input type which has some where clauses already, like:
///
/// struct InputType<T>
/// where
///     T: Zero,
/// {
///     ...
/// }
///
/// Add the necessary `where` clauses so that the output type of a trait
/// fulfils them.
///
/// For example:
///
/// ```ignore
///     <T as ToComputedValue>::ComputedValue: Zero,
/// ```
///
/// This needs to run before adding other bounds to the type parameters.
pub fn propagate_clauses_to_output_type(
    where_clause: &mut Option<syn::WhereClause>,
    generics: &syn::Generics,
    trait_path: &Path,
    trait_output: &Ident,
) {
    let where_clause = match *where_clause {
        Some(ref mut clause) => clause,
        None => return,
    };
    let mut extra_bounds = vec![];
    for pred in &where_clause.predicates {
        let ty = match *pred {
            syn::WherePredicate::Type(ref ty) => ty,
            ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
        };

        let path = match ty.bounded_ty {
            syn::Type::Path(ref p) => &p.path,
            ref ty => panic!("Unhanded complex where type: {:?}", ty),
        };

        assert!(
            ty.lifetimes.is_none(),
            "Unhanded complex lifetime bound: {:?}",
            ty,
        );

        let ident = match path_to_ident(path) {
            Some(i) => i,
            None => panic!("Unhanded complex where type path: {:?}", path),
        };

        if generics.type_params().any(|param| param.ident == *ident) {
            extra_bounds.push(ty.clone());
        }
    }

    for bound in extra_bounds {
        let ty = bound.bounded_ty;
        let bounds = bound.bounds;
        where_clause
            .predicates
            .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
    }
}

pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
    where_clause
        .get_or_insert(parse_quote!(where))
        .predicates
        .push(pred);
}

pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, mut f: F) -> TokenStream
where
    F: FnMut(BindingInfo) -> TokenStream,
{
    let mut s = synstructure::Structure::new(input);
    s.variants_mut().iter_mut().for_each(|v| {
        v.bind_with(|_| bind_style);
    });
    s.each_variant(|variant| {
        let (mapped, mapped_fields) = value(variant, "mapped");
        let fields_pairs = variant.bindings().iter().zip(mapped_fields);
        let mut computations = quote!();
        computations.append_all(fields_pairs.map(|(field, mapped_field)| {
            let expr = f(field.clone());
            quote! { let #mapped_field = #expr; }
        }));
        computations.append_all(mapped);
        Some(computations)
    })
}

pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &Ident) -> Path {
    let segment = PathSegment {
        ident: input.ident.clone(),
        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
            args: input
                .generics
                .params
                .iter()
                .map(|arg| match arg {
                    &GenericParam::Lifetime(ref data) => {
                        GenericArgument::Lifetime(data.lifetime.clone())
                    },
                    &GenericParam::Type(ref data) => {
                        let ident = &data.ident;
                        GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
                    },
                    ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
                })
                .collect(),
            colon2_token: Default::default(),
            gt_token: Default::default(),
            lt_token: Default::default(),
        }),
    };
    segment.into()
}

pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type
where
    F: FnMut(&Ident) -> Type,
{
    match *ty {
        Type::Slice(ref inner) => Type::from(TypeSlice {
            elem: Box::new(map_type_params(&inner.elem, params, f)),
            ..inner.clone()
        }),
        Type::Array(ref inner) => {
            //ref ty, ref expr) => {
            Type::from(TypeArray {
                elem: Box::new(map_type_params(&inner.elem, params, f)),
                ..inner.clone()
            })
        },
        ref ty @ Type::Never(_) => ty.clone(),
        Type::Tuple(ref inner) => Type::from(TypeTuple {
            elems: inner
                .elems
                .iter()
                .map(|ty| map_type_params(&ty, params, f))
                .collect(),
            ..inner.clone()
        }),
        Type::Path(TypePath {
            qself: None,
            ref path,
        }) => {
            if let Some(ident) = path_to_ident(path) {
                if params.iter().any(|ref param| &param.ident == ident) {
                    return f(ident);
                }
            }
            Type::from(TypePath {
                qself: None,
                path: map_type_params_in_path(path, params, f),
            })
        },
        Type::Path(TypePath {
            ref qself,
            ref path,
        }) => Type::from(TypePath {
            qself: qself.as_ref().map(|qself| QSelf {
                ty: Box::new(map_type_params(&qself.ty, params, f)),
                position: qself.position,
                ..qself.clone()
            }),
            path: map_type_params_in_path(path, params, f),
        }),
        Type::Paren(ref inner) => Type::from(TypeParen {
            elem: Box::new(map_type_params(&inner.elem, params, f)),
            ..inner.clone()
        }),
        ref ty => panic!("type {:?} cannot be mapped yet", ty),
    }
}

fn map_type_params_in_path<F>(path: &Path, params: &[&TypeParam], f: &mut F) -> Path
where
    F: FnMut(&Ident) -> Type,
{
    Path {
        leading_colon: path.leading_colon,
        segments: path
            .segments
            .iter()
            .map(|segment| PathSegment {
                ident: segment.ident.clone(),
                arguments: match segment.arguments {
                    PathArguments::AngleBracketed(ref data) => {
                        PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                            args: data
                                .args
                                .iter()
                                .map(|arg| match arg {
                                    ty @ &GenericArgument::Lifetime(_) => ty.clone(),
                                    &GenericArgument::Type(ref data) => {
                                        GenericArgument::Type(map_type_params(data, params, f))
                                    },
                                    &GenericArgument::Binding(ref data) => {
                                        GenericArgument::Binding(Binding {
                                            ty: map_type_params(&data.ty, params, f),
                                            ..data.clone()
                                        })
                                    },
                                    ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
                                })
                                .collect(),
                            ..data.clone()
                        })
                    },
                    ref arg @ PathArguments::None => arg.clone(),
                    ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
                },
            })
            .collect(),
    }
}

fn path_to_ident(path: &Path) -> Option<&Ident> {
    match *path {
        Path {
            leading_colon: None,
            ref segments,
        } if segments.len() == 1 => {
            if segments[0].arguments.is_empty() {
                Some(&segments[0].ident)
            } else {
                None
            }
        },
        _ => None,
    }
}

pub fn parse_field_attrs<A>(field: &Field) -> A
where
    A: FromField,
{
    match A::from_field(field) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse field attributes: {}", e),
    }
}

pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
where
    A: FromDeriveInput,
{
    match A::from_derive_input(input) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse input attributes: {}", e),
    }
}

pub fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
where
    A: FromVariant,
{
    let v = Variant {
        ident: variant.ident.clone(),
        attrs: variant.attrs.to_vec(),
        fields: variant.fields.clone(),
        discriminant: variant.discriminant.clone(),
    };
    parse_variant_attrs(&v)
}

pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
    A: FromVariant,
{
    match A::from_variant(variant) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse variant attributes: {}", e),
    }
}

pub fn ref_pattern<'a>(
    variant: &'a VariantInfo,
    prefix: &str,
) -> (TokenStream, Vec<BindingInfo<'a>>) {
    let mut v = variant.clone();
    v.bind_with(|_| BindStyle::Ref);
    v.bindings_mut().iter_mut().for_each(|b| {
        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
    });
    (v.pat(), v.bindings().to_vec())
}

pub fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {
    let mut v = variant.clone();
    v.bindings_mut().iter_mut().for_each(|b| {
        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
    });
    v.bind_with(|_| BindStyle::Move);
    (v.pat(), v.bindings().to_vec())
}

/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
/// is prepended with "-".
pub fn to_css_identifier(mut camel_case: &str) -> String {
    camel_case = camel_case.trim_end_matches('_');
    let mut first = true;
    let mut result = String::with_capacity(camel_case.len());
    while let Some(segment) = split_camel_segment(&mut camel_case) {
        if first {
            match segment {
                "Moz" | "Webkit" | "Servo" => first = false,
                _ => {},
            }
        }
        if !first {
            result.push_str("-");
        }
        first = false;
        result.push_str(&segment.to_lowercase());
    }
    result
}

/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
    let index = match camel_case.chars().next() {
        None => return None,
        Some(ch) => ch.len_utf8(),
    };
    let end_position = camel_case[index..]
        .find(char::is_uppercase)
        .map_or(camel_case.len(), |pos| index + pos);
    let result = &camel_case[..end_position];
    *camel_case = &camel_case[end_position..];
    Some(result)
}
<|endoftext|>