diff --git a/c/src/metta.rs b/c/src/metta.rs index f4e88ffbb..35c8c3aec 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -926,7 +926,7 @@ pub extern "C" fn metta_nth_search_path(metta: *const metta_t, idx: usize, buf: let path = metta.search_paths().nth(idx); match path { Some(path) => write_into_buf(path.display(), buf, buf_len), - None => 0 + None => write_into_buf("", buf, buf_len) //Write just the terminator char, if there is room } } @@ -1217,7 +1217,7 @@ pub extern "C" fn runner_state_current_results(state: *const runner_state_t, pub extern "C" fn environment_config_dir(buf: *mut c_char, buf_len: usize) -> usize { match Environment::common_env().config_dir() { Some(path) => write_into_buf(path.display(), buf, buf_len), - None => 0 + None => write_into_buf("", buf, buf_len) //Write just the terminator char, if there is room } } @@ -1338,6 +1338,18 @@ pub extern "C" fn env_builder_set_config_dir(builder: *mut env_builder_t, path: *builder_arg_ref = builder.into(); } +/// @brief Configures the environment to create the config dir if it doesn't already exist +/// @ingroup environment_group +/// @param[in] builder A pointer to the in-process environment builder state +/// +#[no_mangle] +pub extern "C" fn env_builder_create_config_dir(builder: *mut env_builder_t) { + let builder_arg_ref = unsafe{ &mut *builder }; + let builder = core::mem::replace(builder_arg_ref, env_builder_t::null()).into_inner(); + let builder = builder.create_config_dir(); + *builder_arg_ref = builder.into(); +} + /// @brief Configures the environment so that no config directory will be read nor created /// @ingroup environment_group /// @param[in] builder A pointer to the in-process environment builder state diff --git a/lib/src/metta/runner/environment.rs b/lib/src/metta/runner/environment.rs index fe765d5aa..ce0e342b8 100644 --- a/lib/src/metta/runner/environment.rs +++ b/lib/src/metta/runner/environment.rs @@ -78,6 +78,7 @@ impl Environment { pub struct EnvBuilder { env: Environment, no_cfg_dir: bool, + create_cfg_dir: bool, } impl EnvBuilder { @@ -97,6 +98,7 @@ impl EnvBuilder { Self { env: Environment::new(), no_cfg_dir: false, + create_cfg_dir: false, } } @@ -124,9 +126,23 @@ impl EnvBuilder { self } + /// Configures the environment to create a config directory with default config files, if no directory is found + /// + /// NOTE: If the config directory exists but some config files are missing, default files will not be created. + pub fn create_config_dir(mut self) -> Self { + self.create_cfg_dir = true; + if self.no_cfg_dir { + panic!("Fatal Error: create_config_dir is incompatible with set_no_config_dir"); + } + self + } + /// Configures the Environment not to load nor create any config files pub fn set_no_config_dir(mut self) -> Self { self.no_cfg_dir = true; + if self.create_cfg_dir { + panic!("Fatal Error: set_no_config_dir is incompatible with create_config_dir"); + } if self.env.config_dir.is_some() { panic!("Fatal Error: set_config_dir is incompatible with set_no_config_dir"); } @@ -169,12 +185,12 @@ impl EnvBuilder { /// /// NOTE: Creating owned Environments is usually not necessary. It is usually sufficient to use the [common_env] method. pub(crate) fn build(self) -> Environment { - let mut env = self.env; //Init the logger. This will have no effect if the logger has already been initialized let _ = env_logger::builder().is_test(env.is_test).try_init(); + //Construct the platform-specific config dir location, if an explicit location wasn't provided if !self.no_cfg_dir { if env.config_dir.is_none() { match ProjectDirs::from("io", "TrueAGI", "metta") { @@ -190,19 +206,17 @@ impl EnvBuilder { if let Some(config_dir) = &env.config_dir { - //Create the modules dir inside the config dir, if it doesn't already exist. - // This will create the cfg_dir iteslf in the process let modules_dir = config_dir.join("modules"); - std::fs::create_dir_all(&modules_dir).unwrap(); + let init_metta_path = config_dir.join("init.metta"); - //Push the "modules" dir, as the last place to search after the other paths that were specified - //TODO: the config.metta file will be able to append / modify the search paths, and can choose not to - // include the "modules" dir in the future. - env.extra_include_paths.push(modules_dir); + //Create the default config dir, if that part of our directive + if self.create_cfg_dir && !config_dir.exists() { - //Create the default init.metta file if it doesn't already exist - let init_metta_path = config_dir.join("init.metta"); - if !init_metta_path.exists() { + //Create the modules dir inside the config dir + // This will create the cfg_dir iteslf in the process + std::fs::create_dir_all(&modules_dir).unwrap(); + + //Create the default init.metta file let mut file = fs::OpenOptions::new() .create(true) .write(true) @@ -210,7 +224,22 @@ impl EnvBuilder { .expect(&format!("Error creating default init file at {init_metta_path:?}")); file.write_all(&DEFAULT_INIT_METTA).unwrap(); } - env.init_metta_path = Some(init_metta_path); + + //If the config_dir in the Environment still doesn't exist (and we couldn't create it), then set it to None + if !config_dir.exists() { + env.config_dir = None; + } + + //Push the "modules" dir, as the last place to search after the other paths that were specified + //TODO: the config.metta file should be able to append / modify the search paths, and can choose not to + // include the "modules" dir in the future. + if modules_dir.exists() { + env.extra_include_paths.push(modules_dir); + } + + if init_metta_path.exists() { + env.init_metta_path = Some(init_metta_path); + } } //TODO: This line below is a stop-gap to match old behavior diff --git a/python/hyperon/runner.py b/python/hyperon/runner.py index 20e35f8d8..d5e5d2120 100644 --- a/python/hyperon/runner.py +++ b/python/hyperon/runner.py @@ -203,22 +203,24 @@ def config_dir(): else: return None - def init_common_env(working_dir = None, config_dir = None, disable_config = False, is_test = False, include_paths = []): + def init_common_env(working_dir = None, config_dir = None, create_config = False, disable_config = False, is_test = False, include_paths = []): """Initialize the common environment with the supplied args""" - builder = Environment.custom_env(working_dir, config_dir, disable_config, is_test, include_paths) + builder = Environment.custom_env(working_dir, config_dir, create_config, disable_config, is_test, include_paths) return hp.env_builder_init_common_env(builder) def test_env(): """Returns an EnvBuilder object specifying a unit-test environment, that can be used to init a MeTTa runner""" return hp.env_builder_use_test_env() - def custom_env(working_dir = None, config_dir = None, disable_config = False, is_test = False, include_paths = []): + def custom_env(working_dir = None, config_dir = None, create_config = False, disable_config = False, is_test = False, include_paths = []): """Returns an EnvBuilder object that can be used to init a MeTTa runner, if you need multiple environments to coexist in the same process""" builder = hp.env_builder_start() if (working_dir is not None): hp.env_builder_set_working_dir(builder, working_dir) if (config_dir is not None): hp.env_builder_set_config_dir(builder, config_dir) + if (create_config): + hp.env_builder_create_config_dir(builder) if (disable_config): hp.env_builder_disable_config_dir(builder) if (is_test): diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index 07d181236..f23a979c2 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -860,6 +860,7 @@ PYBIND11_MODULE(hyperonpy, m) { m.def("env_builder_init_common_env", [](EnvBuilder builder) { return env_builder_init_common_env(builder.obj); }, "Finish initialization of the common environment"); m.def("env_builder_set_working_dir", [](EnvBuilder& builder, std::string path) { env_builder_set_working_dir(builder.ptr(), path.c_str()); }, "Sets the working dir in the environment"); m.def("env_builder_set_config_dir", [](EnvBuilder& builder, std::string path) { env_builder_set_config_dir(builder.ptr(), path.c_str()); }, "Sets the config dir in the environment"); + m.def("env_builder_create_config_dir", [](EnvBuilder& builder) { env_builder_create_config_dir(builder.ptr()); }, "Creates the config dir if it doesn't exist"); m.def("env_builder_disable_config_dir", [](EnvBuilder& builder) { env_builder_disable_config_dir(builder.ptr()); }, "Disables the config dir in the environment"); m.def("env_builder_set_is_test", [](EnvBuilder& builder, bool is_test) { env_builder_set_is_test(builder.ptr(), is_test); }, "Disables the config dir in the environment"); m.def("env_builder_add_include_path", [](EnvBuilder& builder, std::string path) { env_builder_add_include_path(builder.ptr(), path.c_str()); }, "Adds an include path to the environment"); diff --git a/python/tests/test_environment.py b/python/tests/test_environment.py index 3ef64be7a..b86cd4ec3 100644 --- a/python/tests/test_environment.py +++ b/python/tests/test_environment.py @@ -8,7 +8,7 @@ def __init__(self, methodName): super().__init__(methodName) def testEnvironment(self): - self.assertTrue(Environment.init_common_env(config_dir = "/tmp/test_dir")) + self.assertTrue(Environment.init_common_env(config_dir = "/tmp/test_dir", create_config = True)) self.assertEqual(Environment.config_dir(), "/tmp/test_dir") self.assertFalse(Environment.init_common_env(disable_config = True)) diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 0d499ca28..5aa80e0e2 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -355,6 +355,7 @@ pub mod metta_interface_mod { pub fn init_common_env(working_dir: PathBuf, include_paths: Vec) -> Result { EnvBuilder::new() .set_working_dir(Some(&working_dir)) + .create_config_dir() .add_include_paths(include_paths) .init_common_env(); diff --git a/repl/src/py_shim.py b/repl/src/py_shim.py index a0ef111bb..bb4d0dd07 100644 --- a/repl/src/py_shim.py +++ b/repl/src/py_shim.py @@ -2,7 +2,7 @@ from hyperon import * def init_metta(working_dir, include_paths): - Environment.init_common_env(working_dir = working_dir, include_paths = include_paths) + Environment.init_common_env(working_dir = working_dir, create_config = True, include_paths = include_paths) return MeTTa() def load_metta_module(metta, mod_path):