Skip to main content

rlvgl_core/
fs.rs

1//! Asset loading interfaces for filesystem-backed content.
2//!
3//! This module provides traits used by the optional `fs` feature to source
4//! assets such as fonts or images from an underlying filesystem.
5
6use alloc::boxed::Box;
7
8/// Errors that can occur during filesystem operations.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum FsError {
11    /// Underlying device reported an error.
12    Device,
13    /// Provided path was invalid.
14    InvalidPath,
15    /// File or directory was not found.
16    NoSuchFile,
17}
18
19/// Block device abstraction used by the filesystem layer.
20///
21/// Implementors provide sector-based access to a storage medium.
22pub trait BlockDevice {
23    /// Read blocks starting at `lba` into `buf`.
24    fn read_blocks(&mut self, lba: u64, buf: &mut [u8]) -> Result<(), FsError>;
25
26    /// Write blocks starting at `lba` from `buf`.
27    ///
28    /// Implementations may leave this unimplemented if the device is read-only.
29    fn write_blocks(&mut self, lba: u64, buf: &[u8]) -> Result<(), FsError>;
30
31    /// Return the logical block size in bytes.
32    fn block_size(&self) -> usize;
33
34    /// Return the total number of addressable blocks.
35    fn num_blocks(&self) -> u64;
36
37    /// Flush any buffered data to the underlying device.
38    fn flush(&mut self) -> Result<(), FsError>;
39}
40
41/// Error type returned by asset operations.
42#[derive(Debug, Clone)]
43pub enum AssetError {
44    /// Underlying filesystem error.
45    Fs(FsError),
46    /// Asset bytes were retrieved but the decode step failed.
47    ///
48    /// The inner `String` contains a human-readable description of the failure.
49    /// This variant is produced by [`crate::asset::AssetRegistry::resolve_image`]
50    /// when a codec plugin returns an error after the source bytes were
51    /// successfully read.
52    Decode(alloc::string::String),
53}
54
55/// Reader trait for streaming asset data.
56pub trait AssetRead {
57    /// Read data into `out`, returning the number of bytes read.
58    fn read(&mut self, out: &mut [u8]) -> Result<usize, AssetError>;
59
60    /// Total length of the asset in bytes.
61    fn len(&self) -> usize;
62
63    /// Return `true` if the asset has a length of zero bytes.
64    fn is_empty(&self) -> bool;
65
66    /// Seek to an absolute byte position within the asset.
67    fn seek(&mut self, pos: u64) -> Result<u64, AssetError>;
68}
69
70/// Source of assets such as fonts or images.
71pub trait AssetSource {
72    /// Open an asset by logical path, e.g., `"fonts/regular.bin"`.
73    fn open<'a>(&'a self, path: &str) -> Result<Box<dyn AssetRead + 'a>, AssetError>;
74
75    /// Determine whether an asset at `path` exists.
76    fn exists(&self, path: &str) -> bool;
77
78    /// List the contents of `dir`, returning an iterator over asset entries.
79    fn list(&self, dir: &str) -> Result<AssetIter, AssetError>;
80}
81
82/// Iterator over asset entries returned by [`AssetSource::list`].
83pub struct AssetIter;
84
85impl Iterator for AssetIter {
86    type Item = (); // placeholder until fleshed out
87
88    fn next(&mut self) -> Option<Self::Item> {
89        None
90    }
91}
92
93/// Manager that provides convenient typed loading helpers.
94pub struct AssetManager<S: AssetSource> {
95    source: S,
96}
97
98impl<S: AssetSource> AssetManager<S> {
99    /// Create a new [`AssetManager`] from an [`AssetSource`].
100    pub fn new(source: S) -> Self {
101        Self { source }
102    }
103
104    /// Open a raw asset stream at `path`.
105    pub fn open(&self, path: &str) -> Result<Box<dyn AssetRead + '_>, AssetError> {
106        self.source.open(path)
107    }
108
109    /// Load the raw bytes of the asset at `path` into a heap buffer.
110    ///
111    /// This is the foundation for typed loaders such as packed-font loading.
112    /// For a packed font (`PackedFont`) the caller must additionally map the
113    /// returned bytes into static storage because `PackedFont::data` requires a
114    /// `&'static [u8]`; that pattern is outside the scope of this helper.
115    ///
116    /// # Errors
117    ///
118    /// Returns [`AssetError::Fs`] when the source cannot open or read the asset.
119    pub fn load_bytes(&self, path: &str) -> Result<alloc::vec::Vec<u8>, AssetError> {
120        let mut reader = self.source.open(path)?;
121        let len = reader.len();
122        let mut buf = alloc::vec![0u8; len];
123        let mut offset = 0usize;
124        while offset < len {
125            let n = reader.read(&mut buf[offset..])?;
126            if n == 0 {
127                break;
128            }
129            offset += n;
130        }
131        Ok(buf)
132    }
133}