summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-11-18 21:56:51 +0300
committerNamilskyy <alive6863@gmail.com>2025-11-18 22:03:57 +0300
commit26266d70fcd7255fbafded9020437eb17bff5457 (patch)
treefbd4353279e3df2b6e62fd91d456feac8d86031d /src
parent078f43e3257017d271889ae2383b8984cda8aac2 (diff)
Specified no subcommand help text
Diffstat (limited to 'src')
-rw-r--r--src/main.rs124
-rw-r--r--src/parser.rs103
-rw-r--r--src/shared.rs8
3 files changed, 135 insertions, 100 deletions
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<dyn std::error::Error>> {
- let _cfg: Config = get_config()?;
+ let _cfg: Config = get_config()?;
println!("Config is valid");
- Ok(())
+ Ok(())
}
-fn parse_cached() -> Result<WeatherData, Box<dyn std::error::Error>> {
+fn parse_cached() -> Result<WeatherData, Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
let home = std::env::var("HOME")?;
let cache_path = format!("{}/.cache/WeatherFetch/weather.json", home);
@@ -97,15 +98,15 @@ fn clean_cache() -> Result<(), Box<dyn std::error::Error>> {
fn rebuild_cache() -> Result<(), Box<dyn std::error::Error>> {
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<String> {
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<String> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
println!("{:<width$} {}", art_part, table_part, width = max_art_width);
}
Ok(())
- },
+ }
Some(Commands::Tomorrow) => {
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<String, Box<dyn std::error::Error>> {
let home = std::env::var("HOME")?;
Ok(format!("{}/.config/WeatherFetch/Config.toml", home))
}
-
pub async fn parse_weather() -> Result<WeatherData, Box<dyn std::error::Error>> {
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<Config, Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
+*/
+
+/// 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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<u32>) -> &'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<u32>) -> &'static str
"sun"
}
-/// Arts loader with exception wrappers
+/// Arts loader with exception wrappers
fn load_arts() -> Result<ArtsData, Box<dyn std::error::Error>> {
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<String, Box<dyn std::error::Error>> {
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<f32>,
}
-/// WeatherData.hourly_units = ts struct
+/// WeatherData.hourly_units = ts struct
#[derive(Debug, Serialize, Deserialize)]
pub struct HourlyUnits {
pub time: Option<String>,