@@ -642,8 +642,74 @@ pub fn module_env(module_dir: &Path, temp_dir: &Path) -> Vec<(String, String)> {
642642 env
643643}
644644
645+ /// Modules that NGLess knows how to fetch from the download server (mirrors `knownModules` in
646+ /// `NGLess/Modules.hs`). A plain (global) `import` is only allowed for these; any other name must be
647+ /// brought in with `local import`. When one of these is requested but not present locally,
648+ /// `find_load` auto-downloads it.
649+ pub const KNOWN_MODULES : & [ & str ] = & [
650+ "example-cmd" ,
651+ "gmgc" ,
652+ "igc" ,
653+ "om-rgc" ,
654+ "DogGutCatalog" ,
655+ "MouseGutCatalog" ,
656+ "PigGutCatalog" ,
657+ "specI" ,
658+ "motus" ,
659+ ] ;
660+
661+ /// Whether `name` is a known (downloadable, globally importable) module.
662+ pub fn is_known_module ( name : & str ) -> bool {
663+ KNOWN_MODULES . contains ( & name)
664+ }
665+
666+ /// Download a known external module into the user data directory (mirrors `downloadModule`). Returns
667+ /// the module directory (`<user-data>/Modules/<name>.ngm/<version>`).
668+ fn download_module ( name : & str , version : & str ) -> NgResult < PathBuf > {
669+ let data_dir = crate :: reference:: user_data_directory ( ) . ok_or_else ( || {
670+ NgError :: new (
671+ NgErrorType :: SystemError ,
672+ format ! ( "Cannot download module '{name}': no user data directory is configured." ) ,
673+ )
674+ } ) ?;
675+ let nameversion = Path :: new ( & format ! ( "{name}.ngm" ) ) . join ( version) ;
676+ let destdir = data_dir. join ( "Modules" ) . join ( & nameversion) ;
677+ let base_url = crate :: reference:: download_base_url ( ) ;
678+ let url = format ! (
679+ "{}/Modules/{}.tar.gz" ,
680+ base_url. trim_end_matches( '/' ) ,
681+ nameversion. to_string_lossy( )
682+ ) ;
683+ crate :: reference:: download_expand_tar ( & url, & data_dir) ?;
684+ Ok ( destdir)
685+ }
686+
687+ /// Load a module from a directory containing `module.yaml`, checking the version is compatible.
688+ fn load_from_dir ( name : & str , version : & str , dir : PathBuf ) -> NgResult < ExternalModule > {
689+ let yaml = dir. join ( "module.yaml" ) ;
690+ let text = std:: fs:: read_to_string ( & yaml) . map_err ( |e| {
691+ NgError :: new (
692+ NgErrorType :: SystemError ,
693+ format ! ( "Could not read module file {}: {e}" , yaml. display( ) ) ,
694+ )
695+ } ) ?;
696+ let raw: RawModule = serde_yaml:: from_str ( & text) . map_err ( |e| {
697+ NgError :: new (
698+ NgErrorType :: SystemError ,
699+ format ! (
700+ "Could not load module file {}. Error was `{e}`" ,
701+ yaml. display( )
702+ ) ,
703+ )
704+ } ) ?;
705+ let module = ExternalModule :: from_raw ( raw, dir) ?;
706+ check_compatible ( name, version, & module) ?;
707+ Ok ( module)
708+ }
709+
645710/// Find and load an external module (mirrors `findLoad`). Searches the current directory, then the
646- /// global and user data directories, for `Modules/<name>.ngm/<version>/module.yaml`.
711+ /// global and user data directories, for `Modules/<name>.ngm/<version>/module.yaml`. If the module
712+ /// is not found locally but is a [`known module`](KNOWN_MODULES), it is downloaded first.
647713pub fn find_load ( name : & str , version : & str , data_dirs : & [ String ] ) -> NgResult < ExternalModule > {
648714 let modpath = Path :: new ( "Modules" )
649715 . join ( format ! ( "{name}.ngm" ) )
@@ -663,26 +729,15 @@ pub fn find_load(name: &str, version: &str, data_dirs: &[String]) -> NgResult<Ex
663729 ) ;
664730 searched. push ( yaml. clone ( ) ) ;
665731 if yaml. is_file ( ) {
666- let text = std:: fs:: read_to_string ( & yaml) . map_err ( |e| {
667- NgError :: new (
668- NgErrorType :: SystemError ,
669- format ! ( "Could not read module file {}: {e}" , yaml. display( ) ) ,
670- )
671- } ) ?;
672- let raw: RawModule = serde_yaml:: from_str ( & text) . map_err ( |e| {
673- NgError :: new (
674- NgErrorType :: SystemError ,
675- format ! (
676- "Could not load module file {}. Error was `{e}`" ,
677- yaml. display( )
678- ) ,
679- )
680- } ) ?;
681- let module = ExternalModule :: from_raw ( raw, dir) ?;
682- check_compatible ( name, version, & module) ?;
683- return Ok ( module) ;
732+ return load_from_dir ( name, version, dir) ;
684733 }
685734 }
735+ // Not found locally: a known module is downloaded into the user data directory and loaded from
736+ // there (mirrors `findLoad`'s `downloadModule` fallback).
737+ if is_known_module ( name) {
738+ let dir = download_module ( name, version) ?;
739+ return load_from_dir ( name, version, dir) ;
740+ }
686741 let locations = searched
687742 . iter ( )
688743 . map ( |p| format ! ( "\t {}" , p. display( ) ) )
0 commit comments