From 06eff6fcf820698d646949ef64551dea3f894945 Mon Sep 17 00:00:00 2001 From: glitchySid Date: Mon, 29 Dec 2025 00:54:29 +0530 Subject: [PATCH] style: Fix rustfmt formatting issues --- .github/workflows/rust.yml | 19 ++++++- Cargo.lock | 58 ------------------- Cargo.toml | 2 +- src/cache.rs | 38 ++++++------- src/cache_tests.rs | 3 +- src/config.rs | 11 ++-- src/config_tests.rs | 24 ++++++-- src/files.rs | 71 ++++++----------------- src/files_tests.rs | 15 +++-- src/gemini.rs | 22 ++++---- src/gemini_errors.rs | 112 +++++++++++++++++-------------------- src/main.rs | 85 +++++++++++++++++----------- src/prompt.rs | 22 +++++--- 13 files changed, 214 insertions(+), 268 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b1854fa..0f7910d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,10 +16,25 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v4 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - name: Build - run: cargo build --verbose + run: cargo build --release - name: Run tests - run: cargo test --verbose + run: cargo test --release - name: Run clippy run: cargo clippy -- -D warnings - name: Check formatting diff --git a/Cargo.lock b/Cargo.lock index addc0b1..b268079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -762,15 +762,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -898,29 +889,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -978,15 +946,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_users" version = "0.4.6" @@ -1119,12 +1078,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "security-framework" version = "2.11.1" @@ -1229,15 +1182,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook-registry" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.11" @@ -1402,9 +1346,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index a9a6e37..5063308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" sha2 = "0.10.8" thiserror = "2.0.11" -tokio = { version = "1.48.0", features = ["full"] } +tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "sync", "time"] } toml = "0.8.19" [dev-dependencies] diff --git a/src/cache.rs b/src/cache.rs index 79a8416..b52997c 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -46,18 +46,16 @@ impl Cache { pub fn load_or_create(cache_path: &Path) -> Self { if cache_path.exists() { match fs::read_to_string(cache_path) { - Ok(content) => { - match serde_json::from_str::(&content) { - Ok(cache) => { - println!("Loaded cache with {} entries", cache.entries.len()); - cache - } - Err(_) => { - println!("Cache corrupted, creating new cache"); - Self::new() - } + Ok(content) => match serde_json::from_str::(&content) { + Ok(cache) => { + println!("Loaded cache with {} entries", cache.entries.len()); + cache } - } + Err(_) => { + println!("Cache corrupted, creating new cache"); + Self::new() + } + }, Err(_) => { println!("Failed to read cache, creating new cache"); Self::new() @@ -73,13 +71,17 @@ impl Cache { if let Some(parent) = cache_path.parent() { fs::create_dir_all(parent)?; } - + let content = serde_json::to_string_pretty(self)?; fs::write(cache_path, content)?; Ok(()) } - pub fn get_cached_response(&self, filenames: &[String], base_path: &Path) -> Option { + pub fn get_cached_response( + &self, + filenames: &[String], + base_path: &Path, + ) -> Option { let cache_key = self.generate_cache_key(filenames); if let Some(entry) = self.entries.get(&cache_key) { @@ -163,10 +165,7 @@ impl Cache { fn get_file_metadata(file_path: &Path) -> Result> { let metadata = fs::metadata(file_path)?; - let modified = metadata - .modified()? - .duration_since(UNIX_EPOCH)? - .as_secs(); + let modified = metadata.modified()?.duration_since(UNIX_EPOCH)?.as_secs(); Ok(FileMetadata { size: metadata.len(), @@ -182,9 +181,8 @@ impl Cache { let initial_count = self.entries.len(); - self.entries.retain(|_, entry| { - current_time - entry.timestamp < max_age_seconds - }); + self.entries + .retain(|_, entry| current_time - entry.timestamp < max_age_seconds); let removed_count = initial_count - self.entries.len(); if removed_count > 0 { diff --git a/src/cache_tests.rs b/src/cache_tests.rs index 28769fd..f9a8673 100644 --- a/src/cache_tests.rs +++ b/src/cache_tests.rs @@ -76,7 +76,8 @@ fn test_cache_response_file_changed() { std::thread::sleep(std::time::Duration::from_millis(100)); let mut file = File::create(&file_path).unwrap(); - file.write_all(b"modified content longer than original").unwrap(); + file.write_all(b"modified content longer than original") + .unwrap(); let cached = cache.get_cached_response(&filenames, base_path); assert!(cached.is_none()); diff --git a/src/config.rs b/src/config.rs index dca5b4a..209e825 100644 --- a/src/config.rs +++ b/src/config.rs @@ -74,8 +74,9 @@ impl Config { pub fn get_or_prompt_api_key() -> Result> { if let Ok(config) = Config::load() - && !config.api_key.is_empty() { - return Ok(config.api_key); + && !config.api_key.is_empty() + { + return Ok(config.api_key); } println!(); @@ -94,8 +95,10 @@ pub fn get_or_prompt_api_key() -> Result> { pub fn get_or_prompt_download_folder() -> Result> { if let Ok(config) = Config::load() - && !config.download_folder.as_os_str().is_empty() && config.download_folder.exists() { - return Ok(config.download_folder); + && !config.download_folder.as_os_str().is_empty() + && config.download_folder.exists() + { + return Ok(config.download_folder); } println!(); diff --git a/src/config_tests.rs b/src/config_tests.rs index 9dfa6ba..15533a1 100644 --- a/src/config_tests.rs +++ b/src/config_tests.rs @@ -18,30 +18,42 @@ fn test_config_serialization() { #[test] fn test_validate_api_key_valid() { - assert!(crate::prompt::Prompter::validate_api_key("AIzaSyB1234567890123456789012345678")); - assert!(crate::prompt::Prompter::validate_api_key("AIzaSyB123456789012345678901234567890")); + assert!(crate::prompt::Prompter::validate_api_key( + "AIzaSyB1234567890123456789012345678" + )); + assert!(crate::prompt::Prompter::validate_api_key( + "AIzaSyB123456789012345678901234567890" + )); } #[test] fn test_validate_api_key_invalid() { assert!(!crate::prompt::Prompter::validate_api_key("")); assert!(!crate::prompt::Prompter::validate_api_key("invalid_key")); - assert!(!crate::prompt::Prompter::validate_api_key("BizaSyB1234567890123456789012345678")); + assert!(!crate::prompt::Prompter::validate_api_key( + "BizaSyB1234567890123456789012345678" + )); assert!(!crate::prompt::Prompter::validate_api_key("short")); } #[test] fn test_validate_folder_path_valid() { let temp_dir = tempfile::tempdir().unwrap(); - assert!(crate::prompt::Prompter::validate_folder_path(temp_dir.path())); + assert!(crate::prompt::Prompter::validate_folder_path( + temp_dir.path() + )); } #[test] fn test_validate_folder_path_invalid() { - assert!(!crate::prompt::Prompter::validate_folder_path(Path::new("/nonexistent/path/that/does/not/exist"))); + assert!(!crate::prompt::Prompter::validate_folder_path(Path::new( + "/nonexistent/path/that/does/not/exist" + ))); let temp_file = tempfile::NamedTempFile::new().unwrap(); - assert!(!crate::prompt::Prompter::validate_folder_path(temp_file.path())); + assert!(!crate::prompt::Prompter::validate_folder_path( + temp_file.path() + )); } #[test] diff --git a/src/files.rs b/src/files.rs index c3a0555..e6c2e3d 100644 --- a/src/files.rs +++ b/src/files.rs @@ -39,10 +39,11 @@ impl FileBatch { for entry in entries.flatten() { let path = entry.path(); if path.is_file() - && let Ok(relative_path) = path.strip_prefix(&root_path) { - filenames.push(relative_path.to_string_lossy().into_owned()); - paths.push(path); - } + && let Ok(relative_path) = path.strip_prefix(&root_path) + { + filenames.push(relative_path.to_string_lossy().into_owned()); + paths.push(path); + } } FileBatch { filenames, paths } @@ -91,10 +92,7 @@ pub fn execute_move(base_path: &Path, plan: OrganizationPlan) { eprint!("\nDo you want to apply these changes? [y/N]: "); let mut input = String::new(); - if io::stdin() - .read_line(&mut input) - .is_err() - { + if io::stdin().read_line(&mut input).is_err() { eprintln!("\n{}", "Failed to read input. Operation cancelled.".red()); return; } @@ -144,11 +142,7 @@ pub fn execute_move(base_path: &Path, plan: OrganizationPlan) { match move_file_cross_platform(&source, &target) { Ok(_) => { if item.sub_category.is_empty() { - println!( - "Moved: {} -> {}/", - item.filename, - item.category.green() - ); + println!("Moved: {} -> {}/", item.filename, item.category.green()); } else { println!( "Moved: {} -> {}/{}", @@ -187,52 +181,19 @@ pub fn execute_move(base_path: &Path, plan: OrganizationPlan) { moved_count.to_string().green(), error_count.to_string().red() ); -} pub fn is_text_file(path: &Path) -> bool { +} +pub fn is_text_file(path: &Path) -> bool { let text_extensions = [ - "txt", - "md", - "rs", - "py", - "js", - "ts", - "jsx", - "tsx", - "html", - "css", - "json", - "xml", - "csv", - "yaml", - "yml", - "toml", - "ini", - "cfg", - "conf", - "log", - "sh", - "bat", - "ps1", - "sql", - "c", - "cpp", - "h", - "hpp", - "java", - "go", - "rb", - "php", - "swift", - "kt", - "scala", - "lua", - "r", - "m", + "txt", "md", "rs", "py", "js", "ts", "jsx", "tsx", "html", "css", "json", "xml", "csv", + "yaml", "yml", "toml", "ini", "cfg", "conf", "log", "sh", "bat", "ps1", "sql", "c", "cpp", + "h", "hpp", "java", "go", "rb", "php", "swift", "kt", "scala", "lua", "r", "m", ]; if let Some(ext) = path.extension() - && let Some(ext_str) = ext.to_str() { - return text_extensions.contains(&ext_str.to_lowercase().as_str()); - } + && let Some(ext_str) = ext.to_str() + { + return text_extensions.contains(&ext_str.to_lowercase().as_str()); + } false } diff --git a/src/files_tests.rs b/src/files_tests.rs index 07ca7ff..2dfa911 100644 --- a/src/files_tests.rs +++ b/src/files_tests.rs @@ -65,7 +65,8 @@ fn test_read_file_sample_with_limit() { let file_path = temp_dir.path().join("test.txt"); let mut file = File::create(&file_path).unwrap(); - file.write_all(b"Hello, World! This is a long text.").unwrap(); + file.write_all(b"Hello, World! This is a long text.") + .unwrap(); let content = read_file_sample(&file_path, 5); assert_eq!(content, Some("Hello".to_string())); @@ -92,13 +93,11 @@ fn test_read_file_sample_nonexistent() { #[test] fn test_organization_plan_serialization() { let plan = OrganizationPlan { - files: vec![ - FileCategory { - filename: "test.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }, - ], + files: vec![FileCategory { + filename: "test.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], }; let json = serde_json::to_string(&plan).unwrap(); diff --git a/src/gemini.rs b/src/gemini.rs index 40811bf..783df50 100644 --- a/src/gemini.rs +++ b/src/gemini.rs @@ -1,8 +1,8 @@ use crate::cache::Cache; +use crate::files::OrganizationPlan; use crate::gemini_errors::GeminiError; use crate::gemini_helpers::PromptBuilder; use crate::gemini_types::{GeminiResponse, OrganizationPlanResponse}; -use crate::files::OrganizationPlan; use reqwest::Client; use serde_json::json; use std::path::Path; @@ -71,8 +71,9 @@ impl GeminiClient { let url = self.build_url(); if let (Some(cache), Some(base_path)) = (cache.as_ref(), base_path) - && let Some(cached_response) = cache.get_cached_response(&filenames, base_path) { - return Ok(cached_response); + && let Some(cached_response) = cache.get_cached_response(&filenames, base_path) + { + return Ok(cached_response); } let prompt = PromptBuilder::new(filenames.clone()).build_categorization_prompt(); @@ -107,8 +108,8 @@ impl GeminiClient { return Err(GeminiError::from_response(res).await); } - let gemini_response: GeminiResponse = res.json().await - .map_err(GeminiError::NetworkError)?; + let gemini_response: GeminiResponse = + res.json().await.map_err(GeminiError::NetworkError)?; let raw_text = self.extract_text_from_response(&gemini_response)?; let plan_response: OrganizationPlanResponse = serde_json::from_str(&raw_text)?; @@ -116,10 +117,7 @@ impl GeminiClient { Ok(plan_response.to_organization_plan()) } - fn extract_text_from_response( - &self, - response: &GeminiResponse, - ) -> Result { + fn extract_text_from_response(&self, response: &GeminiResponse) -> Result { response .candidates .first() @@ -244,7 +242,11 @@ impl GeminiClient { self.extract_subcategory_from_response(&gemini_response, filename) } - fn extract_subcategory_from_response(&self, response: &GeminiResponse, _filename: &str) -> String { + fn extract_subcategory_from_response( + &self, + response: &GeminiResponse, + _filename: &str, + ) -> String { match self.extract_text_from_response(response) { Ok(text) => { let sub_category = text.trim(); diff --git a/src/gemini_errors.rs b/src/gemini_errors.rs index a4a1cee..6096a54 100644 --- a/src/gemini_errors.rs +++ b/src/gemini_errors.rs @@ -93,24 +93,27 @@ impl GeminiError { fn from_gemini_error(error_detail: GeminiErrorDetail, status: u16) -> Self { let details = error_detail.details; - + match error_detail.status.as_str() { "RESOURCE_EXHAUSTED" => { if let Some(retry_info) = details.iter().find(|d| d.retry_delay.is_some()) && let Some(retry_delay) = &retry_info.retry_delay - && let Ok(seconds) = retry_delay.parse::() { - return GeminiError::RateLimitExceeded { retry_after: seconds }; - } - - if let Some(quota_info) = details.iter().find(|d| d.quota_limit.is_some()) { - let limit = quota_info.quota_limit.as_deref().unwrap_or("unknown"); - return GeminiError::QuotaExceeded { - limit: limit.to_string() + && let Ok(seconds) = retry_delay.parse::() + { + return GeminiError::RateLimitExceeded { + retry_after: seconds, }; } - - GeminiError::QuotaExceeded { - limit: "usage limit".to_string() + + if let Some(quota_info) = details.iter().find(|d| d.quota_limit.is_some()) { + let limit = quota_info.quota_limit.as_deref().unwrap_or("unknown"); + return GeminiError::QuotaExceeded { + limit: limit.to_string(), + }; + } + + GeminiError::QuotaExceeded { + limit: "usage limit".to_string(), } } "NOT_FOUND" => { @@ -118,69 +121,57 @@ impl GeminiError { let model = extract_model_name(&error_detail.message); GeminiError::ModelNotFound { model } } - "UNAUTHENTICATED" => { - GeminiError::InvalidApiKey - } + "UNAUTHENTICATED" => GeminiError::InvalidApiKey, "PERMISSION_DENIED" => { if error_detail.message.to_lowercase().contains("policy") { - GeminiError::ContentPolicyViolation { - reason: error_detail.message + GeminiError::ContentPolicyViolation { + reason: error_detail.message, } } else { - GeminiError::InvalidRequest { - details: error_detail.message + GeminiError::InvalidRequest { + details: error_detail.message, } } } - "INVALID_ARGUMENT" => { - GeminiError::InvalidRequest { - details: error_detail.message - } - } - "UNAVAILABLE" => { - GeminiError::ServiceUnavailable { - reason: error_detail.message - } - } - "DEADLINE_EXCEEDED" => { - GeminiError::Timeout { seconds: 60 } - } - "INTERNAL" => { - GeminiError::InternalError { - details: error_detail.message - } - } - _ => { - GeminiError::ApiError { - status, - message: error_detail.message - } - } + "INVALID_ARGUMENT" => GeminiError::InvalidRequest { + details: error_detail.message, + }, + "UNAVAILABLE" => GeminiError::ServiceUnavailable { + reason: error_detail.message, + }, + "DEADLINE_EXCEEDED" => GeminiError::Timeout { seconds: 60 }, + "INTERNAL" => GeminiError::InternalError { + details: error_detail.message, + }, + _ => GeminiError::ApiError { + status, + message: error_detail.message, + }, } } fn from_status_code(status: reqwest::StatusCode, error_text: &str) -> Self { match status.as_u16() { - 400 => GeminiError::InvalidRequest { - details: error_text.to_string() + 400 => GeminiError::InvalidRequest { + details: error_text.to_string(), }, 401 => GeminiError::InvalidApiKey, - 403 => GeminiError::ContentPolicyViolation { - reason: error_text.to_string() + 403 => GeminiError::ContentPolicyViolation { + reason: error_text.to_string(), }, - 404 => GeminiError::ModelNotFound { - model: "unknown".to_string() + 404 => GeminiError::ModelNotFound { + model: "unknown".to_string(), }, 429 => GeminiError::RateLimitExceeded { retry_after: 60 }, - 500 => GeminiError::InternalError { - details: error_text.to_string() + 500 => GeminiError::InternalError { + details: error_text.to_string(), }, - 502..=504 => GeminiError::ServiceUnavailable { - reason: error_text.to_string() + 502..=504 => GeminiError::ServiceUnavailable { + reason: error_text.to_string(), }, - _ => GeminiError::ApiError { - status: status.as_u16(), - message: error_text.to_string() + _ => GeminiError::ApiError { + status: status.as_u16(), + message: error_text.to_string(), }, } } @@ -216,8 +207,9 @@ fn extract_model_name(message: &str) -> String { // Try to extract model name from error message // Example: "Model 'gemini-1.5-flash' not found" if let Some(start) = message.find('\'') - && let Some(end) = message[start + 1..].find('\'') { - return message[start + 1..start + 1 + end].to_string(); - } + && let Some(end) = message[start + 1..].find('\'') + { + return message[start + 1..start + 1 + end].to_string(); + } "unknown".to_string() -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index bed61c7..1a77809 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,10 @@ async fn main() -> Result<(), Box> { } }; - println!("{}", "Gemini Plan received! Performing deep inspection...".green()); + println!( + "{}", + "Gemini Plan received! Performing deep inspection...".green() + ); let client = Arc::new(client); let semaphore = Arc::new(tokio::sync::Semaphore::new(args.max_concurrent)); @@ -81,7 +84,9 @@ async fn main() -> Result<(), Box> { let _permit = semaphore.acquire().await.unwrap(); if let Some(content) = noentropy::files::read_file_sample(&path, 5000) { println!("Reading content of {}...", filename.green()); - client.get_ai_sub_category(&filename, &category, &content).await + client + .get_ai_sub_category(&filename, &category, &content) + .await } else { String::new() } @@ -101,10 +106,7 @@ async fn main() -> Result<(), Box> { println!("{}", "Deep inspection complete! Moving Files.....".green()); if args.dry_run { - println!( - "{} Dry run mode - skipping file moves.", - "INFO:".cyan() - ); + println!("{} Dry run mode - skipping file moves.", "INFO:".cyan()); } else { execute_move(&download_path, plan); } @@ -119,62 +121,77 @@ async fn main() -> Result<(), Box> { fn handle_gemini_error(error: GeminiError) { use colored::*; - + match error { GeminiError::RateLimitExceeded { retry_after } => { - println!("{} API rate limit exceeded. Please wait {} seconds before trying again.", - "ERROR:".red(), retry_after); + println!( + "{} API rate limit exceeded. Please wait {} seconds before trying again.", + "ERROR:".red(), + retry_after + ); } GeminiError::QuotaExceeded { limit } => { - println!("{} Quota exceeded: {}. Please check your Gemini API usage.", - "ERROR:".red(), limit); + println!( + "{} Quota exceeded: {}. Please check your Gemini API usage.", + "ERROR:".red(), + limit + ); } GeminiError::ModelNotFound { model } => { - println!("{} Model '{}' not found. Please check the model name in the configuration.", - "ERROR:".red(), model); + println!( + "{} Model '{}' not found. Please check the model name in the configuration.", + "ERROR:".red(), + model + ); } GeminiError::InvalidApiKey => { - println!("{} Invalid API key. Please check your GEMINI_API_KEY environment variable.", - "ERROR:".red()); + println!( + "{} Invalid API key. Please check your GEMINI_API_KEY environment variable.", + "ERROR:".red() + ); } GeminiError::ContentPolicyViolation { reason } => { - println!("{} Content policy violation: {}", - "ERROR:".red(), reason); + println!("{} Content policy violation: {}", "ERROR:".red(), reason); } GeminiError::ServiceUnavailable { reason } => { - println!("{} Gemini service is temporarily unavailable: {}", - "ERROR:".red(), reason); + println!( + "{} Gemini service is temporarily unavailable: {}", + "ERROR:".red(), + reason + ); } GeminiError::NetworkError(e) => { - println!("{} Network error: {}", - "ERROR:".red(), e); + println!("{} Network error: {}", "ERROR:".red(), e); } GeminiError::Timeout { seconds } => { - println!("{} Request timed out after {} seconds.", - "ERROR:".red(), seconds); + println!( + "{} Request timed out after {} seconds.", + "ERROR:".red(), + seconds + ); } GeminiError::InvalidRequest { details } => { - println!("{} Invalid request: {}", - "ERROR:".red(), details); + println!("{} Invalid request: {}", "ERROR:".red(), details); } GeminiError::ApiError { status, message } => { - println!("{} API error (HTTP {}): {}", - "ERROR:".red(), status, message); + println!( + "{} API error (HTTP {}): {}", + "ERROR:".red(), + status, + message + ); } GeminiError::InvalidResponse(msg) => { - println!("{} Invalid response from Gemini: {}", - "ERROR:".red(), msg); + println!("{} Invalid response from Gemini: {}", "ERROR:".red(), msg); } GeminiError::InternalError { details } => { - println!("{} Internal server error: {}", - "ERROR:".red(), details); + println!("{} Internal server error: {}", "ERROR:".red(), details); } GeminiError::SerializationError(e) => { - println!("{} JSON serialization error: {}", - "ERROR:".red(), e); + println!("{} JSON serialization error: {}", "ERROR:".red(), e); } } - + println!("\n{} Check the following:", "HINT:".yellow()); println!(" • Your GEMINI_API_KEY is correctly set"); println!(" • Your internet connection is working"); diff --git a/src/prompt.rs b/src/prompt.rs index 26f61bd..f2f6a2a 100644 --- a/src/prompt.rs +++ b/src/prompt.rs @@ -10,7 +10,10 @@ pub struct Prompter; impl Prompter { pub fn prompt_api_key() -> Result> { println!(); - println!("Get your API key at: {}", "https://ai.google.dev/".cyan().underline()); + println!( + "Get your API key at: {}", + "https://ai.google.dev/".cyan().underline() + ); println!("Enter your API Key (starts with 'AIza'):"); let mut attempts = 0; @@ -29,7 +32,10 @@ impl Prompter { } attempts += 1; - Self::print_validation_error("Invalid API key format. Must start with 'AIza' and be around 39 characters.", attempts); + Self::print_validation_error( + "Invalid API key format. Must start with 'AIza' and be around 39 characters.", + attempts, + ); } Err("Max retries exceeded. Please run again with a valid API key.".into()) @@ -102,10 +108,7 @@ impl Prompter { } pub fn validate_api_key(key: &str) -> bool { - !key.is_empty() - && key.starts_with("AIza") - && key.len() >= 35 - && key.len() <= 50 + !key.is_empty() && key.starts_with("AIza") && key.len() >= 35 && key.len() <= 50 } pub fn validate_folder_path(path: &Path) -> bool { @@ -120,9 +123,10 @@ impl Prompter { pub fn expand_home(path: &str) -> String { if path.starts_with("~/") - && let Some(base_dirs) = BaseDirs::new() { - let home = base_dirs.home_dir(); - return path.replacen("~", &home.to_string_lossy(), 1); + && let Some(base_dirs) = BaseDirs::new() + { + let home = base_dirs.home_dir(); + return path.replacen("~", &home.to_string_lossy(), 1); } path.to_string() }