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)
5.1 KiB
oaktimeline Rust crate (declaration draft, for review)
Status: declaration draft. Signatures + doc comments are the spec; every body is
todo!(). Not wired into any build. The crate template (FFI discipline, testing layers) followscrates/oakplugin/README.mdandcrates/oaknode/README.md.
Scope
Replaces the C++ oaktimeline module (src/timeline/src): timeline
markers and work areas, the timeline undo-command family (add/remove
tracks, place/trim/split blocks, ripple edits, slide, gap insertion),
and the shared Timeline namespace / utility helpers.
Public contract: include/timeline/*.h (error.h, displaymode.h,
marker.h, workarea.h, edit.h) — frozen, implemented verbatim by
src/ffi.rs. The timeline value handles (OakTimelineMarkerList,
OakTimelineWorkArea) and every edit command are exported through this
ABI only; consumers (the facade/app, the oaknode crate) never see the
internal Rust types.
Key architectural decisions (C++ → Rust mapping)
-
Domain modules mirror the C++ header files. The C++ module is a flat set of headers, not a deep class hierarchy, so the Rust crate keeps one module per C++ header family (
common,marker,workarea,undocommon,undotrack,undogeneral,undopointer,undoripple,undosplit,util).COVERAGE.mdmaps every C++ type to its Rust home; review that first. -
No C++
UndoCommandsubclass hierarchy. Following the oaknode crate decision (#4), each undo command is a plain Rust struct that exposesprepare()/redo()/undo()(alltodo!()here) and is surfaced to the world through the oakundo C ABI vtable (bridge::undo::oakundo_command_init, Rust callbacks asuserdata). Every command struct carries ato_command() -> CHandlefactory doc comment describing the wiring. -
Value types come from
oakcore-rs. Markers and work areas are built onRational/TimeRange, so the crate depends onoakcore-rs(crates/oakcore) exactly like oaknode does; no pixel/sample formats are involved here. -
All cross-module access goes through the C ABI. Per the project rule (no cross-module C++ member calls), timeline commands touch the node graph exclusively through the oaknode C ABI (
bridge::node), undo through the oakundo C ABI (bridge::undo), and XML/config through the oakcommon C ABI (bridge::common). The C++ internal helpersoakundo_capi::make_command_handle/oaknode_c_api::to_nativeare not replicated in Rust — their role is subsumed by vtable commands and by value handles treated as opaque. -
Handles.
handle.rsprovides the sharedRefBox/CHandlescaffolding (duplicated per crate, as in oaknode) withOAKTIMELINE_ABI_VERSION = 1. Borrowed handles into node-owned objects and owning handles created by*_createshare one box layout{ctx, addref, release, abi_version}.
Layout
src/
lib.rs crate doc + module map
error.rs error codes (mirrors include/timeline/error.h)
handle.rs refcounted-handle scaffolding (same pattern as node)
common.rs Timeline namespace (MovementMode/ThumbnailMode/
WaveformMode, EditToInfo) — timelinecommon.h
marker.rs TimelineMarker/MarkerList + 5 marker commands
workarea.rs TimelineWorkArea + 2 workarea commands
undocommon.rs node/block remove helpers + CHandleCommandWrapper
undotrack.rs track ripple/prepend/insert-after/replace commands
undogeneral.rs resize/media-in/add/remove-track/transition/gap/
enable-disable/insert-gaps/default-transition commands
undopointer.rs BlockTrimCommand/TrackSlideCommand/TrackPlaceBlockCommand
undoripple.rs ripple remove-area / ripple-tool / delete-gaps commands
undosplit.rs BlockSplitCommand/BlockSplitPreservingLinksCommand/
TrackSplitAtTimeCommand
util.rs timelineutil.h inline helpers (rat_nd, same_*,
free_detached_handle, block/track queries)
bridge/ C ABI imports: node.rs, undo.rs, common.rs
ffi.rs include/timeline/*.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. - Timeline objects never outlive their owning node; borrowed handles are created under a guard that owns the node reference.
- Behavior parity with C++ is proven by the C ABI test-suite
(
src/timeline/tests, unchanged) plus the golden tests intests/(XML save/load formats captured verbatim fromsrc/timeline/src/timelinemarker.cpp/timelineworkarea.cpp). - Where C++ behavior is genuinely load-bearing but ugly (e.g. marker
list kept sorted by time, ripple's compensation gap rules), 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.