dyer-cli 0.6.0

dyer-cli is a great tool created to guide you use dyer fast and at ease, helps you build a robust crawler, data processor, netwrok program fast and correctly.
//! # Introduction
//! [dyer-cli] is a great tool created to guide you use [dyer] fast and at ease, helps you build a robust crawler, data processor, netwrok program fast and correctly.
//!
//! [dyer-cli]: https://github.com/HomelyGuy/dyer-cli
//! [dyer]: https://github.com/HomelyGuy/dyer
//!
//! # Installation
//! dyer-cli is built completely by Rust programming language without extra dependencies, So rust must be installed beforehand, to test it with:
//! ```bash
//! rustup --version
//! ```
//! if you ever see some infomation like that
//! ```bash
//! rustup 1.23.1 (3df2264a9 2020-11-30)
//! ```
//! then you are ready to go, the following code would suffice.
//! ```bash
//! cargo install dyer-cli
//! ```
//! the command will download the source code and complie it to build a executable file inside your `$HOME/.cargo/bin`, make sure it's in your `$PATH`
//!
//! # Commands
//! Dyer-cli provides some commands that helps you initialize, debug programm, more commands are to go.
//!
//! ## dyer new
//! This command helps you initialize a project with log level `Info`, other log levels vares from `Error`, `Warn`, `Info`, `Debug`, and `Trace`, and its structure is
//! ```bash
//! |___Cargo.toml
//! |___Readme.md
//! |___data/
//! |___data/tasks/
//! |___src/
//!     |___src/entity.rs
//!     |___src/parser.rs
//!     |___src/actor.rs
//!     |___src/middleware.rs
//!     |___src/pipeline.rs
//! ```
//! Main functionality of each file:                                        
//! * the `affix.rs` serves as an actor to adjust and satisfy additional requirement
//! * the `entity.rs` contains entities/data structure to be used/collected
//! * the `parser.rs` contains functions that extract entities from response
//! * the `actor.rs` contains initial when opening and final things to do when closing
//! * the `middleware.rs` contains Some middlewares that process data at runtime
//! * the `pipeline.rs` contains entities manipulation including data-storage, displsying and so on
//! * `Cargo.toml` is the basic configuration of the project
//! * `README.md` contains some instructions of the project
//! * `data` folder balance the app load when data in app exceeds, and backup app data at certain gap
//!
//! ## dyer check
//!
//! A warper of `cargo check`, if you run it the first time,`dyer-cli` will download the crates and then check the code.
//!
//! ## dyer fix
//!
//! A wraper of `cargo fix`,  if some warning happens such as `unused import` or `dead code` the command does a lot for you. However it won't help if some errors occur, if so, you have to debug the code manually.
//!
//! ## dyer run
//!
//! A wraper of `cargo run`, when the program compiles, run it.
//!
//! ## dyer build
//!
//! A wraper of `cargo build`,   build the program.
//!
//! ## dyer test
//!
//! A wraper of `cargo test`,   test the program.
//!
//! ## dyer clean
//!
//! A wraper of `cargo clean`,   clean the directory.

mod subcommand;
mod util;

use subcommand::{
    SubComBuild, SubComCheck, SubComClean, SubComFix, SubComNew, SubComRun, SubComTest, SubCommand,
};
use util::LogLevel;

#[derive(std::fmt::Debug)]
pub struct Info {
    sub_command: String,
    options: Vec<String>,
    others: Vec<String>,
}
impl From<Vec<String>> for Info {
    fn from(mut args: Vec<String>) -> Self {
        let sub_command = args.remove(0);
        let mut options = Vec::new();
        let mut others = Vec::new();
        args.into_iter().for_each(|item: String| {
            if item.contains("--") {
                options.push(item);
            } else if item.contains("-") {
                options.push(item);
            } else {
                others.push(item);
            }
        });
        Info {
            sub_command,
            options,
            others,
        }
    }
}
impl Into<SubCommand> for Info {
    fn into(mut self) -> SubCommand {
        let mut comd: SubCommand = SubCommand::Null;
        if self.sub_command == "new" {
            let name = self.others.pop().expect("project name must be specified.");
            let level = if self.options.is_empty() {
                Some(LogLevel::Info)
            } else {
                let index = self.options.pop().unwrap();
                Some(index.parse::<LogLevel>().unwrap_or(LogLevel::Info))
            };
            comd = SubCommand::SubComNew(SubComNew {
                name,
                option: level,
            });
        } else if ["run".into(), "r".into()].contains(&self.sub_command) {
            let item = SubComRun {
                options: self.options,
            };
            comd = SubCommand::SubComRun(item);
        } else if self.sub_command == "fix" {
            let item = SubComFix {
                options: self.options,
            };
            comd = SubCommand::SubComFix(item);
        } else if ["c".into(), "check".into()].contains(&self.sub_command) {
            let item = SubComCheck {
                options: self.options,
            };
            comd = SubCommand::SubComCheck(item);
        } else if ["b".into(), "build".into()].contains(&self.sub_command) {
            let item = SubComBuild {
                options: self.options,
            };
            comd = SubCommand::SubComBuild(item);
        } else if ["t".into(), "test".into()].contains(&self.sub_command) {
            let item = SubComTest {
                options: self.options,
            };
            comd = SubCommand::SubComTest(item);
        } else if ["clean".into()].contains(&self.sub_command) {
            let item = SubComClean {
                options: self.options,
            };
            comd = SubCommand::SubComClean(item);
        }
        comd
    }
}

fn main() {
    let mut args: Vec<String> = std::env::args().collect();
    //println!("raw arguments: {:?}", args);
    args.remove(0); // remove the unnecessary path
    let msgs = "Handy tool for dyer\n\nUSAGE:\n\tdyer [subcommand] [options]\n\teg. dyer new myproject --debug create a project with logger level INFO\n\nSUBCOMMAND:\n\tnew:\t\tinitialize a new empty project\n\tcheck:\t a wraper of `cargo check`\n\tfix:\t\ta wraper of `cargo fix`\n\trun:\t\ta wraper of `cargo run`, compile and run the project\n\tbuild:\t a wraper of `cargo build`\n\ttest:\t  a wraper of `cargo test`\n\tclean:\t a wraper of `cargo clean`\n\nOPTIONS:\n\tall options of `cargo SUBCOMMAND`\n\t--off:\t\t  set the log level as Off\n\t--error:\t\tset the log level as ERROR\n\t--warn: \t\tset the log level as WARN\n\t--info: \t\tset the log level as INFO\n\t--debug:\t\tset the debug level as DEBUG\n\t--trace:\t\tset the log level as TRACE".replace("\t", "   ");
    if args.len() > 0 && !["-h", "--help"].contains(&args[0].as_str()) {
        let sub_command: SubCommand = Info::from(args.clone()).into();
        //println!("parsed info: {:?}", sub_command);
        if let SubCommand::Null = sub_command {
            println!(
                "Unknow arguments: \"{}\". Use `dyer -h` to see help",
                args.join(" ")
            );
        } else {
            sub_command.execute();
        }
    } else if args.len() == 0 {
        println!("{}", msgs);
    } else if args.len() > 0 && ["-h", "--help"].contains(&args[0].as_str()) {
        println!("{}", msgs);
    } else {
        println!(
            "Unknow arguments: \"{}\". Use `dyer -h` to see help",
            args.join(" ")
        );
    }
}