From 46d2ebacd3905d407217dd968ed50abe23032ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 22 Nov 2023 18:55:10 +0100 Subject: [PATCH] split code into modules --- README.md | 2 +- src/entries.rs | 21 ++ src/main.rs | 655 +------------------------------------------------ src/render.rs | 125 ++++++++++ src/scan.rs | 506 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 662 insertions(+), 647 deletions(-) create mode 100644 src/entries.rs create mode 100644 src/render.rs create mode 100644 src/scan.rs diff --git a/README.md b/README.md index 4e6f23f..6471778 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ todo@testing test this ### Generic todos -Any todos that don't fall into the two categories above (i.e. their syntax isn't `todo{number}` or `todo@{description}`). +Any todos that don't fall into the two categories above (i.e. their syntax isn't `todo{number}` or `todo@{category}`). ``` TODO: Fix this diff --git a/src/entries.rs b/src/entries.rs new file mode 100644 index 0000000..e7e02ec --- /dev/null +++ b/src/entries.rs @@ -0,0 +1,21 @@ +use std::path::PathBuf; + +#[derive(Debug, PartialEq, Clone)] +pub struct Location { + pub file: PathBuf, + pub line: usize, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Entry { + pub text: String, + pub location: Location, + pub data: EntryData, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum EntryData { + Priority(isize), + Category(String), + Generic, +} diff --git a/src/main.rs b/src/main.rs index 3654cd1..2e60730 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,287 +1,13 @@ -use std::collections::HashMap; -use std::io::{self, Write}; -use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; + use clap::Parser; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use std::cmp::Ordering::{Less, Equal, Greater}; +use crate::entries::Entry; +use crate::render::render_entries; +use crate::scan::{Stats, scan_dir}; -#[derive(Debug, PartialEq, Clone)] -struct Location { - file: PathBuf, - line: usize, -} - -#[derive(Debug, PartialEq, Clone)] -struct Entry { - text: String, - location: Location, - data: EntryData, -} - -#[derive(Debug, PartialEq, Clone)] -enum EntryData { - Priority(isize), - Category(String), - Generic, -} - -impl Entry { - fn render(&self) { - let mut stdout = StandardStream::stdout(ColorChoice::Auto); - - write_ansi(&mut stdout, Color::Ansi256(243), "- [ ] ", false); - - let location = format!("{}:{}", self.location.file.to_string_lossy(), self.location.line); - - if self.text.len() > 0 { - write_ansi(&mut stdout, Color::Blue, self.text.as_str(), true); - - write_ansi(&mut stdout, Color::Ansi256(243), format!(" ({})", location).as_str(), false); - } else { - write_ansi(&mut stdout, Color::Cyan, &location.as_str(), true); - } - - write!(&mut stdout, "\n").unwrap(); - } -} - -fn write_ansi(stdout: &mut StandardStream, color: Color, text: &str, bold: bool) { - stdout.set_color( - ColorSpec::new() - .set_fg(Some(color)) - .set_bold(bold) - ).unwrap(); - - write!(stdout, "{text}").unwrap(); - - stdout.reset().unwrap(); -} - -struct Stats { - visited_folders: usize, - visited_files: usize, -} - -impl Stats { - fn new() -> Stats { - Stats { - visited_folders: 0, - visited_files: 0, - } - } - - fn print(&self) { - eprintln!("[INFO] Visited folders: {}", self.visited_folders); - eprintln!("[INFO] Visited files: {}", self.visited_files); - } -} - -fn scan_string(str: String, filename: PathBuf, entries: &mut Vec) { - for (line_num, line) in str.lines().enumerate() { - if ! line.to_lowercase().contains("todo") { - continue; - } - - for word in line.split(" ") { - if ! word.to_lowercase().starts_with("todo") { - continue; - } - - // Handles: `todo`, `TODO`, `todo:`, `TODO:` - // todo@real `replace` isnt ideal, it should only replace *after* the todo, to avoid merging eg `to:do` - if word.to_lowercase().replace(':', "") == "todo" { - let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); - let text = text_dirty.trim(); - - entries.push(Entry { - text: text.to_string(), - location: Location { - file: filename.clone(), - line: line_num + 1, - }, - data: EntryData::Generic, - }); - - break; - } - - if word.contains('@') { - let category = word.split('@').nth(1).unwrap(); - let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); - let text = text_dirty.trim(); - - entries.push(Entry { - text: text.to_string(), - location: Location { - file: filename.clone(), - line: line_num + 1, - }, - data: EntryData::Category(category.to_string()), - }); - - break; - } - - // [0, 1, ..., 9] - let priority_chars: Vec = (0..10).map(|int| char::from_digit(int, 10).unwrap()).collect(); - - if word.chars().any(|ch| priority_chars.contains(&ch)) { - let cleaned_word = word.to_lowercase(); - let priority_chars = cleaned_word.split("todo").nth(1).unwrap(); - - let priority: isize; - - if priority_chars.len() == 1 { - priority = priority_chars.to_string().parse::().unwrap(); - } else if priority_chars.chars().all(|ch| ch == '0') { - // todo0: 1 - 1 = 0 - // todo00: 1 - 2 = -1 - priority = 1 - priority_chars.len() as isize; - } else { - break; // incorrect syntax like todo11 - } - - let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); - let text = text_dirty.trim(); - - entries.push(Entry { - text: text.to_string(), - location: Location { - file: filename.clone(), - line: line_num + 1, - }, - data: EntryData::Priority(priority), - }); - } - } - } -} - -fn scan_file(path: &Path, entries: &mut Vec) -> io::Result<()> { - match std::fs::read_to_string(path) { - Ok(str) => scan_string(str, path.to_path_buf(), entries), - Err(_) => (), - }; - - Ok(()) -} - -fn scan_dir(path: &Path, entries: &mut Vec, excludes: &Vec, stats: &mut Stats) -> io::Result<()> { - stats.visited_folders += 1; - - 'entry: for entry in fs::read_dir(path)? { - let entry = entry?; - let path = entry.path(); - - if path.components().last().unwrap().as_os_str().to_string_lossy().starts_with('.') { - continue; - } - - if path.is_dir() { - for exclude in excludes { - if path == *exclude { - continue 'entry; - } - } - - scan_dir(path.as_path(), entries, excludes, stats)? - } else { - stats.visited_files += 1; - scan_file(path.as_path(), entries)? - } - } - - Ok(()) -} - -fn render(entries: Vec) { - let mut priority_entries: HashMap> = HashMap::new(); - let mut category_entries: HashMap> = HashMap::new(); - let mut generic_entries: Vec = Vec::new(); - - let mut stdout = StandardStream::stdout(ColorChoice::Auto); - - for entry in entries { - match entry.data { - EntryData::Priority(priority) => { - if ! priority_entries.contains_key(&priority) { - priority_entries.insert(priority, vec![]); - } - - let vec = priority_entries.get_mut(&priority).unwrap(); - vec.push(entry); - }, - EntryData::Category(ref category) => { - if ! category_entries.contains_key(category) { - category_entries.insert(category.clone(), vec![]); - } - - let vec = category_entries.get_mut(category).unwrap(); - vec.push(entry); - }, - EntryData::Generic => { - generic_entries.push(entry); - } - } - } - - write_ansi(&mut stdout, Color::Yellow, "# TODOs", true); - write!(stdout, "\n\n").unwrap(); - - let mut priority_keys = priority_entries.keys().collect::>(); - priority_keys.sort_by(|a, b| a.partial_cmp(b).unwrap()); - - for priority in priority_keys { - let priority_notation = match priority.cmp(&0) { - Less => { - let mut str = "todo0".to_string(); - - // todo0 -> 0 - // todo00 -> -1 - // Therefore: 'todo0' + priority.abs() * '0' - str.push_str(String::from_utf8(vec![b'0'; priority.abs() as usize]).unwrap().as_str()); - - str - }, - Equal => "todo0".to_string(), - Greater => format!("todo{}", priority), - }; - - write_ansi(&mut stdout, Color::Red, format!("## {}", &priority_notation).as_str(), true); - write!(stdout, "\n").unwrap(); - - for item in priority_entries.get(priority).unwrap() { - item.render(); - } - - println!(""); - } - - let mut category_keys = category_entries.keys().collect::>(); - category_keys.sort_by(|a, b| a.partial_cmp(b).unwrap()); - - for category in category_keys { - write_ansi(&mut stdout, Color::Green, format!("## {}", &category).as_str(), true); - write!(stdout, "\n").unwrap(); - - for item in category_entries.get(category).unwrap() { - item.render(); - } - - println!(""); - } - - write_ansi(&mut stdout, Color::White, "## Other", true); - write!(stdout, "\n").unwrap(); - - generic_entries.sort_by(|a, b| a.text.partial_cmp(&b.text).unwrap()); - - for item in generic_entries { - item.render(); - } - -} +pub mod scan; +pub mod render; +pub mod entries; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -337,373 +63,10 @@ fn main() { scan_dir(root_dir.as_path(), &mut entries, &excludes, &mut stats).unwrap(); - render(entries); + render_entries(entries); if args.verbose { eprint!("\n\n"); stats.print(); } } - -#[test] -fn generic_test() { - let str = r#" - 1 - 2 - // todo foo - /* TODO: foo bar */ - /* - - * TODO baz - TODO baz2 - TODO baz2 todo - */ - "#; - - let mut entries: Vec = vec![]; - let mut path = PathBuf::new(); - path.push("foo.txt"); - - scan_string(str.to_string(), path.clone(), &mut entries); - - assert_eq!(5, entries.len()); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("foo"), - location: Location { - file: path.clone(), - line: 4, - } - }, entries[0]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("foo bar"), - location: Location { - file: path.clone(), - line: 5, - } - }, entries[1]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("baz"), - location: Location { - file: path.clone(), - line: 8, - } - }, entries[2]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("baz2"), - location: Location { - file: path.clone(), - line: 9, - } - }, entries[3]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("baz2 todo"), - location: Location { - file: path.clone(), - line: 10, - } - }, entries[4]); -} - -#[test] -fn category_test() { - let str = r#" - 1 - 2 - todo@foo - todo@bar abc def - 3 - todo@baz x y - 4 - // TODO@baz2 a - /* TODO@baz3 */ - // TODO@baz3 b - "#; - - let mut entries: Vec = vec![]; - let mut path = PathBuf::new(); - path.push("foo.txt"); - - scan_string(str.to_string(), path.clone(), &mut entries); - - assert_eq!(6, entries.len()); - - assert_eq!(Entry { - data: EntryData::Category(String::from("foo")), - text: String::from(""), - location: Location { - file: path.clone(), - line: 4, - } - }, entries[0]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("bar")), - text: String::from("abc def"), - location: Location { - file: path.clone(), - line: 5, - } - }, entries[1]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("baz")), - text: String::from("x y"), - location: Location { - file: path.clone(), - line: 7, - } - }, entries[2]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("baz2")), - text: String::from("a"), - location: Location { - file: path.clone(), - line: 9, - } - }, entries[3]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("baz3")), - text: String::from(""), - location: Location { - file: path.clone(), - line: 10, - } - }, entries[4]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("baz3")), - text: String::from("b"), - location: Location { - file: path.clone(), - line: 11, - } - }, entries[5]); -} - -#[test] -fn priority_test() { - let str = r#" - 1 - 2 - todo00 - todo000 abc - todo0 abc def - todo1 foo - 3 - todo1 x y - 4 - // todo0 bar - // TODO1 a - /* TODO2 */ - // TODO3 b - "#; - - let mut entries: Vec = vec![]; - let mut path = PathBuf::new(); - path.push("foo.txt"); - - scan_string(str.to_string(), path.clone(), &mut entries); - - assert_eq!(9, entries.len()); - - assert_eq!(Entry { - data: EntryData::Priority(-1), - text: String::from(""), - location: Location { - file: path.clone(), - line: 4, - } - }, entries[0]); - - assert_eq!(Entry { - data: EntryData::Priority(-2), - text: String::from("abc"), - location: Location { - file: path.clone(), - line: 5, - } - }, entries[1]); - - assert_eq!(Entry { - data: EntryData::Priority(0), - text: String::from("abc def"), - location: Location { - file: path.clone(), - line: 6, - } - }, entries[2]); - - assert_eq!(Entry { - data: EntryData::Priority(1), - text: String::from("foo"), - location: Location { - file: path.clone(), - line: 7, - } - }, entries[3]); - - assert_eq!(Entry { - data: EntryData::Priority(1), - text: String::from("x y"), - location: Location { - file: path.clone(), - line: 9, - } - }, entries[4]); - - assert_eq!(Entry { - data: EntryData::Priority(0), - text: String::from("bar"), - location: Location { - file: path.clone(), - line: 11, - } - }, entries[5]); - - assert_eq!(Entry { - data: EntryData::Priority(1), - text: String::from("a"), - location: Location { - file: path.clone(), - line: 12, - } - }, entries[6]); - - assert_eq!(Entry { - data: EntryData::Priority(2), - text: String::from(""), - location: Location { - file: path.clone(), - line: 13, - } - }, entries[7]); - - assert_eq!(Entry { - data: EntryData::Priority(3), - text: String::from("b"), - location: Location { - file: path.clone(), - line: 14, - } - }, entries[8]); -} - -#[test] -fn sample_test_ts() { - let mut entries: Vec = vec![]; - - let mut path = std::env::current_dir().unwrap(); - path.push("samples"); - - let mut filepath = path.clone(); - filepath.push("1.ts"); - - let excludes: Vec = vec![]; - let mut stats = Stats::new(); - - scan_dir(path.as_path(), &mut entries, &excludes, &mut stats).unwrap(); - - assert_eq!(10, entries.len()); - - assert_eq!(Entry { - data: EntryData::Category(String::from("types")), - text: String::from(""), - location: Location { - file: filepath.clone(), - line: 1, - } - }, entries[0]); - - assert_eq!(Entry { - data: EntryData::Category(String::from("types")), - text: String::from("add types"), - location: Location { - file: filepath.clone(), - line: 5, - } - }, entries[1]); - - assert_eq!(Entry { - data: EntryData::Priority(-2), - text: String::from(""), - location: Location { - file: filepath.clone(), - line: 10, - } - }, entries[2]); - - assert_eq!(Entry { - data: EntryData::Priority(-1), - text: String::from("add return typehint"), - location: Location { - file: filepath.clone(), - line: 14, - } - }, entries[3]); - - assert_eq!(Entry { - data: EntryData::Priority(0), - text: String::from("add name typehint"), - location: Location { - file: filepath.clone(), - line: 19, - } - }, entries[4]); - - assert_eq!(Entry { - data: EntryData::Priority(1), - text: String::from("add return typehint"), - location: Location { - file: filepath.clone(), - line: 23, - } - }, entries[5]); - - assert_eq!(Entry { - data: EntryData::Priority(2), - text: String::from("add return typehint"), - location: Location { - file: filepath.clone(), - line: 27, - } - }, entries[6]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from(""), - location: Location { - file: filepath.clone(), - line: 31, - } - }, entries[7]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("generic todo 2"), - location: Location { - file: filepath.clone(), - line: 33, - } - }, entries[8]); - - assert_eq!(Entry { - data: EntryData::Generic, - text: String::from("generic todo 3"), - location: Location { - file: filepath.clone(), - line: 34, - } - }, entries[9]); -} diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..048871e --- /dev/null +++ b/src/render.rs @@ -0,0 +1,125 @@ +use std::io::Write; +use std::collections::HashMap; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; +use std::cmp::Ordering::{Less, Equal, Greater}; + +use crate::entries::{Entry, EntryData}; + +impl Entry { + pub fn render(&self) { + let mut stdout = StandardStream::stdout(ColorChoice::Auto); + write_ansi(&mut stdout, Color::Ansi256(243), "- [ ] ", false); + + let location = format!("{}:{}", self.location.file.to_string_lossy(), self.location.line); + + if self.text.len() > 0 { + write_ansi(&mut stdout, Color::Blue, self.text.as_str(), true); + write_ansi(&mut stdout, Color::Ansi256(243), format!(" ({})", location).as_str(), false); + } else { + write_ansi(&mut stdout, Color::Cyan, &location.as_str(), true); + } + + write!(&mut stdout, "\n").unwrap(); + } +} + + +pub fn write_ansi(stdout: &mut StandardStream, color: Color, text: &str, bold: bool) { + stdout.set_color( + ColorSpec::new() + .set_fg(Some(color)) + .set_bold(bold) + ).unwrap(); + + write!(stdout, "{text}").unwrap(); + + stdout.reset().unwrap(); +} + +pub fn render_entries(entries: Vec) { + let mut priority_entries: HashMap> = HashMap::new(); + let mut category_entries: HashMap> = HashMap::new(); + let mut generic_entries: Vec = Vec::new(); + + let mut stdout = StandardStream::stdout(ColorChoice::Auto); + + for entry in entries { + match entry.data { + EntryData::Priority(priority) => { + if ! priority_entries.contains_key(&priority) { + priority_entries.insert(priority, vec![]); + } + + let vec = priority_entries.get_mut(&priority).unwrap(); + vec.push(entry); + }, + EntryData::Category(ref category) => { + if ! category_entries.contains_key(category) { + category_entries.insert(category.clone(), vec![]); + } + + let vec = category_entries.get_mut(category).unwrap(); + vec.push(entry); + }, + EntryData::Generic => { + generic_entries.push(entry); + } + } + } + + write_ansi(&mut stdout, Color::Yellow, "# TODOs", true); + write!(stdout, "\n\n").unwrap(); + + let mut priority_keys = priority_entries.keys().collect::>(); + priority_keys.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + for priority in priority_keys { + let priority_notation = match priority.cmp(&0) { + Less => { + let mut str = "todo0".to_string(); + + // todo0 -> 0 + // todo00 -> -1 + // Therefore: 'todo0' + priority.abs() * '0' + str.push_str(String::from_utf8(vec![b'0'; priority.abs() as usize]).unwrap().as_str()); + + str + }, + Equal => "todo0".to_string(), + Greater => format!("todo{}", priority), + }; + + write_ansi(&mut stdout, Color::Red, format!("## {}", &priority_notation).as_str(), true); + write!(stdout, "\n").unwrap(); + + for item in priority_entries.get(priority).unwrap() { + item.render(); + } + + println!(""); + } + + let mut category_keys = category_entries.keys().collect::>(); + category_keys.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + for category in category_keys { + write_ansi(&mut stdout, Color::Green, format!("## {}", &category).as_str(), true); + write!(stdout, "\n").unwrap(); + + for item in category_entries.get(category).unwrap() { + item.render(); + } + + println!(""); + } + + write_ansi(&mut stdout, Color::White, "## Other", true); + write!(stdout, "\n").unwrap(); + + generic_entries.sort_by(|a, b| a.text.partial_cmp(&b.text).unwrap()); + + for item in generic_entries { + item.render(); + } + +} diff --git a/src/scan.rs b/src/scan.rs new file mode 100644 index 0000000..8e4feb5 --- /dev/null +++ b/src/scan.rs @@ -0,0 +1,506 @@ +use std::io; +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::entries::{Entry, EntryData, Location}; + +pub struct Stats { + visited_folders: usize, + visited_files: usize, +} + +impl Stats { + pub fn new() -> Stats { + Stats { + visited_folders: 0, + visited_files: 0, + } + } + + pub fn print(&self) { + eprintln!("[INFO] Visited folders: {}", self.visited_folders); + eprintln!("[INFO] Visited files: {}", self.visited_files); + } +} + +pub fn scan_string(str: String, filename: PathBuf, entries: &mut Vec) { + for (line_num, line) in str.lines().enumerate() { + if ! line.to_lowercase().contains("todo") { + continue; + } + + for word in line.split(" ") { + if ! word.to_lowercase().starts_with("todo") { + continue; + } + + // Handles: `todo`, `TODO`, `todo:`, `TODO:` + // todo@real `replace` isnt ideal, it should only replace *after* the todo, to avoid merging eg `to:do` + if word.to_lowercase().replace(':', "") == "todo" { + let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); + let text = text_dirty.trim(); + + entries.push(Entry { + text: text.to_string(), + location: Location { + file: filename.clone(), + line: line_num + 1, + }, + data: EntryData::Generic, + }); + + break; + } + + if word.contains('@') { + let category = word.split('@').nth(1).unwrap(); + let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); + let text = text_dirty.trim(); + + entries.push(Entry { + text: text.to_string(), + location: Location { + file: filename.clone(), + line: line_num + 1, + }, + data: EntryData::Category(category.to_string()), + }); + + break; + } + + // [0, 1, ..., 9] + let priority_chars: Vec = (0..10).map(|int| char::from_digit(int, 10).unwrap()).collect(); + + if word.chars().any(|ch| priority_chars.contains(&ch)) { + let cleaned_word = word.to_lowercase(); + let priority_chars = cleaned_word.split("todo").nth(1).unwrap(); + + let priority: isize; + + if priority_chars.len() == 1 { + priority = priority_chars.to_string().parse::().unwrap(); + } else if priority_chars.chars().all(|ch| ch == '0') { + // todo0: 1 - 1 = 0 + // todo00: 1 - 2 = -1 + priority = 1 - priority_chars.len() as isize; + } else { + break; // incorrect syntax like todo11 + } + + let text_dirty = line.split_once(word).unwrap().1.replace("*/", ""); + let text = text_dirty.trim(); + + entries.push(Entry { + text: text.to_string(), + location: Location { + file: filename.clone(), + line: line_num + 1, + }, + data: EntryData::Priority(priority), + }); + } + } + } +} + +pub fn scan_file(path: &Path, entries: &mut Vec) -> io::Result<()> { + match std::fs::read_to_string(path) { + Ok(str) => scan_string(str, path.to_path_buf(), entries), + Err(_) => (), + }; + + Ok(()) +} + +pub fn scan_dir(path: &Path, entries: &mut Vec, excludes: &Vec, stats: &mut Stats) -> io::Result<()> { + stats.visited_folders += 1; + + 'entry: for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + + if path.components().last().unwrap().as_os_str().to_string_lossy().starts_with('.') { + continue; + } + + if path.is_dir() { + for exclude in excludes { + if path == *exclude { + continue 'entry; + } + } + + scan_dir(path.as_path(), entries, excludes, stats)? + } else { + stats.visited_files += 1; + scan_file(path.as_path(), entries)? + } + } + + Ok(()) +} + + +#[test] +fn generic_test() { + let str = r#" + 1 + 2 + // todo foo + /* TODO: foo bar */ + /* + + * TODO baz + TODO baz2 + TODO baz2 todo + */ + "#; + + let mut entries: Vec = vec![]; + let mut path = PathBuf::new(); + path.push("foo.txt"); + + scan_string(str.to_string(), path.clone(), &mut entries); + + assert_eq!(5, entries.len()); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("foo"), + location: Location { + file: path.clone(), + line: 4, + } + }, entries[0]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("foo bar"), + location: Location { + file: path.clone(), + line: 5, + } + }, entries[1]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("baz"), + location: Location { + file: path.clone(), + line: 8, + } + }, entries[2]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("baz2"), + location: Location { + file: path.clone(), + line: 9, + } + }, entries[3]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("baz2 todo"), + location: Location { + file: path.clone(), + line: 10, + } + }, entries[4]); +} + +#[test] +fn category_test() { + let str = r#" + 1 + 2 + todo@foo + todo@bar abc def + 3 + todo@baz x y + 4 + // TODO@baz2 a + /* TODO@baz3 */ + // TODO@baz3 b + "#; + + let mut entries: Vec = vec![]; + let mut path = PathBuf::new(); + path.push("foo.txt"); + + scan_string(str.to_string(), path.clone(), &mut entries); + + assert_eq!(6, entries.len()); + + assert_eq!(Entry { + data: EntryData::Category(String::from("foo")), + text: String::from(""), + location: Location { + file: path.clone(), + line: 4, + } + }, entries[0]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("bar")), + text: String::from("abc def"), + location: Location { + file: path.clone(), + line: 5, + } + }, entries[1]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("baz")), + text: String::from("x y"), + location: Location { + file: path.clone(), + line: 7, + } + }, entries[2]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("baz2")), + text: String::from("a"), + location: Location { + file: path.clone(), + line: 9, + } + }, entries[3]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("baz3")), + text: String::from(""), + location: Location { + file: path.clone(), + line: 10, + } + }, entries[4]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("baz3")), + text: String::from("b"), + location: Location { + file: path.clone(), + line: 11, + } + }, entries[5]); +} + +#[test] +fn priority_test() { + let str = r#" + 1 + 2 + todo00 + todo000 abc + todo0 abc def + todo1 foo + 3 + todo1 x y + 4 + // todo0 bar + // TODO1 a + /* TODO2 */ + // TODO3 b + "#; + + let mut entries: Vec = vec![]; + let mut path = PathBuf::new(); + path.push("foo.txt"); + + scan_string(str.to_string(), path.clone(), &mut entries); + + assert_eq!(9, entries.len()); + + assert_eq!(Entry { + data: EntryData::Priority(-1), + text: String::from(""), + location: Location { + file: path.clone(), + line: 4, + } + }, entries[0]); + + assert_eq!(Entry { + data: EntryData::Priority(-2), + text: String::from("abc"), + location: Location { + file: path.clone(), + line: 5, + } + }, entries[1]); + + assert_eq!(Entry { + data: EntryData::Priority(0), + text: String::from("abc def"), + location: Location { + file: path.clone(), + line: 6, + } + }, entries[2]); + + assert_eq!(Entry { + data: EntryData::Priority(1), + text: String::from("foo"), + location: Location { + file: path.clone(), + line: 7, + } + }, entries[3]); + + assert_eq!(Entry { + data: EntryData::Priority(1), + text: String::from("x y"), + location: Location { + file: path.clone(), + line: 9, + } + }, entries[4]); + + assert_eq!(Entry { + data: EntryData::Priority(0), + text: String::from("bar"), + location: Location { + file: path.clone(), + line: 11, + } + }, entries[5]); + + assert_eq!(Entry { + data: EntryData::Priority(1), + text: String::from("a"), + location: Location { + file: path.clone(), + line: 12, + } + }, entries[6]); + + assert_eq!(Entry { + data: EntryData::Priority(2), + text: String::from(""), + location: Location { + file: path.clone(), + line: 13, + } + }, entries[7]); + + assert_eq!(Entry { + data: EntryData::Priority(3), + text: String::from("b"), + location: Location { + file: path.clone(), + line: 14, + } + }, entries[8]); +} + +#[test] +fn sample_test_ts() { + let mut entries: Vec = vec![]; + + let mut path = std::env::current_dir().unwrap(); + path.push("samples"); + + let mut filepath = path.clone(); + filepath.push("1.ts"); + + let excludes: Vec = vec![]; + let mut stats = Stats::new(); + + scan_dir(path.as_path(), &mut entries, &excludes, &mut stats).unwrap(); + + assert_eq!(10, entries.len()); + + assert_eq!(Entry { + data: EntryData::Category(String::from("types")), + text: String::from(""), + location: Location { + file: filepath.clone(), + line: 1, + } + }, entries[0]); + + assert_eq!(Entry { + data: EntryData::Category(String::from("types")), + text: String::from("add types"), + location: Location { + file: filepath.clone(), + line: 5, + } + }, entries[1]); + + assert_eq!(Entry { + data: EntryData::Priority(-2), + text: String::from(""), + location: Location { + file: filepath.clone(), + line: 10, + } + }, entries[2]); + + assert_eq!(Entry { + data: EntryData::Priority(-1), + text: String::from("add return typehint"), + location: Location { + file: filepath.clone(), + line: 14, + } + }, entries[3]); + + assert_eq!(Entry { + data: EntryData::Priority(0), + text: String::from("add name typehint"), + location: Location { + file: filepath.clone(), + line: 19, + } + }, entries[4]); + + assert_eq!(Entry { + data: EntryData::Priority(1), + text: String::from("add return typehint"), + location: Location { + file: filepath.clone(), + line: 23, + } + }, entries[5]); + + assert_eq!(Entry { + data: EntryData::Priority(2), + text: String::from("add return typehint"), + location: Location { + file: filepath.clone(), + line: 27, + } + }, entries[6]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from(""), + location: Location { + file: filepath.clone(), + line: 31, + } + }, entries[7]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("generic todo 2"), + location: Location { + file: filepath.clone(), + line: 33, + } + }, entries[8]); + + assert_eq!(Entry { + data: EntryData::Generic, + text: String::from("generic todo 3"), + location: Location { + file: filepath.clone(), + line: 34, + } + }, entries[9]); +}