cobble_core/minecraft/models/
library.rs

1use crate::consts;
2use crate::minecraft::models::{LibraryDownloads, LibraryExtract, LibraryNatives, Rule};
3use crate::utils::Architecture;
4use serde::{Deserialize, Serialize};
5use std::path::{Path, PathBuf};
6
7/// A library need for running the game.
8#[derive(Clone, Debug, Deserialize, Serialize)]
9pub struct Library {
10    /// Information about downloading the library.
11    pub downloads: LibraryDownloads,
12    /// Name of the library.
13    pub name: String,
14    /// Available natives.
15    pub natives: Option<LibraryNatives>,
16    /// Rules for using this library.
17    #[serde(default)]
18    pub rules: Vec<Rule>,
19    /// Extract options.
20    pub extract: Option<LibraryExtract>,
21}
22
23impl Library {
24    /// Checks if the library needs to be used on the current machine.
25    pub fn check_use(&self) -> bool {
26        for rule in &self.rules {
27            if !rule.allows() {
28                return false;
29            }
30        }
31
32        true
33    }
34
35    /// Builds the name of the library jar file.
36    pub fn jar_name(&self) -> String {
37        let split = self.split_name();
38        // Take name and version
39        let parts = split.iter().skip(1).take(2).cloned();
40        // Take suffixes
41        let suffixes = split.iter().skip(3).cloned();
42
43        let mut name = vec![];
44
45        // Name & version
46        name.extend(parts);
47
48        // Native
49        if let Some(native) = self.get_native() {
50            name.push(native);
51        }
52
53        // Suffixes
54        name.extend(suffixes);
55
56        let mut name = name.join("-");
57        name.push_str(".jar");
58
59        name
60    }
61
62    /// Builds the path where the library jar file is placed.
63    /// This path does not include the file itself.
64    pub fn library_path(&self, libraries_path: impl AsRef<Path>) -> PathBuf {
65        let mut library_path = PathBuf::from(libraries_path.as_ref());
66
67        library_path.push(self.relative_library_path());
68
69        library_path
70    }
71
72    /// Builds the path where the library jar file is placed relative to the libraries folder.
73    /// This path does not include the file itself.
74    pub fn relative_library_path(&self) -> PathBuf {
75        // Take package, name and version
76        let path_parts = self.split_name().into_iter().take(3);
77
78        let mut library_path = PathBuf::new();
79        for (i, path_part) in path_parts.enumerate() {
80            let part = match i {
81                0 => path_part.replace('.', "/"),
82                _ => path_part,
83            };
84
85            library_path.push(part);
86        }
87
88        library_path
89    }
90
91    /// Builds the complete path for the library jar file relat.
92    pub fn jar_path(&self, libraries_path: impl AsRef<Path>) -> PathBuf {
93        let mut jar_path = self.library_path(&libraries_path);
94        jar_path.push(self.jar_name());
95
96        jar_path
97    }
98
99    /// Builds the complete path for the library jar file relative to the libraries folder.
100    pub fn relative_jar_path(&self) -> PathBuf {
101        let mut jar_path = self.relative_library_path();
102        jar_path.push(self.jar_name());
103
104        jar_path
105    }
106
107    /// Builds the download URL for the library.
108    /// Returns a tuble: (URL, SHA1, size).
109    pub fn download_url(&self) -> (String, Option<String>, Option<usize>) {
110        let url: String;
111        let mut sha1 = None;
112        let mut size = None;
113
114        match &self.get_native() {
115            Some(native) => match self.downloads.classifiers.get(native) {
116                Some(file) => {
117                    url = file.url.clone();
118                    sha1 = Some(file.sha1.clone());
119                    size = Some(file.size);
120                }
121                None => {
122                    url = self.build_url_from_name();
123                }
124            },
125            None => match &self.downloads.artifact {
126                Some(file) => {
127                    url = file.url.clone();
128                    sha1 = Some(file.sha1.clone());
129                    size = Some(file.size);
130                }
131                None => {
132                    url = self.build_url_from_name();
133                }
134            },
135        }
136
137        (url, sha1, size)
138    }
139
140    /// Checks if the library needs to be extracted.
141    pub fn needs_extract(&self) -> bool {
142        self.get_native().is_some() && self.extract.is_some()
143    }
144
145    fn build_url_from_name(&self) -> String {
146        // Take package, name and version
147        let parts = self.split_name().into_iter().take(3);
148
149        let mut url = vec![consts::MC_LIBRARIES_BASE_URL.to_string()];
150
151        for (i, part) in parts.enumerate() {
152            if i == 0 {
153                url.push(part.replace('.', "/"));
154            } else {
155                url.push(part);
156            }
157        }
158
159        url.push(self.jar_name());
160
161        url.join("/")
162    }
163
164    /// Gets the native identifier of the library.
165    pub fn get_native(&self) -> Option<String> {
166        let arch = Architecture::current();
167
168        self.natives.as_ref().and_then(|natives| {
169            natives
170                .get_for_current_platform()
171                .map(|n| n.replace("${arch}", &arch.get_bits().to_string()))
172        })
173    }
174
175    fn split_name(&self) -> Vec<String> {
176        self.name.split(':').map(String::from).collect()
177    }
178}