use chrono::{DateTime, Utc};
use livekit_common::EncryptionType;
use parking_lot::RwLock;
use std::{collections::HashMap, sync::Arc};
use crate::{
types::{ByteHeader, ContentHeader, Header, OperationType, TextHeader},
utils::StreamError,
};
#[derive(Clone, Debug)]
pub struct ByteStreamInfo {
pub id: String,
pub topic: String,
pub timestamp: DateTime<Utc>,
pub total_length: Option<u64>,
pub mime_type: String,
pub name: String,
pub encryption_type: EncryptionType,
#[cfg(feature = "test-utils")]
pub is_compressed: bool,
#[cfg(feature = "test-utils")]
pub is_inline: bool,
attributes_map: Arc<RwLock<HashMap<String, String>>>,
}
impl ByteStreamInfo {
pub fn attributes(&self) -> HashMap<String, String> {
self.attributes_map.read().clone()
}
}
#[derive(Clone, Debug)]
pub struct TextStreamInfo {
pub id: String,
pub topic: String,
pub timestamp: DateTime<Utc>,
pub total_length: Option<u64>,
pub mime_type: String,
pub operation_type: OperationType,
pub version: i32,
pub reply_to_stream_id: Option<String>,
pub attached_stream_ids: Vec<String>,
pub generated: bool,
pub encryption_type: EncryptionType,
#[cfg(feature = "test-utils")]
pub is_compressed: bool,
#[cfg(feature = "test-utils")]
pub is_inline: bool,
#[doc(hidden)]
pub attributes_map: Arc<RwLock<HashMap<String, String>>>,
}
impl TextStreamInfo {
pub fn attributes(&self) -> HashMap<String, String> {
self.attributes_map.read().clone()
}
}
impl TryFrom<Header> for AnyStreamInfo {
type Error = StreamError;
fn try_from(header: Header) -> Result<Self, Self::Error> {
Self::try_from_with_encryption(header, EncryptionType::None)
}
}
impl AnyStreamInfo {
pub fn try_from_with_encryption(
mut header: Header,
encryption_type: EncryptionType,
) -> Result<Self, StreamError> {
let Some(content_header) = header.content_header.take() else {
Err(StreamError::InvalidHeader)?
};
let info = match content_header {
ContentHeader::ByteHeader(byte_header) => Self::Byte(
ByteStreamInfo::from_headers_with_encryption(header, byte_header, encryption_type),
),
ContentHeader::TextHeader(text_header) => Self::Text(
TextStreamInfo::from_headers_with_encryption(header, text_header, encryption_type),
),
};
Ok(info)
}
}
impl ByteStreamInfo {
pub(crate) fn from_headers(header: Header, byte_header: ByteHeader) -> Self {
Self::from_headers_with_encryption(header, byte_header, EncryptionType::None)
}
pub(crate) fn from_headers_with_encryption(
header: Header,
byte_header: ByteHeader,
encryption_type: EncryptionType,
) -> Self {
Self {
id: header.stream_id.to_string(),
topic: header.topic,
timestamp: DateTime::<Utc>::from_timestamp_millis(header.timestamp)
.unwrap_or_else(|| Utc::now()),
total_length: header.total_length,
attributes_map: Arc::new(RwLock::new(header.attributes)),
mime_type: header.mime_type,
name: byte_header.name,
encryption_type,
#[cfg(feature = "test-utils")]
is_compressed: header.compression != crate::types::CompressionType::None,
#[cfg(feature = "test-utils")]
is_inline: header.inline_content.is_some_and(|c| !c.is_empty()),
}
}
}
impl TextStreamInfo {
pub(crate) fn from_headers(header: Header, text_header: TextHeader) -> Self {
Self::from_headers_with_encryption(header, text_header, EncryptionType::None)
}
pub(crate) fn from_headers_with_encryption(
header: Header,
text_header: TextHeader,
encryption_type: EncryptionType,
) -> Self {
Self {
id: header.stream_id.to_string(),
topic: header.topic,
timestamp: DateTime::<Utc>::from_timestamp_millis(header.timestamp)
.unwrap_or_else(|| Utc::now()),
total_length: header.total_length,
attributes_map: Arc::new(RwLock::new(header.attributes)),
mime_type: header.mime_type,
operation_type: text_header.operation_type,
version: text_header.version,
reply_to_stream_id: text_header.reply_to_stream_id.map(|stream_id| stream_id.into()),
attached_stream_ids: text_header
.attached_stream_ids
.into_iter()
.map(Into::into)
.collect(),
generated: text_header.generated,
encryption_type,
#[cfg(feature = "test-utils")]
is_compressed: header.compression != crate::types::CompressionType::None,
#[cfg(feature = "test-utils")]
is_inline: header.inline_content.is_some_and(|c| !c.is_empty()),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum AnyStreamInfo {
Byte(ByteStreamInfo),
Text(TextStreamInfo),
}
impl AnyStreamInfo {
livekit_common::enum_dispatch!(
[Byte, Text];
pub fn id(self: &Self) -> &str;
pub fn total_length(self: &Self) -> Option<u64>;
pub fn encryption_type(self: &Self) -> EncryptionType;
pub(crate) fn attributes_map(self: &Self) -> Arc<RwLock<HashMap<String, String>>>;
);
}
#[rustfmt::skip]
macro_rules! stream_info {
() => {
pub(crate) fn id(&self) -> &str { &self.id }
pub(crate) fn total_length(&self) -> Option<u64> { self.total_length }
pub(crate) fn encryption_type(&self) -> EncryptionType { self.encryption_type }
pub(crate) fn attributes_map(self: &Self) -> Arc<RwLock<HashMap<String, String>>> {
self.attributes_map.clone()
}
};
}
impl ByteStreamInfo {
stream_info!();
}
impl TextStreamInfo {
stream_info!();
}
impl From<ByteStreamInfo> for AnyStreamInfo {
fn from(info: ByteStreamInfo) -> Self {
Self::Byte(info)
}
}
impl From<TextStreamInfo> for AnyStreamInfo {
fn from(info: TextStreamInfo) -> Self {
Self::Text(info)
}
}