Skip to content

Commit 8396ce0

Browse files
committed
what a mess, stash!
1 parent 7cd6117 commit 8396ce0

File tree

20 files changed

+450
-863
lines changed

20 files changed

+450
-863
lines changed

rim_common/src/types/configuration.rs

Lines changed: 88 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,13 @@
44
use crate::setter;
55
use crate::{dirs::rim_config_dir, types::TomlParser};
66
use anyhow::Result;
7-
use chrono::{NaiveDateTime, Utc};
87
use serde::{Deserialize, Serialize};
98
use std::str::FromStr;
10-
use std::{collections::HashMap, fmt::Display, time::Duration};
119

12-
/// Default update check timeout is 1440 minutes (1 day)
13-
const DEFAULT_UPDATE_CHECK_TIMEOUT_IN_MINUTES: u64 = 1440;
14-
/// Default update check timeout in duration
15-
pub const DEFAULT_UPDATE_CHECK_DURATION: Duration =
16-
Duration::from_secs(60 * DEFAULT_UPDATE_CHECK_TIMEOUT_IN_MINUTES);
17-
18-
#[derive(Debug, Deserialize, Serialize, Default)]
10+
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
1911
pub struct Configuration {
2012
pub language: Option<Language>,
21-
pub update: UpdateCheckerOpt,
13+
pub update: UpdateConfig,
2214
}
2315

2416
impl TomlParser for Configuration {
@@ -30,15 +22,6 @@ impl Configuration {
3022
Self::default()
3123
}
3224

33-
/// Mark a version as skipped.
34-
///
35-
/// This function can be chained.
36-
pub fn skip_update<T: Into<String>>(mut self, target: UpdateTarget, version: T) -> Self {
37-
let conf = self.update.conf_mut(target);
38-
conf.skip = Some(version.into());
39-
self
40-
}
41-
4225
/// Try loading from [`rim_config_dir`], return `None` if it doesn't exists yet.
4326
pub fn try_load_from_config_dir() -> Option<Self> {
4427
Self::load_from_dir(rim_config_dir()).ok()
@@ -57,11 +40,19 @@ impl Configuration {
5740
self.write_to_dir(rim_config_dir())
5841
}
5942

60-
pub fn update_skipped<T: AsRef<str>>(&self, target: UpdateTarget, version: T) -> bool {
61-
self.update.is_skipped(target, version)
62-
}
63-
64-
setter!(set_language(self.language, Option<Language>));
43+
setter!(set_language(self.language, val: Language) { Some(val) });
44+
setter!(set_manager_update_channel(
45+
self.update.manager_update_channel,
46+
UpdateChannel
47+
));
48+
setter!(set_auto_check_manager_updates(
49+
self.update.auto_check_manager_updates,
50+
bool
51+
));
52+
setter!(set_auto_check_toolkit_updates(
53+
self.update.auto_check_toolkit_updates,
54+
bool
55+
));
6556
}
6657

6758
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq, Clone, Copy)]
@@ -107,186 +98,118 @@ impl FromStr for Language {
10798
}
10899
}
109100

110-
// If we ever need to support more things for update checker,
111-
// just add one in this enum, without breaking compatibility.
112-
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)]
113-
#[serde(rename_all = "kebab-case")]
114-
pub enum UpdateTarget {
115-
Manager,
116-
Toolkit,
101+
/// App update channel.
102+
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
103+
#[non_exhaustive]
104+
pub enum UpdateChannel {
105+
#[default]
106+
Stable,
107+
Beta,
117108
}
118109

119-
// The display implementation must return the same result as
120-
// serde's serialization, which means it should be in 'kebab-case' as well.
121-
impl Display for UpdateTarget {
122-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123-
f.write_str(match self {
124-
Self::Manager => "manager",
125-
Self::Toolkit => "toolkit",
126-
})
127-
}
110+
/// Representing the configuration for update checker.
111+
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
112+
#[serde(rename_all = "kebab-case")]
113+
pub struct UpdateConfig {
114+
/// Channel of manager (app) updates to check, i.e. "stable", "beta"
115+
#[serde(default)]
116+
pub manager_update_channel: UpdateChannel,
117+
/// Automatically checking for manager (app) updates.
118+
#[serde(default = "bool_true")]
119+
pub auto_check_manager_updates: bool,
120+
/// Automatically checking for toolkit updates.
121+
#[serde(default = "bool_true")]
122+
pub auto_check_toolkit_updates: bool,
128123
}
129124

130-
#[derive(Debug, Deserialize, Serialize)]
131-
#[serde(rename_all = "kebab-case")]
132-
pub struct UpdateConf {
133-
/// The datetime when the last update check happened,
134-
/// defaulting to [`UNIX_EPOCH`](NaiveDateTime::UNIX_EPOCH)
135-
last_run: NaiveDateTime,
136-
/// Timeout (in minutes) until next update check
137-
timeout: Option<u64>,
138-
/// The specific version to disable auto check.
139-
///
140-
/// If there's a newer version available, it will still
141-
skip: Option<String>,
125+
// Return `true` for the serde default arg.
126+
// really? there's no better way?
127+
fn bool_true() -> bool {
128+
true
142129
}
143130

144-
impl Default for UpdateConf {
131+
impl Default for UpdateConfig {
145132
fn default() -> Self {
146133
Self {
147-
last_run: NaiveDateTime::default(),
148-
timeout: Some(DEFAULT_UPDATE_CHECK_TIMEOUT_IN_MINUTES),
149-
skip: None,
134+
manager_update_channel: UpdateChannel::default(),
135+
auto_check_manager_updates: true,
136+
auto_check_toolkit_updates: true,
150137
}
151138
}
152139
}
153140

154-
impl UpdateConf {
155-
/// Get the timeout in duration until next update check.
156-
///
157-
/// Default is 1 day.
158-
fn timeout(&self) -> Duration {
159-
self.timeout
160-
.map(|timeout_in_minutes| Duration::from_secs(timeout_in_minutes * 60))
161-
.unwrap_or(DEFAULT_UPDATE_CHECK_DURATION)
162-
}
163-
}
164-
165-
/// Representing the configuration for update checker.
166-
///
167-
/// Containing information about what version to skip (by the user),
168-
/// how often should we check for next update,
169-
/// and when was the last check happened.
170-
///
171-
/// # Configuration example
172-
///
173-
/// ```toml
174-
/// [update.manager]
175-
/// last-run = "2024-01-01 10:30:05" # when was the last update check
176-
/// timeout = 1440 # how long (in minutes) until we need to check for update since `last-run`,
177-
/// skip = "0.5.0" # the version the user choose to skip
178-
/// ```
179-
#[derive(Debug, Default, Deserialize, Serialize)]
180-
pub struct UpdateCheckerOpt(HashMap<UpdateTarget, UpdateConf>);
181-
182-
impl UpdateCheckerOpt {
141+
impl UpdateConfig {
183142
pub fn new() -> Self {
184143
Self::default()
185144
}
186145

187-
fn conf_mut(&mut self, target: UpdateTarget) -> &mut UpdateConf {
188-
self.0.entry(target).or_default()
189-
}
190-
191-
/// Return `true` if the given `version` is marked as skipped before.
192-
fn is_skipped<T: AsRef<str>>(&self, target: UpdateTarget, version: T) -> bool {
193-
let Some(skipped) = self.0.get(&target).and_then(|conf| conf.skip.as_deref()) else {
194-
return false;
195-
};
196-
version.as_ref() == skipped
197-
}
198-
199-
/// Change a target's update checkout timeout to a specific number in minutes.
200-
///
201-
/// This function can be chained.
202-
pub fn remind_later(mut self, target: UpdateTarget, minutes: u64) -> Self {
203-
let conf = self.conf_mut(target);
204-
if let Some(t) = conf.timeout.as_mut() {
205-
*t += minutes
206-
} else {
207-
conf.timeout = Some(minutes);
208-
}
209-
self
210-
}
211-
212-
/// Update the `last-run` value for given target.
213-
pub fn mark_checked(&mut self, target: UpdateTarget) -> &mut Self {
214-
let conf = self.conf_mut(target);
215-
conf.last_run = Utc::now().naive_utc();
216-
self
217-
}
218-
219-
/// Return how much time (in duration) until the next update check.
220-
///
221-
/// - If the update hasn't be checked yet, we should check now,
222-
/// thus returning [`Duration::ZERO`].
223-
/// - If the update has been checked, but right now is not the time for the
224-
/// next check, the remaining time will be returned.
225-
/// - If the update has been checked, and it's already past the time for the next
226-
/// update check, then [`Duration::ZERO`] will be returned.
227-
pub fn duration_until_next_run(&self, target: UpdateTarget) -> Duration {
228-
let Some(conf) = self.0.get(&target) else {
229-
// return the full default duration
230-
return Duration::ZERO;
231-
};
232-
let timeout = conf.timeout();
233-
let next_check_date = conf.last_run + timeout;
234-
let now = Utc::now().naive_utc();
235-
if next_check_date > now {
236-
let time_delta_in_secs = (next_check_date - now).num_seconds();
237-
// safe to unwrap, we are converting a known positive i64 to u64
238-
Duration::from_secs(time_delta_in_secs.try_into().unwrap())
239-
} else {
240-
Duration::ZERO
241-
}
242-
}
146+
setter!(manager_update_channel(
147+
self.manager_update_channel,
148+
UpdateChannel
149+
));
150+
setter!(auto_check_manager_updates(
151+
self.auto_check_manager_updates,
152+
bool
153+
));
154+
setter!(auto_check_toolkit_updates(
155+
self.auto_check_toolkit_updates,
156+
bool
157+
));
243158
}
244159

245160
#[cfg(test)]
246161
mod tests {
247162
use super::*;
248163

249164
#[test]
250-
fn skip_update() {
165+
fn backward_compatible() {
251166
let input = r#"
252167
[update]
253168
manager = { skip = "0.1.0", last-run = "1970-01-01T00:00:00" }
254169
toolkit = { skip = "1.0.0", last-run = "1970-01-01T00:00:00" }"#;
255170
let expected = Configuration::from_str(input).unwrap();
256-
assert!(expected.update_skipped(UpdateTarget::Manager, "0.1.0"));
257-
assert!(expected.update_skipped(UpdateTarget::Toolkit, "1.0.0"));
171+
assert_eq!(expected, Configuration::default());
258172
}
259173

260174
#[test]
261-
fn skip_update_programmatically() {
262-
let vs = Configuration::new()
263-
.skip_update(UpdateTarget::Manager, "0.1.0")
264-
.skip_update(UpdateTarget::Toolkit, "1.0.0");
265-
assert!(vs.update_skipped(UpdateTarget::Manager, "0.1.0"));
266-
assert!(vs.update_skipped(UpdateTarget::Toolkit, "1.0.0"));
175+
fn default_serialization() {
176+
let conf = Configuration::new();
177+
178+
assert_eq!(
179+
conf.to_toml().unwrap(),
180+
r#"[update]
181+
manager-update-channel = "stable"
182+
auto-check-manager-updates = true
183+
auto-check-toolkit-updates = true
184+
"#
185+
);
267186
}
268187

269188
#[test]
270-
fn remind_update_later() {
271-
let input = r#"
272-
[update]
273-
manager = { last-run = "1970-01-01T00:00:00" }"#;
189+
fn configured_serialization() {
190+
let conf = Configuration::new()
191+
.set_language(Language::CN)
192+
.set_manager_update_channel(UpdateChannel::Beta)
193+
.set_auto_check_manager_updates(false)
194+
.set_auto_check_toolkit_updates(false);
195+
196+
let expected = conf.to_toml().unwrap();
197+
assert_eq!(
198+
expected,
199+
r#"
200+
language = "CN"
274201
275-
let mut expected = Configuration::from_str(input).unwrap().update;
276-
let manager = UpdateTarget::Manager;
277-
assert_eq!(expected.conf_mut(manager).timeout, None);
278-
expected = expected.remind_later(manager, 60);
279-
assert_eq!(expected.conf_mut(manager).timeout, Some(60));
280-
expected = expected.remind_later(manager, 60);
281-
assert_eq!(expected.conf_mut(manager).timeout, Some(120));
202+
[update]
203+
manager-update-channel = "beta"
204+
auto-check-manager-updates = false
205+
auto-check-toolkit-updates = false
206+
"#
207+
);
282208
}
283209

284210
#[test]
285211
fn lang_config() {
286-
let input = r#"language = "CN"
287-
288-
[update]
289-
"#;
212+
let input = "language = \"CN\"\nupdate]";
290213

291214
let expected = Configuration::from_str(input).unwrap();
292215
assert_eq!(expected.language, Some(Language::CN));

rim_common/src/utils/download.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,25 @@ use std::path::Path;
33
use std::time::Duration;
44

55
use anyhow::{anyhow, bail, Context, Result};
6-
use indicatif::ProgressBar;
76
use reqwest::{header, Client};
87
use tokio::fs;
98
use tokio::io::AsyncWriteExt;
109
use url::Url;
1110

12-
use super::progress_bar::{CliProgress, Style};
1311
use crate::types::Proxy as CrateProxy;
12+
use crate::utils::ProgressHandler;
1413
use crate::{build_config, setter};
1514

1615
fn default_proxy() -> reqwest::Proxy {
1716
reqwest::Proxy::custom(|url| env_proxy::for_url(url).to_url())
1817
.no_proxy(reqwest::NoProxy::from_env())
1918
}
2019

21-
#[derive(Debug)]
22-
pub struct DownloadOpt<T: Sized> {
20+
pub struct DownloadOpt<T: ProgressHandler> {
2321
/// The verbose name of the file to download.
2422
pub name: String,
25-
/// Download progress handler, aka a progress bar.
26-
pub handler: CliProgress<T>,
23+
/// Download progress handler.
24+
pub handler: T,
2725
/// Option to skip SSL certificate verification when downloading.
2826
pub insecure: bool,
2927
/// Proxy configurations for download.
@@ -32,9 +30,8 @@ pub struct DownloadOpt<T: Sized> {
3230
resume: bool,
3331
}
3432

35-
impl DownloadOpt<ProgressBar> {
36-
pub fn new<S: ToString>(name: S, no_progress_bar: bool) -> Self {
37-
let handler = CliProgress::new(no_progress_bar);
33+
impl<T: ProgressHandler> DownloadOpt<T> {
34+
pub fn new<S: ToString>(name: S, handler: T) -> Self {
3835
Self {
3936
name: name.to_string(),
4037
handler,
@@ -131,6 +128,8 @@ impl DownloadOpt<ProgressBar> {
131128
.content_length()
132129
.ok_or_else(|| anyhow!("unable to get file length of '{url}'"))?;
133130

131+
self.handler.set_progress(format!("downloading '{}'", &self.name), 0);
132+
134133
let maybe_indicator = (self.handler.start)(
135134
format!("downloading '{}'", &self.name),
136135
Style::Bytes(total_size),

0 commit comments

Comments
 (0)