<commit_before>#![cfg_attr(feature = "nightly-testing", feature(plugin))]
#![cfg_attr(feature = "nightly-testing", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate rayon;
extern crate rustfmt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;
extern crate inflector;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;

mod cargo;
mod codegen;
mod config;
mod service;

use std::fs::{self, OpenOptions};
use std::io::{Write, BufWriter};
use std::path::Path;

use rayon::prelude::*;

use clap::{Arg, App};

use codegen::botocore::{ServiceDefinition};
pub use service::Service;
use config::ServiceConfig;

fn main() {
    let matches = App::new("Rusoto Service Crate Generator")
        .version(crate_version!())
        .author(crate_authors!())
        .about(crate_description!())
        .arg(Arg::with_name("services_config")
            .long("config")
            .short("c")
            .takes_value(true))
        .arg(Arg::with_name("out_dir")
            .long("outdir")
            .short("o")
            .takes_value(true)
            .required(true))
        .get_matches();

    let out_dir = Path::new(matches.value_of("out_dir").unwrap());

    if !out_dir.exists() {
        fs::create_dir(out_dir).expect("Unable to create output directory");
    }

    let services_config_path = matches.value_of("services_config").unwrap();
    let service_configs = ServiceConfig::load_all(services_config_path).expect("Unable to read services configuration file.");

    service_configs.par_iter().for_each(|&(ref name, ref service_config)| {
        let service = {
            let service_definition = ServiceDefinition::load(name, &service_config.protocol_version)
                .expect(&format!("Failed to load service {}. Make sure the botocore submodule has been initialized!", name));
            Service::new(service_config.clone(), service_definition)
        };

        let crate_dir = out_dir.join(&name);
        let crate_name = format!("rusoto_{}", &name.replace('-', "_"));

        println!("Generating crate for {} @ {}...", service.full_name(), service.api_version());

        if !crate_dir.exists() {
            fs::create_dir(&crate_dir).expect(&format!("Unable to create directory at {}", crate_dir.display()));
        }

        let service_dependencies = service.get_dependencies();

        let extern_crates = service_dependencies.iter().map(|(k, _)| {
            if k == "xml-rs" {
                return "extern crate xml;".into();
            }
            let safe_name = k.replace("-", "_");
            let use_macro = k == "serde_derive" || k == "log" || k == "lazy_static";
            if use_macro {
                return format!("#[macro_use]\nextern crate {};", safe_name);
            }
            format!("extern crate {};", safe_name)
        }).collect::<Vec<String>>().join("\n");

        let mut cargo_manifest = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(crate_dir.join("Cargo.toml"))
            .expect("Unable to write Cargo.toml");

        let manifest = cargo::Manifest {
            package: cargo::Metadata {
                authors: Some(vec![
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into()
                ]),
                description: Some(format!("AWS SDK for Rust - {} @ {}", service.full_name(), service.api_version())),
                documentation: Some("https://rusoto.github.io/rusoto/rusoto_core/index.html".into()),
                keywords: Some(vec!["AWS".into(), "Amazon".into(), name.clone()]),
                license: Some("MIT".into()),
                name: crate_name.clone(),
                readme: Some("README.md".into()),
                repository: Some("https://github.com/rusoto/rusoto".into()),
                version: service_config.version.clone(),
                homepage: Some("https://www.rusoto.org/".into()),
                ..cargo::Metadata::default()
            },
            dependencies: service_dependencies,
            dev_dependencies: vec![
                ("rusoto_mock".to_owned(), cargo::Dependency::Extended {
                    path: Some("../../../mock".into()),
                    version: Some("0.24.0".into()),
                    optional: None,
                    default_features: None,
                    features: None
                })
            ].into_iter().collect(),
            ..cargo::Manifest::default()
        };

        cargo_manifest.write_all(toml::to_string(&manifest).unwrap().as_bytes()).unwrap();

        let mut readme_file = OpenOptions::new()
            .write(true)
            .truncate(true)<commit_msg>Trim crate keyword if the service name is too long.<commit_after>#![cfg_attr(feature = "nightly-testing", feature(plugin))]
#![cfg_attr(feature = "nightly-testing", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate rayon;
extern crate rustfmt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;
extern crate inflector;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;

mod cargo;
mod codegen;
mod config;
mod service;

use std::fs::{self, OpenOptions};
use std::io::{Write, BufWriter};
use std::path::Path;

use rayon::prelude::*;

use clap::{Arg, App};

use codegen::botocore::{ServiceDefinition};
pub use service::Service;
use config::ServiceConfig;

fn main() {
    let matches = App::new("Rusoto Service Crate Generator")
        .version(crate_version!())
        .author(crate_authors!())
        .about(crate_description!())
        .arg(Arg::with_name("services_config")
            .long("config")
            .short("c")
            .takes_value(true))
        .arg(Arg::with_name("out_dir")
            .long("outdir")
            .short("o")
            .takes_value(true)
            .required(true))
        .get_matches();

    let out_dir = Path::new(matches.value_of("out_dir").unwrap());

    if !out_dir.exists() {
        fs::create_dir(out_dir).expect("Unable to create output directory");
    }

    let services_config_path = matches.value_of("services_config").unwrap();
    let service_configs = ServiceConfig::load_all(services_config_path).expect("Unable to read services configuration file.");

    service_configs.par_iter().for_each(|&(ref name, ref service_config)| {
        let service = {
            let service_definition = ServiceDefinition::load(name, &service_config.protocol_version)
                .expect(&format!("Failed to load service {}. Make sure the botocore submodule has been initialized!", name));
            Service::new(service_config.clone(), service_definition)
        };

        let crate_dir = out_dir.join(&name);
        let crate_name = format!("rusoto_{}", &name.replace('-', "_"));

        println!("Generating crate for {} @ {}...", service.full_name(), service.api_version());

        if !crate_dir.exists() {
            fs::create_dir(&crate_dir).expect(&format!("Unable to create directory at {}", crate_dir.display()));
        }

        let service_dependencies = service.get_dependencies();

        let extern_crates = service_dependencies.iter().map(|(k, _)| {
            if k == "xml-rs" {
                return "extern crate xml;".into();
            }
            let safe_name = k.replace("-", "_");
            let use_macro = k == "serde_derive" || k == "log" || k == "lazy_static";
            if use_macro {
                return format!("#[macro_use]\nextern crate {};", safe_name);
            }
            format!("extern crate {};", safe_name)
        }).collect::<Vec<String>>().join("\n");

        let mut cargo_manifest = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(crate_dir.join("Cargo.toml"))
            .expect("Unable to write Cargo.toml");

        // crates.io has a max of 20 chars for keywords, do an ugly truncate if needed:
        let mut name_for_keyword = name.clone().to_string();
        if name_for_keyword.len() >= 20 {
            name_for_keyword = name_for_keyword[..20].to_string();
        }

        let manifest = cargo::Manifest {
            package: cargo::Metadata {
                authors: Some(vec![
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into(),
                    "<NAME> <<EMAIL>>".into()
                ]),
                description: Some(format!("AWS SDK for Rust - {} @ {}", service.full_name(), service.api_version())),
                documentation: Some("https://rusoto.github.io/rusoto/rusoto_core/index.html".into()),
                keywords: Some(vec!["AWS".into(), "Amazon".into(), name_for_keyword]),
                license: Some("MIT".into()),
                name: crate_name.clone(),
                readme: Some("README.md".into()),
                repository: Some("https://github.com/rusoto/rusoto".into()),
                version: service_config.version.clone(),
                homepage: Some("https://www.rusoto.org/".into()),
                ..cargo::Metadata::default()
            },
            dependencies: service_dependencies,
            dev_dependencies: vec![
                ("rusoto_mock".to_owned(), cargo::Dependency::Extended {
                    path: Some("../../../mock".into()),
                    version: Some("0.24.0".into()),
                    optional: None,
                    default_features: None,
                    features: None
                })
            ].into_iter().collect(),
            ..cargo::Manifest::default()
        };

        cargo_manifest.write_all(toml::to_string(&manifest).unwrap().as_bytes()).unwrap();

        let mut readme_file = OpenOptions::new()
            .write(true)
            .truncate(true)
{'fraction_non_alphanumeric': 0.15751861042183624, 'fraction_numerical': 0.0013895781637717122, 'mean_word_length': 2.2928104575163397, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 32, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6618526', 'n_tokens_mistral': 2923, 'n_tokens_neox': 2837, 'n_words': 570}