- src/facade/rust -> src/engine/rust; package oakfacade -> oakengine - crate-type += cdylib; module crates are real deps; linkage anchors force-link module C ABIs into the dylib - build.rs: -undefined dynamic_lookup for host-provided oakcore_*/fb_* - nm: 749 oakengine_* + 687 module oak*_* exports; undefined set is only the intended host-provided symbols - worker/cli updated to the new path/name; undo test race fix
oak-cli (Rust)
Headless command-line consumer of the liboakengine C ABI facade — the Rust
rewrite of cli/main.cpp (which stays in the tree until cutover). Same
subcommands, same output format, same exit codes:
| exit | meaning |
|---|---|
| 0 | success |
| 1 | general error (bad project/media file, no sequence, I/O failure) |
| 2 | rendering unavailable or failed (e.g. no GL render backend) |
| 64 | usage error |
Build and test
cargo build --release # binary: target/release/oak-cli
cargo test # unit + integration tests (29 tests)
The crate builds standalone: its only dependency besides clap is the
oakfacade rlib (../../src/facade/rust), which has no third-party
dependencies.
Subcommands
Every subcommand of the C++ original is implemented:
oak-cli info <project.ove> <start> <end> <out_dir> project name/sequences/footage
oak-cli render <project.ove> <start_seconds> <end_seconds> <out_dir>
oak-cli probe <mediafile>
oak-cli transcode <input_media> <out> [width] [--format ppm|mp4]
Argument validation is faithful to the C++ (invalid start seconds,
invalid width, unknown --format … all exit 64). The output formatters
(src/fmt.rs) reproduce the C++ printf output byte for byte and are
golden-tested against the output captured from the C++ binary on the test
fixtures (tests/project_with_footage.ove, tests/demo.mp4); the PPM and
WAV writers (src/ppm.rs, src/wav.rs) are the exact ports of the C++
write_ppm/write_wav and are unit-tested.
Facade status: everything is currently deferred
All four subcommands depend on facade families that are still deferred
in the oakfacade crate (src/facade/rust/src/deferred.rs), so today each
subcommand validates its arguments, then prints a clear "not yet available"
error naming the missing families and the reasons, and exits with the
C++-compatible code — it never crashes and never fakes output:
| subcommand | needs | current behavior |
|---|---|---|
info |
init + node (project/footage) + timeline | "not yet available", exit 1 |
probe |
init + node (footage) | "not yet available", exit 1 |
render |
init + node + timeline + render | "not yet available", exit 2 |
transcode |
init + node + timeline + render + exporter | "not yet available", exit 2 |
The deferral registry is src/deferred.rs (field-for-field in sync with the
facade's own deferred.rs). When a family is wrapped by the facade:
- remove its entry from
src/deferred.rs, - wire the call-through in
src/cmd/using the extern declarations insrc/ffi.rs(verbatim mirrors of the engine headers) and the tested formatters/writers — no manifest or signature change is needed, because the externs resolve against the already-linkedoakfacaderlib.
Layout
src/
main.rs clap surface, --help/-h + unknown-command handling, dispatch
ffi.rs the oakengine_* surface oak-cli consumes (declarations only)
deferred.rs facade-family availability registry (mirror of facade deferred.rs)
fmt.rs golden output formatters (info/probe)
ppm.rs P6 PPM writer (f32/u8 frames)
wav.rs PCM s16 WAV writer (interleaved float samples)
cmd/ per-subcommand validation + deferred gate
tests/cli.rs binary-level tests (exit codes, messages, usage errors)