feat(types): add StreamTypes newtype for stream-kind bit masks#52
Merged
BlindMaster24 merged 2 commits intomainfrom Apr 24, 2026
Merged
feat(types): add StreamTypes newtype for stream-kind bit masks#52BlindMaster24 merged 2 commits intomainfrom
BlindMaster24 merged 2 commits intomainfrom
Conversation
Public recording APIs previously accepted the TeamTalk stream-kind
bitmask as a raw `u32`, which made it easy to pass an unrelated
integer or to lose the intent of a combined mask (e.g. voice +
desktop). The new `StreamTypes` newtype wraps the same `u32`
bitmask with:
* Associated constants mirroring
`teamtalk_sys::StreamType::STREAMTYPE_*` (VOICE, VIDEO_CAPTURE,
MEDIAFILE_AUDIO, MEDIAFILE_VIDEO, DESKTOP, DESKTOP_INPUT,
MEDIAFILE, CHANNEL_MSG, LOCAL_MEDIAPLAYBACK_AUDIO,
CLASSROOM_ALL).
* Standard bit operators (`|`, `|=`, `&`, `&=`, `!`).
* `contains_any` / `contains_all` / `is_empty` inspectors.
* `raw()` / `from_raw()` round-trip with arbitrary `u32` so
callers reading a mask from an event do not lose unknown bits.
* `From<u32>` / `From<StreamTypes> for u32` so the new type
slides into existing `impl Into<StreamTypes>` parameters with
no caller changes at the `u32` call sites.
Public recording surfaces are migrated to the new type:
* `RecordingTarget::Streams { stream_types: StreamTypes, .. }`
* `SyncedUserRecordingOptions.stream_types: StreamTypes`
(default now `StreamTypes::VOICE` instead of a raw
`STREAMTYPE_VOICE as u32` cast).
* `Client::start_recording_streams(stream_types: impl Into<StreamTypes>, ..)`
* `RecordingSession::start_streams(stream_types: impl Into<StreamTypes>, ..)`
* `SyncedUserRecordingOptions::with_stream_types(types: impl Into<StreamTypes>)`
Internal boundaries (the `TeamTalkBackend` trait, FFI
`acquire_user_audio_block` / `enable_audio_block_event`,
`AudioBlockView.stream_types`, and low-level debug helpers) stay
`u32` for now; the newtype converts at the public boundary via
`into().raw()` so this PR is strictly additive at those layers
and can be followed up with further type-safety passes later
without touching FFI.
Tests:
* `crates/teamtalk/tests/stream_types_tests.rs` adds 9 new
integration tests:
- Constants match the FFI enum values bit-for-bit.
- `empty()` is zero and equals `Default::default()`.
- `from_raw` round-trips arbitrary 32-bit patterns.
- `|`, `|=`, `&`, `&=`, `!` behave as expected.
- `contains_any` vs `contains_all` semantics.
- `impl Into<StreamTypes>` accepts both raw `u32` and typed
combinations.
- `CLASSROOM_ALL` composition (voice + video_capture +
mediafile_audio + mediafile_video + desktop + channel_msg =
95; desktop_input intentionally excluded).
* `tests/recording_synced_tests.rs` is updated to compare the
field as a `StreamTypes` and also asserts the raw value.
Local verification:
* cargo fmt --all clean.
* cargo clippy --workspace --all-targets --all-features -- -D warnings
clean.
* cargo test --workspace --all-features — all tests pass, including
the 9 new stream_types integration tests.
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
3 tasks
Devin Review pointed out that the previous doc comment on StreamTypes::CLASSROOM_ALL listed "voice + media file + desktop + desktop input" but the actual value 95 decomposes to voice(1) + video_capture(2) + mediafile_audio(4) + mediafile_video(8) + desktop(16) + channel_msg(64). DESKTOP_INPUT(32) is intentionally **not** part of this mask. The test `classroom_all_matches_documented_value` already pins down this composition, so only the doc comment needed fixing. Also mention the intentional absence of DESKTOP_INPUT so future readers do not try to add it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Public recording APIs previously accepted the TeamTalk stream-kind bitmask as a raw
u32, which made it easy to pass an unrelated integer or to lose the intent of a combined mask (voice + desktop, etc.). This PR introduces a type-safeStreamTypesnewtype inteamtalk::typesand migrates the public recording surfaces to use it.New type (
teamtalk::types::StreamTypes):teamtalk_sys::StreamType::STREAMTYPE_*:NONE,VOICE,VIDEO_CAPTURE,MEDIAFILE_AUDIO,MEDIAFILE_VIDEO,DESKTOP,DESKTOP_INPUT,MEDIAFILE,CHANNEL_MSG,LOCAL_MEDIAPLAYBACK_AUDIO,CLASSROOM_ALL.|,|=,&,&=,!).contains_any/contains_all/is_emptyinspectors.raw()/from_raw()round-trip with anyu32so callers reading a mask from an event do not lose unknown bits.From<u32>/From<StreamTypes> for u32so the new type slides into existingimpl Into<StreamTypes>parameters without caller changes at raw-u32call sites.Migrated public surfaces:
RecordingTarget::Streams { stream_types: StreamTypes, .. }SyncedUserRecordingOptions.stream_types: StreamTypes(default nowStreamTypes::VOICEinstead of a rawSTREAMTYPE_VOICE as u32cast).Client::start_recording_streams(stream_types: impl Into<StreamTypes>, ..)RecordingSession::start_streams(stream_types: impl Into<StreamTypes>, ..)SyncedUserRecordingOptions::with_stream_types(types: impl Into<StreamTypes>)Existing callers that pass a raw
u32continue to compile viaInto<StreamTypes>.Left at
u32(intentional, follow-up scope):Internal boundaries stay
u32: the sealedTeamTalkBackendtrait, FFI surfaces (acquire_user_audio_block,enable_audio_block_event,stream_audio_blocks*),AudioBlockView.stream_types, andClient::dbg_set_input_tone. The newtype converts at the public boundary viainto().raw(), so this PR is strictly additive at those layers — a subsequent PR can tighten them without touching FFI.Tests:
crates/teamtalk/tests/stream_types_tests.rsadds 9 new integration tests (FFI-value-parity, round-trip, bit ops,contains_*,Into<StreamTypes>,CLASSROOM_ALLcomposition).tests/recording_synced_tests.rsupdated to compare viaStreamTypes.Local verification:
cargo fmt --all --checkclean.cargo clippy --workspace --all-targets --all-features -- -D warningsclean.cargo test --workspace --all-features— all tests pass, including the 9 newstream_types_tests.rsintegration tests.Review & Testing Checklist for Human
TEAMTALK_DLL/TeamTalk.hStreamType_*values. The testconstants_match_ffi_stream_type_valuesruns this check against the generated bindings at compile time, so if the header diverges the test will catch it — but a human eyeball pass on the constant list incrates/teamtalk/src/types/base.rsis still worth it.AudioBlockView.stream_typesasu32is acceptable. The rationale is that it is a raw read of an FFI event field; a follow-up PR can convert it if you want full type-safety on the read side too.with_stream_types(StreamTypes::VOICE),with_stream_types(StreamTypes::VOICE | StreamTypes::DESKTOP), andwith_stream_types(0x5u32).Notes
Second P1 API improvement after the ExponentialBackoff jitter PR (#51). Branches from clean
main, independent of the other PRs in flight, so can be merged in any order.Next up:
SdkErrorCodemapping forError::CommandFailed,TimeoutKindenum forError::Timeout,SecretString/zeroizefor passwords, indexedEventBus::dispatch/Router::dispatchviaHashMap, then a big coverage-gap test-fill PR driven byscripts/audit_teamtalk_coverage.py.The pre-existing
semverCI gate is expected to remain red;release-plzhandles the eventual version bump.Link to Devin session: https://app.devin.ai/sessions/71fdd6196cb74723a2e277bb81993a9c
Requested by: @BlindMaster24