arcly-stream 0.1.6

An open-extensible live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, a pluggable multi-protocol ingestion layer (RTMP, RTSP, SRT, WHIP/WHEP shipped), and a feature-gated pure-Rust media plane (MPEG-TS/HLS/fMP4) — runtime, config, and metrics free.
Documentation
//! Cheaply-cloneable identifiers for applications and streams.

use std::fmt;
use std::sync::Arc;

/// Shared implementation for cheaply-cloneable `Arc<str>` newtype identifiers.
macro_rules! arc_str_newtype {
    ($(#[$meta:meta])* $Name:ident) => {
        $(#[$meta])*
        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
        pub struct $Name(Arc<str>);

        impl serde::Serialize for $Name {
            fn serialize<S: serde::Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
                s.serialize_str(&self.0)
            }
        }

        impl<'de> serde::Deserialize<'de> for $Name {
            fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
                let s = String::deserialize(d)?;
                Ok(Self(Arc::from(s.as_str())))
            }
        }

        impl $Name {
            /// Create a new identifier from any string-like value.
            pub fn new(s: impl Into<String>) -> Self {
                Self(Arc::from(s.into().as_str()))
            }

            /// Borrow the identifier as a `&str`.
            pub fn as_str(&self) -> &str {
                &self.0
            }
        }

        impl fmt::Display for $Name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                f.write_str(&self.0)
            }
        }

        impl From<&str> for $Name {
            fn from(s: &str) -> Self { Self(Arc::from(s)) }
        }

        impl From<String> for $Name {
            fn from(s: String) -> Self { Self(Arc::from(s.as_str())) }
        }
    };
}

arc_str_newtype!(
    /// Name of an application (e.g. `"live"`, `"vod"`).
    /// Cheaply cloneable — backed by an `Arc<str>`.
    AppName
);

arc_str_newtype!(
    /// Unique identifier for a stream within an application.
    /// Cheaply cloneable — backed by an `Arc<str>`.
    StreamId
);

/// Composite key uniquely identifying a stream: `(app_name, stream_id)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct StreamKey {
    /// Application the stream belongs to.
    pub app: AppName,
    /// Stream identifier within the application.
    pub stream_id: StreamId,
}

impl StreamKey {
    /// Build a key from an application name and stream id.
    pub fn new(app: impl Into<AppName>, stream_id: impl Into<StreamId>) -> Self {
        Self {
            app: app.into(),
            stream_id: stream_id.into(),
        }
    }
}

impl fmt::Display for StreamKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}/{}", self.app, self.stream_id)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn newtypes_are_cheap_clones_with_value_equality() {
        let a = AppName::from("live");
        let b = a.clone();
        assert_eq!(a, b);
        assert_eq!(a.as_str(), "live");
        // Clones share the same Arc allocation (cheap, not a deep copy).
        assert!(std::ptr::eq(a.as_str().as_ptr(), b.as_str().as_ptr()));
    }

    #[test]
    fn stream_key_display_is_app_slash_stream() {
        let key = StreamKey::new("live", "cam-1");
        assert_eq!(key.to_string(), "live/cam-1");
    }

    #[test]
    fn identifiers_roundtrip_through_serde() {
        let key = StreamKey::new("vod", "movie");
        let json = serde_json::to_string(&key).expect("serialize");
        let back: StreamKey = serde_json::from_str(&json).expect("deserialize");
        assert_eq!(key, back);
    }
}