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
|
use std::{io::{Error, ErrorKind, Write}, fs::File};
use reqwest::Client;
use futures_util::StreamExt;
use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{get_mods_from_list, get_versions, get_list_version, change_list_versions}, List};
pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
let current_list = get_current_list(config.clone())?;
let mods = get_mods_from_list(config.clone(), current_list.clone())?;
let mut versions = get_versions(config.clone(), mods.clone())?;
versions.sort_by_key(|ver| ver.mod_id.clone());
let mut projects = projects(String::from(&config.apis.modrinth), mods).await;
projects.sort_by_key(|pro| pro.id.clone());
let mut updatestack: Vec<Version> = vec![];
for (index, project) in projects.into_iter().enumerate() {
let current_version = &versions[index];
let p_id = String::from(&project.id);
let v_id = ¤t_version.mod_id;
if &p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "SORTING_ERROR"))) };
if project.versions.join("|") != current_version.versions {
updatestack.push(match specific_update(config.clone(), current_list.clone(), project).await {
Ok(ver) => ver,
//TODO handle errors (only continue on "NO_UPDATE_AVAILABLE")
Err(_) => { continue; },
});
};
};
//println!("{:?}", updatestack);
download_updates(config, updatestack).await?;
Ok(())
}
async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> {
print!("Checking update for '{}' in {}", project.title, list.id);
let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
let mut versions: Vec<String> = vec![];
for ver in &applicable_versions {
versions.push(String::from(&ver.id));
}
let mut current: Vec<Version> = vec![];
if versions.join("|") != get_list_version(config.clone(), list.clone(), String::from(&project.id))? {
//get new versions
print!(" | getting new version");
let current_str = extract_current_version(applicable_versions.clone())?;
current.push(applicable_versions.into_iter().find(|ver| ver.id == current_str).unwrap());
change_list_versions(config, list, current_str, versions, project.id)?;
}
if current.is_empty() { return Err(Box::new(Error::new(ErrorKind::NotFound, "NO_UPDATE_AVAILABLE"))) };
println!(" | ✔️");
Ok(current[0].clone())
}
async fn download_updates(config: Cfg, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {
let dl_path = String::from(&config.downloads);
for ver in versions {
let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
let dl_path_file = format!("{}/{}", config.downloads, primary_file.filename);
println!("Downloading {}", primary_file.url);
let res = Client::new()
.get(String::from(&primary_file.url))
.send()
.await
.or(Err(format!("Failed to GET from '{}'", &primary_file.url)))?;
// download chunks
let mut file = File::create(String::from(&dl_path_file)).or(Err(format!("Failed to create file '{}'", dl_path_file)))?;
let mut stream = res.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item.or(Err("Error while downloading file"))?;
file.write_all(&chunk)
.or(Err("Error while writing to file"))?;
}
}
Ok(dl_path)
}
#[tokio::test]
async fn download_updates_test() {
use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, config::{Cfg, Apis}};
let config = Cfg { data: "...".to_string(), clean_remove: false, downloads: "./dl".to_string(), apis: Apis { modrinth: "...".to_string() } };
let versions = vec![Version {
id: "dEqtGnT9".to_string(),
project_id: "kYuIpRLv".to_string(),
author_id: "Qnt13hO8".to_string(),
featured: true,
name: "1.2.2-1.19 - Fabric".to_string(),
version_number: "1.2.2-1.19".to_string(),
changelog: None,
date_published: "2022-11-02T17:41:43.072267Z".to_string(),
downloads: 58,
version_type: VersionType::release,
files: vec![VersionFile {
hashes: Hash {
sha1: "fdc6dc39427fc92cc1d7ad8b275b5b83325e712b".to_string(),
sha512: "5b372f00d6e5d6a5ef225c3897826b9f6a2be5506905f7f71b9e939779765b41be6f2a9b029cfc752ad0751d0d2d5f8bb4544408df1363eebdde15641e99a849".to_string()
},
url: "https://cdn.modrinth.com/data/kYuIpRLv/versions/dEqtGnT9/waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
filename: "waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
primary: true,
size: 323176
}],
game_versions: vec![
"1.19".to_string(),
"1.19.1".to_string(),
"1.19.2".to_string()
],
loaders: vec![
"fabric".to_string()
]
}];
assert_eq!(download_updates(config, versions).await.unwrap(), "./dl")
}
|