1
0
Fork 0
mirror of https://github.com/archtechx/todo-system.git synced 2025-12-12 00:54:03 +00:00

scan todos in readme.md

This commit is contained in:
Samuel Štancl 2023-11-22 22:13:31 +01:00
parent c708a45ab9
commit 5737232baa
5 changed files with 161 additions and 11 deletions

View file

@ -1,6 +1,7 @@
use std::path::PathBuf;
use clap::Parser;
use scan::scan_readme_file;
use crate::entries::Entry;
use crate::render::render_entries;
use crate::scan::{Stats, scan_dir, scan_todo_file};
@ -44,7 +45,12 @@ fn main() {
for p in args.paths {
let mut path = root_dir.clone();
path.push(p);
if p != "." {
// This isn't necessary and the code works just fine without it
// but it adds unnecessary /./ to the paths in the generated output.
path.push(p);
}
paths.push(path);
}
@ -56,8 +62,6 @@ fn main() {
excludes.push(path);
}
// todo@real logic for readme.md
let mut entries: Vec<Entry> = vec![];
let mut stats = Stats::new();
@ -72,6 +76,13 @@ fn main() {
scan_todo_file(&todos_path, &mut entries).unwrap();
}
let mut readme_path = root_dir.clone();
readme_path.push(&args.readme);
if readme_path.exists() {
scan_readme_file(&readme_path, &mut entries).unwrap();
}
render_entries(entries);
if args.verbose {

View file

@ -2,6 +2,8 @@ use std::io;
use std::fs;
use std::path::{Path, PathBuf};
const PRIORITY_CHARS: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
use crate::entries::{Entry, EntryData, Location};
pub struct Stats {
@ -80,9 +82,7 @@ pub fn scan_string(str: String, filename: PathBuf, entries: &mut Vec<Entry>) {
break;
}
let priority_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
if word.chars().any(|ch| priority_chars.contains(&ch)) {
if word.chars().any(|ch| PRIORITY_CHARS.contains(&ch)) {
if let Some(priority) = parse_priority(word) {
entries.push(Entry {
text: text.to_string(),
@ -140,7 +140,6 @@ pub fn scan_dir(path: &Path, entries: &mut Vec<Entry>, excludes: &Vec<PathBuf>,
pub fn scan_todo_file(path: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
let str = fs::read_to_string(path)?;
let mut current_category: Option<&str> = None;
let priority_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
// This can produce:
// - generic todos (above any category)
@ -158,7 +157,7 @@ pub fn scan_todo_file(path: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
}
for word in line.split_whitespace() {
if word.to_lowercase().starts_with("todo") && word.chars().any(|ch| priority_chars.contains(&ch)) {
if word.to_lowercase().starts_with("todo") && word.chars().any(|ch| PRIORITY_CHARS.contains(&ch)) {
if let Some(priority) = parse_priority(word) {
entries.push(Entry {
text: line.split_once(word).unwrap().1.trim().trim_end_matches("*/").trim().to_string(),
@ -202,6 +201,65 @@ pub fn scan_todo_file(path: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
Ok(())
}
pub fn scan_readme_file(path: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
let str = fs::read_to_string(path)?;
let mut in_todo_section = false;
// This can produce:
// - generic todos (above any category)
// - catgory todos (below a ## category heading)
// - priority todos (priority keyword part of the line)
'line: for (line_num, line) in str.lines().enumerate() {
if line.starts_with('#') {
let section = line.split_once("# ").unwrap().1;
let cleaned_section = section.to_lowercase().trim_end_matches(':').trim().to_string();
if cleaned_section == "todo" || cleaned_section == "todos" {
in_todo_section = true;
}
continue;
}
if ! in_todo_section {
continue;
}
if ! line.starts_with('-') {
continue;
}
for word in line.split_whitespace() {
if word.to_lowercase().starts_with("todo") && word.chars().any(|ch| PRIORITY_CHARS.contains(&ch)) {
if let Some(priority) = parse_priority(word) {
entries.push(Entry {
text: line.split_once(word).unwrap().1.trim().trim_end_matches("*/").trim().to_string(),
location: Location {
file: path.to_path_buf(),
line: line_num + 1,
},
data: EntryData::Priority(priority),
});
}
continue 'line;
}
}
// README.md can only have priority entries and generic entries
entries.push(Entry {
text: line.trim_start_matches("- [ ] ").trim_start_matches("- ").to_string(),
location: Location {
file: path.to_path_buf(),
line: line_num + 1,
},
data: EntryData::Generic,
});
}
Ok(())
}
#[test]
fn generic_test() {
let str = r#"
@ -644,3 +702,52 @@ fn todo_file_test() {
}
}, entries[7]);
}
#[test]
fn readme_file_test() {
let mut entries: Vec<Entry> = vec![];
let mut path = std::env::current_dir().unwrap();
path.push("samples");
path.push("README.md");
scan_readme_file(path.as_path(), &mut entries).unwrap();
assert_eq!(4, entries.len());
assert_eq!(Entry {
data: EntryData::Generic,
text: String::from("abc"),
location: Location {
file: path.clone(),
line: 19,
}
}, entries[0]);
assert_eq!(Entry {
data: EntryData::Priority(0),
text: String::from("def"),
location: Location {
file: path.clone(),
line: 20,
}
}, entries[1]);
assert_eq!(Entry {
data: EntryData::Generic,
text: String::from("bar"),
location: Location {
file: path.clone(),
line: 21,
}
}, entries[2]);
assert_eq!(Entry {
data: EntryData::Generic,
text: String::from("baz"),
location: Location {
file: path.clone(),
line: 22,
}
}, entries[3]);
}