From af156e7f7c3e994231d43bd9c7c0f29924027996 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 26 Jul 2020 14:34:11 +0800 Subject: [PATCH] chore: first forked commit --- .gitignore | 3 - Cargo.lock | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 23 +++ README.md | 108 +++++++++++++- src/main.rs | 331 ++++++++++++++++++++++++++++++++++++++++++ src/parser.rs | 40 ++++++ src/regex.rs | 32 +++++ src/token.rs | 133 +++++++++++++++++ 8 files changed, 1057 insertions(+), 5 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 src/parser.rs create mode 100644 src/regex.rs create mode 100644 src/token.rs diff --git a/.gitignore b/.gitignore index 65f1105..19712ce 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ # will have compiled files and executables /target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..66306a5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,392 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "cargo-todo" +version = "0.2.0" +dependencies = [ + "chrono", + "clap", + "colored", + "dirs", + "glob", + "regex", + "string-parser", + "string_format", + "walkdir", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "clap" +version = "2.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "string-parser" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e079466fad2cd4673f6ccd80b3a2c8aa94e981a7ab7b4e5c023a0a37e23596" + +[[package]] +name = "string_format" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a0f5f60043c6e865cc477a5cd3981df2210ec02592bd9ba18d32bcd72abb4eb" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..551a7bc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-todo" +version = "0.2.0" +authors = ["Clément Guiton "] +edition = "2018" +description = "Cargo tool to display TODOs" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/ProbablyClem/cargo-todo" +exclude = ["target"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glob = "0.3.0" +walkdir = "2.3.1" +colored = "1.9.3" +string-parser= "0.1.5" +regex = "1.3.9" +dirs = "2.0.2" +string_format = "0.1.0" +clap = "2.33.1" +chrono = "0.4.13" \ No newline at end of file diff --git a/README.md b/README.md index 669fcf5..42a8695 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,107 @@ -# cargo-todo +# A tool to view every TODOs in the rust code -Fork from: https://github.com/ProbablyClem/cargo-todo \ No newline at end of file +> Fork from: https://github.com/ProbablyClem/cargo-todo + +## Installation +``` +$ cargo install cargo-todo +``` + +## Usage + + +you can add parameters to you TODOs +``` +//todo 18-11-2001 5 !clement implement getters +``` +The supported parameters are :
+ * Priority : A number between 1 and 9 + * Deadline : A date format yyyy/mm/dd + * Member : A text that begin with '!' + * Content : Every text other thant the previouses will be considered as content + +
Those parameters can be added in any order as long as they follow the syntax they will be automaticaly added + +### run +```rust +$cargo todo +src/main.rs line: 331 //todo +Member: clement +Priority: 5 +Deadline: 2020-08-14 +implement getters +``` + +### Default supported regex + * ^s*//s*todo\b (//todo) + * ^s*//s*fix\b (//fix) + * ^s*//s*fixme\b (//fixme) +### cargo todo now support customizable regex +add all your customs regex in the ~/.cargo/todo_config file (will be created at launch) +
all regex are case-insensitive
+## Features + * -i, --inline : display todo in one line + ```rust + $cargo todo -i + src/main.rs line: 331 //todo Member: clement Priority: 5 Deadline: 2020-08-14 implement getters + ``` + * -v, --verbose : Sets the level of verbosity +
default or -vv +
full verbose + -v less verbose + ```rust + $cargo todo -v + src/main.rs line: 331 //todo +implement getters +``` + * -x, --exclude ... : Exclude some todos from the list + ```rust + $cargo todo -x //fix + //wil display every todos expect those having the '//fix' keyword + ``` + * -f, --filter ... : Filter todos to show + ```rust + $cargo todo -f //fix + //wil only display todos having the '//fix' keyword + ``` + * -l, --list : Number of values to display + ```rust + $cargo todo -l 5 + ///wil display the first 5 todos + ``` + * m, --member ... : Filter from member + ```rust + $cargo todo -m clement + ///wil only display todos having as member clement + ``` + * -s, --sort : Sort todos [possible values: priority, deadline, member] + ```rust + $cargo todo -s priority + ///wil display todos sorted by their priority + ``` + + + +## Legacy mode +### Can be used for legacy code base as it's support todo!() and unimplemented!() +### Will display every line with a supported token (listed below) and the inside of the macro +### /!\ Legacy mode is way slower the the default mode and lacks a lot of cool features +#### Example +code base +```rust +todo!("implement getters"); +``` +### run +```rust +$ cargo todo --legacy +src/main.rs TODO Line 125 : implement getters +``` +## Supported tokens +- //todo +- todo!() +- unimplemented!() +- fix + + +### /!\ WARNING +cargo todo will no longer use regex but only the default tokens listed above diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..388d044 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,331 @@ +extern crate string_format; +extern crate clap; +extern crate walkdir; +extern crate string_parser; +extern crate dirs; +extern crate glob; +extern crate chrono; +use glob::glob; +use colored::Colorize; +use clap::{Arg, App, SubCommand}; +use chrono::NaiveDate; + +//local files +mod parser; +mod regex; +mod token; +use crate::parser::*; +use crate::regex::regex_parser; +use crate::token::Token; + +//std +use std::io::Write; +use std::fs::OpenOptions; +use std::env; +use std::path::Path; +use std::fs::File; +use std::io::{self, BufRead}; + + + +fn main() -> std::io::Result<()> { + + let matches = App::new("Cargo-todo") + .author("Clément Guiton ") + .about("cargo tool to find TODOs in your code") + .arg(Arg::with_name("inline") + .short("i") + .long("inline") + .value_name("inline") + .help("display todos in one line") + .takes_value(false)) + .arg(Arg::with_name("filter") + .help("Filter todos to show") + .short("f") + .long("filter") + .takes_value(true) + .multiple(true) + .min_values(1)) + .arg(Arg::with_name("verbose") + .short("v") + .long("verbose") + .multiple(true) + .help("Sets the level of verbosity")) + .arg(Arg::with_name("exclude") + .short("x") + .long("exclude") + .takes_value(true) + .multiple(true) + .help("Exclude some todos from the list")) + .arg(Arg::with_name("list") + .short("l") + .long("list") + .takes_value(true) + .help("Number of values to display")) + .arg(Arg::with_name("sort") + .short("s") + .long("sort") + .takes_value(true) + .possible_values(&["priority", "deadline", "member"]) + .help("Sort todos")) + .arg(Arg::with_name("member") + .short("m") + .long("member") + .takes_value(true) + .multiple(true) + .min_values(1) + .help("Filter from member")) + .subcommand(SubCommand::with_name("legacy") + .about("Launch program in legacy mode (supports todo!(), etc...")) + .get_matches(); + + + if let Some(_matches) = matches.subcommand_matches("legacy") { + let mut parsers : Vec = vec!(); + + let mut path = String::from(env::current_dir().unwrap().to_str().unwrap()); + path.push_str("/**/*.rs"); + + //we add a parser looking for the //todo keyword + parsers.push(Parser::new(String::from("//todo"), Box::from(|x : Vec| {if x.last().unwrap() == &'\n' {return true;} else { return false}}))); + //we add a parser looking for the todo!() token + let _todo_macro_callback = Box::from(|mut text : String, line : usize, file : &str| { + text.retain(|c| c != '\"'); + println!("{} {} {} {} : {}",file,"TODO".green() ,"Line ".green(), line.to_string().green(), text.blue()); + }); + parsers.push(Parser::new_callback(String::from("todo!("), Box::from(|x : Vec| {if x.last().unwrap() == &')' {return true;} else { return false}}), _todo_macro_callback)); + + //support for unimplemented + let _unimplemented_macro_callback = Box::from(|text : String, line : usize, file : &str| { + println!("{} {} {} {} : {}{}{} ",file,"TODO".green() ,"Line ".green(), line.to_string().green(), "unimplemented!(".blue(), text.magenta(), ")".blue()); + }); + parsers.push(Parser::new_callback(String::from("unimplemented!("), Box::from(|x : Vec| {if x.last().unwrap() == &')' {return true;} else { return false}}), _unimplemented_macro_callback)); + + parsers.push(Parser::new(String::from("//fix"), Box::from(|x : Vec| {if x.last().unwrap() == &'\n' {return true;} else { return false}}))); + + + //loop on every file within the current dir + for entry in match glob(&path) { + Ok(entry) => entry, + Err(e) => { + println!("Couldn't access files. Error {}", e); + Err(e).unwrap() + } + } { + + let path = entry.unwrap(); + let path = Path::new(&path).strip_prefix(env::current_dir().unwrap().to_str().unwrap()).unwrap(); + if !path.starts_with("target/"){ + let path = path.to_str().unwrap(); + //execute each parsers on the current file + for p in &parsers { + p.parse(path); + } + } + + + } + + Ok(()) + } + else{ + let mut tokens : Vec = Vec::new(); + + let mut path = String::from(dirs::home_dir().unwrap().to_str().unwrap()); + path.push_str("/.cargo/todo_config"); + // println!("{}",path); + fn read_lines

(filename: P) -> io::Result>> + where P: AsRef, { + let file = match File::open(&filename){ + Ok(line) => line, + Err(_) => { + println!("{}", "File '~/.cargo/todo_config' not found, creating it".red()); + let mut f = OpenOptions::new().write(true).read(true).create(true).open(&filename).unwrap(); + f.write_all(b"^s*//s*todo\\b\n").unwrap(); + f.write_all(b"^s*//s*fix\\b\n").unwrap(); + f.write_all(b"^s*//s*fixme\\b\n").unwrap(); + return read_lines(filename); + } + }; + Ok(io::BufReader::new(file).lines()) + } + + let mut regex = Vec::new(); + for line in read_lines(path).unwrap() { + let line = line.unwrap(); + regex.push(line); + } + + let mut path = String::from(env::current_dir().unwrap().to_str().unwrap()); + path.push_str("/**/*.rs"); + + for entry in match glob(&path) { + Ok(entry) => entry, + Err(e) => { + println!("Couldn't access files. Error {}", e); + Err(e).unwrap() + } + } { + let path = entry.unwrap(); + let path = Path::new(&path).strip_prefix(env::current_dir().unwrap().to_str().unwrap()).unwrap(); + // println!("{}", path.to_str().unwrap()); + if !path.starts_with("target/"){ + let path = path.to_str().unwrap(); + + if matches.occurrences_of("verbose") == 0 || matches.occurrences_of("verbose") == 2{ + match regex_parser(path, regex.clone(), 2){ + Ok(mut t) => { + tokens.append(&mut t); + }, + Err(e) => eprintln!{"{}", e}, + } + } + else { + match regex_parser(path, regex.clone(), 1){ + Ok(mut t) => { + tokens.append(&mut t); + }, + Err(e) => eprintln!{"{}", e}, + } + } + + } + + } + + if matches.is_present("sort"){ + if matches.value_of("sort").unwrap() == "priority"{ + fn token_priority_sort(t : &Token) -> String { + + if t.priority.is_none() { + return String::from("z"); + } + else { + return t.priority.clone().unwrap() + } + } + tokens.sort_unstable_by_key(token_priority_sort); + } + else if matches.value_of("sort").unwrap() == "deadline"{ + fn token_deadline_sort(t : &Token) -> NaiveDate { + + if t.date.is_none() { + return NaiveDate::from_ymd(3000,01,01); + } + else { + return t.date.clone().unwrap() + } + } + tokens.sort_unstable_by_key(token_deadline_sort); + } + else if matches.value_of("sort").unwrap() == "member"{ + fn token_member_sort(t : &Token) -> String { + + if t.priority.is_none() { + return String::from("z"); + } + else { + return t.priority.clone().unwrap() + } + } + tokens.sort_unstable_by_key(token_member_sort); + } + } + + if matches.is_present("list"){ + let lines = match matches.value_of("list").unwrap().parse::(){ + Ok(lines) => lines, + Err(_) => { + eprintln!("{}", "list argument should be a valid number!".red()); + panic!() + }}; + + let mut new_tokens : Vec = Vec::new(); + for i in tokens{ + if new_tokens.len() < lines{ + &new_tokens.push(i.clone()); + } + else + { + break; + } + } + tokens = new_tokens; + } + + if matches.is_present("member"){ + let filters : Vec<&str> = matches.values_of("member").unwrap().collect(); + let mut new_tokens : Vec = Vec::new(); + for i in tokens{ + // println!("{}", i); + for y in &filters { + if i.member.clone().is_some() && i.member.clone().unwrap() == *y.to_string(){ + println!("pushing"); + &new_tokens.push(i.clone()); + break; + } + } + } + tokens = new_tokens; + } + + if matches.is_present("filter"){ + let filters : Vec<&str> = matches.values_of("filter").unwrap().collect(); + let mut new_tokens : Vec = Vec::new(); + for i in tokens{ + for y in &filters { + if i.keyword == String::from(*y){ + &new_tokens.push(i.clone()); + break; + } + } + } + tokens = new_tokens; + // tokens = new.into_iter().filter(|t| t.keyword == String::from(matches.value_of("filter").unwrap())).collect(); + } + + if matches.is_present("exclude"){ + let excludes : Vec<&str> = matches.values_of("exclude").unwrap().collect(); + let mut new_tokens : Vec = Vec::new(); + for i in tokens{ + for y in 0..excludes.len() { + if i.keyword == String::from(excludes[y]){ + break; + } + else if y == excludes.len() -1{ + &new_tokens.push(i.clone()); + } + } + + } + tokens = new_tokens; + // tokens = new.into_iter().filter(|t| t.keyword == String::from(matches.value_of("filter").unwrap())).collect(); + } + if matches.is_present("inline"){ + for i in tokens{ + i.inline(); + } + } + else { + for i in tokens { + println!("{}", i); + } + } + + Ok(()) +} +} + +#[allow(dead_code)] +// test zone +//TODO refactor +//todo implement 2001/11/01 3 getters !clement +//todo implement 2001/11/01 3 getters !thomas +//fix implement 18/11/2001 getters +//4 +//10/10/10 +fn test(){ + todo!("implements getters"); +} + +//todo implement 2020/08/14 5 getters !clement \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..b009dad --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,40 @@ +extern crate string_parser; +use string_parser::string_parser_with_file; +use colored::Colorize; + +pub struct Parser{ + keyword : String, + end_filter : Box) -> bool>, + callback : Box, +} + +impl Parser { + pub fn new(keyword : String, end_filter : Box) -> bool>) -> Parser{ + let callback = Box::from(|text : String, line : usize, file : &str| { + // let path = Path::new(file).strip_prefix(env::current_dir().unwrap().to_str().unwrap()).unwrap(); + println!("{} {} {} {} : {}",file,"TODO".green() ,"Line ".green(), line.to_string().green(), text.blue()); + }); + Parser{keyword: keyword, end_filter : end_filter, callback} + } + + pub fn new_callback(keyword : String, end_filter : Box) -> bool>, callback : Box) -> Parser{ + + Parser{keyword: keyword, end_filter : end_filter, callback} + } + + fn get_keyword(&self) -> String { + self.keyword.clone() + } + + fn get_end_filter(&self) -> &Box) -> bool> { + &self.end_filter + } + + fn get_callback(&self) -> &Box { + &self.callback + } + + pub fn parse(&self, path : &str) { + string_parser_with_file(path, self.get_keyword().as_str(), self.get_end_filter(), self.get_callback()).expect("failed to open file"); + } +} \ No newline at end of file diff --git a/src/regex.rs b/src/regex.rs new file mode 100644 index 0000000..0c6564b --- /dev/null +++ b/src/regex.rs @@ -0,0 +1,32 @@ +extern crate regex; + +use std::fs::File; +use std::io::{self, BufRead}; +use std::path::Path; + +use regex::{RegexSet}; +use crate::token::*; + +// The output is wrapped in a Result to allow matching on errors +// Returns an Iterator to the Reader of the lines of the file. +fn read_lines

(filename: P) -> io::Result>> +where P: AsRef, { + let file = File::open(filename)?; + Ok(io::BufReader::new(file).lines()) +} + +pub fn regex_parser(path : &str, regex : Vec, verbosity : i8) -> Result, io::Error>{ + + let set = RegexSet::new(regex).unwrap(); + let mut tokens = Vec::new(); + let mut line_cpt = 0; + for line in read_lines(path)? { + line_cpt +=1; + let line = line.unwrap(); + if set.is_match(line.to_lowercase().as_str()){ + tokens.push(Token::new(path.to_string(), line_cpt, line, verbosity)); + // println!("{}", t); + } + } + Ok(tokens) +} \ No newline at end of file diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..ca2aefc --- /dev/null +++ b/src/token.rs @@ -0,0 +1,133 @@ +extern crate string_format; +extern crate regex; +extern crate chrono; + +use chrono::NaiveDate; +use string_format::string_format; +use std::fmt; +use colored::Colorize; +use regex::Regex; + +#[derive(Clone)] +pub struct Token{ + file : String, + line : usize, + pub keyword : String, + pub comment : Option, + pub priority : Option, + pub date : Option, + pub member : Option, + verbosity : i8, +} + +impl Token { + pub fn new (file : String, line : usize, s : String, verbosity : i8) -> Token{ + // println!("{}", s); + let fields : Vec<&str>= s.split_whitespace().collect(); + let number_regex = Regex::new("\\b[1-9]\\b").unwrap(); + let date_regex = Regex::new("(\\d*/\\d*/\\d*)").unwrap(); + let member_regex = Regex::new("!\\w*").unwrap(); + if date_regex.is_match("5") { + panic!("regex"); + } + // for i in &fields { + // println!("{}", i); + // } + + let mut t = Token { + file : file, + line : line, + keyword: "todo".to_string(), + comment : None, + priority : None, + date : None, + member : None, + verbosity : verbosity + }; + + for i in 0..fields.len() { + if i == 0{ + t.keyword = fields[0].to_string().to_lowercase(); + } + else if number_regex.is_match(fields[i]) { + t.priority = Some(fields[i].to_string()); + } + else if date_regex.is_match(fields[i]){ + let date : Vec<&str> = fields[i].split("/").collect(); + t.date = NaiveDate::from_ymd_opt(date[0].parse::().unwrap(), date[1].parse::().unwrap(), date[2].parse::().unwrap()); + // t.date = Some(fields[i].to_string()); + } + else if member_regex.is_match(fields[i]){ + let mut member = String::new(); //from(fields[i].clone()).chars().next().map(|c| &s[c.len_utf8()..]).unwrap(); + let it = fields[i].chars().skip(1); + for i in it{ + member.push(i); + } + + t.member = Some(member); + } + else { + if t.comment.is_none(){ + t.comment = Some(fields[i].to_string()); + } + else{ + t.comment = Some(string_format!("{} {}".to_string(),t.comment.unwrap(), fields[i].to_string())); + } + } + } + + t + } + + pub fn inline(&self) { + let mut s; + s = string_format!("{} line: {} {} ".to_string(), self.file.clone(), self.line.to_string().green().to_string(), self.keyword.clone().green().to_string()); + if self.member.is_some(){ + s = string_format!("{} Member: {}".to_string(),s ,self.member.clone().unwrap().red().to_string()); + } + if self.priority.is_some(){ + s = string_format!("{} Priority: {}".to_string(), s, self.priority.clone().unwrap().red().to_string()); + } + if self.date.is_some(){ + s = string_format!("{} Deadline: {}".to_string(), s, self.date.clone().unwrap().to_string().red().to_string()); + } + if self.comment.is_some() { + s = string_format!("{} {}".to_string(), s, self.comment.clone().unwrap().blue().to_string()); + } + println!("{}", s); + } + +} + +// To use the `{}` marker, the trait `fmt::Display` must be implemented +// manually for the type. +impl fmt::Display for Token { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut s; + + s = string_format!("{} line: {} {} \n".to_string(), self.file.clone(), self.line.to_string().green().to_string(), self.keyword.clone().green().to_string()); + if self.verbosity <= 1{ + if self.comment.is_some() { + s = string_format!("{}{}\n".to_string(), s, self.comment.clone().unwrap().blue().to_string()); + } + } + else { + if self.member.is_some(){ + s = string_format!("{}Member: {}\n".to_string(),s ,self.member.clone().unwrap().red().to_string()); + } + if self.priority.is_some(){ + s = string_format!("{}Priority: {}\n".to_string(), s, self.priority.clone().unwrap().red().to_string()); + } + if self.date.is_some(){ + s = string_format!("{}Deadline: {}\n".to_string(), s, self.date.clone().unwrap().to_string().red().to_string()); + } + if self.comment.is_some() { + s = string_format!("{}{}\n".to_string(), s, self.comment.clone().unwrap().blue().to_string()); + } + } + + write!(f, "{}", s)?; + Ok(()) + } +}