char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
51266f9
main
1use std:: cell:: RefCell ; 2use std:: collections::{ BTreeMap , BTreeSet }; 3use std:: rc:: Rc ; 4use std:: sync:: Arc ; 5 6use crate :: authority:: SourceAuthority ; 7use crate :: package_config::{ PackageConfig , Source }; 8use crate :: registry::{ PackageStore , checksum}; 9use avril_core:: Error ; 10use url:: Url ; 11 12use crate :: local_packages::{ 13PackageMapping , Resolution , declared_name, package_mapping, select_binding, 14}; 15 16/// A remote mapping step: an already-decided resolution, or an export of 17/// a directory by key. The adapters acquire (`export`) or reuse prepared 18/// metadata (`resolve_export_meta`) afterwards. 19enum RemoteStep { 20Done ( Resolution ), 21Export ( Url , String ), 22} 23 24struct Package { 25requested : Url , 26directory : Url , 27identity : ( Url , String ), 28config : PackageConfig , 29source : String , 30url : Url , 31bindings : RefCell < Option < BTreeMap < String , Source >>>, 32} 33 34impl Package { 35fn region ( & self , name : & str ) ->Option < & Url > { 36if name. starts_with ( self . directory . as_str ()) { 37Some ( & self . directory ) 38} else if name. starts_with ( self . requested . as_str ()) { 39Some ( & self . requested ) 40} else { 41None 42} 43} 44} 45 46# [ derive ( Clone )] 47struct Owner { 48package : Rc < Package >, 49region : Url , 50} 51 52pub struct RemotePackages { 53authority : SourceAuthority , 54store : Arc < PackageStore >, 55packages : RefCell < BTreeMap < Url , Rc < Package >>>, 56owners : RefCell < BTreeMap < String , Option < Owner >>>, 57authorities : RefCell < BTreeMap <( Url , String ), BTreeSet < Url >>>, 58} 59 60impl RemotePackages { 61pub fn new ( authority : SourceAuthority , store : Arc < PackageStore >) ->Self { 62Self { 63 authority, 64 store, 65packages : RefCell :: default (), 66owners : RefCell :: default (), 67authorities : RefCell :: default (), 68} 69} 70 71fn check ( & self , package : & Package ) ->Result <(), Error > { 72if let Some ( urls) =self . authorities . borrow (). get ( & package. identity ) { 73for urlin urls{ 74self . store 75. check_acquired_remote ( url. as_str (), & self . authority ) 76. map_err ( |error|Error :: Module ( error. to_string ())) ?; 77} 78} 79self . store 80. check_acquired_remote ( package. url . as_str (), & self . authority ) 81. map_err ( |error|Error :: Module ( error. to_string ())) 82} 83 84async fn load ( & self , directory : & Url ) ->Result < Rc < Package >, Error > { 85let config_url = directory 86. join ( "avril.conf" ) 87. map_err ( |error|Error :: Module ( error. to_string ())) ?; 88self . authority 89. check_remote_module ( & config_url) 90. map_err ( |error|Error :: Module ( error. to_string ())) ?; 91if let Some ( package) =self . packages . borrow (). get ( directory). cloned () { 92self . check ( & package) ?; 93return Ok ( package); 94} 95let source =self 96. store 97. remote_config ( config_url. as_str ()) 98. await 99. map_err ( |error|Error :: Module ( error. to_string ())) ?; 100let base =Url :: parse ( 101& self 102. store 103. remote_base ( config_url. as_str ()) 104. map_err ( |error|Error :: Module ( error. to_string ())) ?, 105) 106. map_err ( |error|Error :: Module ( error. to_string ())) ?; 107let config =PackageConfig :: parse ( & source, & base) 108. map_err ( |error|Error :: Module ( error. render ( config_url. as_str (), & source))) ?; 109let package =Rc :: new ( Package { 110requested : directory. clone (), 111directory : base. join ( "./" ). unwrap (), 112identity : ( base, checksum ( source. as_bytes ())), 113 config, 114 source, 115url : config_url, 116bindings : RefCell :: default (), 117}); 118self . check ( & package) ?; 119Ok ( self 120. packages 121. borrow_mut () 122. entry ( directory. clone ()) 123. or_insert ( package) 124. clone ()) 125} 126 127pub async fn bindings ( 128& self , 129config : & PackageConfig , 130) ->Result < BTreeMap < String , Source >, crate :: config:: Error > { 131 config 132. bindings ( |source|async move { 133match source{ 134Source :: Local ( directory) =>declared_name ( & directory, & self . authority ), 135Source :: Http ( directory) =>self 136. load ( & directory) 137. await 138. map ( |package| package. config . name . clone ()) 139. map_err ( |error| error. to_string ()), 140Source :: Jsr ( _) =>unreachable! (), 141Source :: Npm ( _) =>unreachable! (), 142} 143}) 144. await 145} 146 147fn owner ( & self , name : & str ) ->Option < Owner > { 148self . owners . borrow (). get ( name). cloned (). flatten () 149} 150 151fn claim ( & self , name : & str , owner : Option < Owner >) ->Result <(), Error > { 152let mut owners =self . owners . borrow_mut (); 153if let Some ( previous) = owners. get ( name) { 154let same =match ( previous, & owner) { 155( None , None ) =>true , 156( Some ( previous), Some ( owner)) =>{ 157 previous. package . identity == owner. package . identity 158 && previous. region == owner. region 159} 160 _ =>false , 161}; 162if !same{ 163let label = |owner : & Option < Owner > |{ 164 owner 165. as_ref () 166. map ( |owner| owner. package . url . as_str ()) 167. unwrap_or ( "plain HTTP module" ) 168. to_owned () 169}; 170return Err ( Error :: Module ( format! ( 171"Conflicting remote package ownership for {name}: {} versus {}" , 172label ( previous), 173label ( & owner) 174))); 175} 176} 177if let Some ( owner) =& owner{ 178self . check ( & owner. package ) ?; 179self . authorities 180. borrow_mut () 181. entry ( owner. package . identity . clone ()) 182. or_default () 183. insert ( owner. package . url . clone ()); 184} 185 owners. entry ( name. to_owned ()). or_insert ( owner); 186Ok (()) 187} 188 189async fn owner_for ( & self , name : & str , mut directory : Url ) ->Result < Owner , Error > { 190let mut seen =BTreeSet :: new (); 191loop { 192if !seen. insert ( directory. clone ()) { 193return Err ( Error :: Module ( format! ( 194"Cyclic remote package boundary for {name}: {directory}" 195))); 196} 197let package =self . load ( & directory). await ?; 198let region = package 199. region ( name) 200. ok_or_else ( ||{ 201Error :: Module ( format! ( "Module is outside its remote package: {name}" )) 202}) ? 203. clone (); 204let length = region. as_str (). len (); 205let child = package 206. config 207. dependencies 208. iter () 209. filter_map ( |dependency|match & dependency. source { 210Source :: Http ( directory) 211if name. starts_with ( directory. as_str ()) 212 && directory. as_str (). len () > length => 213{ 214Some ( directory) 215} 216 _ =>None , 217}) 218. max_by_key ( |directory| directory. as_str (). len ()) 219. cloned (); 220match child{ 221Some ( child) => directory = child, 222None =>return Ok ( Owner { package, region}), 223} 224} 225} 226 227pub async fn import ( & self , name : & str , referrer : & str , local : & [ Url ]) ->Result <(), Error > { 228if name. starts_with ( self . store . options (). jsr_registry . as_str ()) { 229return self . claim ( name, None ); 230} 231let owner =self . owner ( referrer); 232let mut selected = owner 233. as_ref () 234. filter ( |owner| name. starts_with ( owner. region . as_str ())) 235. map ( |owner|( owner. region . as_str (). len (), owner. package . requested . clone ())); 236let declarations: Vec < _ > =match & owner{ 237Some ( owner) =>{ 238self . check ( & owner. package ) ?; 239 owner 240. package 241. config 242. dependencies 243. iter () 244. filter_map ( |dependency|match & dependency. source { 245Source :: Http ( directory) =>Some ( directory. clone ()), 246 _ =>None , 247}) 248. collect () 249} 250None => local. to_vec (), 251}; 252for directoryin declarations{ 253let length = directory. as_str (). len (); 254if name. starts_with ( directory. as_str ()) 255 && selected 256. as_ref () 257. is_none_or ( |( previous, _) | length >* previous) 258{ 259 selected =Some (( length, directory)); 260} 261} 262let owner =match selected{ 263Some (( _, directory)) =>Some ( self . owner_for ( name, directory). await ?), 264None =>None , 265}; 266self . claim ( name, owner) 267} 268 269pub async fn export ( 270& self , 271directory : & Url , 272key : & str , 273specifier : & str , 274) ->Result < Resolution , Error > { 275let package =self . load ( directory). await ?; 276let name =self . export_target ( & package, key, specifier) ?; 277let owner =self . owner_for ( & name, directory. clone ()). await ?; 278self . claim ( & name, Some ( owner)) ?; 279Ok ( Resolution :: Remote ( name)) 280} 281 282pub async fn initialize ( & self , name : & str ) ->Result <(), Error > { 283let owner =self 284. owners 285. borrow_mut () 286. entry ( name. to_owned ()) 287. or_default () 288. clone (); 289if let Some ( owner) = owner{ 290let package = owner. package ; 291self . check ( & package) ?; 292if package. bindings . borrow (). is_none () { 293let bindings =self . bindings ( & package. config ). await . map_err ( |error|{ 294Error :: Module ( error. render ( package. url . as_str (), & package. source )) 295}) ?; 296self . check ( & package) ?; 297* package. bindings . borrow_mut () =Some ( bindings); 298} 299} 300Ok (()) 301} 302 303/// Shared synchronous decision over an owned remote package: what its 304/// own imports/scopes select, falling back to its prepared bindings. 305/// `None` selects nothing: the caller resolves the raw specifier, so 306/// the prepared path still never initializes or loads packages. 307fn classify ( 308& self , 309package : & Rc < Package >, 310specifier : & str , 311referrer : & str , 312) ->Result < Option < RemoteStep >, Error > { 313let url =Url :: parse ( 314& self 315. store 316. remote_base ( referrer) 317. map_err ( |error|Error :: Module ( error. to_string ())) ?, 318) 319. unwrap (); 320match package_mapping ( & package. config , specifier, & url) 321. map_err ( |error|Error :: Module ( error. to_string ())) ? 322{ 323Some ( PackageMapping :: Specifier ( target)) =>{ 324Ok ( Some ( RemoteStep :: Done ( Resolution :: Specifier ( target)))) 325} 326Some ( PackageMapping :: Export ( key)) =>{ 327Ok ( Some ( RemoteStep :: Export ( package. requested . clone (), key))) 328} 329None =>match select_binding ( package. bindings . borrow (). as_ref (), specifier) { 330Some (( key, Source :: Http ( directory))) =>{ 331Ok ( Some ( RemoteStep :: Export ( directory, key))) 332} 333 _ =>Ok ( None ), 334}, 335} 336} 337 338pub fn type_override ( & self , specifier : & str , referrer : & str ) ->Result < Option < String >, Error > { 339let Some ( owner) =self . owner ( referrer) else { 340return Ok ( None ); 341}; 342self . check ( & owner. package ) ?; 343let referrer_url = 344Url :: parse ( referrer). map_err ( |error|Error :: Module ( error. to_string ())) ?; 345let result = owner 346. package 347. config 348. types 349. imports 350. resolve ( specifier, & referrer_url) 351. map_err ( |error|Error :: Module ( error. to_string ())) ?; 352self . check ( & owner. package ) ?; 353Ok ( result) 354} 355 356pub fn type_globals ( & self , referrer : & str ) ->Result < Vec < String >, Error > { 357let Some ( owner) =self . owner ( referrer) else { 358return Ok ( Vec :: new ()); 359}; 360self . check ( & owner. package ) ?; 361let globals = owner. package . config . types . globals . clone (); 362self . check ( & owner. package ) ?; 363Ok ( globals) 364} 365 366pub async fn resolve ( 367& self , 368specifier : & str , 369referrer : & str , 370) ->Result < Option < Resolution >, Error > { 371let Some ( owner) =self . owner ( referrer) else { 372return Ok ( None ); 373}; 374let package = owner. package ; 375self . initialize ( referrer). await ?; 376self . check ( & package) ?; 377match self . classify ( & package, specifier, referrer) ?{ 378None =>Ok ( None ), 379Some ( RemoteStep :: Done ( resolution)) =>Ok ( Some ( resolution)), 380Some ( RemoteStep :: Export ( directory, key)) =>{ 381self . export ( & directory, & key, specifier). await . map ( Some ) 382} 383} 384} 385 386pub fn resolve_export_meta ( 387& self , 388directory : & Url , 389key : & str , 390specifier : & str , 391) ->Result < Resolution , Error > { 392let package =self 393. packages 394. borrow () 395. get ( directory) 396. cloned () 397. ok_or_else ( ||{ 398Error :: Module ( format! ( "Package metadata is not prepared: {directory}" )) 399}) ?; 400self . export_target ( & package, key, specifier) 401. map ( Resolution :: Remote ) 402} 403 404fn export_target ( 405& self , 406package : & Package , 407key : & str , 408specifier : & str , 409) ->Result < String , Error > { 410self . check ( package) ?; 411if package. config . exports . is_empty () { 412return Err ( Error :: Module ( format! ( 413"Dependency {specifier:?} has no exports: {}" , 414 package. url 415))); 416} 417let export = package. config . exports . get ( key). ok_or_else ( ||{ 418Error :: Module ( format! ( 419"Package has no export {key:?} for {specifier:?}: {}" , 420 package. url 421)) 422}) ?; 423self . authority 424. check_remote_module ( & export. target ) 425. map_err ( |error|Error :: Module ( error. to_string ())) ?; 426Ok ( export. target . to_string ()) 427} 428 429pub fn resolve_import_meta ( 430& self , 431specifier : & str , 432referrer : & str , 433) ->Result < Option < Resolution >, Error > { 434let Some ( owner) =self . owner ( referrer) else { 435return Ok ( None ); 436}; 437let package = owner. package ; 438self . check ( & package) ?; 439match self . classify ( & package, specifier, referrer) ?{ 440None =>Ok ( None ), 441Some ( RemoteStep :: Done ( resolution)) =>Ok ( Some ( resolution)), 442Some ( RemoteStep :: Export ( directory, key)) =>self 443. resolve_export_meta ( & directory, & key, specifier) 444. map ( Some ), 445} 446} 447 448pub fn metadata ( & self , name : & str ) ->Result < Option < serde_json:: Value >, Error > { 449let Some ( owner) =self . owner ( name) else { 450return Ok ( None ); 451}; 452let package = owner. package ; 453self . check ( & package) ?; 454let exports: BTreeMap < _ , _ > = package 455. config 456. exports 457. iter () 458. map ( |( key, export) |( key, export. target . as_str ())) 459. collect (); 460let bindings = package. bindings . borrow (); 461let dependencies: BTreeMap < _ , _ > = bindings 462. as_ref () 463. into_iter () 464. flatten () 465. map ( |( name, source) |{ 466( 467 name, 468match source{ 469Source :: Http ( url) => serde_json:: json!( url. as_str ()), 470Source :: Jsr ( request) => serde_json:: json!( request. specifier ), 471Source :: Npm ( request) => serde_json:: json!( request. request_key ()), 472Source :: Local ( directory) => serde_json:: json!( directory), 473}, 474) 475}) 476. collect (); 477Ok ( Some ( serde_json:: json!({ 478"config" : package. identity . 0 . as_str (), "name" : package. config . name , "version" : package. config . version , 479"description" : package. config . description , "exports" : exports, "dependencies" : dependencies 480}))) 481} 482}