Skip to content

Commit 6cf0ab1

Browse files
committed
💥 forecast as option (#38)
improves accessibility, but reduces further extensibility of the forecast sub-command. Considering the rather small end product this app aims to be, this is a fair trade. The change can be seen as a breaking change as the weather forecast is now accessed without flags e.g.,: `-f w` | `-f d` | `-f w,d` instead of e.g., `-f -w`,`-f` solely.
1 parent 4469cc6 commit 6cf0ab1

File tree

7 files changed

+99
-169
lines changed

7 files changed

+99
-169
lines changed

src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::Result;
22
use clap::Parser;
33

4+
use modules::config::Config;
45
use modules::*;
56
use modules::{args::Cli, display::Product, location::Geolocation, params::Params, weather::Weather};
67

@@ -9,8 +10,8 @@ mod modules;
910
#[tokio::main]
1011
async fn main() -> Result<()> {
1112
let args = Cli::parse();
12-
let config = confy::load("weathercrab", "wthrr")?;
13-
let params = Params::get(&args, &config).await?;
13+
let config: Config = confy::load("weathercrab", "wthrr")?;
14+
let params = Params::get(&args, config.clone()).await?;
1415

1516
let product = run(&params).await?;
1617
product

src/modules/args.rs

+35-25
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,61 @@
1-
use clap::{Args, Parser, Subcommand, ValueEnum};
1+
// use clap::{Args, Parser, Subcommand, ValueEnum};
2+
use clap::{Parser, ValueEnum};
23
use serde::{Deserialize, Serialize};
34
use strum_macros::AsRefStr;
45

56
#[derive(Parser)]
6-
#[command(author, version, about, long_about = None)]
7+
#[command(author, version, about, long_about = None, next_line_help = true)]
78
pub struct Cli {
89
/// Address to check the weather for
910
pub address: Option<String>,
1011

11-
#[command(subcommand)]
12-
pub commands: Option<Commands>,
12+
/// [e.g.: -f w,d]
13+
#[arg(long, short, use_value_delimiter = true, value_name = "FORECAST,...")]
14+
pub forecast: Vec<Forecast>,
1315

14-
/// Units for temperature and/or speed
15-
#[arg(long, short, next_line_help = false, use_value_delimiter = true)]
16+
/// [e.g.: -u f,12h]
17+
#[arg(long, short, use_value_delimiter = true, value_name = "UNIT,...")]
1618
pub units: Vec<ArgUnits>,
1719

18-
/// Output language
20+
/// Output language [e.g.: en_US]
1921
#[arg(short, long, global = true)]
2022
pub language: Option<String>,
2123

2224
/// Toggle greeting message
23-
#[arg(short, long, action, global = true)]
25+
#[arg(short, long, action, hide = true)]
2426
pub greeting: bool,
2527

2628
/// Save the supplied values as default
27-
#[arg(short, long, action, group = "config_file_action", global = true)]
29+
#[arg(short, long, action, group = "config_file_action")]
2830
pub save: bool,
2931

3032
/// Wipe wthrr's configuration data
31-
#[arg(short, long, action, group = "config_file_action", global = true)]
33+
#[arg(short, long, action, group = "config_file_action")]
3234
pub reset: bool,
3335
}
3436

35-
#[derive(Subcommand)]
36-
pub enum Commands {
37-
/// Include the weather forecast
38-
#[clap(short_flag = 'f')]
39-
Forecast(Forecast),
40-
}
41-
42-
#[derive(Debug, Args)]
43-
pub struct Forecast {
44-
/// Show the seven day forecast
45-
#[arg(short, value_parser, action)]
46-
pub week: bool,
47-
/// Show the forecast for the day
48-
#[arg(short, value_parser, action)]
49-
pub day: bool,
37+
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, AsRefStr, Serialize, Deserialize)]
38+
#[allow(non_camel_case_types)]
39+
pub enum Forecast {
40+
disable,
41+
#[value(name = "(w)eek", aliases = ["w", "week"])]
42+
week,
43+
#[value(name = "(d)ay", aliases = ["d", "day", "today"])]
44+
day,
45+
// #[value(alias = "monday")]
46+
// mo,
47+
// #[value(alias = "tuesday")]
48+
// tu,
49+
// #[value(alias = "wednesday")]
50+
// we,
51+
// #[value(alias = "thursday")]
52+
// th,
53+
// #[value(alias = "friday")]
54+
// fr,
55+
// #[value(alias = "saturday")]
56+
// sa,
57+
// #[value(alias = "sunday")]
58+
// su,
5059
}
5160

5261
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, AsRefStr, Serialize, Deserialize)]
@@ -70,5 +79,6 @@ pub enum ArgUnits {
7079
#[value(name = "24h", alias = "military")]
7180
Military,
7281
Mm,
82+
#[value(name = "(in)ch", alias = "in")]
7383
Inch,
7484
}

src/modules/config.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,27 @@ use dialoguer::{theme::ColorfulTheme, Confirm, Select};
33
use serde::{Deserialize, Serialize};
44

55
use crate::{
6-
args::Cli,
7-
params::{forecast::Forecast, units::Units, Params},
6+
args::{Cli, Forecast},
7+
params::{units::Units, Params},
88
translation::translate,
99
};
1010

11-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
11+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
1212
pub struct Config {
1313
pub address: Option<String>,
1414
pub greeting: Option<bool>,
1515
pub language: Option<String>,
16+
pub forecast: Option<Vec<Forecast>>,
1617
pub units: Option<Units>,
17-
pub forecast: Option<Forecast>,
1818
}
1919

2020
impl Default for Config {
2121
fn default() -> Self {
2222
Self {
2323
address: None,
24+
forecast: None,
2425
greeting: Some(true),
2526
language: Some("en".to_string()),
26-
forecast: Some(Forecast::default()),
2727
units: Some(Units::default()),
2828
}
2929
}
@@ -43,7 +43,11 @@ impl Config {
4343
},
4444
greeting: Some(params.greeting),
4545
language: Some(params.language),
46-
forecast: Some(params.forecast),
46+
forecast: if !params.forecast.is_empty() {
47+
Some(params.forecast)
48+
} else {
49+
None
50+
},
4751
units: Some(params.units),
4852
};
4953

src/modules/display.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use term_painter::{
55
ToStyle,
66
};
77

8-
use crate::{
9-
params::{forecast::Forecast as ForecastParams, units::Units},
10-
weather::Weather,
11-
};
8+
use crate::{args::Forecast as ForecastParams, params::units::Units, weather::Weather};
129

1310
use self::{current::Current, forecast::Forecast};
1411

@@ -31,14 +28,14 @@ pub const MIN_WIDTH: usize = 34;
3128
impl Product {
3229
pub async fn render(
3330
&self,
34-
forecast: &ForecastParams,
31+
forecast: &[ForecastParams],
3532
units: &Units,
3633
include_greeting: bool,
3734
lang: &str,
3835
) -> Result<()> {
3936
greeting::render(include_greeting, lang).await?;
4037

41-
if forecast.week.unwrap_or_default() || forecast.day.unwrap_or_default() {
38+
if !forecast.is_empty() {
4239
Forecast::render(self, forecast, units, lang).await?;
4340
} else {
4441
Current::render(self, false, units, lang).await?;

src/modules/display/forecast.rs

+25-27
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use chrono::prelude::*;
44
use serde::{Deserialize, Serialize};
55
use term_painter::{Color::*, ToStyle};
66

7-
use crate::params::{forecast::Forecast as ForecastParams, units::Units};
7+
use crate::{args::Forecast as ForecastParams, params::units::Units};
88

99
use super::{
1010
border::{Border, Separator},
@@ -28,40 +28,38 @@ pub struct ForecastDay {
2828
}
2929

3030
impl Forecast {
31-
pub async fn render(product: &Product, forecast_args: &ForecastParams, units: &Units, lang: &str) -> Result<()> {
31+
pub async fn render(product: &Product, forecast_args: &[ForecastParams], units: &Units, lang: &str) -> Result<()> {
3232
let forecast = Self::prepare(product, lang).await?;
3333
let mut width = forecast.width + 10;
3434
let mut cell_width = MIN_WIDTH / 2;
3535

36-
match forecast_args {
37-
// Only display daily forecast
38-
ForecastParams {
39-
day: Some(true),
40-
week: Some(false),
41-
} => {
42-
Current::render(product, true, units, lang).await?;
36+
let (mut include_day, mut include_week) = (false, false);
37+
for val in forecast_args {
38+
if ForecastParams::disable == *val {
39+
Current::render(product, false, units, lang).await?;
4340
return Ok(());
4441
}
45-
// Display both forecasts if no or both forecast subcommand flags are added
46-
ForecastParams {
47-
day: Some(false),
48-
week: Some(false),
42+
if ForecastParams::day == *val {
43+
include_day = true;
4944
}
50-
| ForecastParams {
51-
day: Some(true),
52-
week: Some(true),
53-
} => {
54-
// Align dimensions of daily and weekly forecast
55-
let dimensions_current = Current::render(product, true, units, lang).await?;
56-
57-
if dimensions_current.cell_width > cell_width {
58-
cell_width = dimensions_current.cell_width
59-
}
60-
if dimensions_current.width > width {
61-
width = dimensions_current.width
62-
}
45+
if ForecastParams::week == *val {
46+
include_week = true;
6347
}
64-
_ => {}
48+
}
49+
50+
if include_day {
51+
let dimensions_current = Current::render(product, true, units, lang).await?;
52+
53+
if dimensions_current.cell_width > cell_width {
54+
cell_width = dimensions_current.cell_width
55+
}
56+
if dimensions_current.width > width {
57+
width = dimensions_current.width
58+
}
59+
}
60+
61+
if !include_week {
62+
return Ok(());
6563
}
6664

6765
// Border Top

src/modules/params.rs

+8-16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use anyhow::Result;
22

3-
use crate::{args::Cli, config::Config};
3+
use crate::{
4+
args::{Cli, Forecast},
5+
config::Config,
6+
};
47

5-
use self::{forecast::Forecast, units::Units};
6-
7-
use super::args::Commands;
8+
use self::units::Units;
89

910
mod address;
1011
pub mod forecast;
@@ -17,26 +18,17 @@ pub struct Params {
1718
pub units: Units,
1819
pub greeting: bool,
1920
pub language: String,
20-
pub forecast: Forecast,
21+
pub forecast: Vec<Forecast>,
2122
}
2223

2324
impl Params {
24-
pub async fn get(args: &Cli, config: &Config) -> Result<Self> {
25+
pub async fn get(args: &Cli, config: Config) -> Result<Self> {
2526
let language = language::get(
2627
args.language.as_deref().unwrap_or_default(),
2728
config.language.as_deref().unwrap_or_default(),
2829
)?;
2930

30-
let forecast = forecast::get(
31-
match &args.commands {
32-
Some(Commands::Forecast(args_forecast)) => Some(Forecast {
33-
day: Some(args_forecast.day),
34-
week: Some(args_forecast.week),
35-
}),
36-
_ => None,
37-
},
38-
config.forecast,
39-
)?;
31+
let forecast = forecast::get(&args.forecast, config.forecast)?;
4032

4133
if args.reset {
4234
Config::reset(&language).await?;

0 commit comments

Comments
 (0)