Created
January 17, 2026 18:48
-
-
Save algesten/417fdae74e0b6b05680e0ba8e880205f to your computer and use it in GitHub Desktop.
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
| //! # Proposed API Changes for str0m | |
| //! | |
| //! Type-state pattern enforcing correct API usage at compile time. | |
| //! | |
| //! ## Core Principles | |
| //! | |
| //! 1. **Time is driven externally** — caller provides `now: Instant` | |
| //! 2. **Every mutation requires poll-to-timeout** — type system enforces this | |
| //! 3. **All state changes are mutations** — media writes, DirectApi, SDP | |
| //! | |
| //! ## Usage | |
| //! | |
| //! ```ignore | |
| //! let tx = rtc.begin(now); | |
| //! let tx = tx.media(mid).write(rtp_time, payload)?; | |
| //! | |
| //! loop { | |
| //! match tx.poll() { | |
| //! Output::Timeout(when) => break, | |
| //! Output::Transmit(pkt) => send(pkt), | |
| //! Output::Event(evt) => handle(evt), | |
| //! } | |
| //! } | |
| //! ``` | |
| use std::marker::PhantomData; | |
| use std::time::Instant; | |
| /// The main WebRTC session object. | |
| /// | |
| /// All interaction happens through transactions started with [`begin()`](Self::begin). | |
| pub struct Rtc {} | |
| impl Rtc { | |
| /// Begins a new transaction, advancing time to `now`. | |
| /// | |
| /// Processes pending timeouts, then returns an [`RtcTx<Mutation>`] for | |
| /// performing a mutation. Must poll to timeout before starting another. | |
| pub fn begin(&mut self, now: Instant) -> RtcTx<'_, Mutation> { | |
| self.handle_timeout(now); | |
| RtcTx { | |
| rtc: Some(self), | |
| _ph: PhantomData, | |
| polled_to_timeout: false, | |
| } | |
| } | |
| fn handle_timeout(&mut self, _now: Instant) { | |
| todo!() | |
| } | |
| } | |
| /// A transaction handle that borrows the [`Rtc`] session. | |
| /// | |
| /// The type parameter `S` tracks state: | |
| /// - [`Mutation`]: can perform a mutation (media write, DirectApi, etc.) | |
| /// - [`PollToTimeout`]: must poll until timeout | |
| /// | |
| /// Dropping without completing the transaction panics. | |
| pub struct RtcTx<'a, S> { | |
| /// Option enables transferring the borrow to `MediaWriter`/`DirectApi` via `.take()`. | |
| /// This sidesteps borrow checker issues with Drop + returning the reference. | |
| rtc: Option<&'a mut Rtc>, | |
| _ph: PhantomData<S>, | |
| polled_to_timeout: bool, | |
| } | |
| /// Type-state: can perform a mutation. | |
| pub struct Mutation; | |
| /// Type-state: must poll until timeout. | |
| pub struct PollToTimeout; | |
| /// Methods take `self` to enforce single mutation per transaction. | |
| impl<'a> RtcTx<'a, Mutation> { | |
| /// Gets a writer for the specified media track. | |
| pub fn media(mut self, _mid: Mid) -> MediaWriter<'a> { | |
| MediaWriter { | |
| rtc: self.rtc.take().unwrap(), | |
| } | |
| } | |
| /// Gets the direct API for changes without SDP negotiation. | |
| pub fn direct_api(mut self) -> DirectApi<'a> { | |
| DirectApi { | |
| rtc: self.rtc.take().unwrap(), | |
| } | |
| } | |
| /// Skips mutation, transitions directly to polling. | |
| pub fn timeout_only(mut self) -> RtcTx<'a, PollToTimeout> { | |
| RtcTx { | |
| rtc: self.rtc.take(), | |
| polled_to_timeout: false, | |
| _ph: PhantomData, | |
| } | |
| } | |
| } | |
| /// Takes `&mut self` to allow repeated polling until timeout. | |
| impl<'a> RtcTx<'a, PollToTimeout> { | |
| /// Polls for next output. Call in a loop until `Output::Timeout`. | |
| pub fn poll(&mut self) -> Output { | |
| let is_timeout = todo!(); | |
| // If we encounter timeout, mark as polled for Drop trait. | |
| if is_timeout { | |
| self.polled_to_timeout = true; | |
| } | |
| todo!() | |
| } | |
| } | |
| /// Panics if dropped without polling to timeout. | |
| /// | |
| /// This catches the case where a transaction is abandoned mid-way, e.g. due to | |
| /// an early return or `?` operator. The panic ensures bugs are caught during | |
| /// development rather than causing silent state corruption. | |
| /// | |
| /// When `rtc` is `None`, ownership was transferred (e.g. to `MediaWriter`), | |
| /// so the new owner is responsible for completing the transaction. | |
| impl<'a, S> Drop for RtcTx<'a, S> { | |
| fn drop(&mut self) { | |
| if self.rtc.is_some() && !self.polled_to_timeout { | |
| panic!("RtcTx dropped without polling to timeout"); | |
| } | |
| } | |
| } | |
| /// Handle for writing media to a track. Obtained from [`RtcTx::media()`]. | |
| pub struct MediaWriter<'a> { | |
| rtc: &'a mut Rtc, | |
| } | |
| impl<'a> MediaWriter<'a> { | |
| /// Writes media data. Returns `RtcTx<PollToTimeout>` that must be polled. | |
| pub fn write( | |
| self, | |
| _rtp_time: Ts, | |
| _payload: Vec<u8>, | |
| ) -> Result<RtcTx<'a, PollToTimeout>, Error> { | |
| Ok(RtcTx { | |
| rtc: Some(self.rtc), | |
| _ph: PhantomData, | |
| polled_to_timeout: false, | |
| }) | |
| } | |
| } | |
| /// Direct API for changes without SDP. Obtained from [`RtcTx::direct_api()`]. | |
| /// | |
| /// Methods take `&mut self` to allow multiple mutations before [`commit()`](Self::commit). | |
| pub struct DirectApi<'a> { | |
| rtc: &'a mut Rtc, | |
| } | |
| impl<'a> DirectApi<'a> { | |
| /// Declares a data channel. | |
| pub fn declare_data_channel(&mut self, _id: String) { | |
| todo!() | |
| } | |
| /// Commits changes. Returns `RtcTx<PollToTimeout>` that must be polled. | |
| pub fn commit(self) -> RtcTx<'a, PollToTimeout> { | |
| RtcTx { | |
| rtc: Some(self.rtc), | |
| _ph: PhantomData, | |
| polled_to_timeout: false, | |
| } | |
| } | |
| } | |
| /// Media identifier for a track. | |
| pub struct Mid(String); | |
| /// Errors from mutations. | |
| pub enum Error {} | |
| /// RTP timestamp. | |
| pub struct Ts(u64); | |
| /// Output from polling. Poll until `Timeout`. | |
| pub enum Output { | |
| // Transmit(Packet), | |
| // Event(Event), | |
| // Timeout(Instant), | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment