-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathidf_versions.rs
237 lines (221 loc) · 7.54 KB
/
idf_versions.rs
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
use log::error;
use serde_derive::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Deserialize, Clone)]
pub struct Version {
pub name: String,
#[serde(default)]
pub pre_release: bool,
#[serde(default)]
pub old: bool,
#[serde(default)]
pub end_of_life: bool,
#[serde(default)]
pub has_targets: bool,
#[serde(default)]
pub supported_targets: Vec<String>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct IDFTarget {
pub text: String,
pub value: String,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Release {
pub start_date: String,
pub end_date: String,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Releases {
pub VERSIONS: Vec<Version>,
pub IDF_TARGETS: Vec<IDFTarget>,
pub RELEASES: std::collections::HashMap<String, Release>,
}
// TODO: handle the possibility of multiple downloads
pub async fn get_idf_versions() -> Result<Releases, String> {
Ok(download_idf_versions().await.unwrap())
}
/// Retrieves the available IDF targets from the official website.
///
/// This function fetches the IDF versions from the official website, extracts the available targets,
/// and returns a vector of these target names.
///
/// # Returns
///
/// * A `Result` containing a vector of strings representing the available IDF targets if successful.
/// If there is an error fetching the IDF versions or processing them, a `String` containing the error message is returned.
///
/// # Errors
///
/// * If there is an error fetching the IDF versions or processing them, a `String` containing the error message is returned.
///
pub async fn get_avalible_targets() -> Result<Vec<String>, String> {
let versions = get_idf_versions().await;
match versions {
Ok(releases) => {
let mut avalible_targets = vec![];
for target in &releases.IDF_TARGETS {
avalible_targets.push(target.value.clone());
}
avalible_targets.sort();
Ok(avalible_targets)
}
Err(err) => Err(err),
}
}
/// This function downloads the IDF versions from the official website.
///
/// # Returns
///
/// * A Result containing a `Releases` struct if the download and parsing are successful.
/// If there is an error during the download or parsing, a `Box<dyn std::error::Error>` is returned.
///
/// # Errors
///
/// * If there is an error during the HTTP request, the error is returned as a `reqwest::Error`.
/// * If there is an error during the JSON deserialization, the error is returned as a `serde_json::Error`.
///
pub async fn download_idf_versions() -> Result<Releases, Box<dyn std::error::Error>> {
let url = "https://dl.espressif.com/dl/esp-idf/idf_versions.json".to_string();
let client = reqwest::Client::builder()
.user_agent("esp-idf-installer")
.build()?;
let response = client.get(&url).send().await?;
let json_versions_file = response.text().await?;
let versions: Releases = serde_json::from_str(&json_versions_file)?;
Ok(versions)
}
/// This function groups the IDF versions by their supported targets.
///
/// # Arguments
///
/// * `versions` - A reference to a `Releases` struct containing the IDF versions and targets.
///
/// # Returns
///
/// * A HashMap where the keys are the target names (as strings) and the values are vectors of `Version` structs.
/// Each vector contains the IDF versions that support the corresponding target.
///
/// # Errors
///
/// * This function does not return any errors.
pub fn get_idf_versions_by_target(versions: &Releases) -> HashMap<String, Vec<Version>> {
let mut versions_by_target = HashMap::new();
for target in &versions.IDF_TARGETS {
let version_list = versions
.VERSIONS
.iter()
.filter(|v| v.supported_targets.contains(&target.value))
.cloned()
.collect();
versions_by_target.insert(target.value.clone(), version_list);
}
versions_by_target
}
/// This function retrieves the IDF version names for a given target.
///
/// # Arguments
///
/// * `target` - A reference to a string representing the target for which the IDF versions are needed.
///
/// # Returns
///
/// * A vector of strings containing the IDF version names for the given target.
/// If the target is not found or there are no valid versions, an empty vector is returned.
///
/// # Errors
///
/// * If there is an error fetching the IDF versions or processing them, an error message is returned as a string.
///
pub async fn get_idf_name_by_target(target: &String) -> Vec<String> {
let versions = get_idf_versions().await;
let versions_by_target = get_idf_versions_by_target(&versions.unwrap());
let mut selected_versions = vec![];
if let Some(versions) = versions_by_target.get(target) {
for v in versions {
if v.end_of_life || v.pre_release || v.old || v.name == "latest" {
continue;
}
selected_versions.push(v.name.clone());
}
}
selected_versions
}
/// Retrieves the names of all valid IDF versions.
///
/// This function fetches the IDF versions from the official website, filters out invalid versions,
/// and returns a vector of valid IDF version names.
///
/// # Returns
///
/// * A vector of strings containing the names of valid IDF versions.
/// If there is an error fetching the IDF versions or processing them, an empty vector is returned.
///
/// # Errors
///
/// * If there is an error fetching the IDF versions or processing them, an error message is logged.
pub async fn get_idf_names() -> Vec<String> {
let versions = get_idf_versions().await;
match versions {
Ok(releases) => {
let mut names = vec![];
for version in &releases.VERSIONS {
if version.end_of_life
|| version.pre_release
|| version.old
|| version.name == "latest"
{
continue;
}
names.push(version.name.clone());
}
names
}
Err(err) => {
error!("{}", err);
vec![]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_idf_versions_by_target() {
let releases = Releases {
VERSIONS: vec![
Version {
name: "v4.4.5".to_string(),
pre_release: false,
old: false,
end_of_life: false,
has_targets: true,
supported_targets: vec!["esp32".to_string(), "esp32s2".to_string()],
},
Version {
name: "v5.0.0".to_string(),
pre_release: false,
old: false,
end_of_life: false,
has_targets: true,
supported_targets: vec!["esp32".to_string()],
},
],
IDF_TARGETS: vec![
IDFTarget {
text: "ESP32".to_string(),
value: "esp32".to_string(),
},
IDFTarget {
text: "ESP32-S2".to_string(),
value: "esp32s2".to_string(),
},
],
RELEASES: HashMap::new(),
};
let versions_by_target = get_idf_versions_by_target(&releases);
assert_eq!(versions_by_target.len(), 2);
assert_eq!(versions_by_target.get("esp32").unwrap().len(), 2);
assert_eq!(versions_by_target.get("esp32s2").unwrap().len(), 1);
}
}