Skip to content

Commit 9664951

Browse files
committed
refactor: move errors to new module
feat: begin forge metadata fetching Signed-off-by: Rachel Powers <[email protected]>
1 parent 42912a0 commit 9664951

File tree

5 files changed

+150
-65
lines changed

5 files changed

+150
-65
lines changed

libmcmeta/src/models/forge.rs

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
use std::collections::HashMap;
2-
31
use serde::{Deserialize, Serialize};
2+
use serde_valid::Validate;
3+
use serde_with::skip_serializing_none;
4+
use std::collections::HashMap;
45

5-
#[derive(Deserialize, Serialize, Clone, Debug)]
6+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
67
pub struct ForgeMavenMetadata {
78
#[serde(flatten)]
89
pub versions: HashMap<String, Vec<String>>,
910
}
1011

11-
#[derive(Deserialize, Serialize, Clone, Debug)]
12+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
1213
pub struct ForgeMavenPromotions {
1314
pub homepage: String,
1415
pub promos: HashMap<String, String>,
1516
}
1617

17-
#[derive(Deserialize, Serialize, Clone, Debug)]
18+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
1819
pub struct ForgeVersionMeta {
1920
pub classifiers: ForgeVersionClassifiers,
2021
}
2122

22-
#[derive(Deserialize, Serialize, Clone, Debug)]
23+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
24+
#[skip_serializing_none]
2325
#[serde(deny_unknown_fields)]
2426
pub struct ForgeVersionClassifier {
2527
pub txt: Option<String>,
@@ -28,7 +30,8 @@ pub struct ForgeVersionClassifier {
2830
pub stash: Option<String>,
2931
}
3032

31-
#[derive(Deserialize, Serialize, Clone, Debug)]
33+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
34+
#[skip_serializing_none]
3235
#[serde(deny_unknown_fields)]
3336
pub struct ForgeVersionClassifiers {
3437
pub changelog: Option<ForgeVersionClassifier>,
@@ -47,14 +50,15 @@ pub struct ForgeVersionClassifiers {
4750
pub src_zip: Option<ForgeVersionClassifier>,
4851
}
4952

50-
#[derive(Deserialize, Serialize, Clone, Debug)]
53+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
54+
#[skip_serializing_none]
5155
#[serde(deny_unknown_fields)]
5256
pub struct ForgeVersionArguments {
5357
pub game: Vec<String>,
5458
pub jvm: Option<Vec<String>>,
5559
}
5660

57-
#[derive(Deserialize, Serialize, Clone, Debug)]
61+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
5862
#[serde(deny_unknown_fields)]
5963
pub struct ForgeVersionLibraryArtifact {
6064
pub path: String,
@@ -63,20 +67,20 @@ pub struct ForgeVersionLibraryArtifact {
6367
pub size: u64,
6468
}
6569

66-
#[derive(Deserialize, Serialize, Clone, Debug)]
70+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
6771
#[serde(deny_unknown_fields)]
6872
pub struct ForgeVersionLibraryDownloads {
6973
pub artifact: ForgeVersionLibraryArtifact,
7074
}
7175

72-
#[derive(Deserialize, Serialize, Clone, Debug)]
76+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
7377
#[serde(deny_unknown_fields)]
7478
pub struct ForgeVersionLibrary {
7579
pub name: String,
7680
pub downloads: ForgeVersionLibraryDownloads,
7781
}
7882

79-
#[derive(Deserialize, Serialize, Clone, Debug)]
83+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
8084
#[serde(deny_unknown_fields)]
8185
pub struct ForgeVersionLoggingFile {
8286
pub id: String,
@@ -85,7 +89,7 @@ pub struct ForgeVersionLoggingFile {
8589
pub url: String,
8690
}
8791

88-
#[derive(Deserialize, Serialize, Clone, Debug)]
92+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
8993
#[serde(deny_unknown_fields)]
9094
pub struct ForgeVersionLoggingClient {
9195
pub argument: String,
@@ -94,13 +98,14 @@ pub struct ForgeVersionLoggingClient {
9498
pub client_type: String,
9599
}
96100

97-
#[derive(Deserialize, Serialize, Clone, Debug)]
101+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
98102
#[serde(deny_unknown_fields)]
99103
pub struct ForgeVersionLogging {
100104
pub client: Option<ForgeVersionLoggingClient>,
101105
}
102106

103-
#[derive(Deserialize, Serialize, Clone, Debug)]
107+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
108+
#[skip_serializing_none]
104109
#[serde(rename_all = "camelCase", deny_unknown_fields)]
105110
pub struct ForgeVersion {
106111
#[serde(rename = "_comment_")]
@@ -118,14 +123,15 @@ pub struct ForgeVersion {
118123
pub minecraft_arguments: Option<String>,
119124
}
120125

121-
#[derive(Deserialize, Serialize, Clone, Debug)]
126+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
122127
#[serde(deny_unknown_fields)]
123128
pub struct ForgeInstallerDataInfo {
124129
pub client: String,
125130
pub server: String,
126131
}
127132

128-
#[derive(Deserialize, Serialize, Clone, Debug)]
133+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
134+
#[skip_serializing_none]
129135
#[serde(deny_unknown_fields, rename_all = "SCREAMING_SNAKE_CASE")]
130136
pub struct ForgeInstallerData {
131137
pub mappings: Option<ForgeInstallerDataInfo>,
@@ -145,7 +151,8 @@ pub struct ForgeInstallerData {
145151
pub mc_data_sha: Option<ForgeInstallerDataInfo>,
146152
}
147153

148-
#[derive(Deserialize, Serialize, Clone, Debug)]
154+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
155+
#[skip_serializing_none]
149156
#[serde(deny_unknown_fields)]
150157
pub struct ForgeInstallerProcessor {
151158
pub sides: Option<Vec<String>>,
@@ -155,11 +162,11 @@ pub struct ForgeInstallerProcessor {
155162
pub outputs: Option<HashMap<String, String>>,
156163
}
157164

158-
#[derive(Deserialize, Serialize, Clone, Debug)]
165+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
159166
#[serde(deny_unknown_fields)]
160167
pub struct ForgeLegacyLogging {}
161168

162-
#[derive(Deserialize, Serialize, Clone, Debug)]
169+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
163170
#[serde(deny_unknown_fields, rename_all = "camelCase")]
164171
pub struct ForgeLegacyInstall {
165172
pub profile_name: String,
@@ -174,36 +181,40 @@ pub struct ForgeLegacyInstall {
174181
pub mod_list: Option<String>,
175182
}
176183

177-
#[derive(Deserialize, Serialize, Clone, Debug)]
184+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
185+
#[skip_serializing_none]
178186
#[serde(deny_unknown_fields, rename_all = "camelCase")]
179187
pub struct ForgeLegacyLibraryNatives {
180188
pub linux: Option<String>,
181189
pub osx: Option<String>,
182190
pub windows: Option<String>,
183191
}
184192

185-
#[derive(Deserialize, Serialize, Clone, Debug)]
193+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
186194
#[serde(deny_unknown_fields, rename_all = "camelCase")]
187195
pub struct ForgeLegacyLibraryExtract {
188196
pub exclude: Vec<String>,
189197
}
190198

191-
#[derive(Deserialize, Serialize, Clone, Debug)]
199+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
200+
#[skip_serializing_none]
192201
#[serde(rename_all = "camelCase", deny_unknown_fields)]
193202
pub struct ManifestRule {
194203
pub action: String,
195204
pub os: Option<ManifestRuleOS>,
196205
}
197206

198-
#[derive(Deserialize, Serialize, Clone, Debug)]
207+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
208+
#[skip_serializing_none]
199209
#[serde(rename_all = "camelCase", deny_unknown_fields)]
200210
pub struct ManifestRuleOS {
201211
pub name: Option<String>,
202212
pub version: Option<String>,
203213
pub arch: Option<String>,
204214
}
205215

206-
#[derive(Deserialize, Serialize, Clone, Debug)]
216+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
217+
#[skip_serializing_none]
207218
#[serde(deny_unknown_fields, rename_all = "camelCase")]
208219
pub struct ForgeLegacyLibrary {
209220
pub name: String,
@@ -217,7 +228,8 @@ pub struct ForgeLegacyLibrary {
217228
pub comment: Option<String>,
218229
}
219230

220-
#[derive(Deserialize, Serialize, Clone, Debug)]
231+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
232+
#[skip_serializing_none]
221233
#[serde(deny_unknown_fields, rename_all = "camelCase")]
222234
pub struct ForgeLegacyVersionInfo {
223235
pub id: String,
@@ -236,7 +248,7 @@ pub struct ForgeLegacyVersionInfo {
236248
pub logging: Option<ForgeLegacyLogging>,
237249
}
238250

239-
#[derive(Deserialize, Serialize, Clone, Debug)]
251+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
240252
#[serde(deny_unknown_fields)]
241253
pub struct ForgeLegacyOptional {
242254
pub name: String,
@@ -250,7 +262,8 @@ pub struct ForgeLegacyOptional {
250262
pub maven: String,
251263
}
252264

253-
#[derive(Deserialize, Serialize, Clone, Debug)]
265+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
266+
#[skip_serializing_none]
254267
#[serde(deny_unknown_fields, rename_all = "camelCase")]
255268
pub struct ForgeLegacyInstallerManifest {
256269
#[serde(rename = "_comment_")]
@@ -260,7 +273,8 @@ pub struct ForgeLegacyInstallerManifest {
260273
pub optionals: Option<Vec<ForgeLegacyOptional>>,
261274
}
262275

263-
#[derive(Deserialize, Serialize, Clone, Debug)]
276+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
277+
#[skip_serializing_none]
264278
#[serde(deny_unknown_fields, rename_all = "camelCase")]
265279
pub struct ForgeInstallerManifest {
266280
#[serde(rename = "_comment_")]
@@ -281,7 +295,7 @@ pub struct ForgeInstallerManifest {
281295
pub libraries: Vec<ForgeVersionLibrary>,
282296
}
283297

284-
#[derive(Deserialize, Serialize, Clone, Debug)]
298+
#[derive(Deserialize, Serialize, Clone, Debug, Validate)]
285299
#[serde(untagged)]
286300
pub enum ForgeInstallerManifestVersion {
287301
Legacy(Box<ForgeLegacyInstallerManifest>),

mcmeta/src/download/errors.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use crate::utils::get_json_context_back;
2+
use custom_error::custom_error;
3+
4+
custom_error! {
5+
pub MetadataError
6+
Config { source: config::ConfigError } = "Error while reading config from environment",
7+
Request { source: reqwest::Error } = "Request error: {source}",
8+
Deserialization { source: serde_json::Error } = "Deserialization error: {source}",
9+
BadData {
10+
ctx: String,
11+
source: serde_json::Error
12+
} = @{
13+
format!("{}. Context at {}:{} (may be truncated) \" {} \"", source, source.line(), source.column(), ctx)
14+
},
15+
Validation { source: serde_valid::validation::Errors } = "Validation Error: {source}",
16+
}
17+
18+
impl MetadataError {
19+
pub fn from_json_err(err: serde_json::Error, body: &str) -> Self {
20+
match err.classify() {
21+
serde_json::error::Category::Data => Self::BadData {
22+
ctx: get_json_context_back(&err, body, 200),
23+
source: err,
24+
},
25+
_ => err.into(),
26+
}
27+
}
28+
}

mcmeta/src/download/forge.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use libmcmeta::models::forge::{ForgeMavenMetadata, ForgeMavenPromotions};
2+
use serde::Deserialize;
3+
use serde_valid::Validate;
4+
use tracing::debug;
5+
6+
use crate::download::errors::MetadataError;
7+
8+
fn default_maven_url() -> String {
9+
"https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json".to_string()
10+
}
11+
12+
fn default_promotions_url() -> String {
13+
"https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json".to_string()
14+
}
15+
16+
#[derive(Deserialize, Debug)]
17+
struct DownloadConfig {
18+
#[serde(default = "default_maven_url")]
19+
pub maven_url: String,
20+
#[serde(default = "default_promotions_url")]
21+
pub promotions_url: String,
22+
}
23+
24+
impl DownloadConfig {
25+
fn from_config() -> Result<Self, MetadataError> {
26+
let config = config::Config::builder()
27+
.add_source(config::Environment::with_prefix("MCMETA_FORGE"))
28+
.build()?;
29+
30+
config.try_deserialize::<'_, Self>().map_err(Into::into)
31+
}
32+
}
33+
34+
pub async fn load_maven_metadata() -> Result<ForgeMavenMetadata, MetadataError> {
35+
let client = reqwest::Client::new();
36+
let config = DownloadConfig::from_config()?;
37+
38+
let body = client
39+
.get(&config.maven_url)
40+
.send()
41+
.await?
42+
.error_for_status()?
43+
.text()
44+
.await?;
45+
46+
let metadata: ForgeMavenMetadata =
47+
serde_json::from_str(&body).map_err(|err| MetadataError::from_json_err(err, &body))?;
48+
metadata.validate()?;
49+
Ok(metadata)
50+
}
51+
52+
pub async fn load_maven_promotions() -> Result<ForgeMavenPromotions, MetadataError> {
53+
let client = reqwest::Client::new();
54+
let config = DownloadConfig::from_config()?;
55+
56+
let body = client
57+
.get(&config.promotions_url)
58+
.send()
59+
.await?
60+
.error_for_status()?
61+
.text()
62+
.await?;
63+
64+
let promotions: ForgeMavenPromotions =
65+
serde_json::from_str(&body).map_err(|err| MetadataError::from_json_err(err, &body))?;
66+
promotions.validate()?;
67+
Ok(promotions)
68+
}

mcmeta/src/download/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
pub mod errors;
2+
pub mod forge;
13
pub mod mojang;

0 commit comments

Comments
 (0)