z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
216c7c7
rust-ice
1#! [ feature ( iter_intersperse )] 2 3use std:: fs; 4use std:: path:: PathBuf ; 5 6use anyhow:: Result ; 7use console:: style; 8use reqwest:: blocking:: Client ; 9use semver:: Version ; 10use structopt:: StructOpt ; 11 12mod config; 13mod dependency; 14mod directory; 15mod download; 16mod read; 17mod sync; 18mod types; 19 20use config:: * ; 21use directory:: * ; 22use types:: * ; 23 24# [ derive ( Clone , StructOpt )] 25# [ structopt ( name = "fmm" , about = "Manage your Factorio mods." )] 26pub struct App { 27/// The path to the configuration file 28# [ structopt ( long )] 29config : Option < PathBuf >, 30/// Disables all mods in the directory 31# [ structopt ( short = "o" , long )] 32disable_all : bool , 33/// Disables the given mods. Mods are formatted as `Name` 34# [ structopt ( short , long )] 35disable : Vec < ModIdent >, 36/// Enables all mods in the directory 37# [ structopt ( short = "a" , long )] 38enable_all : bool , 39/// Enables the mods in the given set 40# [ structopt ( short = "E" , long )] 41enable_set : Option < String >, 42/// Enables the given mods. Mods are formatted as `Name` or `Name@Version` 43# [ structopt ( short , long )] 44enable : Vec < ModIdent >, 45/// The game directory to manipulate. Optional if a configuration file is in use 46# [ structopt ( long )] 47game_dir : Option < PathBuf >, 48/// Lists all mods in the directory 49# [ structopt ( short , long )] 50list : bool , 51/// The mods directory to manipulate. Optional if a configuration file is in use 52# [ structopt ( long )] 53mods_dir : Option < PathBuf >, 54/// Removes the given mods from the mods directory. Mods are formatted as `Name` or `Name@Version` 55# [ structopt ( short , long )] 56remove : Vec < ModIdent >, 57/// A path to a save file to sync with 58# [ structopt ( short , long )] 59sync : Option < PathBuf >, 60} 61 62// DESIGN NOTES: 63// - Get a list of all mods + versions in the folder _without_ reading the ZIP files (use filenames) 64// - Only read ZIPs if we need to get dependencies or other info 65// - Cache will only be used once we have advanced features that would benefit from it 66 67fn main () ->Result <()> { 68let mut app =App :: from_args (); 69let client =Client :: new (); 70 71let config =Config :: new ( & app) ?; 72 73let mut directory =Directory :: new ( config. mods_dir . clone ()) ?; 74 75// List mods 76if app. list { 77let mut lines: Vec < String > = directory 78. mods 79. iter () 80. flat_map ( |( mod_name, mod_versions) |{ 81 mod_versions 82. iter () 83. map ( |version|format! ( "{} v{}" , mod_name, version. version )) 84. collect ::< Vec < String >>() 85}) 86. collect (); 87 88 lines. sort (); 89 90for linein lines{ 91println! ( "{}" , line) 92} 93} 94 95// Combine enable commands 96let mut combined_enable =vec! []; 97// Enable set 98if let Some ( set_name) = app. enable_set { 99if let Some ( sets) = config. sets . as_ref () { 100if let Some ( set) = sets. get ( & set_name) { 101 combined_enable. append ( & mut set. to_vec ()); 102} 103} 104} 105// Sync with save 106if let Some ( sync_path) = app. sync { 107let save_file = sync:: SaveFile :: from ( sync_path) ?; 108 109let mut mods = save_file. mods . to_vec (); 110 111if config. sync_latest_versions { 112for mod_identin mods. iter_mut () { 113 mod_ident. version_req =None ; 114} 115} 116 117 combined_enable. append ( & mut mods); 118} 119// Manually enable 120 combined_enable. append ( & mut app. enable . to_vec ()); 121 app. enable = combined_enable; 122 123// Remove specified mods 124for mod_identin app. remove { 125if mod_ident. name !="base" { 126 directory. remove ( & mod_ident); 127} 128} 129 130// Disable all mods 131if app. disable_all { 132 directory. disable_all (); 133} 134 135// Disable specified mods 136for mod_identin app. disable { 137 directory. disable ( & mod_ident); 138} 139 140// Enable all mods 141if app. enable_all { 142 directory. enable_all (); 143} 144 145// Enable and/or download specified mods 146let mut cycle_orders: Vec < ManageOrder > = app 147. enable 148. iter () 149. map ( |mod_ident|ManageOrder :: Enable ( mod_ident. clone ())) 150. collect (); 151 152while !cycle_orders. is_empty () { 153 cycle_orders = cycle_orders 154. iter_mut () 155. filter ( |order| order. get_name () !="base" ) 156. filter_map ( |order|match order{ 157ManageOrder :: Download ( mod_ident) =>{ 158if config. auto_download { 159if download:: download_mod ( mod_ident, & mut directory, & config, & client) 160. ok () ? 161{ 162Some ( vec! [ ManageOrder :: Enable ( mod_ident. clone ())]) 163} else { 164None 165} 166} else { 167println! ( "{} {}" , style ( "Did not download" ). red (), mod_ident. name ); 168None 169} 170} 171ManageOrder :: Enable ( mod_ident) => directory. enable ( mod_ident), 172}) 173. flatten () 174. collect (); 175} 176 177// Write mod-list.json 178 fs:: write ( 179& directory. mod_list_path , 180 serde_json:: to_string_pretty ( & ModListJson { 181mods : directory. mod_list , 182}) ?, 183) ?; 184 185Ok (()) 186} 187 188trait HasVersion { 189fn get_version ( & self ) ->& Version ; 190} 191 192fn get_mod_version < ' a , T >( list : &' a [ T ], mod_ident : & ModIdent ) ->Option < &' a T > 193where 194T : HasVersion , 195{ 196if let Some ( version_req) =& mod_ident. version_req { 197 list. iter () 198. rev () 199. find ( |entry| version_req. matches ( entry. get_version ())) 200} else { 201 list. last () 202} 203}