From 26266d70fcd7255fbafded9020437eb17bff5457 Mon Sep 17 00:00:00 2001 From: Namilskyy Date: Tue, 18 Nov 2025 21:56:51 +0300 Subject: Specified no subcommand help text --- src/main.rs | 124 +++++++++++++++++++++++++++++++++++----------------------- src/parser.rs | 103 +++++++++++++++++++++++++----------------------- src/shared.rs | 8 ++-- 3 files changed, 135 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 5079f34..dc6d371 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,18 +29,20 @@ SOFTWARE. */ -use std::fs::{self, File}; -use std::path::PathBuf; +use std::{ + fs::{self, File}, + path::PathBuf, +}; -use clap::{Parser, Subcommand}; +use clap::{Parser, Subcommand}; mod parser; mod shared; -use parser::{get_config, parse_weather, generate_config, Config}; +use parser::{generate_config, get_config, parse_weather, Config}; use shared::WeatherData; -use crate::parser::{determine_weather_type, prepare_art}; +use crate::parser::{determine_weather_type, prepare_art}; #[derive(Parser)] #[command(name = "wfetch")] @@ -52,36 +54,35 @@ struct Cli { #[derive(Subcommand)] enum Commands { - Config, + Config, Fetch, - Clean, - Today, - Tomorrow, + Clean, + Today, + Tomorrow, RebuildCache, - CheckCfg + CheckCfg, } -/// Micro config-validator, easily you can just `wfetch fetch` and see the error +/// Micro config-validator, easily you can just `wfetch fetch` and see the error fn process_config() -> Result<(), Box> { - let _cfg: Config = get_config()?; + let _cfg: Config = get_config()?; println!("Config is valid"); - Ok(()) + Ok(()) } -fn parse_cached() -> Result> { +fn parse_cached() -> Result> { let home = std::env::var("HOME")?; let cache_path = format!("{}/.cache/WeatherFetch/weather.json", home); - + if !PathBuf::from(&cache_path).exists() { return Err("Cache file not found".into()); } - + let cache_data = fs::read_to_string(&cache_path)?; let weather_data: WeatherData = serde_json::from_str(&cache_data)?; Ok(weather_data) } - fn clean_cache() -> Result<(), Box> { let home = std::env::var("HOME")?; let cache_path = format!("{}/.cache/WeatherFetch/weather.json", home); @@ -97,15 +98,15 @@ fn clean_cache() -> Result<(), Box> { fn rebuild_cache() -> Result<(), Box> { let rt = tokio::runtime::Runtime::new()?; let weather_data = rt.block_on(parse_weather())?; - + let home = std::env::var("HOME")?; let cache_dir = format!("{}/.cache/WeatherFetch", home); fs::create_dir_all(&cache_dir)?; - + let cache_path = format!("{}/weather.json", cache_dir); let json_data = serde_json::to_string_pretty(&weather_data)?; fs::write(&cache_path, json_data)?; - + println!("Cache rebuilt successfully"); Ok(()) } @@ -116,17 +117,38 @@ fn generate_weather_table_content(data: &WeatherData) -> Vec { lines.push("╔═══════════════════════════════════════╗".to_string()); lines.push("║ Today`s weather ║".to_string()); lines.push("╠═══════════════════════════════════════╣".to_string()); - lines.push(format!("║ Time: {} ║", format!("{:>28}", data.current.time))); - lines.push(format!("║ Temp: {}°C ║", format!("{:>22}", data.current.temperature_2m))); - lines.push(format!("║ Wind speed: {} m/s ║", format!("{:>19}", data.current.wind_speed_10m))); - lines.push(format!("║ Type: {} ║", determine_weather_type(data.current.temperature_2m, Some(data.hourly.relative_humidity_2m[0])))); + lines.push(format!( + "║ Time: {} ║", + format!("{:>28}", data.current.time) + )); + lines.push(format!( + "║ Temp: {}°C ║", + format!("{:>22}", data.current.temperature_2m) + )); + lines.push(format!( + "║ Wind speed: {} m/s ║", + format!("{:>19}", data.current.wind_speed_10m) + )); + lines.push(format!( + "║ Type: {} ║", + determine_weather_type( + data.current.temperature_2m, + Some(data.hourly.relative_humidity_2m[0]) + ) + )); if let Some(elevation) = data.elevation { lines.push(format!("║ Height: {} m ║", format!("{:>26}", elevation))); } if let Some(timezone) = &data.timezone { - lines.push(format!("║ Time: {} ║", format!("{:>22}", timezone))); + lines.push(format!( + "║ Time: {} ║", + format!("{:>22}", timezone) + )); } - lines.push(format!("║ Coords: {:.2}, {:.2}, ║", data.latitude, data.longitude)); + lines.push(format!( + "║ Coords: {:.2}, {:.2}, ║", + data.latitude, data.longitude + )); lines.push("╚═══════════════════════════════════════╝".to_string()); if !data.hourly.time.is_empty() { @@ -141,44 +163,49 @@ fn generate_weather_table_content(data: &WeatherData) -> Vec { let humidity = data.hourly.relative_humidity_2m[i]; let wind = data.hourly.wind_speed_10m[i]; - lines.push(format!("│ {:12} │ {:>10}°C │ {:>10}% │ {:>10} m/s │", - time, temp, humidity, wind)); + lines.push(format!( + "│ {:12} │ {:>10}°C │ {:>10}% │ {:>10} m/s │", + time, temp, humidity, wind + )); } lines.push("└──────────────────┴──────────────┴──────────────┴──────────────┘".to_string()); } - lines + lines } fn main() -> Result<(), Box> { let cli = Cli::parse(); - + match cli.command { Some(Commands::Config) => { println!("Config checker command"); let home = std::env::var("HOME")?; let config_path = format!("{}/.config/WeatherFetch/Config.toml", home); if File::open(&config_path).is_err() { - generate_config()?; - println!("Config generated successfully"); + generate_config()?; + println!("Config generated successfully"); } else { process_config()?; - println!("Config already exists and is valid."); + println!("Config already exists and is valid."); } Ok(()) - }, + } Some(Commands::Fetch) => { println!("Fetch weather-data command"); let rt = tokio::runtime::Runtime::new()?; let _weather_data = rt.block_on(parse_weather())?; - let home = std::env::var("HOME")?; - println!("Weather data fetched to: {}", format!("{}/.cache/WeatherFetch", home)); + let home = std::env::var("HOME")?; + println!( + "Weather data fetched to: {}", + format!("{}/.cache/WeatherFetch", home) + ); Ok(()) - }, + } Some(Commands::Clean) => { println!("Clean cache command"); clean_cache()?; Ok(()) - }, + } Some(Commands::Today) => { // to much vars let data: WeatherData = parse_cached()?; @@ -198,33 +225,34 @@ fn main() -> Result<(), Box> { println!("{: { println!("Tomorrow weather command"); Ok(()) - }, + } Some(Commands::RebuildCache) => { println!("Rebuild cache command"); rebuild_cache()?; Ok(()) - }, + } Some(Commands::CheckCfg) => { - println!("Validating cfg..."); + println!("Validating cfg..."); let home = std::env::var("HOME")?; let config_path = format!("{}/.config/WeatherFetch/Config.toml", home); if File::open(&config_path).is_ok() { - process_config()?; + process_config()?; } else { - println!("Config file not found, try `wfetch config`, its will generate default cfg."); + println!( + "Config file not found, try `wfetch config`, its will generate default cfg." + ); } Ok(()) } None => { println!("No subcommand specified."); - println!("Run `wfetch -h` or `wfetch help`"); + println!("Run `wfetch -h` or `wfetch help`"); println!("to see help message."); Ok(()) - }, + } } - -} \ No newline at end of file +} diff --git a/src/parser.rs b/src/parser.rs index daa9059..ab1db94 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,93 +1,98 @@ +use std::{ + fs::{self, File}, + io::Read, +}; + use reqwest::Client; -use std::fs::{self, File}; -use std::io::Read; -use toml; +use toml; use serde::{Deserialize, Serialize}; use serde_json; use serde_yml; // use crate::configmanager::Config; -use crate::shared::*; +use crate::shared::*; fn get_config_path() -> Result> { let home = std::env::var("HOME")?; Ok(format!("{}/.config/WeatherFetch/Config.toml", home)) } - pub async fn parse_weather() -> Result> { let config = get_config()?; let client = Client::new(); - let response = client.get("https://api.open-meteo.com/v1/forecast") + let response = client + .get("https://api.open-meteo.com/v1/forecast") .query(&[ ("latitude", config.lat.to_string()), ("longitude", config.lon.to_string()), ("current", "temperature_2m,wind_speed_10m".to_string()), - ("hourly", "temperature_2m,relative_humidity_2m,wind_speed_10m".to_string()), + ( + "hourly", + "temperature_2m,relative_humidity_2m,wind_speed_10m".to_string(), + ), ]) .send() .await?; - + let response_text = response.text().await?; let weather_data: WeatherData = serde_json::from_str(&response_text)?; - + let home = std::env::var("HOME")?; let cache_dir = format!("{}/.cache/WeatherFetch", home); fs::create_dir_all(&cache_dir)?; - + let cache_path = format!("{}/weather.json", cache_dir); let json_data = serde_json::to_string_pretty(&weather_data)?; fs::write(&cache_path, json_data)?; - + Ok(weather_data) } - -// TODO: Add exclude processing +// TODO: Add exclude processing #[derive(Debug, Deserialize)] pub struct Config { lat: f64, lon: f64, - exclude: String + exclude: String, } pub fn get_config() -> Result> { let config_path = get_config_path()?; - + if File::open(&config_path).is_err() { - generate_config()?; + generate_config()?; } - + let mut file = File::open(&config_path)?; let mut content = String::new(); file.read_to_string(&mut content)?; - let config: Config = toml::from_str(&content)?; + let config: Config = toml::from_str(&content)?; Ok(config) } -/* +/* pub fn generate_cachedir() -> Result<(), Box> { let home = std::env::var("HOME")?; let cache_path = format!("{}/.cache/WeatherFetch/", home); - - let _ = fs::create_dir(cache_path); + + let _ = fs::create_dir(cache_path); Ok(()) } -*/ - -/// Just writing example config to ~/.config/WeatherFetch/Config.toml -/// let _ = generate_config() -/// Result: ~/.config/WeatherFetch/Config.toml: -/// lat = 55.75 -/// lon = 37.62 -/// exclude = "" -pub fn generate_config() -> Result<(), Box> { +*/ + +/// Just writing example config to ~/.config/WeatherFetch/Config.toml +/// let _ = generate_config() +/// Result: ~/.config/WeatherFetch/Config.toml: +/// lat = 55.75 +/// lon = 37.62 +/// exclude = "" +pub fn generate_config() -> Result<(), Box> { let config_path = get_config_path()?; - let config = "lat = 55.75\nlon = 37.62\nexclude = \"\""; + let config = "lat = 55.75\nlon = 37.62\nexclude = \"\""; let path = std::path::Path::new(&config_path); if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; @@ -97,14 +102,14 @@ pub fn generate_config() -> Result<(), Box> { Ok(()) } -/// For usage like type in `let` +/// For usage like type in `let` #[derive(Serialize, Deserialize, Debug)] struct Arts { #[serde(rename = "Arts")] arts: ArtsData, } -/// Strings with arts from arts.yaml +/// Strings with arts from arts.yaml #[derive(Serialize, Deserialize, Debug)] struct ArtsData { sun: String, @@ -117,7 +122,7 @@ pub fn determine_weather_type(temp: f32, humidity: Option) -> &'static str if temp < 0.0 { return "snow"; } - + if let Some(hum) = humidity { if hum > 70 && temp < 25.0 { return "rain"; @@ -127,23 +132,26 @@ pub fn determine_weather_type(temp: f32, humidity: Option) -> &'static str "sun" } -/// Arts loader with exception wrappers +/// Arts loader with exception wrappers fn load_arts() -> Result> { let home = std::env::var("HOME")?; let arts_path = format!("{}/.config/WeatherFetch/arts.yaml", home); - + let content = match fs::read_to_string(&arts_path) { Ok(c) => c, Err(_) => { // if arts.yaml not found return Ok(ArtsData { - sun: "☀️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml".to_string(), - snow: "❄️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml".to_string(), - rain: "🌧️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml".to_string(), + sun: "☀️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml" + .to_string(), + snow: "❄️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml" + .to_string(), + rain: "🌧️ - ts emoji means program cant found ~/.config/WeatherFetch/arts.yaml" + .to_string(), }); } }; - + let arts: Arts = serde_yml::from_str(&content)?; Ok(arts.arts) } @@ -152,27 +160,26 @@ fn process_placeholders(art: &str) -> String { art.replace("{0}", "") } -/// Choosing and retuns art (String) -/// Usage: +/// Choosing and retuns art (String) +/// Usage: /// let data: WeatherData = parse_cached()?; -/// prepare_art(&data); +/// prepare_art(&data); pub fn prepare_art(weather_data: &WeatherData) -> Result> { let arts = load_arts()?; - + let weather_type = determine_weather_type( weather_data.current.temperature_2m, weather_data.hourly.relative_humidity_2m.first().copied(), ); - + let selected_art = match weather_type { "snow" => &arts.snow, "rain" => &arts.rain, "sun" => &arts.sun, _ => &arts.sun, }; - + let processed_art = process_placeholders(selected_art); - + Ok(processed_art) } - diff --git a/src/shared.rs b/src/shared.rs index 43589aa..ce2163d 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,7 +1,7 @@ -use serde::{Serialize, Deserialize}; -// API answer struct`s +use serde::{Deserialize, Serialize}; +// API answer struct`s -/// Main struct with fetched weather data +/// Main struct with fetched weather data #[derive(Debug, Serialize, Deserialize)] pub struct WeatherData { pub latitude: f64, @@ -42,7 +42,7 @@ pub struct Hourly { pub wind_speed_10m: Vec, } -/// WeatherData.hourly_units = ts struct +/// WeatherData.hourly_units = ts struct #[derive(Debug, Serialize, Deserialize)] pub struct HourlyUnits { pub time: Option, -- cgit v1.2.3