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}