1use std::fmt::{Display, Formatter};
2use std::num::ParseIntError;
3use std::str::FromStr;
4
5use lazy_static::lazy_static;
6use regex::{Captures, Regex};
7use serde::Serialize;
8use thiserror::Error;
9
10use crate::from_str_deserialize_impl;
11use crate::modules::ship::{
12 HardpointModule, HardpointMounting, HardpointSize, HardpointSizeError, HardpointType,
13 ModuleClass, ModuleClassError,
14};
15
16pub mod hardpoint_module;
17pub mod hardpoint_mounting;
18pub mod hardpoint_size;
19pub mod hardpoint_type;
20
21#[derive(Debug, Serialize, Clone, PartialEq)]
25pub struct ShipHardpointModule {
26 pub module: HardpointModule,
28
29 pub mounting: HardpointMounting,
32
33 pub size: HardpointSize,
35
36 pub class: ModuleClass,
38}
39
40#[derive(Debug, Error)]
41pub enum ShipHardpointModuleError {
42 #[error("Failed to parse hardpoint module: {0}")]
43 FailedToParseHardpointModule(#[source] serde_json::Error),
44
45 #[error("Failed to parse hardpoint mounting: {0}")]
46 FailedToParseHardpointMounting(#[source] serde_json::Error),
47
48 #[error("Failed to parse hardpoint size: {0}")]
49 FailedToParseHardpointSize(#[from] HardpointSizeError),
50
51 #[error("Failed to parse class number: {0}")]
52 FailedToParseClassNumber(#[source] ParseIntError),
53
54 #[error("Mounting type cannot be missing when the module size is not 'tiny'")]
55 MissingMounting,
56
57 #[error(transparent)]
58 ModuleClassError(#[from] ModuleClassError),
59
60 #[error("Failed to parse ship hardpoint module: '{0}'")]
61 FailedToParse(String),
62}
63
64lazy_static! {
65 static ref GRADED_HARDPOINT_MODULE_REGEX: Regex = Regex::new(r#"^\$?[hH]pt_(\w+?)_[sS]ize(\d+)_[cC]lass(\d+)(_[nN]ame;)?$"#).unwrap();
66 static ref HARDPOINT_MODULE_REGEX: Regex = Regex::new(r#"^\$?[hH]pt_(\w+?)(_([gG]imbal|[fF]ixed|[tT]urret))?_([tT]iny|[sS]mall|[mM]edium|[lL]arge|[hH]uge)(_[a-zA-Z0-9_]+?)?(_[nN]ame;)?$"#).unwrap();
67}
68
69impl FromStr for ShipHardpointModule {
70 type Err = ShipHardpointModuleError;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 if let Some(captures) = GRADED_HARDPOINT_MODULE_REGEX.captures(s) {
74 return Self::from_graded_hardpoint_captures(captures);
75 }
76
77 if let Some(captures) = HARDPOINT_MODULE_REGEX.captures(s) {
78 return Self::from_hardpoint_captures(captures);
79 };
80
81 Err(ShipHardpointModuleError::FailedToParse(s.to_string()))
82 }
83}
84
85impl ShipHardpointModule {
86 pub fn hardpoint_type(&self) -> HardpointType {
88 self.module.hardpoint_type()
89 }
90
91 pub fn is_full_sized_module(&self) -> bool {
93 self.module.is_full_sized()
94 }
95
96 pub fn is_utility_module(&self) -> bool {
98 self.module.is_utility()
99 }
100
101 pub fn is_powerplay_module(&self) -> bool {
103 self.module.is_powerplay_module()
104 }
105
106 pub fn is_guardian_module(&self) -> bool {
109 self.module.is_guardian_module()
110 }
111
112 fn from_graded_hardpoint_captures(
113 captures: Captures,
114 ) -> Result<ShipHardpointModule, ShipHardpointModuleError> {
115 let module = captures
116 .get(1)
117 .expect("Should have been captured already")
118 .as_str()
119 .parse()
120 .map_err(ShipHardpointModuleError::FailedToParseHardpointModule)?;
121
122 let class = captures
123 .get(3)
124 .expect("Should have been captured already")
125 .as_str()
126 .parse::<u8>()
127 .map_err(ShipHardpointModuleError::FailedToParseClassNumber)?
128 .try_into()?;
129
130 Ok(ShipHardpointModule {
131 module,
132 mounting: HardpointMounting::Turreted,
133 size: HardpointSize::Tiny,
134 class,
135 })
136 }
137
138 fn from_hardpoint_captures(
139 captures: Captures,
140 ) -> Result<ShipHardpointModule, ShipHardpointModuleError> {
141 let module = captures
142 .get(1)
143 .expect("Should have been captured already")
144 .as_str();
145
146 let module_suffix = captures
147 .get(5)
148 .map(|capture| capture.as_str())
149 .unwrap_or_default();
150
151 let mut module = format!("{module}{module_suffix}")
152 .parse()
153 .map_err(ShipHardpointModuleError::FailedToParseHardpointModule)?;
154
155 let size = captures
156 .get(4)
157 .expect("Should have been captured already")
158 .as_str()
159 .parse()?;
160
161 let mounting = match captures.get(3) {
163 Some(capture) => capture
164 .as_str()
165 .parse()
166 .map_err(ShipHardpointModuleError::FailedToParseHardpointMounting),
167 None => {
168 if matches!(size, HardpointSize::Tiny) {
169 Ok(HardpointMounting::Turreted)
170 } else {
171 Err(ShipHardpointModuleError::MissingMounting)
172 }
173 }
174 }?;
175
176 if module == HardpointModule::AXMultiCannon && mounting == HardpointMounting::Gimballed {
178 module = HardpointModule::EnhancedAXMultiCannon;
179 }
180
181 let class = Self::get_module_class(&module, &mounting, &size);
182
183 Ok(ShipHardpointModule {
184 module,
185 mounting,
186 class,
187 size,
188 })
189 }
190
191 #[rustfmt::skip]
192 fn get_module_class(
193 module: &HardpointModule,
194 mounting: &HardpointMounting,
195 size: &HardpointSize,
196 ) -> ModuleClass {
197 match (&module, &mounting, &size) {
198 (HardpointModule::PulseLaser, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::F,
200 (HardpointModule::PulseLaser, HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::G,
201 (HardpointModule::PulseLaser, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::G,
202 (HardpointModule::PulseLaser, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::E,
203 (HardpointModule::PulseLaser, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::F,
204 (HardpointModule::PulseLaser, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::F,
205 (HardpointModule::PulseDisruptorLaser, _, _) => ModuleClass::E,
206 (HardpointModule::PulseLaser, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::D,
207 (HardpointModule::PulseLaser, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::E,
208 (HardpointModule::PulseLaser, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::F,
209 (HardpointModule::PulseLaser, _, HardpointSize::Huge) => ModuleClass::A,
210
211 (HardpointModule::BurstLaser, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::F,
213 (HardpointModule::BurstLaser, HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::G,
214 (HardpointModule::BurstLaser, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::G,
215 (HardpointModule::CytoscramblerBurstLaser, _, _) => ModuleClass::F,
216 (HardpointModule::BurstLaser, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::E,
217 (HardpointModule::BurstLaser, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::F,
218 (HardpointModule::BurstLaser, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::F,
219 (HardpointModule::BurstLaser, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::D,
220 (HardpointModule::BurstLaser, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::E,
221 (HardpointModule::BurstLaser, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::E,
222 (HardpointModule::BurstLaser, _, HardpointSize::Huge) => ModuleClass::E,
223
224 (HardpointModule::Cannon, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
226 (HardpointModule::Cannon, HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::E,
227 (HardpointModule::Cannon, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::F,
228 (HardpointModule::Cannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
229 (HardpointModule::Cannon, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::D,
230 (HardpointModule::Cannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
231 (HardpointModule::Cannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
232 (HardpointModule::Cannon, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::C,
233 (HardpointModule::Cannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
234 (HardpointModule::Cannon, _, HardpointSize::Huge) => ModuleClass::B,
235
236 (HardpointModule::RemoteReleaseFlakLauncher, _, HardpointSize::Medium) => ModuleClass::B,
238 (HardpointModule::RemoteReleaseFlechetteLauncher, _, HardpointSize::Medium) => ModuleClass::B,
239 (HardpointModule::EnzymeMissileRack, _, HardpointSize::Medium) => ModuleClass::B,
240
241 (HardpointModule::AXMissileRack, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::E,
243 (HardpointModule::AXMissileRack, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::F,
244 (HardpointModule::EnhancedAXMissileRack, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
245 (HardpointModule::EnhancedAXMissileRack, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
246 (HardpointModule::AXMissileRack, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
247 (HardpointModule::AXMissileRack, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::E,
248 (HardpointModule::EnhancedAXMissileRack, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::B,
249 (HardpointModule::EnhancedAXMissileRack, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
250
251 (HardpointModule::AXMultiCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::E,
253 (HardpointModule::AXMultiCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::F,
254 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
255 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::E,
256 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
257 (HardpointModule::AXMultiCannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
258 (HardpointModule::AXMultiCannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::E,
259 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::B,
260 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::C,
261 (HardpointModule::EnhancedAXMultiCannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
262
263 (HardpointModule::GuardianGaussCannon, _, HardpointSize::Small) => ModuleClass::D,
265 (HardpointModule::GuardianGaussCannon, _, HardpointSize::Medium) => ModuleClass::B,
266
267 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
269 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::F,
270 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::B,
271 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
272 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
273 (HardpointModule::GuardianPlasmaCharger, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
274
275 (HardpointModule::GuardianShardCannon, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
277 (HardpointModule::GuardianShardCannon, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::F,
278 (HardpointModule::GuardianShardCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::A,
279 (HardpointModule::GuardianShardCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::A,
280 (HardpointModule::GuardianShardCannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
281 (HardpointModule::GuardianShardCannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
282
283 (HardpointModule::NaniteTorpedoPylon, _, HardpointSize::Medium) => ModuleClass::I,
285 (HardpointModule::NaniteTorpedoPylon, _, HardpointSize::Large) => ModuleClass::I,
286
287 (HardpointModule::ShockCannon, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
289 (HardpointModule::ShockCannon, HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::E,
290 (HardpointModule::ShockCannon, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::F,
291 (HardpointModule::ShockCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
292 (HardpointModule::ShockCannon, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::D,
293 (HardpointModule::ShockCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
294 (HardpointModule::ShockCannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
295 (HardpointModule::ShockCannon, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::C,
296 (HardpointModule::ShockCannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
297
298 (HardpointModule::FragmentCannon, _, HardpointSize::Small) => ModuleClass::E,
300 (HardpointModule::FragmentCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::A,
301 (HardpointModule::FragmentCannon, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::D,
302 (HardpointModule::FragmentCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::D,
303 (HardpointModule::FragmentCannon, _, HardpointSize::Large) => ModuleClass::C,
304 (HardpointModule::PacifierFragCannon, _, _) => ModuleClass::C,
305
306 (HardpointModule::MineLauncher, _, HardpointSize::Small) => ModuleClass::I,
308 (HardpointModule::ShockMineLauncher, _, HardpointSize::Small) => ModuleClass::I,
309 (HardpointModule::MineLauncher, _, HardpointSize::Medium) => ModuleClass::I,
310
311 (HardpointModule::MiningLaser, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
313 (HardpointModule::MiningLaser, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::D,
314 (HardpointModule::AbrasionBlaster, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::D,
315 (HardpointModule::AbrasionBlaster, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::D,
316 (HardpointModule::MiningLanceBeamLaser, _, _) => ModuleClass::D,
317 (HardpointModule::MiningLaser, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
318 (HardpointModule::MiningLaser, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::D,
319
320 (HardpointModule::DisplacementMissile, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::B,
322 (HardpointModule::DisplacementMissile, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::B,
323 (HardpointModule::DisplacementMissile, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::B,
324 (HardpointModule::DisplacementMissile, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::B,
325 (HardpointModule::SeismicCharge, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::B,
326 (HardpointModule::SeismicCharge, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::B,
327 (HardpointModule::SubSurfaceExtractionMissile, _, _) => ModuleClass::B,
328
329 (HardpointModule::MissileRack, _, HardpointSize::Small) => ModuleClass::B,
331 (HardpointModule::SeekerMissileRack, _, HardpointSize::Small) => ModuleClass::B,
332 (HardpointModule::AdvancedMissileRack, _, HardpointSize::Small) => ModuleClass::B,
333 (HardpointModule::MissileRack, _, HardpointSize::Medium) => ModuleClass::B,
334 (HardpointModule::SeekerMissileRack, _, HardpointSize::Medium) => ModuleClass::B,
335 (HardpointModule::AdvancedMissileRack, _, HardpointSize::Medium) => ModuleClass::B,
336 (HardpointModule::PackHoundMissileRack, _, _) => ModuleClass::B,
337 (HardpointModule::RocketPropelledFSDDisruptor, _, _) => ModuleClass::B,
338 (HardpointModule::MissileRack, _, HardpointSize::Large) => ModuleClass::A,
339 (HardpointModule::SeekerMissileRack, _, HardpointSize::Large) => ModuleClass::A,
340
341 (HardpointModule::MultiCannon, HardpointMounting::Fixed, HardpointSize::Small) => ModuleClass::F,
343 (HardpointModule::MultiCannon, HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::G,
344 (HardpointModule::MultiCannon, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::G,
345 (HardpointModule::AdvancedMultiCannon, _, HardpointSize::Small) => ModuleClass::F,
346 (HardpointModule::EnforcerCannon, _, _) => ModuleClass::F,
347 (HardpointModule::MultiCannon, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::E,
348 (HardpointModule::MultiCannon, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::F,
349 (HardpointModule::MultiCannon, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::F,
350 (HardpointModule::AdvancedMultiCannon, _, HardpointSize::Medium) => ModuleClass::E,
351 (HardpointModule::MultiCannon, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
352 (HardpointModule::MultiCannon, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::C,
353 (HardpointModule::MultiCannon, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::E,
354 (HardpointModule::MultiCannon, _, _) => ModuleClass::A,
355
356 (HardpointModule::PlasmaAccelerator, _, HardpointSize::Medium) => ModuleClass::C,
358 (HardpointModule::PlasmaAccelerator, _, HardpointSize::Large) => ModuleClass::B,
359 (HardpointModule::AdvancedPlasmaAccelerator, _, _) => ModuleClass::B,
360 (HardpointModule::PlasmaAccelerator, _, HardpointSize::Huge) => ModuleClass::A,
361 (HardpointModule::MkIIPlasmaShockAccelerator, _, HardpointSize::Large) => ModuleClass::B,
362
363 (HardpointModule::BeamLaser, HardpointMounting::Fixed | HardpointMounting::Gimballed, HardpointSize::Small) => ModuleClass::E,
365 (HardpointModule::BeamLaser, HardpointMounting::Turreted, HardpointSize::Small) => ModuleClass::F,
366 (HardpointModule::RetributorBeamLaser, _, _) => ModuleClass::E,
367 (HardpointModule::BeamLaser, HardpointMounting::Fixed, HardpointSize::Medium) => ModuleClass::D,
368 (HardpointModule::BeamLaser, HardpointMounting::Gimballed, HardpointSize::Medium) => ModuleClass::D,
369 (HardpointModule::BeamLaser, HardpointMounting::Turreted, HardpointSize::Medium) => ModuleClass::E,
370 (HardpointModule::BeamLaser, HardpointMounting::Fixed, HardpointSize::Large) => ModuleClass::C,
371 (HardpointModule::BeamLaser, HardpointMounting::Gimballed, HardpointSize::Large) => ModuleClass::C,
372 (HardpointModule::BeamLaser, HardpointMounting::Turreted, HardpointSize::Large) => ModuleClass::D,
373 (HardpointModule::BeamLaser, _, HardpointSize::Huge) => ModuleClass::A,
374
375 (HardpointModule::RailGun, _, HardpointSize::Small) => ModuleClass::D,
377 (HardpointModule::RailGun, _, HardpointSize::Medium) => ModuleClass::B,
378 (HardpointModule::ImperialHammerRailGun, _, _) => ModuleClass::B,
379
380 (HardpointModule::TorpedoPylon, _, HardpointSize::Small) => ModuleClass::I,
382 (HardpointModule::TorpedoPylon, _, HardpointSize::Medium) => ModuleClass::I,
383 (HardpointModule::TorpedoPylon, _, HardpointSize::Large) => ModuleClass::I,
384
385 (HardpointModule::ChaffLauncher, _, _) => ModuleClass::I,
387
388 (HardpointModule::ElectronicCountermeasures, _, _) => ModuleClass::F,
390
391 (HardpointModule::EnhancedXenoScanner, _, _) => ModuleClass::C,
393 (HardpointModule::PulseWaveXenoScanner, _, _) => ModuleClass::C,
394 (HardpointModule::ShutdownFieldNeutralizer, _, _) => ModuleClass::F,
395 (HardpointModule::ThargoidPulseNeutralizer, _, _) => ModuleClass::E,
396 (HardpointModule::XenoScanner, _, _) => ModuleClass::E,
397
398 (HardpointModule::CausticSinkLauncher, _, _) => ModuleClass::I,
400 (HardpointModule::HeatsinkLauncher, _, _) => ModuleClass::I,
401
402 (HardpointModule::PointDefenceTurret, _, _) => ModuleClass::I,
404
405 _ => ModuleClass::E,
406 }
407 }
408}
409
410from_str_deserialize_impl!(ShipHardpointModule);
411
412impl Display for ShipHardpointModule {
413 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
414 if let HardpointSize::Tiny = self.size {
415 return write!(f, "{}{} {}", self.size.size_nr(), self.class, self.module);
416 }
417
418 write!(
419 f,
420 "{}{}/{} {}",
421 self.size.size_nr(),
422 self.class,
423 self.mounting,
424 self.module
425 )
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use std::str::FromStr;
432
433 use crate::modules::ship::{
434 HardpointModule, HardpointMounting, HardpointSize, ModuleClass, ShipHardpointModule,
435 };
436
437 #[test]
438 fn ship_hardpoint_module_test_cases_all_pass() {
439 let test_cases = [
440 (
441 "$hpt_beamlaser_gimbal_medium_name;",
442 ShipHardpointModule {
443 module: HardpointModule::BeamLaser,
444 mounting: HardpointMounting::Gimballed,
445 size: HardpointSize::Medium,
446 class: ModuleClass::D,
447 },
448 ),
449 (
450 "$hpt_heatsinklauncher_turret_tiny_name;",
451 ShipHardpointModule {
452 module: HardpointModule::HeatsinkLauncher,
453 mounting: HardpointMounting::Turreted,
454 size: HardpointSize::Tiny,
455 class: ModuleClass::I,
456 },
457 ),
458 (
459 "Hpt_CausticSinkLauncher_Turret_Tiny",
460 ShipHardpointModule {
461 module: HardpointModule::CausticSinkLauncher,
462 mounting: HardpointMounting::Turreted,
463 size: HardpointSize::Tiny,
464 class: ModuleClass::I,
465 },
466 ),
467 (
468 "$hpt_chafflauncher_tiny_name;",
469 ShipHardpointModule {
470 module: HardpointModule::ChaffLauncher,
471 mounting: HardpointMounting::Turreted,
472 size: HardpointSize::Tiny,
473 class: ModuleClass::I,
474 },
475 ),
476 (
477 "$hpt_shieldbooster_size0_class5_name;",
478 ShipHardpointModule {
479 module: HardpointModule::ShieldBooster,
480 mounting: HardpointMounting::Turreted,
481 size: HardpointSize::Tiny,
482 class: ModuleClass::A,
483 },
484 ),
485 (
486 "Hpt_Guardian_GaussCannon_Fixed_Medium",
487 ShipHardpointModule {
488 module: HardpointModule::GuardianGaussCannon,
489 mounting: HardpointMounting::Fixed,
490 size: HardpointSize::Medium,
491 class: ModuleClass::B,
492 },
493 ),
494 (
495 "Hpt_CargoScanner_Size0_Class1",
496 ShipHardpointModule {
497 module: HardpointModule::ManifestScanner,
498 mounting: HardpointMounting::Turreted,
499 size: HardpointSize::Tiny,
500 class: ModuleClass::E,
501 },
502 ),
503 (
504 "Hpt_PlasmaAccelerator_Fixed_Large_Advanced",
505 ShipHardpointModule {
506 module: HardpointModule::AdvancedPlasmaAccelerator,
507 mounting: HardpointMounting::Fixed,
508 size: HardpointSize::Large,
509 class: ModuleClass::B,
510 },
511 ),
512 (
513 "Hpt_MkIIPlasmaShockAutocannon_Fixed_Large",
514 ShipHardpointModule {
515 module: HardpointModule::MkIIPlasmaShockAccelerator,
516 mounting: HardpointMounting::Fixed,
517 size: HardpointSize::Large,
518 class: ModuleClass::B,
519 },
520 ),
521 ];
522
523 for (case, expected) in test_cases {
524 let result = ShipHardpointModule::from_str(case);
525
526 dbg!(&result);
527 assert!(result.is_ok());
528 assert_eq!(result.unwrap(), expected);
529 }
530 }
531
532 #[test]
533 fn incorrect_combination_of_missing_mounting_type_and_size_is_rejected() {
534 let result = ShipHardpointModule::from_str("$hpt_chafflauncher_mediun_name;");
535 assert!(result.is_err());
536 }
537}