mod_installer/weidu/
component.rs1use std::error::Error;
2
3use crate::config::{log_options::LogOptions, weidu_log_options::WeiduLogOptions};
4
5use crate::weidu::install_block::WeiduInstallBlock;
6
7#[derive(Debug, PartialOrd, Ord, Eq, Clone, Default)]
10pub(crate) struct WeiduComponent {
11 pub(crate) tp_file: String,
12 pub(crate) name: String,
13 pub(crate) lang: String,
14 pub(crate) component: String,
15 pub(crate) component_name: String,
16 pub(crate) sub_component: String,
17 pub(crate) version: String,
18}
19
20impl PartialEq for WeiduComponent {
21 fn eq(&self, other: &Self) -> bool {
22 self.tp_file == other.tp_file
23 && self.name.to_lowercase() == other.name.to_lowercase()
24 && self.lang.to_lowercase() == other.lang.to_lowercase()
25 && self.component.to_lowercase() == other.component.to_lowercase()
26 }
27}
28
29impl WeiduComponent {
30 pub(crate) fn strict_matching(&self, other: &Self) -> bool {
31 self.eq(other)
32 && self.component_name == other.component_name
33 && self.sub_component == other.sub_component
34 && self.version == other.version
35 }
36
37 pub(crate) fn full_component_name(&self) -> String {
38 format!("{}{}{}", self.name, std::path::MAIN_SEPARATOR, self.tp_file).to_lowercase()
39 }
40}
41
42impl TryFrom<String> for WeiduComponent {
43 type Error = Box<dyn Error>;
44
45 fn try_from(line: String) -> Result<Self, Self::Error> {
46 let mut parts = line.split('~');
47
48 let install_path = parts.nth(1).ok_or(format!(
49 "Could not get full name of mod, from provided string: {line}"
50 ))?;
51
52 let (tp_file, name) = if install_path.split('\\').nth(1).is_some() {
53 let mut component_path_string = install_path
54 .split('\\')
55 .collect::<Vec<&str>>()
56 .into_iter()
57 .rev();
58 (
59 component_path_string.next().unwrap_or_default(),
60 component_path_string.next().unwrap_or_default(),
61 )
62 } else if install_path.split('/').nth(1).is_some() {
63 let mut component_path_string = install_path
64 .split('/')
65 .collect::<Vec<&str>>()
66 .into_iter()
67 .rev();
68 (
69 component_path_string.next().unwrap_or_default(),
70 component_path_string.next().unwrap_or_default(),
71 )
72 } else {
73 return Err(format!("Could not find tp2 file name, from provided string: {line}").into());
74 };
75
76 let mut tail = parts
77 .next()
78 .ok_or(format!(
79 "Could not find lang and component, from provided string {line}"
80 ))?
81 .split("//");
82
83 let mut lang_and_component = tail.next().unwrap_or_default().split(' ');
84
85 let lang = lang_and_component
86 .nth(1)
87 .ok_or(format!("Could not find lang, from provided string: {line}"))?
88 .replace('#', "");
89
90 let component = lang_and_component
91 .next()
92 .ok_or(format!(
93 "Could not find component, from provided string {line}"
94 ))?
95 .replace('#', "");
96
97 let mut component_name_sub_component_version = tail.next().unwrap_or_default().split(':');
98
99 let mut component_name_sub_component = component_name_sub_component_version
100 .next()
101 .unwrap_or_default()
102 .split("->");
103
104 let component_name = component_name_sub_component
105 .next()
106 .unwrap_or_default()
107 .trim()
108 .to_string();
109
110 let sub_component = component_name_sub_component
111 .next()
112 .unwrap_or_default()
113 .trim()
114 .to_string();
115
116 let version = component_name_sub_component_version
117 .next()
118 .unwrap_or_default()
119 .trim()
120 .to_string();
121
122 Ok(Self {
123 tp_file: tp_file.to_string(),
124 name: name.to_string(),
125 lang,
126 component,
127 component_name,
128 sub_component,
129 version,
130 })
131 }
132}
133
134impl WeiduInstallBlock for WeiduComponent {
135 fn generate_weidu_args(
136 &self,
137 weidu_log_mode: Vec<LogOptions>,
138 language: &str,
139 generic_weidu_args: &[String],
140 ) -> Vec<String> {
141 let mut args = vec![
142 self.full_component_name(),
143 "--force-install".to_string(),
144 self.component.to_string(),
145 "--use-lang".to_string(),
146 language.to_string(),
147 "--language".to_string(),
148 self.lang.to_string(),
149 "--no-exit-pause".to_string(),
150 ];
151 args.extend(WeiduLogOptions::new(weidu_log_mode).to_args(&self.log_file_name()));
152 args.extend_from_slice(generic_weidu_args);
153 args
154 }
155 fn log_file_name(&self) -> String {
156 format!("{}-{}.log", self.name, self.component).to_lowercase()
157 }
158}
159
160#[cfg(test)]
161mod tests {
162
163 use super::*;
164 use pretty_assertions::assert_eq;
165
166 #[test]
167 fn test_parse_windows() -> Result<(), Box<dyn Error>> {
168 let mod_string = r"~TOBEX\TOBEX.TP2~ #0 #100 // TobEx - Core: v28";
169 let mod_component = WeiduComponent::try_from(mod_string.to_string())?;
170 let expected = WeiduComponent {
171 tp_file: "TOBEX.TP2".to_string(),
172 name: "tobex".to_string(),
173 lang: "0".to_string(),
174 component: "100".to_string(),
175 component_name: "TobEx - Core".to_string(),
176 sub_component: "".to_string(),
177 version: "v28".to_string(),
178 };
179 assert_eq!(mod_component, expected);
180 Ok(())
181 }
182
183 #[test]
184 fn test_strict_match() -> Result<(), Box<dyn Error>> {
185 let non_strict_match_1 = WeiduComponent {
186 tp_file: "TOBEX.TP2".to_string(),
187 name: "tobex".to_string(),
188 lang: "0".to_string(),
189 component: "100".to_string(),
190 component_name: "TobEx - Core".to_string(),
191 sub_component: "".to_string(),
192 version: "v28".to_string(),
193 };
194
195 let non_strict_match_2 = WeiduComponent {
196 tp_file: "TOBEX.TP2".to_string(),
197 name: "tobex".to_string(),
198 lang: "0".to_string(),
199 component: "100".to_string(),
200 component_name: "TobEx - Core Chicken".to_string(),
201 sub_component: "".to_string(),
202 version: "v28".to_string(),
203 };
204 assert_eq!(non_strict_match_1, non_strict_match_2);
205 assert_eq!(
206 non_strict_match_1.strict_matching(&non_strict_match_2),
207 false
208 );
209 Ok(())
210 }
211}