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 thiserror::Error;
16
17/// Error returned by the packet transport when a data-stream packet fails to send.
18///
19/// The stream managers only need to know that a send failed (they map it to
20/// [`StreamError::SendFailed`]); the concrete engine error type stays in the `livekit` crate,
21/// which bridges the outgoing packet channel to the RTC engine.
22#[derive(Debug, Clone)]
23pub struct SendError;
24
25/// Result type for data stream operations.
26pub type StreamResult<T> = Result<T, StreamError>;
27
28/// Error type for data stream operations.
29#[derive(Debug, Error)]
30pub enum StreamError {
31    // TODO(ladvoc): standardize error cases and expose over FFI.
32    #[error("stream has already been closed")]
33    AlreadyClosed,
34
35    #[error("stream closed abnormally: {0}")]
36    AbnormalEnd(String),
37
38    #[error("UTF-8 decoding error: {0}")]
39    Utf8(#[from] std::string::FromUtf8Error),
40
41    #[error("incoming header was invalid")]
42    InvalidHeader,
43
44    #[error("expected chunk index to be exactly one more than the previous")]
45    MissedChunk,
46
47    #[error("read length exceeded total length specified in stream header")]
48    LengthExceeded,
49
50    #[error("stream data is incomplete")]
51    Incomplete,
52
53    #[error("unable to send packet")]
54    SendFailed,
55
56    #[error("I/O error: {0}")]
57    Io(#[from] std::io::Error),
58
59    #[error("internal error")]
60    Internal,
61
62    #[error("encryption type mismatch")]
63    EncryptionTypeMismatch,
64
65    #[error("stream header exceeds maximum size")]
66    HeaderTooLarge,
67
68    #[error("stream payload exceeds maximum size")]
69    PayloadTooLarge,
70
71    #[error("decompression failed")]
72    Decompression,
73
74    #[error("file name must be a plain file name without path separators or '..'")]
75    InvalidFileName,
76}
77
78/// Progress of a data stream.
79#[derive(Clone, Copy, Default, Debug, Hash, Eq, PartialEq)]
80pub struct StreamProgress {
81    pub(crate) chunk_index: u64,
82    /// Number of bytes read or written so far.
83    pub(crate) bytes_processed: u64,
84    /// Total number of bytes expected to be read or written for finite streams.
85    pub(crate) bytes_total: Option<u64>,
86}
87
88impl StreamProgress {
89    /// Number of bytes read or written so far.
90    pub fn bytes_processed(&self) -> u64 {
91        self.bytes_processed
92    }
93
94    /// Total number of bytes expected for finite streams, or `None` for streams of unknown size.
95    pub fn bytes_total(&self) -> Option<u64> {
96        self.bytes_total
97    }
98
99    /// Returns the completion fraction (`0.0..=1.0`) for finite streams, or `None` for streams of
100    /// unknown size.
101    pub fn percentage(&self) -> Option<f32> {
102        self.bytes_total.map(|total| {
103            if total == 0 {
104                1.0
105            } else {
106                self.bytes_processed as f32 / total as f32
107            }
108        })
109    }
110}