Single mechanical restructure commit: - root Cargo.toml = oakapp bin + workspace; one cargo build produces oakapp, oak-cli, oak-worker, liboakengine.dylib - app/rust/src -> src/ (app at repo root, no rust/ nesting) - src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore; src/bindings/oakotio -> crates/oakotio; src/engine/rust -> crates/oakengine (keeps cdylib+staticlib+rlib) - public C headers include/<mod>/ -> crates/oakengine/include/<mod>/ - OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone) - legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/, app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets submodule, otio-install, all build-* output (~40GB) - oakstorage kept but excluded from the workspace (skeleton w/ todos); gpui excluded (own workspace) - verified: cargo build green, cargo test --workspace 1845/0 (with the documented OCIO_RS_* env override for the homebrew OCIO)
oaknode Rust crate (implementation)
Status: all FFI headers implemented. Phase 1 (core engine: graph arena, values, keyframes, project, factory, ~55 FFI exports), Phase 2 (sequence/track/block/footage/colormanager + traverser + serializer, the folder/group/keyframe/dragger FFI families, the undo/XML bridges with test stubs, and the contract tests) and Phase 3 (the multicam grid family and the deferred bridge exports: markers/work-area/frame cache accessors, viewer params, sequence/footage stream params via the videoparams/audioparams C ABIs, and the colormanager compliant transform) are complete;
cargo test --features test-stubsis green (84 tests, 1 ignored byte-exact golden). The remainingtodo!()s are the concrete node-type behaviors undersrc/nodes/(registered in the factory, bodies deferred) — the multicam node behavior and the effect/generator nodes. The crate template (FFI discipline, testing layers) followscrates/oakplugin/README.md.
Scope
Replaces the C++ oaknode module (src/node/src, ~40k lines):
the node graph engine, project/folder/sequence/track/block hierarchy,
footage, color manager, keyframes, evaluation (traverser), project
serialization, and the undo bridge.
Public contract: include/node/*.h (14 headers, ~280 functions) —
frozen, implemented verbatim by src/ffi.rs.
Key architectural decisions (C++ → Rust mapping)
- Inheritance → arena + trait objects. The C++ design is deep
inheritance (
Node→ViewerOutput/Track/Block/… and ~50 effect nodes). Rust: a slab-allocatedGrapharena ofNodeEntry { core: NodeCore, behavior: Box<dyn NodeBehavior> }, addressed by generationalNodeId. No reference cycles exist by construction (edges are IDs, not pointers). - Cross-module inheritance disappears. C++
RenderProcessor : NodeTraverser(render subclassing a node class) becomes a plain evaluation API:traverser::evaluate(...) -> NodeValueTableis a function, and oakrender supplies backend hooks via a trait (RenderHooks) instead of overriding virtuals. - Value system.
olive::Variant/type-erasure becomes a closedNodeValueenum (value.rs). C ABI marshalling lives only inffi.rs. - Undo. Commands are created through the oakundo C ABI
(
bridge::undo); the C++UndoCommandsubclass hierarchy becomes vtable commands whose userdata is a Rust closure. - Serialization. XML read/write goes through the oakcommon C ABI
(
bridge::common) until oakcommon itself is rewritten. - Threading. The C++ code relied on Qt's event thread +
called_on_owner_thread()assertions. Rust replaces this withMutex<Graph>interior mutability plus explicit&mut Graphphases for structural edits; the threading contract is documented per function.
Layout
COVERAGE.md maps every method of the C++ olive::Node (260
declaration lines, ~150 unique methods) to its Rust home — trait /
core / graph / ops / bridge / drop-with-reason. Review that first.
src/
lib.rs crate doc + module map
error.rs error codes (mirrors include/node/error.h)
handle.rs refcounted-handle scaffolding (same pattern as plugin)
value.rs NodeValue / NodeValueTable / ValueHint
id.rs NodeId, generational arena ids
node.rs NodeCore + NodeBehavior trait (the virtual surface)
graph.rs Graph arena, edges, topological order
input.rs Input descriptors, flags, array inputs, hints
keyframe.rs NodeKeyframe + track interpolation
project.rs Project, settings, folder tree
sequence.rs Sequence (ViewerOutput equivalent)
track.rs Track, TrackList
block.rs Block/ClipBlock/GapBlock/TransitionBlock
footage.rs Footage (probe via oakcodec C ABI)
colormanager.rs ColorManager (OCIO via oakrender C ABI for now)
traverser.rs Evaluation engine (iterative, hook-based)
serializer.rs XML project load/save (bridge::common)
factory.rs Node type registry (id -> constructor)
nodes/ The concrete built-in node types
bridge/ C ABI imports: common.rs, undo.rs, render.rs, codec.rs
ffi.rs include/node/*.h export layer
tests/ contract + golden tests (see README test section)
Hard rules for the implementer
- Every
extern "C"body goes throughhandle::guard*; no panic crosses FFI. Graphis the only owner of nodes; the public API never hands out references into the arena, onlyNodeId-carrying handles.- Behavior parity with C++ is proven by the C ABI test-suite
(
src/node/tests, unchanged) plus the golden tests intests/. - Where C++ behavior is genuinely load-bearing but ugly (e.g.
Blocklength-change side effects onTrack), port the behavior, not the aesthetics; leave a// CPP-PARITY:comment with the C++ file:line.
Dependency policy
Prefer mature third-party crates (MIT/Apache-2.0/BSD, GPL-compatible) over hand-rolling; register each addition (name + reason) here. Large existing C++ libraries (OTIO, OCIO, OIIO, FFmpeg) are NEVER rewritten — they are consumed through their C ABI / bridge layers.