fix(moq-mux): keep catalog Consumer Clone + stable FramedFormat discriminants#1661
Conversation
The catalog Consumer lost its Clone impl when it was rewritten to wrap moq_json::Consumer in #1655. Add a manual Clone impl to moq_json::Consumer (every field is already Clone; manual to avoid a spurious T: Clone bound since T only lives in PhantomData) and re-derive Clone on the moq-mux catalog Consumer. A cloned consumer inherits the current reconstruction state and gets independent track/group cursors that advance in parallel.
… 0.5.4 #1625 inserted Vp8/Vp9 in the middle of FramedFormat, shifting the implicit discriminants of Aac/Opus/Mkv/Ts (5..8 -> 7..10) and tripping cargo-semver-checks. Move the new variants to the end so existing discriminants stay put, and add a note so future variants are appended too. With the discriminant shift gone and Clone restored, the only remaining API break is the removal of the public `track` field on the catalog Consumer. That field was never a sensible part of the API, so ship this as a patch (0.5.3 -> 0.5.4) rather than a minor bump.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThis PR adds Clone implementations to Consumer types and updates moq-mux. In rs/moq-json, Consumer gains a manual Clone that shares the TrackConsumer and duplicates reconstruction state (group, current, frames_read). In rs/moq-mux/catalog/hang, the Consumer wrapper now derives Clone. In rs/moq-mux/src/import.rs, FramedFormat variants were reordered (Vp8/Vp9 moved to the end) and matching FromStr/Display arms were updated. The rs/moq-mux crate version was bumped to 0.5.4. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
rs/moq-mux/src/catalog/hang/consumer.rs (1)
11-14: ⚡ Quick winConsider adding a test for Clone behavior.
While the derive is correct, a test verifying that cloned catalog consumers work independently would strengthen the test suite. As per coding guidelines, tests should be added where they're easy to write, and this would be straightforward given the existing test infrastructure in this file.
🧪 Example test structure
#[test] fn cloned_consumer_reads_independently() { let mut track = Catalog::default_track().produce(); let mut consumer1 = Consumer::new(track.consume()); let waiter = kio::Waiter::noop(); let (catalog, payload) = catalog_payload("test"); let mut group = track.append_group().expect("group should append"); group.write_frame(payload).expect("frame should write"); group.finish().expect("group should finish"); let mut consumer2 = consumer1.clone(); // Both consumers should read the same catalog independently assert_eq!(expect_catalog(consumer1.poll_next(&waiter)), catalog); assert_eq!(expect_catalog(consumer2.poll_next(&waiter)), catalog); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-mux/src/catalog/hang/consumer.rs` around lines 11 - 14, Add a unit test that verifies Consumer (the #[derive(Clone)] struct wrapping moq_json::Consumer<Catalog>) clones produce independent readers: create a Catalog::default_track(), produce and append a group with catalog payload using catalog_payload, instantiate consumer1 via Consumer::new(track.consume()), clone it to consumer2, and assert both consumer1.poll_next(&waiter) and consumer2.poll_next(&waiter) return the same expected catalog (use expect_catalog to compare); place the test alongside other tests in this file to exercise Consumer::clone and poll_next behavior.Source: Coding guidelines
rs/moq-json/src/lib.rs (1)
227-239: ⚡ Quick winConsider adding a test for Clone semantics.
The Clone implementation is correct, but a test verifying that cloned consumers can reconstruct values independently would be valuable and straightforward to add. As per coding guidelines, Rust tests are integrated within source files and should be added where they're easy to write.
🧪 Example test structure
#[test] fn cloned_consumer_reconstructs_independently() { let config = Config { delta_ratio: Some(100.0) }; let (mut producer, track) = producer(config); let mut consumer1 = Consumer::<Value>::new(track); let waiter = kio::Waiter::noop(); producer.update(&json!({ "a": 1 })).unwrap(); let _ = consumer1.poll_next(&waiter); // Read snapshot let mut consumer2 = consumer1.clone(); // Clone mid-stream producer.update(&json!({ "a": 2 })).unwrap(); producer.finish().unwrap(); // Both consumers should independently reconstruct the same final value let val1 = consumer1.poll_next(&waiter); let val2 = consumer2.poll_next(&waiter); assert_eq!(val1, val2); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-json/src/lib.rs` around lines 227 - 239, Add a unit test next to the Consumer implementation that verifies Clone semantics: create a producer and a Consumer::<Value>::new(track), let the producer publish a snapshot, clone the consumer using the existing Clone impl for Consumer<T>, then publish another update and finish the producer; call Consumer::poll_next (using a kio::Waiter::noop()) on both the original and cloned consumers and assert the reconstructed values are equal. Reference symbols: Consumer<T> Clone impl, Consumer::new, Consumer::poll_next, producer.update, and producer.finish; the test should live in the same source file as the impl and use the same helper producer(...) used in other tests.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@rs/moq-json/src/lib.rs`:
- Around line 227-239: Add a unit test next to the Consumer implementation that
verifies Clone semantics: create a producer and a Consumer::<Value>::new(track),
let the producer publish a snapshot, clone the consumer using the existing Clone
impl for Consumer<T>, then publish another update and finish the producer; call
Consumer::poll_next (using a kio::Waiter::noop()) on both the original and
cloned consumers and assert the reconstructed values are equal. Reference
symbols: Consumer<T> Clone impl, Consumer::new, Consumer::poll_next,
producer.update, and producer.finish; the test should live in the same source
file as the impl and use the same helper producer(...) used in other tests.
In `@rs/moq-mux/src/catalog/hang/consumer.rs`:
- Around line 11-14: Add a unit test that verifies Consumer (the
#[derive(Clone)] struct wrapping moq_json::Consumer<Catalog>) clones produce
independent readers: create a Catalog::default_track(), produce and append a
group with catalog payload using catalog_payload, instantiate consumer1 via
Consumer::new(track.consume()), clone it to consumer2, and assert both
consumer1.poll_next(&waiter) and consumer2.poll_next(&waiter) return the same
expected catalog (use expect_catalog to compare); place the test alongside other
tests in this file to exercise Consumer::clone and poll_next behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fcabf70f-4f7f-4f34-9557-3e5f8981fa75
📒 Files selected for processing (2)
rs/moq-json/src/lib.rsrs/moq-mux/src/catalog/hang/consumer.rs
A clone taken mid-group must carry its own reconstruction state and an independent cursor, then apply a later delta on top of its own copy. Covers the manual Clone impl added in this PR.
Summary
cargo-semver-checks flagged
moq-muxas API-breaking in the release-plz PR (#1618). This walks back the avoidable breaks so the crate can ship as a patch (0.5.3 → 0.5.4) instead of a minor bump.Cloneon the catalogConsumer. It lostClonewhen feat(moq-json): JSON Merge Patch snapshot/delta helper, route hang catalog through it #1655 rewrote it to wrapmoq_json::Consumer<Catalog>. Every field of that struct is alreadyClone, so this adds a manualimpl<T> Clone for moq_json::Consumer<T>(manual rather than#[derive]to avoid a spuriousT: Clonebound, sinceTonly lives inPhantomData<fn() -> T>) and re-derivesCloneon themoq-muxside. A cloned consumer inherits the current reconstruction state and gets independentTrackConsumer/GroupConsumercursors that "inherit this offset, but then run in parallel" (group.rs).FramedFormatto keep discriminants stable. feat(moq-mux): add VP8 and VP9 codec modules (import + fMP4 export) #1625 insertedVp8/Vp9in the middle of the enum, shifting the implicit discriminants ofAac/Opus/Mkv/Ts(5..8 → 7..10). Moving the new variants to the end restores the prior values; a comment notes new variants should be appended.moq-muxto 0.5.4 (manual patch).What's intentionally left breaking
moq-mux: the removed publictrackfield on the catalogConsumerstays gone. It was never a sensible part of the API and nobody should depend on it, so it ships under the patch bump.moq-native: its semver changes are kept as-is (the removed TLS/iroh/websocket types are internal plumbing). No changes here.Test plan
cargo test -p moq-json— addedcloned_consumer_reconstructs_independently, which clones a consumer mid-group and asserts both copies apply a later delta on their own reconstruction statecargo test -p moq-mux --lib(175 passed, incl.FramedFormatreorder)cargo build -p moq-json -p moq-muxcompiles cleanly(Written by Claude)