The two BlockCore length setters swapped their anchors relative to the C++ semantics they document, so the ported edit commands produced wrong geometry on the live UI paths: roll edits kept the seam still, slides left negative in-points, and trims wrote the timeline in-point into media_in (playing the wrong media content). Adopt three stored-range primitives in block.rs: - set_length_and_media_out: in fixed, out moves, media untouched (resize, trim-out, gaps growing rightward). - set_length_and_media_in: in fixed, out moves, media_in += old-new (resize-with-media-in, splice right half, ripple trim-in). - set_length_keeping_out (new): out fixed, in moves, media_in += old-new (trim-in body and out-neighbour, slide out-neighbour, ripple trim-in of the trailing block). Point every command at the primitive matching its intent (undopointer, undogeneral, undoripple, undosplit, graphops, cli, nodeops) and fix the two real defects the swap hid: - TrackReplaceBlockWithGapCommand grew a following gap rightward, swallowing whatever followed it: the "dragging one clip moves unrelated clips" regression. The gap now grows leftward over the removed block's span; regression test in domain_test. - The ripple/splice trims now advance media_in instead of rewriting it, and BlockSplitCommand writes both halves' ranges and media explicitly (the second half continues from the split point). Rewrite the KNOWN-SWAP expectations to the correct geometry (roll moves the seam, slide has no negative in-point, insert-gaps grows rightward, resize-with-media-in yields media_in = 20) and add the missing media assertions. TrackSlideCommand documents that the caller positions the sliding blocks (the stored model has no track layout).
oak-cli (Rust)
Headless command-line consumer of the oak editor module crates — 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 render backend) |
| 64 | usage error |
Build and test
cargo build --release # binary: target/release/oak-cli
cargo test # unit + integration tests
The crate is self-contained (M14 R2): it links the oak* module rlibs
directly (oaknode, oaktimeline, oakcodec, oakrender, oaktask,
oak_core) — no liboakengine dylib, no C ABI, no build.rs link step.
cargo test -p oak-cli stands alone.
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.
Layout
src/
main.rs clap surface, --help/-h + unknown-command handling, dispatch
engine.rs module-native assembly layer (M14 R2): project load/create,
footage probe, sequence + clip assembly, montage resolution,
ticket rendering, synchronous export
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 + module-crate calls
tests/cli.rs binary-level tests (exit codes, messages, usage errors)