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:
parent
c708a45ab9
commit
5737232baa
5 changed files with 161 additions and 11 deletions
17
src/main.rs
17
src/main.rs
|
|
@ -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 {
|
||||
|
|
|
|||
117
src/scan.rs
117
src/scan.rs
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue