mc_launchermeta/version/
mod.rs

1////////////////////////////////////////////////////////////////////////////////
2// Copyright (c) 2023. Rob Bailey                                              /
3// This Source Code Form is subject to the terms of the Mozilla Public         /
4// License, v. 2.0. If a copy of the MPL was not distributed with this         /
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.                   /
6////////////////////////////////////////////////////////////////////////////////
7
8//! This module contains the types used by the version JSON files.
9//!
10//! These files specify info about how to run the game, and are fetched from the URLs specified in
11//! the version manifest.
12
13pub mod library;
14pub mod logging;
15pub mod rule;
16
17use std::fmt;
18use std::str::FromStr;
19
20use library::Library;
21use logging::Logging;
22use rule::Rule;
23use serde::de::{Error, MapAccess, SeqAccess, Visitor};
24use serde::{de, Deserialize, Deserializer, Serialize};
25
26use crate::VersionKind;
27
28
29#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
30pub struct Argument {
31    pub rules: Vec<Rule>,
32    pub values: Vec<String>,
33}
34
35#[derive(Debug, Clone, Eq, PartialEq, Hash)]
36struct ArrayOrStringHelper(pub Vec<String>);
37
38/// deserialize either an array of strings or a single string into always a vector of strings
39impl<'de> Deserialize<'de> for ArrayOrStringHelper {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: Deserializer<'de>,
43    {
44        struct ArrayOrStringVisitor;
45
46        impl<'de> Visitor<'de> for ArrayOrStringVisitor {
47            type Value = ArrayOrStringHelper;
48
49            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
50                formatter.write_str("string or array of strings")
51            }
52
53            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
54            where
55                E: de::Error,
56            {
57                Ok(ArrayOrStringHelper(vec![s.to_owned()]))
58            }
59
60            fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
61            where
62                S: SeqAccess<'de>,
63            {
64                let mut vec = Vec::new();
65                while let Some(elem) = seq.next_element::<String>()? {
66                    vec.push(elem);
67                }
68                Ok(ArrayOrStringHelper(vec))
69            }
70        }
71
72        deserializer.deserialize_any(ArrayOrStringVisitor)
73    }
74}
75
76impl FromStr for Argument {
77    type Err = ();
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        Ok(Argument {
81            rules: vec![],
82            values: vec![s.to_owned()],
83        })
84    }
85}
86
87impl<'de> Deserialize<'de> for Argument {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: Deserializer<'de>,
91    {
92        struct ArgumentVisitor;
93
94        impl<'de> Visitor<'de> for ArgumentVisitor {
95            type Value = Argument;
96
97            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
98                formatter.write_str("string or object with rules and value fields")
99            }
100
101            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
102            where
103                E: de::Error,
104            {
105                Ok(Argument {
106                    rules: vec![],
107                    values: vec![s.to_owned()],
108                })
109            }
110
111            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
112            where
113                M: MapAccess<'de>,
114            {
115                let mut rules = None;
116                let mut value = None;
117
118                while let Some(key) = map.next_key::<String>()? {
119                    match key.as_str() {
120                        "rules" => {
121                            if rules.is_some() {
122                                return Err(de::Error::duplicate_field("rules"));
123                            }
124                            rules = Some(map.next_value()?);
125                        }
126                        "value" => {
127                            if value.is_some() {
128                                return Err(de::Error::duplicate_field("value"));
129                            }
130                            value = Some(map.next_value::<ArrayOrStringHelper>()?.0);
131                        }
132                        _ => {
133                            return Err(Error::unknown_field(&key, &["rules", "value"]));
134                        }
135                    }
136                }
137
138                let rules = rules.ok_or_else(|| de::Error::missing_field("rules"))?;
139                let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
140
141                Ok(Argument {
142                    rules,
143                    values: value,
144                })
145            }
146        }
147
148        deserializer.deserialize_any(ArgumentVisitor)
149    }
150}
151
152#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
153#[serde(deny_unknown_fields)]
154pub struct Arguments {
155    pub game: Vec<Argument>,
156    pub jvm: Vec<Argument>,
157}
158
159#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
160#[serde(deny_unknown_fields, rename_all = "camelCase")]
161pub struct AssetIndex {
162    pub id: String,
163    pub sha1: String,
164    pub size: u64,
165    pub total_size: u64,
166    pub url: String,
167}
168
169#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
170#[serde(deny_unknown_fields)]
171pub struct Download {
172    pub sha1: String,
173    pub size: u64,
174    pub url: String,
175}
176
177#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
178#[serde(deny_unknown_fields)]
179pub struct Downloads {
180    pub client: Download,
181    #[serde(default)]
182    pub client_mappings: Option<Download>,
183    #[serde(default)]
184    pub server: Option<Download>,
185    #[serde(default)]
186    pub server_mappings: Option<Download>,
187    #[serde(default)]
188    pub windows_server: Option<Download>,
189}
190
191#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
192#[serde(deny_unknown_fields, rename_all = "camelCase")]
193pub struct JavaVersion {
194    pub component: String,
195    pub major_version: u8,
196}
197
198#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200#[serde(deny_unknown_fields)]
201pub struct Version {
202    #[serde(default)]
203    pub arguments: Option<Arguments>,
204    #[serde(default)]
205    pub minecraft_arguments: Option<String>,
206    pub asset_index: AssetIndex,
207    pub assets: String,
208    #[serde(default)]
209    pub compliance_level: Option<u8>,
210    pub downloads: Downloads,
211    pub id: String,
212    #[serde(default)]
213    pub java_version: Option<JavaVersion>,
214    pub libraries: Vec<Library>,
215    #[serde(default)]
216    pub logging: Option<Logging>,
217    pub main_class: String,
218    pub minimum_launcher_version: u8,
219    pub release_time: String,
220    pub time: String,
221    #[serde(rename = "type")]
222    pub kind: VersionKind,
223}