Skip to main content

rar_stream/
file_media.rs

1//! FileMedia trait - abstract byte source for RAR reading.
2
3use crate::error::Result;
4use std::io::{Read, Seek, SeekFrom};
5
6/// Interval for reading a byte range.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub struct ReadInterval {
9    pub start: u64,
10    pub end: u64,
11}
12
13/// Local file implementation.
14#[derive(Debug, Clone)]
15pub struct LocalFileMedia {
16    path: String,
17    name: String,
18    length: u64,
19}
20
21impl LocalFileMedia {
22    pub fn new(path: &str) -> std::io::Result<Self> {
23        let metadata = std::fs::metadata(path)?;
24        let name = std::path::Path::new(path)
25            .file_name()
26            .and_then(|n| n.to_str())
27            .unwrap_or("unknown")
28            .to_string();
29
30        Ok(Self {
31            path: path.to_string(),
32            name,
33            length: metadata.len(),
34        })
35    }
36
37    pub fn length(&self) -> u64 {
38        self.length
39    }
40
41    pub fn name(&self) -> &str {
42        &self.name
43    }
44
45    /// Sync read
46    pub fn read_range_sync(&self, interval: ReadInterval) -> Result<Vec<u8>> {
47        let mut file = std::fs::File::open(&self.path)?;
48        file.seek(SeekFrom::Start(interval.start))?;
49        let len = (interval.end - interval.start + 1) as usize;
50        let mut buffer = vec![0u8; len];
51        file.read_exact(&mut buffer)?;
52        Ok(buffer)
53    }
54}
55
56// Async FileMedia trait (requires 'async' feature)
57#[cfg(feature = "async")]
58use std::future::Future;
59#[cfg(feature = "async")]
60use std::pin::Pin;
61
62/// Abstract file source that can provide byte ranges asynchronously.
63///
64/// Implement this trait for custom byte sources (e.g., HTTP range requests).
65/// The library provides [`LocalFileMedia`] for local files.
66#[cfg(feature = "async")]
67#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
68pub trait FileMedia: Send + Sync {
69    fn length(&self) -> u64;
70    fn name(&self) -> &str;
71    fn read_range(
72        &self,
73        interval: ReadInterval,
74    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + Send + '_>>;
75}
76
77#[cfg(feature = "async")]
78impl FileMedia for LocalFileMedia {
79    fn length(&self) -> u64 {
80        self.length
81    }
82
83    fn name(&self) -> &str {
84        &self.name
85    }
86
87    fn read_range(
88        &self,
89        interval: ReadInterval,
90    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + Send + '_>> {
91        let path = self.path.clone();
92        Box::pin(async move {
93            use tokio::io::{AsyncReadExt, AsyncSeekExt};
94            let mut file = tokio::fs::File::open(&path).await?;
95            file.seek(std::io::SeekFrom::Start(interval.start)).await?;
96            let len = (interval.end - interval.start + 1) as usize;
97            let mut buffer = vec![0u8; len];
98            file.read_exact(&mut buffer).await?;
99            Ok(buffer)
100        })
101    }
102}