z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
0dfe100
rust-ice
1use anyhow::{ anyhow, Result }; 2use directories:: BaseDirs ; 3use serde:: Deserialize ; 4use serde_with::{ serde_as, DisplayFromStr }; 5use std:: collections:: HashMap ; 6use std:: fs; 7use std:: path:: PathBuf ; 8use thiserror:: Error ; 9 10use crate :: types:: ModIdent ; 11use crate :: App ; 12 13# [ derive ( Debug )] 14pub struct Config { 15pub auto_download : bool , 16pub game_dir : Option < PathBuf >, 17pub mods_dir : PathBuf , 18pub portal_auth : Option < PortalAuth >, 19pub sets : ModSets , 20pub sync_latest_versions : bool , 21} 22 23impl Config { 24pub fn new ( app : & App ) ->Result < Self > { 25// Input 26let config_file =ConfigFile :: new ( & app. config ) ?. unwrap_or_default (); 27 28// Merge config options 29let game_dir = app. game_dir . clone (). or ( config_file. game_dir ); 30Ok ( Config { 31auto_download : config_file. auto_download , 32game_dir : game_dir. clone (), 33mods_dir : match [ 34 game_dir. clone (), 35 app. mods_dir . clone (). or ( config_file. mods_dir ), 36] { 37[ _, Some ( mods_dir)] if mods_dir. exists () => mods_dir, 38[ Some ( game_dir), None ] if game_dir. exists () =>{ 39let mut mods_dir = game_dir; 40 mods_dir. push ( "mods" ); 41if !mods_dir. exists () { 42return Err ( anyhow! ( "Could not find mods directory" )); 43} 44 mods_dir 45} 46 _ =>return Err ( anyhow! ( "Invalid game or mods directories" )), 47}, 48portal_auth : config_file. portal . or_else ( ||{ 49if let Some ( game_dir) =& game_dir{ 50let mut player_data_path = game_dir. clone (); 51 player_data_path. push ( "player-data.json" ); 52if player_data_path. exists () { 53let player_data_json = fs:: read_to_string ( & player_data_path). ok () ?; 54if let PlayerDataJson { 55service_token : Some ( token), 56service_username : Some ( username), 57} = serde_json:: from_str ( & player_data_json). ok () ? 58{ 59return Some ( PortalAuth { token, username}); 60} 61} 62} 63None 64}), 65sets : config_file. sets , 66sync_latest_versions : config_file. sync_latest_versions , 67}) 68} 69} 70 71# [ derive ( Debug , Deserialize )] 72pub struct PortalAuth { 73pub token : String , 74pub username : String , 75} 76 77pub type ModSets =Option < HashMap < String , Vec < ModIdent >>>; 78 79# [ serde_as ] 80# [ derive ( Deserialize , Default )] 81struct ConfigFile { 82# [ serde ( default )] 83auto_download : bool , 84game_dir : Option < PathBuf >, 85mods_dir : Option < PathBuf >, 86portal : Option < PortalAuth >, 87# [ serde_as ( as = "Option<HashMap<_, Vec<DisplayFromStr>>>" )] 88sets : ModSets , 89# [ serde ( default )] 90sync_latest_versions : bool , 91} 92 93impl ConfigFile { 94pub fn new ( path : & Option < PathBuf >) ->Result < Option < Self >, ConfigFileErr > { 95let config_path: Option < PathBuf > = path 96. clone () 97. or_else ( ||{ 98BaseDirs :: new (). map ( |base_dirs|{ 99let mut config_path: PathBuf = base_dirs. config_dir (). into (); 100 config_path. push ( "fmm" ); 101 config_path. push ( "fmm.toml" ); 102 config_path 103}) 104}) 105. filter ( |config_path| config_path. exists ()); 106 107if config_path. is_none () { 108return Ok ( None ); 109} 110 111let file = fs:: read_to_string ( config_path. unwrap ()). map_err ( |_|ConfigFileErr :: Open ) ?; 112 113let config: ConfigFile = toml:: from_str ( & file). map_err ( |_|ConfigFileErr :: ParseFile ) ?; 114Ok ( Some ( config)) 115} 116} 117 118# [ derive ( Debug , Error )] 119pub enum ConfigFileErr { 120# [ error ( "Could not open config file." )] 121Open , 122# [ error ( "Could not parse config file." )] 123ParseFile , 124} 125 126# [ derive ( Deserialize )] 127# [ serde ( rename_all = "kebab-case" )] 128struct PlayerDataJson { 129service_username : Option < String >, 130service_token : Option < String >, 131}