Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
7ff3855
fix: removed obsolete ABI struct parameter
justpav05 Sep 5, 2026
4a54623
fix: fixed static linking for setup
justpav05 Sep 5, 2026
5f4e54b
fix: update localization for library structure changes
justpav05 Sep 5, 2026
19396ca
fix: removed obsolete ABI struct field
justpav05 Sep 5, 2026
1026b70
fix: refactored the library pipeline: installation now creates a proper
justpav05 Sep 5, 2026
973523f
fix: fixed test errors
justpav05 Sep 5, 2026
c3503ef
fix: update TODO
justpav05 Sep 5, 2026
bc53fe3
fix: extraction and separation of the 3 ABI levels
justpav05 Sep 5, 2026
8cb4812
fix: fix var for abi in decoders
justpav05 Sep 5, 2026
4f7e4d8
fix: fix var for lib
justpav05 Sep 5, 2026
f7e5bf4
fix: fix vars for lib and decode
justpav05 Sep 5, 2026
2cd924b
fix: added binary copying to the image for setup
justpav05 Sep 5, 2026
d490a9f
fix: switched the mechanism to use a plugin-based approach
justpav05 Sep 5, 2026
3b64fa6
fix: update TODO
justpav05 Sep 5, 2026
756859f
fix: fix search for upac-to.efi
justpav05 Sep 6, 2026
a59032f
new: add upac-from slot name
justpav05 Sep 6, 2026
bab2c9e
fix: added new functions for ABI and API registration
justpav05 Sep 6, 2026
fa222c4
fix: fix test
justpav05 Sep 6, 2026
d3b3c7f
fix: simplified the default function for the trait
justpav05 Sep 6, 2026
5e9ba0d
fix: implemented creation of to and from for uki
justpav05 Sep 6, 2026
2e12931
fix: added a stub to all remaining booters
justpav05 Sep 6, 2026
b0d0212
fix: removed an extra exported function
justpav05 Sep 6, 2026
e91cf95
fix: added changes for static linking in setup
justpav05 Sep 6, 2026
97495d0
fix: restored symbols for booters
justpav05 Sep 6, 2026
5276486
fix: fixed description for SAFTY section
justpav05 Sep 6, 2026
45dbf3e
fix: added generation of everything required for the UKI format to work
justpav05 Sep 6, 2026
7588ca6
fix: update TODO
justpav05 Sep 6, 2026
6614997
fix: fixed test placement
justpav05 Sep 6, 2026
ef3be2a
fix: add more tests
justpav05 Sep 6, 2026
91cdd14
fix: update TODO
justpav05 Sep 6, 2026
e171b7f
fix: add more tests
justpav05 Sep 6, 2026
0787e9d
fix: update TODO
justpav05 Sep 6, 2026
2193620
fix: updated Booter structure and trait
justpav05 Sep 6, 2026
c882d56
fix: added plugins for installation
justpav05 Sep 6, 2026
6d966a7
fix: updated boot plugin contracts
justpav05 Sep 6, 2026
233f0b9
fix: update install for grub
justpav05 Sep 6, 2026
4cef362
fix: update TODO
justpav05 Sep 6, 2026
17ac1c5
fix: removed the 'config' folder as it was unnecessary
justpav05 Sep 6, 2026
85de292
fix: update TODO
justpav05 Sep 6, 2026
05057ee
fix: split up the large mod.rs for the plugin
justpav05 Sep 6, 2026
1f2c76e
fix: update TODO
justpav05 Sep 7, 2026
53b6165
fix: added system installation of upac and a hook to load composefs
justpav05 Sep 7, 2026
1afda84
fix: propagated library setup changes to the installer CLI
justpav05 Sep 7, 2026
682e0f1
fix: update TODO
justpav05 Sep 7, 2026
6d37962
fix: reorganize ABI crate around C-ABI-only structs
justpav05 Sep 7, 2026
9026bf2
fix: sync VALIDATABLE_COMPOSITES with current abi structs
justpav05 Sep 7, 2026
010dd40
fix: split upac_types::lib.rs into per-domain modules, fix broken
justpav05 Sep 7, 2026
3abe56d
fix: round out CNew/CValidate coverage on package + response structs
justpav05 Sep 8, 2026
17df178
fix: add boot.rs and decode request/response mirrors
justpav05 Sep 8, 2026
e06448c
fix: propagate C-ABI struct moves through upac-lib, extract
justpav05 Sep 8, 2026
871dfea
fix: teach CTryToRust/RustToC raw pointers and Vec<String>, CValidate
justpav05 Sep 8, 2026
5e84717
fix: added issues and fixed formatting
justpav05 Sep 8, 2026
1177418
fix: build out types::request/response mirrors, drop dead boot/decoder
justpav05 Sep 8, 2026
779f160
fix: return Response structs from diff/list/search unmutated commands
justpav05 Sep 8, 2026
7ee09e3
fix: teach CTryToRust to pass through Option<HookMessageFn>
justpav05 Sep 8, 2026
540cc85
fix: added a validation trait for type-agnostic validation
justpav05 Sep 8, 2026
53ca2da
fix: added use of validation trait
justpav05 Sep 8, 2026
3754560
fix: migrate upac-cli commands onto types::request mirrors, validate
justpav05 Sep 8, 2026
c8eb0d6
fix: removed unnecessary options, fixed design-related imports
justpav05 Sep 8, 2026
b2fc943
fix: removed unnecessary options, fixed design-related imports
justpav05 Sep 8, 2026
0262e69
fix: rebuild boot-plugin loading around BootPlugins/BootPlugin, fix
justpav05 Sep 8, 2026
84ae14b
fix: require boot_plugin on install/update/uninstall/rollback/files
justpav05 Sep 9, 2026
422c520
fix: require boot_plugin in types::request mirrors, matching abi
justpav05 Sep 9, 2026
3168497
fix: require boot_plugin end-to-end in lib mutating commands, switch to
justpav05 Sep 9, 2026
62655f3
fix: require boot_plugin in install/update/remove/rollback/file
justpav05 Sep 9, 2026
ed2d8e5
fix: add boot.plugin to RuntimeSettings for a configurable default boot
justpav05 Sep 9, 2026
588e6ad
fix: add CTryToRust to
justpav05 Sep 9, 2026
ab75777
fix: rebuild upac-grub around current
justpav05 Sep 9, 2026
b71b75d
fix: rebuild upac-refind around current
justpav05 Sep 9, 2026
541cb7a
fix: function naming and style corrections
justpav05 Sep 9, 2026
b4b3617
fix: rebuild upac-systemd-boot around current Booter shape, generate
justpav05 Sep 9, 2026
3ce69b3
fix: rebuild upac-uki around current Booter shape, restore slot
justpav05 Sep 9, 2026
efbc11d
fix: drop dead booter.toml keys (efi_sysfs_path, loader_info_var,
justpav05 Sep 9, 2026
fb30cea
fix: fix REUSE specification
justpav05 Sep 10, 2026
6aa4865
fix: rebuild decoder plugin loading around DecoderPlugin, split
justpav05 Sep 10, 2026
5e247b2
fix: fix api for new decoder api
justpav05 Sep 10, 2026
588d38d
fix: fix symbol name
justpav05 Sep 10, 2026
b2051b3
feat: add ContextValue derive for Deref/DerefMut/From on single-field
justpav05 Sep 10, 2026
f031fb3
fix: group orchestrator context values and apply ContextValue derive
justpav05 Sep 10, 2026
73a2693
fix: update TODO
justpav05 Sep 10, 2026
7210aa2
fix: add shared upac_types::decoder::verify checksum helper, identical
justpav05 Sep 10, 2026
582c867
fix: add reuse head
justpav05 Sep 10, 2026
22299e2
fix: add shared upac_types::decoder::build_decode_response, identical
justpav05 Sep 10, 2026
eefa95b
fix: rebuild upac-decoder-alpm around current upac_abi/upac_types
justpav05 Sep 10, 2026
bb3cc24
fix: fix test
justpav05 Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
116 changes: 50 additions & 66 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,53 @@ Near-term, concrete items. See `ROADMAP.md` for the bigger picture.

## upac-lib

Test-coverage pass in progress, going file by file through the non-command core first
(`errors.rs`/`lock.rs`/`search.rs`/`fs.rs`/`orchestrator/*`/`database/*` done), commands
(`mutated`/`unmutated`) last. Remaining core files not yet visited: `deploy/{error,retention,mod}.rs`
(`esp.rs` skipped — real mount), `scripts/{error,file,load,pipeline,primitive}.rs`,
`plugin/decoder/{error,unpack,mod}.rs`, `plugin/boot/{error,manifest,mod}.rs`,
`composefs/{diff,error,mod}.rs`, `config/mod.rs`, `boot/{error,mod}.rs`.

`boot/mod.rs`'s UKI staging only ever writes the single fixed `upac-to` slot
(`layout::boot::UPAC_TO_SLOT`) — doc chapter 3's disk-layout map still describes a two-slot
`upac-from.efi`/`upac-to.efi` A/B scheme, but no code anywhere writes/reads an `upac-from` slot.
Real unfinished A/B swap, not just a stale doc — needs a decision (implement the second slot, or
formally drop it and fix the doc to match the single-slot design `lib.toml`'s own comment already
argues for).

Genesis (`up-sp`) now installs the actual bootloader binary onto a fresh ESP for systemd-boot and
rEFInd (`StageBootStage::run`, `lib/setup/lib.toml`'s `[genesis]` source paths) — confirmed working
via a live VM test for systemd-boot; rEFInd wired the same way but not yet VM-verified. grub is NOT
handled — a real `grub-install`-equivalent (target-specific generated `grubx64.efi`, not a plain
file copy) is out of scope for now; either shell out to `grub-install` against the mounted ESP, or
explicitly document grub as unsupported for genesis whole-disk mode.

`StageBootStage` picks which ESP loader binary to copy via a `match input.boot_plugin.as_deref()`
against literal `"systemd-boot"`/`"refind"` strings — this bypasses the actual dynamic boot-plugin
system (`resolve_boot_plugin`/`BootPluginManifest`/`static_plugins`), which is supposed to be the
one place plugin names are known. Adding a 5th booter plugin would require editing this match by
hand instead of just dropping in a new plugin. The correct fix is extending the `Booter` ABI itself
with a 4th function (e.g. `esp_loader_source() -> CSlice`, empty for uki/grub) so genesis asks the
already-resolved plugin for its own install-time source path instead of hardcoding names — but that
means bumping `BOOT_ABI_VERSION` and touching all 4 `booters/*` crates, so deliberately deferred;
the hardcoded match stays as a known, scoped limitation until then.

**Genesis-produced disks don't actually boot into the installed system yet** — found via a live
QEMU/OVMF test (systemd-boot now starts, finds the BLS entry, loads kernel+initramfs — that part
works after the bootloader-binary fix above). Two separate gaps, both required:
1. `partition.rs`'s `LINUX_PARTITION_TYPE_GUID` (`0fc63daf-8483-4772-8e79-3d69d8477de4`, generic
"Linux filesystem data") should be the discoverable-root GUID
(`4f68bce3-e8cd-4db1-96e7-fbcaf984b709`, "Linux root x86-64") so `systemd-gpt-auto-generator`
can find the deploy partition at all instead of hanging on `/dev/gpt-auto-root`.
2. Even with (1) fixed, a plain partition mount isn't how composefs systems boot — nothing in this
project resolves `composefs.digest=<hash>` (the kernel cmdline param `write_boot_entry` already
writes) against the on-disk repository, mounts the erofs image with fs-verity, and overlays
`state/deploy/<digest>/etc/`. **Found a real, existing upstream tool for exactly this**:
`composefs-setup-root` (crates.io, same `composefs-rs` project/version as our `composefs`/
`composefs-boot` deps) — a Rust binary, not something we'd write ourselves. Our on-disk layout
already matches its hardcoded expectations (`composefs/`, `state/deploy/<digest>/`) after
renaming `etc-upper` → `etc` (done, `lib.toml`'s `config_dir_name`). What's still missing: the
actual boot-time integration — the live VM's initramfs is systemd-based (mkinitcpio's `systemd`
hook, not classic busybox-style hooks), so this needs a systemd unit ordered between
`sysroot.mount` and `initrd-switch-root.target` (same role as ostree's
`ostree-prepare-root.service`), not a classic mkinitcpio hook script. Also unresolved: whether
upac needs to ship/package this integration itself, or whether it's expected to already exist
on the source distro (same assumption as the systemd-boot/rEFInd binary copy above) — needs
checking whether Arch/AUR already has a package for this.

**Genesis tracks the entire bootstrapped system as a single synthetic "rootfs" package**, not
per-package (`ReadMetaStage` reads one `meta.toml`, `ImportTreesStage` imports all of source's
`usr`/`etc` wholesale). Found while reasoning about the `composefs-setup-root` hook: if it needs to
already be installed on the source system (via pacman) for genesis to pick it up, its files still
end up attributed to the one fake "rootfs" package in our database — no real per-package
provenance for anything baked into the source image, unlike a `pacstrap`-then-`up install` flow
would give. Decision made: genesis should eventually be rewritten to install real, individually
decoded packages through the same pipeline `up install` uses, instead of importing a pre-built
directory wholesale — no special-casing even for the kernel package. This is a genesis rewrite, not
a patch; deliberately deferred until after a dedicated code-cleanup/macro-consolidation pass
(reduce duplicated lines, extract shared macros) elsewhere in the codebase first.

Test-coverage pass in progress. The entire non-command core is covered (`errors.rs`/`lock.rs`/
`search.rs`/`fs.rs`/`orchestrator/*`/`database/*`/`deploy/*`/`scripts/*`/`composefs/*`/`config/*`/
`boot/*`/`plugin/decoder/{error,manifest,triggers}.rs`/`plugin/boot/{error,manifest}.rs`), except
`plugin/decoder/unpack.rs`/`plugin/decoder/mod.rs`/`plugin/boot/mod.rs` (need a real dlopen'd/
`builtin-*` plugin) and `deploy/esp.rs` (real mount table) — both explicit, justified skips. Every
`mutated`/`unmutated` command's own `<Command>Error` enum is also now covered (inline tests next to
each `error.rs`, since `mutated`/`unmutated` aren't `pub`) — only each variant's own logic, not the
macro-generated `Common(...)` delegation shared with `errors.rs`'s already-tested `CommonError`.
Remaining: the `Stage::run()` bodies themselves — each needs a real composefs `Repository`/`Deploy`/
database in context, likely out of scope for unit tests unless a pure-logic helper turns out to be
extractable.

**`genesis`'s `system/` mechanism is done**: `ImportSystemStage` requires `<source>/system/` (a
literal 1:1 mirror of the target's real `/usr`, sibling to the package archives —
`EnumeratePackagesStage` already skips it, it only looks at files) to contain
`lib/systemd/system/composefs-setup-root.service` (hard error, `SetupError::
ComposefsSetupRootUnitNotFound`, if missing), imports the whole tree into `PrefixTree`, and creates
the unit's `*.target.wants/` enablement symlink itself. This is also how a built `up`/`upac-lib`/
booters gets onto a genesis'd disk at all — genesis never installs itself automatically, whoever
assembles `--source` has to place it under `system/` too, same assumption already made for the
systemd-boot/rEFInd binaries. Confirmed `composefs-setup-root`'s own hardcoded expectations already
match upac's on-disk layout exactly (repo at `composefs/`, per-deploy state at `state/deploy/<hex>/`,
`composefs=<hex>` cmdline karg) — no restructuring was needed, only the unit + the `system/` plumbing.
Still unresolved: whether upac ships/packages the `composefs-setup-root` binary itself or expects it
to already exist on the source distro (same open question as the systemd-boot/rEFInd binaries).

**Booter ABI redesign — decided this session, execution in progress file-by-file under direct
supervision (no batch edits).** Four canonical plugin responsibilities:

1. Plugin sets itself for one-time boot (`set_one_shot`) — done.
2. Plugin sets itself for persistent boot (`confirm_boot`) — done, including UKI's `to.efi`↔
`from.efi` file swap (needed a new `esp_mount_point` parameter on `confirm_boot`, added this
session; the swap only fires when the confirmed `entry_name` is the `to` slot specifically).
3. Plugin installs itself onto the ESP (`install`) — done for grub (real `grub-install
--removable --no-nvram` + a minimal `blscfg` `grub.cfg`), no-op for the other 3.
4. Plugin declares where its own pre-built loader binary lives in the source package tree
(`esp_loader_source`) — done, stays a separate passive query (only genesis can reach the
composefs tree to copy the bytes out, plugins can't do this step themselves).

**`write_boot_entry` must search only for the resource type the selected plugin needs, not
autonomously scan everything and guess.** Right now (`lib/lib/src/boot/mod.rs`) it calls
`get_boot_resources` unconditionally, takes whichever single boot resource exists in the tree
(Type1/Type2/`UsrLibModulesVmLinuz`), and only errors if more than one is found total — completely
independent of which plugin was actually selected. This is the case in all 6 call sites:
`lib/lib/src/boot/mod.rs` itself, `mutated/{files,installer,uninstaller,update,rollback}/checkout.rs`,
and `lib/setup/src/genesis/entry.rs`. Needs to take the (now always-explicit) plugin name and require
specifically: `uki` → `Type2` only; `grub`/`systemd-boot`/`rEFInd` → `Type1`/`UsrLibModulesVmLinuz`
only — hard error if that type isn't present, even if a different type is. This also means
`resolve_boot_plugin` must run before `write_boot_entry` everywhere — today the 5 ordinary
`checkout.rs` stages call it after (only genesis already has the order right).
64 changes: 48 additions & 16 deletions booters/booter.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,74 @@
# own build.rs reads this same file and generates its own private layout::boot module; nothing
# here is a Cargo dependency between plugins, just a shared data file.
#
# efi_sysfs_path is the fixed Linux kernel sysfs mount point for UEFI firmware (never
# distro/deployment-configurable, unlike the ESP mount point). efivarfs_path is the fixed
# subdirectory where the kernel exposes individual EFI variables as files (`efivar`'s own crate
# never surfaces this path, so plugins that need to clear the immutable attribute on an existing
# variable file — kernel efivarfs sets it after first write, and `efivar` never clears it back off
# before overwriting — have to reconstruct the exact same path themselves). sd_boot_loader_guid is
# systemd's vendor GUID for its own boot-loader-interface EFI variables (LoaderInfo,
# LoaderEntryOneShot, LoaderEntryDefault), per systemd's BOOT_LOADER_INTERFACE.md. boot_next_var/
# boot_order_var are UEFI global-namespace variables, no vendor GUID needed — boot_order_var is
# only needed to clear its immutable attribute before `VarManager::set_boot_order` touches it
# (that method writes the variable internally, so the uki plugin can't intercept the write() call
# itself the way it can for boot_next_var).
# efivarfs_path is the fixed subdirectory where the kernel exposes individual EFI variables as
# files (`efivar`'s own crate never surfaces this path, so plugins that need to clear the
# immutable attribute on an existing variable file — kernel efivarfs sets it after first write,
# and `efivar` never clears it back off before overwriting — have to reconstruct the exact same
# path themselves). sd_boot_loader_guid is systemd's vendor GUID for its own boot-loader-interface
# EFI variables (LoaderEntryOneShot, LoaderEntryDefault), per systemd's BOOT_LOADER_INTERFACE.md.
# boot_next_var/boot_order_var are UEFI global-namespace variables, no vendor GUID needed —
# boot_order_var is only needed to clear its immutable attribute before
# `VarManager::set_boot_order` touches it (that method writes the variable internally, so the uki
# plugin can't intercept the write() call itself the way it can for boot_next_var).
[boot]
efi_sysfs_path = "/sys/firmware/efi"
efivarfs_path = "/sys/firmware/efi/efivars"
sd_boot_loader_guid = "4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
boot_next_var = "BootNext"
boot_order_var = "BootOrder"
loader_info_var = "LoaderInfo"
loader_entry_one_shot_var = "LoaderEntryOneShot"
loader_entry_default_var = "LoaderEntryDefault"

# efi_linux_dir is the fixed UEFI-style (backslash) directory composefs-boot's own
# `write_t2_simple` always writes UKI images under (`EFI/Linux/`, hardcoded upstream, not
# configurable) — used to build the file-path device-path segment when genesis registers a
# UEFI Boot#### entry for a UKI slot, so the entry's path always matches where the image
# actually lands. efi_linux_real_path is the same directory spelled as a real, mounted-
# filesystem-relative path (forward slashes, joined via `Path::join`) — used by
# `Uki::confirm_boot` to find `to.efi`/`from.efi` on the real, mounted ESP; the two can't share
# a definition (see `lib/setup/lib.toml`'s own `genesis.efi_linux_dir` for the same split,
# there for the same reason: different crate, different path flavor needed).
#
# to_slot/from_slot must stay in sync with `upac-lib`'s own `lib.toml`
# (`boot.upac_uki_to_slot`/`upac_uki_from_slot`) — two separate crates, can't share a single
# Rust constant, so each carries its own literal copy. `Uki::confirm_boot` only performs the
# to/from file swap when the confirmed `entry_name` is `to_slot` specifically (confirming a
# fallback boot into `from_slot` means `to_slot` may be broken — nothing should be promoted).
[uki]
efi_linux_dir = "\\EFI\\Linux\\"
efi_linux_real_path = "EFI/Linux"
to_slot = "upac-to"
from_slot = "upac-from"

# grub has no EFI-variable-based one-shot mechanism — it's file-based (grubenv), driven through
# grub's own grub-reboot/grub-set-default tools rather than a hand-rolled binary-format writer.
# Both the grubenv location and the tool names differ across distro packaging: Debian/Ubuntu/Arch
# ship grub-reboot/grub-set-default writing /boot/grub/grubenv, while Fedora/RHEL keep the
# "grub-*" names reserved for legacy GRUB Legacy and ship grub2-reboot/grub2-set-default writing
# /boot/grub2/grubenv instead. *_primary/*_fallback are tried in that order.
#
# install_bin_primary/install_bin_fallback: same Debian/Arch-vs-Fedora naming split as the
# reboot/set-default tools above, for the one-time `grub-install` genesis runs against a freshly
# partitioned ESP. install_target is grub's own `--target` platform name (this project is
# UEFI/x86_64-only throughout, matching systemd_boot/refind's own *_x64 binary names below).
# install_bootloader_id is grub's `--bootloader-id`, used only to name the install's own
# NVRAM-independent EFI/BOOT/BOOTX64.EFI fallback copy (see backend.rs's `--removable --no-nvram`
# — genesis relies on the same UEFI firmware fallback path uki/systemd-boot/refind's binaries
# already occupy, not a Boot#### entry, so a fresh disk boots without any NVRAM setup).
[grub]
grubenv_primary = "/boot/grub/grubenv"
grubenv_fallback = "/boot/grub2/grubenv"
reboot_bin_primary = "grub-reboot"
reboot_bin_fallback = "grub2-reboot"

set_default_bin_primary = "grub-set-default"
set_default_bin_fallback = "grub2-set-default"

install_bin_primary = "grub-install"
install_bin_fallback = "grub2-install"

install_target = "x86_64-efi"

install_bootloader_id = "upac"

# rEFInd has no separate one-shot/persistent pair of variables like systemd-boot's
# LoaderEntryOneShot/LoaderEntryDefault — it has exactly one, PreviousBoot, which it writes itself
# just before launching any OS and reads back as the default on its next launch. Selecting an
Expand Down
3 changes: 2 additions & 1 deletion booters/grub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ name = "upac_boot_grub"
crate-type = ["cdylib", "rlib"]

[dependencies]
upac-abi = { workspace = true }
upac-abi = { workspace = true }
upac-types = { workspace = true }

[build-dependencies]
toml = { workspace = true }
Expand Down
29 changes: 14 additions & 15 deletions booters/grub/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,25 @@ fn main() -> Result<(), Box<dyn Error>> {
let raw = read_to_string(&source)?;
let config: Value = from_str(&raw)?;

let mut generated = String::new();

let sections = config.as_table().ok_or("booter.toml: root must be a table")?;
for (section, entries) in sections {
generated.push_str(&format!("pub mod {section} {{\n"));
let section = "grub";
let entries = config
.get(section)
.and_then(Value::as_table)
.ok_or_else(|| format!("booter.toml: [{section}] must be a table"))?;

let entries = entries
.as_table()
.ok_or_else(|| format!("booter.toml: [{section}] must be a table"))?;
for (key, value) in entries {
let value = value
.as_str()
.ok_or_else(|| format!("booter.toml: {section}.{key} must be a string"))?;
let mut generated = String::new();
generated.push_str(&format!("pub mod {section} {{\n"));

generated.push_str(&format!(" pub const {}: &str = {value:?};\n", key.to_uppercase()));
}
for (key, value) in entries {
let value = value
.as_str()
.ok_or_else(|| format!("booter.toml: {section}.{key} must be a string"))?;

generated.push_str("}\n");
generated.push_str(&format!(" pub const {}: &str = {value:?};\n", key.to_uppercase()));
}

generated.push_str("}\n");

let out = Path::new(&var("OUT_DIR")?).join("layout.rs");
write(out, generated)?;

Expand Down
Loading
Loading