Skip to main content

livekit_data_stream/
utils.rs

1// Copyright 2026 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use livekit_common::EncryptionType;
16use thiserror::Error;
17
18/// Error returned by the packet transport when a data-stream packet fails to send.
19///
20/// The stream managers only need to know that a send failed (they map it to
21/// [`StreamError::SendFailed`]); the concrete engine error type stays in the `livekit` crate,
22/// which bridges the outgoing packet channel to the RTC engine.
23#[derive(Debug, Clone)]
24pub struct SendError;
25
26/// Result type for data stream operations.
27pub type StreamResult<T> = Result<T, StreamError>;
28
29/// Error type for data stream operations.
30#[derive(Debug, Error)]
31pub enum StreamError {
32    // TODO(ladvoc): standardize error cases and expose over FFI.
33    #[error("stream has already been closed")]
34    AlreadyClosed,
35
36    #[error("stream closed abnormally: {0}")]
37    AbnormalEnd(String),
38
39    #[error("UTF-8 decoding error: {0}")]
40    Utf8(#[from] std::string::FromUtf8Error),
41
42    #[error("incoming header was invalid")]
43    InvalidHeader,
44
45    #[error("expected chunk index to be exactly one more than the previous")]
46    MissedChunk,
47
48    #[error("read length exceeded total length specified in stream header")]
49    LengthExceeded,
50
51    #[error("stream data is incomplete")]
52    Incomplete,
53
54    #[error("unable to send packet")]
55    SendFailed,
56
57    #[error("I/O error: {0}")]
58    Io(#[from] std::io::Error),
59
60    #[error("internal error")]
61    Internal,
62
63    #[error("encryption type mismatch: expected {expected:?}, received {received:?}")]
64    EncryptionTypeMismatch {
65        /// The encryption type the stream's header declared.
66        expected: EncryptionType,
67        /// The encryption type of the offending chunk.
68        received: EncryptionType,
69    },
70
71    #[error("stream header exceeds maximum size")]
72    HeaderTooLarge,
73
74    #[error("stream payload exceeds maximum size")]
75    PayloadTooLarge,
76
77    #[error("decompression failed")]
78    Decompression,
79
80    #[error("file name must be a plain file name without path separators or '..'")]
81    InvalidFileName,
82}
83
84/// Progress of a data stream.
85#[derive(Clone, Copy, Default, Debug, Hash, Eq, PartialEq)]
86pub struct StreamProgress {
87    pub(crate) chunk_index: u64,
88    /// Number of bytes read or written so far.
89    pub(crate) bytes_processed: u64,
90    /// Total number of bytes expected to be read or written for finite streams.
91    pub(crate) bytes_total: Option<u64>,
92}
93
94impl StreamProgress {
95    /// Number of bytes read or written so far.
96    pub fn bytes_processed(&self) -> u64 {
97        self.bytes_processed
98    }
99
100    /// Total number of bytes expected for finite streams, or `None` for streams of unknown size.
101    pub fn bytes_total(&self) -> Option<u64> {
102        self.bytes_total
103    }
104
105    /// Returns the completion fraction (`0.0..=1.0`) for finite streams, or `None` for streams of
106    /// unknown size.
107    pub fn percentage(&self) -> Option<f32> {
108        self.bytes_total.map(|total| {
109            if total == 0 {
110                1.0
111            } else {
112                self.bytes_processed as f32 / total as f32
113            }
114        })
115    }
116}