z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
ee3d597
rewrite
1use once_cell:: sync:: OnceCell ; 2use regex:: Regex ; 3use semver:: VersionReq ; 4use serde::{ Deserialize , Serialize }; 5use std:: fmt:: Display ; 6use std:: str:: FromStr ; 7use thiserror:: Error ; 8 9# [ derive ( Debug , Deserialize , PartialEq , Serialize )] 10pub struct ModDependency { 11pub dep_type : ModDependencyType , 12pub name : String , 13pub version_req : Option < VersionReq >, 14} 15 16impl FromStr for ModDependency { 17type Err =ModDependencyErr ; 18 19fn from_str ( s : & str ) ->Result < Self , Self :: Err > { 20// Avoid creating the regex object every time 21static DEP_STRING_REGEX : OnceCell < Regex > =OnceCell :: new (); 22let captures =DEP_STRING_REGEX 23. get_or_init ( ||{ 24Regex :: new ( 25r"^(?:(?P<type>[!?~]|\(\?\)) *)?(?P<name>(?: *[a-zA-Z0-9_-]+)+(?: *$)?)(?: *(?P<version_req>[<>=]=?) *(?P<version>(?:\d+\.){1,2}\d+))?$" , 26). unwrap () 27}) 28. captures ( s) 29. ok_or_else ( ||ModDependencyErr :: InvalidDependencyString ( s. to_string ())) ?; 30 31Ok ( Self { 32dep_type : match captures. name ( "type" ). map ( |mtch| mtch. as_str ()) { 33None =>ModDependencyType :: Required , 34Some ( "!" ) =>ModDependencyType :: Incompatible , 35Some ( "~" ) =>ModDependencyType :: NoLoadOrder , 36Some ( "?" ) =>ModDependencyType :: Optional , 37Some ( "(?)" ) =>ModDependencyType :: OptionalHidden , 38Some ( str) =>return Err ( ModDependencyErr :: UnknownModifier ( str. to_string ())), 39}, 40name : match captures. name ( "name" ) { 41Some ( mtch) => mtch. as_str (). to_string (), 42None =>return Err ( ModDependencyErr :: NameIsUnparsable ( s. to_string ())), 43}, 44version_req : match [ captures. name ( "version_req" ), captures. name ( "version" )] { 45[ Some ( req_match), Some ( version_match)] =>{ 46// Factorio does not sanitize leading zeros, so we must do it ourselves 47let version_str = version_match. as_str (); 48let sanitized = version_str 49. split ( '.' ) 50. map ( |sub|{ 51Ok ( sub 52. parse ::< usize >() 53. map_err ( |_|{ 54ModDependencyErr :: InvalidDependencyString ( s. to_string ()) 55}) ? 56. to_string ()) 57}) 58. intersperse ( Ok ( "." . to_string ())) 59. collect ::< Result < String , ModDependencyErr >>() ?; 60 61// Assemble version req from parts 62let mut version_req =String :: new (); 63 version_req. push_str ( req_match. as_str ()); 64 version_req. push ( ' ' ); 65 version_req. push_str ( & sanitized); 66 67match VersionReq :: parse ( & version_req) { 68Ok ( version_req) =>Some ( version_req), 69Err ( _) =>{ 70return Err ( ModDependencyErr :: InvalidVersionReq ( 71 req_match. as_str (). to_string (), 72)) 73} 74} 75} 76 _ =>None , 77}, 78}) 79} 80} 81 82impl Display for ModDependency { 83fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ >) -> std:: fmt:: Result { 84write! ( 85 f, 86"{}{}{}" , 87match self . dep_type { 88ModDependencyType :: Incompatible =>"! " , 89ModDependencyType :: NoLoadOrder =>"~ " , 90ModDependencyType :: Optional =>"? " , 91ModDependencyType :: OptionalHidden =>"(?) " , 92ModDependencyType :: Required =>"" , 93}, 94self . name , 95match & self . version_req { 96Some ( version_req) =>format! ( " {}" , version_req), 97None =>String :: new (), 98} 99) 100} 101} 102 103# [ derive ( Debug , Deserialize , PartialEq , Serialize )] 104pub enum ModDependencyType { 105Incompatible , 106NoLoadOrder , 107Optional , 108OptionalHidden , 109Required , 110} 111 112# [ derive ( Clone , Debug , Error )] 113pub enum ModDependencyErr { 114# [ error ( "Invalid dependency string: `{0}`" )] 115InvalidDependencyString ( String ), 116# [ error ( "Invalid dependency version requirement: `{0}`" )] 117InvalidVersionReq ( String ), 118# [ error ( "Dependency name could not be parsed: `{0}`" )] 119NameIsUnparsable ( String ), 120# [ error ( "Unknown dependency modifier: `{0}`" )] 121UnknownModifier ( String ), 122}