diff --git a/Cargo.lock b/Cargo.lock index 1a334e9..b74222f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -106,6 +106,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -131,6 +152,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.3.3" @@ -209,15 +241,22 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "open-meteo-cli" -version = "0.1.3" +version = "0.1.4" dependencies = [ "billboard", + "directories", "serde", "serde_json", "structopt", "ureq", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "percent-encoding" version = "2.3.0" @@ -266,6 +305,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "ring" version = "0.16.20" @@ -331,9 +390,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ "itoa", "ryu", @@ -532,6 +591,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -643,7 +708,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -652,13 +726,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -667,38 +756,80 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index 04e4fd5..3fab334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ serde = {version = "1.0.163", features = ["derive"]} serde_json = "1.0.96" ureq = {version = "2.6.2", features = ["json"]} billboard = "0.2" +directories = "5.0.1" diff --git a/src/ip_api.rs b/src/ip_api.rs index 0618941..8bdcc36 100644 --- a/src/ip_api.rs +++ b/src/ip_api.rs @@ -1,5 +1,9 @@ -pub fn extract_coords_and_city(ip: &str) -> Result { +use crate::ip_location::IpLocation; + +pub fn extract_coords_and_city(ip: &str) -> Result { let url = format!("http://ip-api.com/json/{}?fields=16592", ip); let body: serde_json::Value = ureq::get(&url).call()?.into_json()?; - Ok(body) + + let current_weather: IpLocation = serde_json::from_value(body).unwrap(); + Ok(current_weather) } diff --git a/src/ip_location.rs b/src/ip_location.rs new file mode 100644 index 0000000..0498027 --- /dev/null +++ b/src/ip_location.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct IpLocation { + pub lat: f64, + pub lon: f64, + pub city: String, +} diff --git a/src/ip_location_cache.rs b/src/ip_location_cache.rs new file mode 100644 index 0000000..d8acbb6 --- /dev/null +++ b/src/ip_location_cache.rs @@ -0,0 +1,68 @@ +use std::{ + fs::{create_dir_all, File}, + io::{Read, Write}, + path::PathBuf, +}; + +use crate::ip_location::IpLocation; +use directories::ProjectDirs; +pub fn save_to_cache(ip: &str, data: &IpLocation) { + let cache_dir = get_cache_dir(); + if cache_dir.is_none() { + return; + } + + let mut cache_dir = cache_dir.unwrap(); + cache_dir.push(format!("{}.{}", ip, "json")); + + let json = serde_json::to_string(data).expect("Error al serializar a JSON"); + let mut file = File::create(&cache_dir).expect("Error al crear el archivo"); + file.write_all(json.as_bytes()) + .expect("Error al escribir en el archivo"); +} + +pub fn get_from_cache(ip: &str) -> Option { + let cache_dir = get_cache_dir(); + if cache_dir.is_none() { + return None; + } + + let mut cache_dir = cache_dir.unwrap(); + cache_dir.push(format!("{}.{}", ip, "json")); + + if !is_in_cache(&cache_dir) { + return None; + } + + let mut file = File::open(&cache_dir).expect("Error al abrir el archivo"); + let mut content = String::new(); + file.read_to_string(&mut content) + .expect("Error al leer el archivo"); + + let ip_location: IpLocation = + serde_json::from_str(&content).expect("Error al deserializar JSON"); + Some(ip_location) +} + +fn is_in_cache(path: &PathBuf) -> bool { + path.is_file() +} + +fn get_cache_dir() -> Option { + let dir = ProjectDirs::from("com", "midefos", "open-meteo-cli"); + if dir.is_some() { + let project_dir = dir.unwrap(); + + let cache_dir = project_dir.cache_dir(); + if !cache_dir.exists() { + let res = create_dir_all(cache_dir); + if res.is_err() { + return None; + } + } + + Some(cache_dir.to_owned()) + } else { + None + } +} diff --git a/src/main.rs b/src/main.rs index cdf22ad..a37e322 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,25 +4,26 @@ mod current_weather; mod current_weather_extractor; mod current_weather_output; mod current_weather_output_model; - mod current_weather_print_params; mod data_format; mod ifconfig; mod ip_api; +mod ip_location; +mod ip_location_cache; mod open_meteo; mod speed_unit; mod temp_unit; use billboard::{Alignment, Billboard}; -use current_weather_print_params::CurrentWeatherPrintParams; use data_format::DataFormat; +use ip_location_cache::{get_from_cache, save_to_cache}; use std::process::exit; use structopt::StructOpt; use cli::Arguments; use coords::Coordinates; - use current_weather_extractor::CurrentWeatherExtractor; +use current_weather_print_params::CurrentWeatherPrintParams; use ifconfig::extract_public_ip; use ip_api::extract_coords_and_city; use open_meteo::request_current_weather; @@ -82,16 +83,24 @@ fn main() { exit(1); }); - let coords_result = extract_coords_and_city(&ip); - let coords = coords_result.unwrap_or_else(|_| { - eprintln!("[ERROR] Could not extract IP coordinates from ip_api"); - exit(1); - }); + let ip_location; + let cache_ip_location = get_from_cache(&ip); - let latitude = coords["lat"].as_f64().unwrap(); - let longitude = coords["lon"].as_f64().unwrap(); + if cache_ip_location.is_some() { + ip_location = cache_ip_location.unwrap(); + } else { + let ip_location_res = extract_coords_and_city(&ip); + ip_location = ip_location_res.unwrap_or_else(|_| { + eprintln!("[ERROR] Could not extract IP location from ip_api"); + exit(1); + }); + save_to_cache(&ip, &ip_location); + } + + let latitude = ip_location.lat; + let longitude = ip_location.lon; coordinates = Coordinates::new(latitude, longitude); - city = Some(coords["city"].as_str().unwrap().to_string()); + city = Some(ip_location.city); } let result = request_current_weather(&coordinates, &temperature_unit, &speed_unit);