1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::mpsc;
use std::thread;
use std::time::SystemTime;
use crate::settings::get_wrangler_home_dir;
use reqwest::header::USER_AGENT;
use semver::Version;
use serde::{Deserialize, Serialize};
const ONE_DAY: u64 = 60 * 60 * 24;
pub fn background_check_for_updates() -> mpsc::Receiver<Version> {
let (sender, receiver) = mpsc::channel();
let _detached_thread = thread::spawn(move || match check_wrangler_versions() {
Ok(wrangler_versions) => {
if wrangler_versions.is_outdated() {
let _ = sender.send(wrangler_versions.latest);
}
}
Err(e) => log::debug!("could not determine if update is needed:\n{}", e),
});
receiver
}
#[derive(Debug, Clone)]
struct WranglerVersion {
pub current: Version,
pub latest: Version,
pub checked: bool,
}
impl WranglerVersion {
pub fn is_outdated(&self) -> bool {
!self.checked && (self.current != self.latest)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
struct LastCheckedVersion {
latest_version: String,
last_checked: SystemTime,
}
impl FromStr for LastCheckedVersion {
type Err = toml::de::Error;
fn from_str(serialized_toml: &str) -> Result<Self, Self::Err> {
toml::from_str(serialized_toml)
}
}
fn get_installed_version() -> Result<Version, failure::Error> {
let version = option_env!("CARGO_PKG_VERSION").unwrap_or_else(|| "unknown");
let parsed_version = Version::parse(version)?;
Ok(parsed_version)
}
fn check_wrangler_versions() -> Result<WranglerVersion, failure::Error> {
let config_dir = get_wrangler_home_dir()?;
let version_file = config_dir.join("version.toml");
let current_time = SystemTime::now();
let mut checked = false;
let current = get_installed_version()?;
let latest = match get_version_disk(&version_file) {
Some(last_checked_version) => {
let time_since_last_checked =
current_time.duration_since(last_checked_version.last_checked)?;
if time_since_last_checked.as_secs() < ONE_DAY {
checked = true;
Version::parse(&last_checked_version.latest_version)?
} else {
get_latest_version(¤t.to_string(), &version_file, current_time)?
}
}
None => get_latest_version(¤t.to_string(), &version_file, current_time)?,
};
Ok(WranglerVersion {
current,
latest,
checked,
})
}
fn get_version_disk(version_file: &PathBuf) -> Option<LastCheckedVersion> {
match fs::read_to_string(&version_file) {
Ok(contents) => match LastCheckedVersion::from_str(&contents) {
Ok(last_checked_version) => Some(last_checked_version),
Err(_) => None,
},
Err(_) => None,
}
}
fn get_latest_version(
installed_version: &str,
version_file: &PathBuf,
current_time: SystemTime,
) -> Result<Version, failure::Error> {
let latest_version = get_latest_version_from_api(installed_version)?;
let updated_file_contents = toml::to_string(&LastCheckedVersion {
latest_version: latest_version.to_string(),
last_checked: current_time,
})?;
fs::write(&version_file, updated_file_contents)?;
Ok(latest_version)
}
fn get_latest_version_from_api(installed_version: &str) -> Result<Version, failure::Error> {
let url = "https://crates.io/api/v1/crates/wrangler";
let user_agent = format!(
"wrangler/{} ({})",
installed_version,
env!("CARGO_PKG_REPOSITORY")
);
let client = reqwest::blocking::Client::builder().build()?;
let response = client
.get(url)
.header(USER_AGENT, user_agent)
.send()?
.error_for_status()?;
let text = response.text()?;
let crt: ApiResponse = serde_json::from_str(&text)?;
let version = Version::parse(&crt.info.max_version)?;
Ok(version)
}
#[derive(Deserialize, Debug)]
struct ApiResponse {
#[serde(rename = "crate")]
info: CrateInformation,
}
#[derive(Deserialize, Debug)]
struct CrateInformation {
max_version: String,
}