diff --git a/.direnv/flake-profile b/.direnv/flake-profile index 0c05709..519b17b 120000 --- a/.direnv/flake-profile +++ b/.direnv/flake-profile @@ -1 +1 @@ -flake-profile-1-link \ No newline at end of file +flake-profile-3-link \ No newline at end of file diff --git a/.direnv/flake-profile-2-link b/.direnv/flake-profile-2-link new file mode 120000 index 0000000..c0c3d8f --- /dev/null +++ b/.direnv/flake-profile-2-link @@ -0,0 +1 @@ +/nix/store/s3bdhnm2dx7jldz6179klz4lcx97jnwa-nix-shell-env \ No newline at end of file diff --git a/.direnv/flake-profile-3-link b/.direnv/flake-profile-3-link new file mode 120000 index 0000000..6418ed1 --- /dev/null +++ b/.direnv/flake-profile-3-link @@ -0,0 +1 @@ +/nix/store/i3rlyi8v4kyhgadm396y1cqv2grr7w14-nix-shell-env \ No newline at end of file diff --git a/result b/result new file mode 120000 index 0000000..6f1a21e --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/1027jkd1x56049465arvbvyrcghm25rg-yunodo-0.6.0 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0a3a06a..d130bc7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; -use std::{io, fs, path::PathBuf,collections::HashMap}; -//TODO: implement mod file_tree :ODOT// +use std::{io, fs, path::PathBuf, collections::HashMap}; + #[derive(Parser)] #[command(name = "YUNODO")] #[command(version = "0.5.0")] @@ -13,32 +13,30 @@ struct Cli { #[arg(short, long)] debug: Option, } + fn main() { let cli = Cli::parse(); + if let Some(path) = cli.path.as_deref() { let path_string = path.display().to_string(); let mut output_csv_item: String = String::new(); + match read_files_in_directory(&path_string.as_str()) { Ok(files_content) => { for (filename, lines) in files_content { for (line_number, line) in lines.iter().enumerate() { - // Check if the line starts with "//" and not within strings or other signatures - if !line.contains("//") { + if !line.contains("//TODO:") { continue; } - // Variables to track if we are within a string, comment, or signature let mut in_string = false; let mut in_comment = false; - let mut _in_signature = false; - // Iterate through each character in the line for (i, c) in line.chars().enumerate() { match c { '"' => in_string = !in_string, '/' if !in_string => { if i + 1 < line.len() && line.chars().nth(i + 1).unwrap() == '/' { - // Check if it's a comment in_comment = true; break; } else if i + 1 < line.len() && line.chars().nth(i + 1).unwrap() == '*' { @@ -46,31 +44,26 @@ fn main() { } } '*' if i + 1 < line.len() && line.chars().nth(i + 1).unwrap() == '/' && in_comment => { - // Check for end of block comment in_comment = false; break; } _ => (), } } - if in_comment { - // Extract TODO comments - let v: Vec<&str> = line.split("//TODO:").collect(); - if let Some(last_part) = v.last() { - if let Some(end_index) = last_part.find(":ODOT//") { - let extracted = &last_part[..end_index]; - // Format output CSV item - if !output_csv_item.is_empty(){ - //OUTPUT_CSV_TO_APPEND, PATH, FILENAME, LINE_NUMBER, EXTRACTED_COMMENT - output_csv_item = format!("{},{},{},{},{}", output_csv_item, path_string, filename, line_number + 1, extracted); - } else { - //PATH, FILENAME, LINE_NUMBER, EXTRACTED_COMMENT - output_csv_item = format!("{},{},{},{}",path_string, filename, line_number + 1, extracted); - } + if let Some(todo_comment) = extract_todo_comment(line) { + let (adjusted_todo_comment, uscore) = extract_uscore(&todo_comment); + let line_number_str = (line_number + 1).to_string(); // Convert line number to String + let uscore_str = uscore.to_string(); // Convert uscore to String + + if !output_csv_item.is_empty() { + output_csv_item.push_str(","); } + + output_csv_item.push_str(&format!("{},{},{},{},{}", path_string, filename, line_number_str, adjusted_todo_comment, uscore_str)); } } + } } } @@ -78,29 +71,19 @@ fn main() { eprintln!("Error: {}", err); } } + if let Some(format) = cli.format.as_deref() { - //TODO: write match to match format and select which output :ODOT// match format { - "md"|"MD" => out_as_md_table(output_csv_item.clone()), - "json"|"JSON" => out_as_json_object(output_csv_item.clone()), - "yaml"|"YAML" => out_as_yaml_file(output_csv_item.clone()), - "toml"|"TOML" => out_as_toml_file(output_csv_item.clone()), + "md" | "MD" => out_as_md_table(output_csv_item.clone()), + "json" | "JSON" => out_as_json_object(output_csv_item.clone()), + "yaml" | "YAML" => out_as_yaml_file(output_csv_item.clone()), + "toml" | "TOML" => out_as_toml_file(output_csv_item.clone()), _ => println!("That's not a supported format") } } } } -/// Reads files in a given directory and its subdirectories. -/// -/// # Arguments -/// -/// * `dir_path` - A string slice representing the path to the directory. -/// -/// # Returns -/// -/// A Result containing a vector of tuples where each tuple contains the filename and -/// a vector of lines in the file, or an io::Error if an error occurs during file I/O. fn read_files_in_directory(dir_path: &str) -> io::Result)>> { let mut files_content = Vec::new(); let paths = fs::read_dir(dir_path)?; @@ -109,13 +92,11 @@ fn read_files_in_directory(dir_path: &str) -> io::Result = content.lines().map(|s| s.to_string()).collect(); files_content.push((filename, lines)); } else if path.is_dir() { - // If the entry is a directory, recursively call the function to read its content let subdir_path = path.to_string_lossy().into_owned(); let subdir_content = read_files_in_directory(&subdir_path)?; files_content.extend(subdir_content); @@ -124,63 +105,91 @@ fn read_files_in_directory(dir_path: &str) -> io::Result = input_csv.split(',').collect(); - //Formatting Values - let headers = String::from("| File Path | File Name | Line Number | Comment |"); - let divider = String::from("|:----------|:---------:|:-----------:|:--------|"); - //Output Vector - let mut table:Vec = Vec::new(); +fn extract_todo_comment(line: &str) -> Option { + if let Some(start) = line.find("//TODO:") { + if let Some(end) = line[start + "//TODO:".len()..].find(":ODOT//") { + return Some(line[start + "//TODO:".len()..start + "//TODO:".len() + end].trim().to_string()); + } + } + None +} +fn extract_uscore(comment: &str) -> (String, u8) { + // Initialize uscore to 0 by default + let mut uscore = 0; + + // Check if comment starts with "U:" + if let Some(start_uscore) = comment.find("U:") { + // Calculate the start and end positions for slicing + let start_idx = start_uscore + "U:".len(); + if let Some(end_uscore) = comment[start_idx..].find(' ') { + // Extract the number part after "U:" + let num_str = &comment[start_idx..start_idx + end_uscore]; + // Attempt to parse the substring as u8 + if let Ok(num) = num_str.parse::() { + uscore = num; + } + } + // Remove "U: number" part from the comment + let new_comment = format!("{}{}", &comment[..start_uscore], &comment[start_uscore + "U: ".len()..]); + return (new_comment, uscore); + } + + // Return original comment and 0 uscore if "U:" or a valid number is not found + (comment.to_string(), uscore) +} + +fn out_as_md_table(input_csv: String) { + let mut split_input: Vec<&str> = input_csv.split(',').collect(); + let headers = String::from("| File Path | File Name | Line Number | Comment | Uscore |"); + let divider = String::from("|:----------|:---------:|:-----------:|:--------|:------|"); + + let mut table: Vec = Vec::new(); table.push(headers); table.push(divider); split_and_print(&mut split_input, &mut table); + for line in table { - println!("{}",line); + println!("{}", line); } } -fn split_csv(vec: &mut Vec<&str>,split:usize) -> Vec { - let mut rows: Vec = Vec::new(); - if !vec.is_empty() { - let mut vec2 = vec.split_off(split); - let row = vec.join(","); - rows.push(row); - let mut remaining_rows = split_csv(&mut vec2,split); - rows.append(&mut remaining_rows); - } - rows -} -fn split_and_print(vec: &mut Vec<&str>,table: &mut Vec) { - if vec.is_empty() { - return; - } +fn split_and_print(vec: &mut Vec<&str>, table: &mut Vec) { + while !vec.is_empty() { + // Extract elements from the vec + let path = vec.remove(0).trim().to_string(); + let name = vec.remove(0).trim().to_string(); + let line = vec.remove(0).trim().to_string(); + let comment = vec.remove(0).trim().to_string(); - let mut vec2 = vec.split_off(4); - let comment = vec.pop(); - let line = vec.pop(); - let name = vec.pop(); - let path = vec.pop(); - let spacer = '|'; - let formatted_row = format!("{} {} {} {} {} {} {} {} {}",spacer,path.unwrap(),spacer,name.unwrap(),spacer,line.unwrap(),spacer,comment.unwrap(),spacer); - table.push(formatted_row); - split_and_print(&mut vec2, table); + // Uscore is the last element and needs parsing + let uscore = if let Some(last) = vec.pop() { + last.trim().parse::().unwrap_or(0) + } else { + 0 + }; + + // Format as Markdown table row + let formatted_row = format!("| {} | {} | {} | {} | {} |", path, name, line, comment, uscore); + table.push(formatted_row); + } } -fn out_as_json_object(input_csv:String){ - let object_open_char = "{".to_string(); + +fn out_as_json_object(input_csv: String) { + let mut output: Vec = Vec::new(); + let object_open_char = "{".to_string(); let object_close_char = "}".to_string(); - let depth_counter = 0; - + let mut split_input: Vec<&str> = input_csv.split(',').collect(); - let rows:Vec = split_csv(&mut split_input,4); - - let mut output:Vec = Vec::new(); - output.push(object_open_char); + let rows: Vec = split_csv(&mut split_input, 5); + + output.push(object_open_char.clone()); + for row in rows { let mut cols: Vec<_> = row.split(',').collect(); let obj_open = " {"; let obj_close = " },"; + let uscore = format!(" \"uscore\":\"{}\",", cols.pop().unwrap()); let comment = format!(" \"todo_comment\":\"{}\",", cols.pop().unwrap()); let line_number = format!(" \"line_number\":\"{}\",", cols.pop().unwrap()); let file_name = format!(" \"file_name\":\"{}\",", cols.pop().unwrap()); @@ -190,89 +199,92 @@ fn out_as_json_object(input_csv:String){ output.push(file_name.clone()); output.push(line_number.clone()); output.push(comment.clone()); + output.push(uscore.clone()); output.push(obj_close.to_string()); } output.push(object_close_char); for line in output { - println!("{}",line) + println!("{}", line) } } -fn out_as_toml_file(input_csv: String) { - // Split input CSV into rows - let mut split_input: Vec<&str> = input_csv.split(',').collect(); - // Remove any empty elements +fn out_as_toml_file(input_csv: String) { + let mut split_input: Vec<&str> = input_csv.split(',').collect(); split_input.retain(|&x| x.trim() != ""); - // Define data structures to store todos and headers - let mut todos: HashMap> = HashMap::new(); + let mut todos: HashMap> = HashMap::new(); - // Parse each row of the CSV while !split_input.is_empty() { let path = split_input.remove(0).trim().to_string(); let file = split_input.remove(0).trim().to_string(); let line = split_input.remove(0).trim().to_string(); let comment = split_input.remove(0).trim().to_string(); + let uscore = split_input.remove(0).trim().parse::().unwrap_or(0); let header = format!("{}{}", path, file); - // Store todo item under the header - todos.entry(header.clone()).or_insert(Vec::new()).push((line.clone(), comment.clone())); + todos.entry(header.clone()).or_insert(Vec::new()).push((line.clone(), comment.clone(), uscore)); } - // Write todos to TOML file let mut toml_output = String::new(); for (header, todo_list) in todos { - // Write header toml_output.push_str(&format!("[{}]\n", header)); - // Write todo items under this header - for (i, (line, comment)) in todo_list.clone().into_iter().enumerate() { - toml_output.push_str(&format!("[[todo]]\n")); + for (i, (line, comment, uscore)) in todo_list.iter().enumerate() { + toml_output.push_str("[[todo]]\n"); toml_output.push_str(&format!("line = {}\n", line)); toml_output.push_str(&format!("comment = \"{}\"\n", comment)); + toml_output.push_str(&format!("uscore = {}\n", uscore)); + if i < todo_list.len() - 1 { - toml_output.push_str("\n"); // Separate todo items with a newline + toml_output.push_str("\n"); } } } println!("{}", toml_output); } -fn out_as_yaml_file(input_csv: String) { - // Split input CSV into rows - let mut split_input: Vec<&str> = input_csv.split(',').collect(); - // Remove any empty elements +fn out_as_yaml_file(input_csv: String) { + let mut split_input: Vec<&str> = input_csv.split(',').collect(); split_input.retain(|&x| x.trim() != ""); - // Define data structures to store todos and headers - let mut todos: HashMap> = HashMap::new(); + let mut todos: HashMap> = HashMap::new(); - // Parse each row of the CSV while !split_input.is_empty() { let path = split_input.remove(0).trim().to_string(); let file = split_input.remove(0).trim().to_string(); let line = split_input.remove(0).trim().to_string(); let comment = split_input.remove(0).trim().to_string(); + let uscore = split_input.remove(0).trim().parse::().unwrap_or(0); let header = format!("{}{}", path, file); - // Store todo item under the header - todos.entry(header.clone()).or_insert(Vec::new()).push((line.clone(), comment.clone())); + todos.entry(header.clone()).or_insert(Vec::new()).push((line.clone(), comment.clone(), uscore)); } - // Write todos to YAML file let mut yaml_output = String::new(); for (header, todo_list) in todos { - // Write header yaml_output.push_str(&format!("\"{}\":\n", header)); - // Write todo items under this header - for (line, comment) in todo_list { + for (line, comment, uscore) in todo_list { yaml_output.push_str(" \"item\":\n"); yaml_output.push_str(&format!(" \"line_number\": \"{}\"\n", line)); yaml_output.push_str(&format!(" \"comment\": \"{}\"\n", comment)); + yaml_output.push_str(&format!(" \"uscore\": \"{}\"\n", uscore)); } } - // Print YAML output to terminal println!("{}", yaml_output); } + +fn split_csv(vec: &mut Vec<&str>, split: usize) -> Vec { + let mut rows: Vec = Vec::new(); + + if !vec.is_empty() { + let mut vec2 = vec.split_off(split); + let row = vec.join(","); + rows.push(row); + let mut remaining_rows = split_csv(&mut vec2, split); + rows.append(&mut remaining_rows); + } + + rows +}