Changeset 83d90d2 in gignore
- Timestamp:
- 19 Jan 2026, 19:49:46 (3 months ago)
- Branches:
- error_handling, file_locations, help, json_tlib, logging, man, multi_arg, trunk, visibility
- Children:
- 1503595
- Parents:
- b6c2333
- Files:
-
- 1 added
- 3 edited
-
clippywarn (added)
-
src/lib.rs (modified) (3 diffs)
-
src/main.rs (modified) (1 diff)
-
src/template.rs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/lib.rs
rb6c2333 r83d90d2 1 #![warn(clippy::unwrap_used)] 2 #![warn(clippy::all, clippy::pedantic, clippy::restriction)] 1 #![warn(clippy::unwrap_used, clippy::single_call_fn)] 3 2 4 mod template; 5 6 use clap::{Subcommand, Parser}; 3 pub mod template; 7 4 8 5 use std::{ 9 fs::{self, File, OpenOptions,}, 10 io::Write, 6 fs::{File, OpenOptions}, 11 7 error::Error, 12 8 process::exit, 13 io,14 9 path::Path, 15 10 }; 16 11 17 trait HandleErr {12 pub trait HandleErr { 18 13 type OkType; 19 14 … … 45 40 } 46 41 47 #[derive(Parser)]48 #[command(about = ".gitignore manipulator tool.", long_about = None, version)]49 pub struct ClapArgs {50 #[command(subcommand)]51 pub operation: OperCommand,52 }53 42 54 #[derive(Subcommand)] 55 pub enum OperCommand { 56 /// Removes paths within .gitignore, or the file itself. 57 Remove { 58 /// Path to remove 59 path: Option<String>, 60 }, 61 62 /// Adds paths or .gitignore file. 63 Add { 64 /// Path to add 65 path: Option<String>, 66 }, 67 68 /// Lists items within .gitignore 69 List { 70 /// Print contents on one line, separated by whitespace. 71 #[arg(long)] 72 oneline: bool, 73 }, 74 75 /// Add, remove, or use templates. 76 Template { 77 #[command(subcommand)] 78 operation: TemplateCommand, 79 }, 80 } 81 82 #[derive(Subcommand)] 83 pub enum TemplateCommand { 84 /// Add template names and source files to the library 85 Add { 86 /// Name to add to library 87 name: String, 88 /// Source file to add to library 89 source_file: String, 90 91 #[arg(long, short)] 92 /// Append template to .gitignore when using (rather than truncating) 93 append: bool, 94 }, 95 96 /// Remove templates from the library 97 Remove { 98 /// Name to remove from library 99 name: String, 100 }, 101 102 /// Use templates 103 Use { 104 /// Name to write to gitignore 105 name: String, 106 }, 107 108 /// List entries in library 109 List { 110 #[arg(long)] 111 /// Print entries on one line 112 oneline: bool 113 }, 114 115 /// Clear template library 116 Clear, 117 } 118 119 pub fn handle(args: &ClapArgs) { 120 match &args.operation { 121 122 OperCommand::Add { 123 path 124 } => { 125 let Some(path) = path else { 126 File::create(".gitignore").handle_err("creating file"); 127 128 std::process::exit(0); 129 }; 130 131 if fs::read_to_string(".gitignore") 132 .handle_err("reading file") 133 .lines() 134 .collect::<Vec<&str>>() 135 .contains(&&path[..]) { 136 println!("That path already exists in .gitignore!"); 137 exit(1); 138 } 139 140 let mut append_handle = open_append(".gitignore".as_ref()).handle_err("opening file for append"); 141 142 append_handle.write_all(format!("{}\n", path.trim()).as_bytes()).handle_err("writing to file"); 143 }, 144 OperCommand::Remove { 145 path 146 } => { 147 let Some(path) = path else { 148 if fs::exists(".gitignore").handle_err("searching for file") { 149 loop { 150 println!("This will permanently remove .gitignore!"); 151 println!("Do you really want to do this? y/N"); 152 let mut s = String::new(); 153 io::stdin() 154 .read_line(&mut s) 155 .handle_err("reading stdin"); 156 157 match &s.trim().to_lowercase()[..] { 158 "" | "n" => break, 159 "y" => { 160 println!("Removing .gitignore..."); 161 162 fs::remove_file(".gitignore").handle_err("removing file"); 163 164 exit(0); 165 } 166 _ => {}, 167 } 168 } 169 exit(0); 170 } else { 171 println!("File is already removed."); 172 exit(1); 173 } 174 }; 175 176 let contents = fs::read_to_string(".gitignore").handle_err("reading file"); 177 178 let mut lines: Vec<&str> = contents.lines().collect(); 179 180 let mut extracted = lines.extract_if(.., |line| *line == path); 181 182 if extracted.next().is_none() { 183 eprintln!("There was no match for `{path}`."); 184 exit(0); 185 } 186 187 drop(extracted); 188 189 open_truncate(".gitignore".as_ref()).handle_err("opening file for truncate").write_all(b"").handle_err("writing to file"); 190 191 // Re-add lines to file, without [PATH]. 192 for line in lines { 193 open_append(".gitignore".as_ref()).handle_err("opening file for append") 194 .write_all( 195 format!("{line}\n").as_bytes() 196 ).handle_err("writing to file"); 197 } 198 }, 199 200 OperCommand::List { 201 oneline 202 } => { 203 let result = fs::read_to_string(".gitignore"); 204 205 if let Err(e) = result { 206 println!("Error reading file: {e}"); 207 exit(1); 208 } 209 210 #[expect(clippy::missing_panics_doc)] 211 #[expect(clippy::unwrap_used)] 212 // unwrap() is okay here because result is checked for Err in a diverging if let 213 // statement above. This means result will never be Err here. 214 let result = result.unwrap(); 215 216 if *oneline { 217 for line in result.lines() { 218 print!("{} ", line.trim()); 219 } 220 println!(); 221 exit(0); 222 } 223 224 println!("{}", result.trim()); 225 }, 226 227 OperCommand::Template { 228 operation: TemplateCommand::Add { 229 name, 230 source_file, 231 append, 232 } 233 } => { 234 template::add(&template::Template::new(source_file.as_ref(), name, *append)); 235 } 236 237 OperCommand::Template { 238 operation: TemplateCommand::Remove { 239 name, 240 } 241 } => { 242 template::remove(name); 243 }, 244 245 OperCommand::Template { 246 operation: TemplateCommand::List { 247 oneline, 248 } 249 } => { 250 template::list(*oneline); 251 }, 252 253 OperCommand::Template { 254 operation: TemplateCommand::Use { 255 name, 256 } 257 } => { 258 template::gitignore_write(name); 259 }, 260 261 OperCommand::Template { 262 operation: TemplateCommand::Clear 263 } => { 264 template::clear(); 265 } 266 } 267 } 268 269 fn open_append(path: &Path) -> Result<File, Box<dyn Error>> { 43 pub fn open_append(path: &Path) -> Result<File, Box<dyn Error>> { 270 44 Ok( 271 45 OpenOptions::new() … … 275 49 ) 276 50 } 277 fn open_truncate(path: &Path) -> Result<File, Box<dyn Error>> {51 pub fn open_truncate(path: &Path) -> Result<File, Box<dyn Error>> { 278 52 Ok( 279 53 OpenOptions::new() -
src/main.rs
rb6c2333 r83d90d2 1 use clap::Parser; 2 use gignore::ClapArgs; 1 use clap::{Parser, Subcommand}; 2 use gignore::{ 3 open_append, 4 open_truncate, 5 HandleErr, 6 template, 7 }; 8 9 use std::{ 10 fs::{self, File}, 11 io::Write, 12 process::exit, 13 io, 14 }; 15 16 17 #[derive(Parser)] 18 #[command(about = ".gitignore manipulator tool.", long_about = None, version)] 19 struct ClapArgs { 20 #[command(subcommand)] 21 operation: OperCommand, 22 } 23 24 #[derive(Subcommand)] 25 enum OperCommand { 26 /// Removes paths within .gitignore, or the file itself. 27 Remove { 28 /// Path to remove 29 path: Option<String>, 30 }, 31 32 /// Adds paths or .gitignore file. 33 Add { 34 /// Path to add 35 path: Option<String>, 36 }, 37 38 /// Lists items within .gitignore 39 List { 40 /// Print contents on one line, separated by whitespace. 41 #[arg(long)] 42 oneline: bool, 43 }, 44 45 /// Add, remove, or use templates. 46 Template { 47 #[command(subcommand)] 48 operation: TemplateCommand, 49 }, 50 } 51 52 #[derive(Subcommand)] 53 enum TemplateCommand { 54 /// Add template names and source files to the library 55 Add { 56 /// Name to add to library 57 name: String, 58 /// Source file to add to library 59 source_file: String, 60 61 #[arg(long, short)] 62 /// Append template to .gitignore when using (rather than truncating) 63 append: bool, 64 }, 65 66 /// Remove templates from the library 67 Remove { 68 /// Name to remove from library 69 name: String, 70 }, 71 72 /// Use templates 73 Use { 74 /// Name to write to gitignore 75 name: String, 76 }, 77 78 /// List entries in library 79 List { 80 #[arg(long)] 81 /// Print entries on one line 82 oneline: bool 83 }, 84 85 /// Clear template library 86 Clear, 87 } 3 88 4 89 fn main() { 5 90 let args = ClapArgs::parse(); 6 91 7 gignore::handle(&args); 8 } 92 match &args.operation { 93 94 OperCommand::Add { 95 path 96 } => { 97 let Some(path) = path else { 98 File::create(".gitignore").handle_err("creating file"); 99 100 std::process::exit(0); 101 }; 102 103 if fs::read_to_string(".gitignore") 104 .handle_err("reading file") 105 .lines() 106 .collect::<Vec<&str>>() 107 .contains(&&path[..]) { 108 println!("That path already exists in .gitignore!"); 109 exit(1); 110 } 111 112 let mut append_handle = open_append(".gitignore".as_ref()).handle_err("opening file for append"); 113 114 append_handle.write_all(format!("{}\n", path.trim()).as_bytes()).handle_err("writing to file"); 115 }, 116 OperCommand::Remove { 117 path 118 } => { 119 let Some(path) = path else { 120 if fs::exists(".gitignore").handle_err("searching for file") { 121 loop { 122 println!("This will permanently remove .gitignore!"); 123 println!("Do you really want to do this? y/N"); 124 let mut s = String::new(); 125 io::stdin() 126 .read_line(&mut s) 127 .handle_err("reading stdin"); 128 129 match &s.trim().to_lowercase()[..] { 130 "" | "n" => break, 131 "y" => { 132 println!("Removing .gitignore..."); 133 134 fs::remove_file(".gitignore").handle_err("removing file"); 135 136 exit(0); 137 } 138 _ => {}, 139 } 140 } 141 exit(0); 142 } else { 143 println!("File is already removed."); 144 exit(1); 145 } 146 }; 147 148 let contents = fs::read_to_string(".gitignore").handle_err("reading file"); 149 150 let mut lines: Vec<&str> = contents.lines().collect(); 151 152 let mut extracted = lines.extract_if(.., |line| *line == path); 153 154 if extracted.next().is_none() { 155 eprintln!("There was no match for `{path}`."); 156 exit(0); 157 } 158 159 drop(extracted); 160 161 open_truncate(".gitignore".as_ref()).handle_err("opening file for truncate").write_all(b"").handle_err("writing to file"); 162 163 // Re-add lines to file, without [PATH]. 164 for line in lines { 165 open_append(".gitignore".as_ref()).handle_err("opening file for append") 166 .write_all( 167 format!("{line}\n").as_bytes() 168 ).handle_err("writing to file"); 169 } 170 }, 171 172 OperCommand::List { 173 oneline 174 } => { 175 let result = fs::read_to_string(".gitignore"); 176 177 if let Err(e) = result { 178 println!("Error reading file: {e}"); 179 exit(1); 180 } 181 182 #[expect(clippy::missing_panics_doc)] 183 #[expect(clippy::unwrap_used)] 184 // unwrap() is okay here because result is checked for Err in a diverging if let 185 // statement above. This means result will never be Err here. 186 let result = result.unwrap(); 187 188 if *oneline { 189 for line in result.lines() { 190 print!("{} ", line.trim()); 191 } 192 println!(); 193 exit(0); 194 } 195 196 println!("{}", result.trim()); 197 }, 198 199 OperCommand::Template { 200 operation: TemplateCommand::Add { 201 name, 202 source_file, 203 append, 204 } 205 } => { 206 template::add(&template::Template::new(source_file.as_ref(), name, *append)); 207 } 208 209 OperCommand::Template { 210 operation: TemplateCommand::Remove { 211 name, 212 } 213 } => { 214 template::remove(name); 215 }, 216 217 OperCommand::Template { 218 operation: TemplateCommand::List { 219 oneline, 220 } 221 } => { 222 template::list(*oneline); 223 }, 224 225 OperCommand::Template { 226 operation: TemplateCommand::Use { 227 name, 228 } 229 } => { 230 template::gitignore_write(name); 231 }, 232 233 OperCommand::Template { 234 operation: TemplateCommand::Clear 235 } => { 236 template::clear(); 237 } 238 } 239 } -
src/template.rs
rb6c2333 r83d90d2 1 2 1 use std::{ 3 2 path::{
Note:
See TracChangeset
for help on using the changeset viewer.
