Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ thiserror = "2.0"
directories = "6.0"
capitalize = "0.3"
enum_dispatch = "0.3"
crossterm = "0.28"

[dev-dependencies]
test-case = "3"
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,17 @@ lon = -0.1278
#### Live mode

Live mode can be enabled with `live_mode = true` to update weather data every `live_mode_interval` seconds
(default is 300 seconds, i.e., 5 minutes)
(default is 300 seconds, i.e., 5 minutes). It uses the terminal's alternate screen buffer, so the original
screen is restored on exit. While in live mode, press `q`, `Esc`, or `Ctrl+C` to quit, or `r` to force an
immediate refresh.

A footer with key hints and the last-update timestamp is shown by default. Set `live_mode_footer = false`
(or pass `--no-footer` on the command line) to hide it.

```toml
live_mode = false
live_mode_interval = 300
live_mode_footer = true
```

---
Expand Down Expand Up @@ -270,6 +276,8 @@ Options:
Live mode - continuously update weather data every 5 minutes (or specified interval)
-i, --interval <LIVE_MODE_INTERVAL>
Live mode update interval in seconds (default: 300)
--no-footer
Hide the live-mode footer (key hints + last-update time)
--no-cache
Disable caching of geocoding results
--clear-cache
Expand Down
50 changes: 27 additions & 23 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use crate::config::{Cli, Config};
use crate::display::formatter::WeatherFormatter;
use crate::errors::RustormyError;
use crate::models::Provider;
use crate::live::run as run_live;
use crate::models::{Provider, Weather};
use crate::weather::{GetWeather, GetWeatherProvider};
use reqwest::blocking::Client;
use std::time::Duration;

fn clear_screen() {
print!("\x1B[2J\x1B[1;1H\x1B[?25l");
std::io::Write::flush(&mut std::io::stdout()).ok();
}

pub struct App {
client: Client,
config: Config,
Expand All @@ -35,38 +31,46 @@ impl App {
})
}

pub fn run(&mut self) {
pub fn config(&self) -> &Config {
&self.config
}

pub fn formatter(&self) -> &WeatherFormatter {
&self.formatter
}

pub fn fetch_with_fallback(&mut self) -> Result<Weather, RustormyError> {
loop {
match self.provider.get_weather(&self.client, &self.config) {
Ok(weather) => {
if self.config.live_mode() {
clear_screen();
}
self.formatter.display(weather);
}
Ok(weather) => return Ok(weather),
Err(error) => match error {
RustormyError::ApiReturnedError(_) | RustormyError::HttpRequestFailed(_) => {
let p: Provider = (&self.provider).into();
if self.config.verbose() >= 1 {
// TODO: Log this instead of printing to stderr
eprintln!("Provider {p:?} failed: {error:?}");
}
let Some(next) = self.config.take_next_provider() else {
self.formatter.display_error(&error);
return Err(error);
};
self.provider = GetWeatherProvider::new(next);
continue;
}
_ => {
self.formatter.display_error(&error);
}
_ => return Err(error),
},
}
if !self.config.live_mode() {
break;
}
}

pub fn run(&mut self) {
if self.config.live_mode() {
if let Err(error) = run_live(self) {
self.formatter.display_error(&error);
}
let sleep_duration = Duration::from_secs(self.config.live_mode_interval());
std::thread::sleep(sleep_duration);
return;
}

match self.fetch_with_fallback() {
Ok(weather) => self.formatter.display(&weather),
Err(error) => self.formatter.display_error(&error),
}
}
}
4 changes: 4 additions & 0 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub struct Cli {
)]
pub live_mode_interval: Option<u64>, // in seconds, default to 300 (5 minutes)

/// Hide the live-mode footer (key hints + last-update time)
#[arg(long="no-footer", requires = "live_mode", action = ArgAction::SetTrue)]
pub no_footer: bool,

/// Disable caching of geocoding results
#[arg(long, action = ArgAction::SetTrue)]
pub no_cache: bool,
Expand Down
Loading
Loading