Skip to main content

mod_installer/weidu/
component.rs

1use std::error::Error;
2
3use crate::config::{log_options::LogOptions, weidu_log_options::WeiduLogOptions};
4
5use crate::weidu::install_block::WeiduInstallBlock;
6
7// This should mirror the weidu component
8// https://github.com/WeiDUorg/weidu/blob/devel/src/tp.ml#L98
9#[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}