1+ use std:: collections:: HashMap ;
12use std:: path:: Path ;
23
34use anyhow:: Context ;
@@ -20,6 +21,72 @@ pub struct EquipmentStats {
2021 pub bv : Option < i32 > ,
2122}
2223
24+ /// Build a mapping from clean JSON slugs to MegaMek DB slugs.
25+ ///
26+ /// MegaMek uses internal names like `CLERLargeLaser` or `ISUltraAC5` which get
27+ /// slugified to `clerlargelaser` / `isultraac5`. The JSON uses human-readable
28+ /// slugs like `clan-er-large-laser` / `ultra-autocannon-5`. This table bridges
29+ /// the two naming conventions.
30+ fn slug_aliases ( ) -> HashMap < & ' static str , & ' static str > {
31+ HashMap :: from ( [
32+ // Clan energy weapons
33+ ( "clan-er-large-laser" , "clerlargelaser" ) ,
34+ ( "clan-er-medium-laser" , "clermediumlaser" ) ,
35+ ( "clan-er-small-laser" , "clersmalllaser" ) ,
36+ ( "clan-er-ppc" , "clerppc" ) ,
37+ ( "clan-large-pulse-laser" , "cllargepulselaser" ) ,
38+ ( "clan-medium-pulse-laser" , "clmediumpulselaser" ) ,
39+ ( "clan-small-pulse-laser" , "clsmallpulselaser" ) ,
40+ ( "clan-er-flamer" , "clerflamer" ) ,
41+ ( "clan-plasma-cannon" , "clplasmacannon" ) ,
42+ // IS energy weapons (pulse)
43+ ( "pulse-large-laser" , "islargepulselaser" ) ,
44+ ( "pulse-medium-laser" , "ismediumpulselaser" ) ,
45+ ( "pulse-small-laser" , "issmallpulselaser" ) ,
46+ // IS ballistic weapons
47+ ( "ultra-autocannon-2" , "isultraac2" ) ,
48+ ( "ultra-autocannon-5" , "isultraac5" ) ,
49+ ( "ultra-autocannon-10" , "isultraac10" ) ,
50+ ( "ultra-autocannon-20" , "isultraac20" ) ,
51+ ( "rotary-autocannon-5" , "isrotaryac5" ) ,
52+ ( "light-autocannon-5" , "light-ac-5" ) ,
53+ // Clan ballistic weapons
54+ ( "clan-ultra-autocannon-2" , "clultraac2" ) ,
55+ ( "clan-ultra-autocannon-5" , "clultraac5" ) ,
56+ ( "clan-ultra-autocannon-10" , "clultraac10" ) ,
57+ ( "clan-ultra-autocannon-20" , "clultraac20" ) ,
58+ ( "clan-lb-2-x-ac" , "cllbxac2" ) ,
59+ ( "clan-lb-5-x-ac" , "cllbxac5" ) ,
60+ ( "clan-lb-10-x-ac" , "cllbxac10" ) ,
61+ ( "clan-lb-20-x-ac" , "cllbxac20" ) ,
62+ ( "clan-gauss-rifle" , "clgaussrifle" ) ,
63+ // Clan missile weapons
64+ ( "clan-srm-2" , "clsrm2" ) ,
65+ ( "clan-srm-4" , "clsrm4" ) ,
66+ ( "clan-srm-6" , "clsrm6" ) ,
67+ ( "clan-lrm-5" , "cllrm5" ) ,
68+ ( "clan-lrm-10" , "cllrm10" ) ,
69+ ( "clan-lrm-15" , "cllrm15" ) ,
70+ ( "clan-lrm-20" , "cllrm20" ) ,
71+ ( "clan-streak-srm-2" , "clstreaksrm2" ) ,
72+ ( "clan-streak-srm-4" , "clstreaksrm4" ) ,
73+ ( "clan-streak-srm-6" , "clstreaksrm6" ) ,
74+ ( "clan-arrow-iv" , "clarrowiv" ) ,
75+ // IS missile
76+ ( "narc-missile-beacon" , "narc" ) ,
77+ // Electronics / equipment
78+ ( "guardian-ecm-suite" , "isguardianecmsuite" ) ,
79+ ( "clan-ecm-suite" , "clecmsuite" ) ,
80+ ( "beagle-active-probe" , "beagleactiveprobe" ) ,
81+ ( "clan-active-probe" , "clactiveprobe" ) ,
82+ ( "clan-anti-missile-system" , "clantimissilesystem" ) ,
83+ ( "targeting-computer" , "istargeting-computer" ) ,
84+ ( "artemis-iv-fcs" , "isartemisiv" ) ,
85+ ( "c3-master-computer" , "isc3mastercomputer" ) ,
86+ ( "c3-slave-unit" , "isc3slaveunit" ) ,
87+ ] )
88+ }
89+
2390pub async fn run ( file : & Path , database_url : & str , pool_size : u32 , force : bool ) -> anyhow:: Result < ( ) > {
2491 let pool = sqlx:: postgres:: PgPoolOptions :: new ( )
2592 . max_connections ( pool_size)
@@ -34,23 +101,37 @@ pub async fn run(file: &Path, database_url: &str, pool_size: u32, force: bool) -
34101
35102 info ! ( count = entries. len( ) , "loaded equipment stats entries" ) ;
36103
104+ let aliases = slug_aliases ( ) ;
37105 let mut updated = 0u32 ;
38- let skipped = 0u32 ;
39106 let mut not_found = 0u32 ;
40107 let mut unchanged = 0u32 ;
108+ let mut alias_hits = 0u32 ;
41109
42110 for entry in & entries {
43- let exists = sqlx:: query ( "SELECT id FROM equipment WHERE slug = $1" )
111+ // Try exact slug match first, then alias fallback
112+ let mut row = sqlx:: query ( "SELECT id FROM equipment WHERE slug = $1" )
44113 . bind ( & entry. slug )
45114 . fetch_optional ( & pool)
46115 . await ?;
47116
48- let Some ( row) = exists else {
117+ if row. is_none ( ) {
118+ if let Some ( & alt) = aliases. get ( entry. slug . as_str ( ) ) {
119+ row = sqlx:: query ( "SELECT id FROM equipment WHERE slug = $1" )
120+ . bind ( alt)
121+ . fetch_optional ( & pool)
122+ . await ?;
123+ if row. is_some ( ) {
124+ alias_hits += 1 ;
125+ }
126+ }
127+ }
128+
129+ let Some ( r) = row else {
49130 warn ! ( slug = %entry. slug, "no matching equipment row" ) ;
50131 not_found += 1 ;
51132 continue ;
52133 } ;
53- let eq_id: i32 = row . try_get ( "id" ) ?;
134+ let eq_id: i32 = r . try_get ( "id" ) ?;
54135
55136 if force {
56137 let result = sqlx:: query (
@@ -129,14 +210,11 @@ pub async fn run(file: &Path, database_url: &str, pool_size: u32, force: bool) -
129210
130211 info ! (
131212 updated,
132- skipped ,
213+ alias_hits ,
133214 not_found,
134215 unchanged,
135216 "equipment seed complete"
136217 ) ;
137218
138- // Suppress unused variable warning
139- let _ = skipped;
140-
141219 Ok ( ( ) )
142220}
0 commit comments