Migrate to Reef 0.7.5 Result/Option stdlib
Migrate to Reef 0.7.5 Result/Option stdlib Reef 0.7.5 replaced in-band failure sentinels with Result/Option across the stdlib. Repoint core.result_generic -> core.result (removed module; keeps the rg alias and repoman's own Result[T, string] convention) and bind+branch on the newly Result-returning stdlib calls: - iofile.readFile/writeFile/appendFile/deleteFile/rename/fsync -> Result - iodir.create_dir_all -> Result; iodir.list_dir -> Result[[string]] (rewrote profile.list_all from buffer-fill to returned arrays) - toml.toml_parse_doc -> Result; drop removed TomlDoc.truncated checks error.Error is converted to repoman's string errors at the stdlib boundary via error.error_message. Tests repointed likewise. Build clean, all suites pass.
Author:
Chris Tusa <chris.tusa@leafscale.com>
Date:
Jul 15, 2026 11:31
Changeset:
c792cbf72723b8f8b43125113f6f8374af21db7a
Branch:
default
Changed files:
Diff
diff -r 69a21bb79b71 -r c792cbf72723 src/cli.reef --- a/src/cli.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/cli.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,8 @@ module cli import core.str -import core.result_generic as rg +import core.result as rg +import core.error as error import core.convert as convert import time.time as time import io.console as console @@ -138,7 +139,12 @@ env_keys: new [string](0), env_values: new [string](0) } if iofile.fileExists(override_path) - let ov_r = config.parse_override(iofile.readFile(override_path)) + let ov_read = iofile.readFile(override_path) + if rg.is_err(ov_read) + log.write("repoman: error: cannot read override " + override_path + ": " + error.error_message(rg.unwrap_err(ov_read))) + return 3 + end if + let ov_r = config.parse_override(rg.unwrap_ok(ov_read)) if rg.is_err(ov_r) log.write("repoman: error: bad override " + override_path + ": " + rg.unwrap_err(ov_r)) return 3 diff -r 69a21bb79b71 -r c792cbf72723 src/config.reef --- a/src/config.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/config.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,8 @@ module config import core.str -import core.result_generic as rg +import core.result as rg +import core.error as error import encoding.toml as toml import io.file as iofile import io.dir as iodir @@ -171,10 +172,11 @@ end parse_string_array fn parse_registry(toml_text: string): rg.Result[Registry, string] - let doc: toml.TomlDoc = toml.toml_parse_doc(toml_text) - if doc.truncated - return @Result[Registry, string].Err("registry too large (>1024 entries)") + let doc_r = toml.toml_parse_doc(toml_text) + if rg.is_err(doc_r) + return @Result[Registry, string].Err("registry parse error: " + error.error_message(rg.unwrap_err(doc_r))) end if + let doc: toml.TomlDoc = rg.unwrap_ok(doc_r) let schema: int = toml.toml_get_int_doc(doc, "repoman.schema") if schema != 1 and schema != 2 and schema != 3 @@ -283,10 +285,11 @@ end serialize_registry fn parse_override(toml_text: string): rg.Result[Override, string] - let doc: toml.TomlDoc = toml.toml_parse_doc(toml_text) - if doc.truncated - return @Result[Override, string].Err("override too large") + let doc_r = toml.toml_parse_doc(toml_text) + if rg.is_err(doc_r) + return @Result[Override, string].Err("override parse error: " + error.error_message(rg.unwrap_err(doc_r))) end if + let doc: toml.TomlDoc = rg.unwrap_ok(doc_r) let image: string = toml.toml_get_doc(doc, "container.image") let profiles_raw: string = toml.toml_get_doc(doc, "container.profiles") @@ -515,14 +518,17 @@ // Ensure ~/.config/repoman/ exists if not iodir.dir_exists(cfg_dir) - if not iodir.create_dir_all(cfg_dir) + if rg.is_err(iodir.create_dir_all(cfg_dir)) return @Result[Registry, string].Err("cannot create config dir: " + cfg_dir) end if end if if iofile.fileExists(cfg_path) - let contents: string = iofile.readFile(cfg_path) - return parse_registry(contents) + let contents_r = iofile.readFile(cfg_path) + if rg.is_err(contents_r) + return @Result[Registry, string].Err("cannot read registry: " + error.error_message(rg.unwrap_err(contents_r))) + end if + return parse_registry(rg.unwrap_ok(contents_r)) end if // Init: write default registry. @@ -540,19 +546,22 @@ let serialized: string = serialize_registry(reg) let tmp_path: string = cfg_path + ".tmp" - if not iofile.writeFile(tmp_path, serialized) - return @Result[bool, string].Err("write failed: " + tmp_path) + let write_r = iofile.writeFile(tmp_path, serialized) + if rg.is_err(write_r) + return @Result[bool, string].Err("write failed: " + tmp_path + ": " + error.error_message(rg.unwrap_err(write_r))) end if - if not iofile.fsync(tmp_path) + let fsync_r = iofile.fsync(tmp_path) + if rg.is_err(fsync_r) // Best effort: clean up tmp - let _d: bool = iofile.deleteFile(tmp_path) - return @Result[bool, string].Err("fsync failed: " + tmp_path) + let _d = iofile.deleteFile(tmp_path) + return @Result[bool, string].Err("fsync failed: " + tmp_path + ": " + error.error_message(rg.unwrap_err(fsync_r))) end if - if not iofile.rename(tmp_path, cfg_path) - let _d: bool = iofile.deleteFile(tmp_path) - return @Result[bool, string].Err("rename failed: " + tmp_path + " → " + cfg_path) + let rename_r = iofile.rename(tmp_path, cfg_path) + if rg.is_err(rename_r) + let _d = iofile.deleteFile(tmp_path) + return @Result[bool, string].Err("rename failed: " + tmp_path + " → " + cfg_path + ": " + error.error_message(rg.unwrap_err(rename_r))) end if return @Result[bool, string].Ok(true) diff -r 69a21bb79b71 -r c792cbf72723 src/hermes.reef --- a/src/hermes.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/hermes.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,7 @@ module hermes import core.str -import core.result_generic as rg +import core.result as rg import core.convert as convert import io.dir as iodir import sys.process as p @@ -111,7 +111,7 @@ if not iodir.dir_exists(source) return @Result[bool, string].Err("hermes source dir not found: " + source) end if - if not iodir.create_dir_all(dest) + if rg.is_err(iodir.create_dir_all(dest)) return @Result[bool, string].Err("cannot create dest dir: " + dest) end if diff -r 69a21bb79b71 -r c792cbf72723 src/incus.reef --- a/src/incus.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/incus.reef Wed Jul 15 11:31:03 2026 -0500 @@ -20,7 +20,7 @@ import core.str import sys.process as p import sys.fd as fd -import core.result_generic as rg +import core.result as rg import core.convert as convert export diff -r 69a21bb79b71 -r c792cbf72723 src/log.reef --- a/src/log.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/log.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,6 +18,7 @@ module log import core.str +import core.result as rg import io.console as console import io.file as iofile import io.dir as iodir @@ -43,7 +44,7 @@ let expanded: string = paths.expand_home(logdir) if not iodir.dir_exists(expanded) - if not iodir.create_dir_all(expanded) + if rg.is_err(iodir.create_dir_all(expanded)) console.printErr("repoman: warning: cannot create logdir " + expanded + "; file logging disabled") log_enabled = false return false @@ -54,7 +55,7 @@ let path: string = paths.join(expanded, filename) // Truncate (or create) the file by writing an empty string. - if not iofile.writeFile(path, "") + if rg.is_err(iofile.writeFile(path, "")) console.printErr("repoman: warning: cannot open " + path + " for writing; file logging disabled") log_enabled = false return false @@ -71,7 +72,7 @@ proc write(msg: string) console.printErr(msg) if log_enabled - let _w: bool = iofile.appendFile(log_path, msg + "\n") + let _w = iofile.appendFile(log_path, msg + "\n") end if end write diff -r 69a21bb79b71 -r c792cbf72723 src/profile.reef --- a/src/profile.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/profile.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,8 @@ module profile import core.str -import core.result_generic as rg +import core.result as rg +import core.error as error import core.convert as convert import io.file as iofile import io.dir as iodir @@ -89,23 +90,28 @@ end lookup fn list_all(home_dir: string): [ProfileEntry] - // Buffers for filename lists. Cap at 256 per dir; profile libraries - // are not expected to be enormous. - let max_files: int = 256 - mut user_buf: [string] = new [string](max_files) - mut vendor_buf: [string] = new [string](max_files) - + // list_dir returns the directory's entries as an array (Result-wrapped); + // a missing/unreadable dir is treated as empty. Profile libraries are + // not expected to be enormous. let u_dir: string = user_dir(home_dir) - mut user_count: int = 0 + mut user_buf: [string] = new [string](0) if iodir.dir_exists(u_dir) - user_count = iodir.list_dir(u_dir, user_buf, max_files) + let ur = iodir.list_dir(u_dir) + if rg.is_ok(ur) + user_buf = rg.unwrap_ok(ur) + end if end if + let user_count: int = user_buf.length() let v_dir: string = vendor_dir() - mut vendor_count: int = 0 + mut vendor_buf: [string] = new [string](0) if iodir.dir_exists(v_dir) - vendor_count = iodir.list_dir(v_dir, vendor_buf, max_files) + let vr = iodir.list_dir(v_dir) + if rg.is_ok(vr) + vendor_buf = rg.unwrap_ok(vr) + end if end if + let vendor_count: int = vendor_buf.length() // Pre-allocate result buffer at worst-case size. let cap: int = user_count + vendor_count @@ -195,9 +201,13 @@ let path: string = rg.unwrap_ok(path_r) // Read the file - let yaml: string = iofile.readFile(path) + let yaml_r = iofile.readFile(path) + if rg.is_err(yaml_r) + return @Result[bool, string].Err("unreadable profile file: " + path + ": " + error.error_message(rg.unwrap_err(yaml_r))) + end if + let yaml: string = rg.unwrap_ok(yaml_r) if str.length(yaml) == 0 - return @Result[bool, string].Err("empty or unreadable profile file: " + path) + return @Result[bool, string].Err("empty profile file: " + path) end if // Render — but if HOST_LAN_IP is needed and host.lan_ip is empty, fail with a hint @@ -220,8 +230,11 @@ return @Result[string, string].Err(rg.unwrap_err(path_r)) end if let path: string = rg.unwrap_ok(path_r) - let yaml: string = iofile.readFile(path) - let rendered: string = render(yaml, host) + let yaml_r = iofile.readFile(path) + if rg.is_err(yaml_r) + return @Result[string, string].Err("cannot read profile file: " + path + ": " + error.error_message(rg.unwrap_err(yaml_r))) + end if + let rendered: string = render(rg.unwrap_ok(yaml_r), host) return @Result[string, string].Ok(rendered) end show @@ -232,8 +245,11 @@ return @Result[string, string].Err(rg.unwrap_err(path_r)) end if let path: string = rg.unwrap_ok(path_r) - let yaml: string = iofile.readFile(path) - let rendered: string = render(yaml, host) + let yaml_r = iofile.readFile(path) + if rg.is_err(yaml_r) + return @Result[string, string].Err("cannot read profile file: " + path + ": " + error.error_message(rg.unwrap_err(yaml_r))) + end if + let rendered: string = render(rg.unwrap_ok(yaml_r), host) // Fetch incus state let incus_r = incus.profile_get("default", name) diff -r 69a21bb79b71 -r c792cbf72723 src/setup.reef --- a/src/setup.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/setup.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,7 @@ module setup import core.str -import core.result_generic as rg +import core.result as rg import io.console as console import io.dir as iodir import sys.env @@ -150,7 +150,7 @@ // Ensure the user-profile-shadow dir exists so users can drop overrides // there without mkdir ceremony. iodir.create_dir_all is idempotent. let profiles_d: string = profile.user_dir(env.home_dir) - let _md: bool = iodir.create_dir_all(profiles_d) + let _md = iodir.create_dir_all(profiles_d) return @Result[config.Registry, string].Ok(new_reg) end if diff -r 69a21bb79b71 -r c792cbf72723 src/sync.reef --- a/src/sync.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/src/sync.reef Wed Jul 15 11:31:03 2026 -0500 @@ -18,7 +18,7 @@ module sync import core.str -import core.result_generic as rg +import core.result as rg import sys.process as p import sys.fd as fd diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_io.reef --- a/tests/test_config_io.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_io.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg import io.dir as iodir import io.file as iofile import sys.process as pr @@ -29,7 +29,7 @@ let tmp: string = "/tmp/repoman-test-load-init" // Wipe and recreate let _w: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) - let _c: bool = iodir.create_dir_all(tmp) + let _c = iodir.create_dir_all(tmp) // First call: no .config/repoman/repoman.toml exists → init writes default let r1 = config.load_or_init(tmp) diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_migrate.reef --- a/tests/test_config_migrate.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_migrate.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg proc main() let runner = new framework.TestRunner() diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_mutate.reef --- a/tests/test_config_mutate.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_mutate.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg fn empty_defaults(): config.Defaults return config.Defaults { diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_override.reef --- a/tests/test_config_override.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_override.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg proc main() let runner = new framework.TestRunner() diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_parse.reef --- a/tests/test_config_parse.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_parse.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg proc main() let runner = new framework.TestRunner() diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_roundtrip.reef --- a/tests/test_config_roundtrip.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_roundtrip.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg proc main() let runner = new framework.TestRunner() diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_save.reef --- a/tests/test_config_save.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_save.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg import io.dir as iodir import io.file as iofile import sys.process as pr @@ -27,7 +27,7 @@ let tmp: string = "/tmp/repoman-test-save" let _w: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) - let _c: bool = iodir.create_dir_all(tmp) + let _c = iodir.create_dir_all(tmp) let cfg_path: string = tmp + "/repoman.toml" let reg: config.Registry = config.default_registry("/home/u") @@ -37,8 +37,9 @@ runner.assert_eq_bool(iofile.fileExists(cfg_path + ".tmp"), false, "tmp removed after rename") // Round-trip: read what we wrote - let contents: string = iofile.readFile(cfg_path) - let r2 = config.parse_registry(contents) + let read_r = iofile.readFile(cfg_path) + runner.assert_eq_bool(rg.is_ok(read_r), true, "saved file readable") + let r2 = config.parse_registry(rg.unwrap_ok(read_r)) runner.assert_eq_bool(rg.is_ok(r2), true, "saved file parses") if rg.is_ok(r2) let reg2 = rg.unwrap_ok(r2) diff -r 69a21bb79b71 -r c792cbf72723 tests/test_config_schema_v3.reef --- a/tests/test_config_schema_v3.reef Tue Jun 02 15:58:49 2026 -0500 +++ b/tests/test_config_schema_v3.reef Wed Jul 15 11:31:03 2026 -0500 @@ -17,7 +17,7 @@ import config import test.framework -import core.result_generic as rg +import core.result as rg import core.str proc main()