Resource manager can now load all entries from the directory.

This commit is contained in:
Filipe Rodrigues 2025-09-12 06:22:24 +01:00
parent c2b43001a8
commit ab876fa9e2
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
5 changed files with 98 additions and 27 deletions

1
Cargo.lock generated
View File

@ -4867,6 +4867,7 @@ dependencies = [
"serde_json",
"serde_with",
"tokio",
"tokio-stream",
"toml 0.9.5",
"tracing",
]

View File

@ -4,19 +4,20 @@ name = "zsw-util"
version = "0.1.0"
[dependencies]
app-error = { workspace = true }
cgmath = { features = ["serde"], workspace = true }
duplicate = { workspace = true }
extend = { workspace = true }
futures = { workspace = true }
image = { workspace = true }
pin-project = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_with = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
app-error = { workspace = true }
cgmath = { features = ["serde"], workspace = true }
duplicate = { workspace = true }
extend = { workspace = true }
futures = { workspace = true }
image = { workspace = true }
pin-project = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_with = { workspace = true }
tokio = { workspace = true }
tokio-stream = { features = ["fs"], workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
[lints]
workspace = true

View File

@ -20,7 +20,8 @@
async_fn_traits,
trait_alias,
unboxed_closures,
tuple_trait
tuple_trait,
try_blocks
)]
// Modules

View File

@ -3,12 +3,13 @@
// Imports
use {
crate::AppError,
app_error::Context,
app_error::{Context, app_error},
core::marker::PhantomData,
futures::lock::Mutex,
futures::{TryStreamExt, lock::Mutex},
serde::{Serialize, de::DeserializeOwned},
std::{collections::HashMap, hash::Hash, path::PathBuf, sync::Arc},
std::{collections::HashMap, ffi::OsStr, hash::Hash, path::PathBuf, sync::Arc},
tokio::sync::OnceCell,
tokio_stream::{StreamExt, wrappers::ReadDirStream},
};
/// Resource storage
@ -30,13 +31,60 @@ pub struct ResourceManager<N, V, S> {
impl<N, V, S> ResourceManager<N, V, S> {
/// Creates a new resource manager over a root directory
#[must_use]
pub fn new(root: PathBuf) -> Self {
Self {
pub async fn new(root: PathBuf) -> Result<Self, AppError> {
tokio::fs::create_dir_all(&root)
.await
.context("Unable to create root directory")?;
Ok(Self {
root,
values: Mutex::new(HashMap::new()),
_phantom: PhantomData,
}
})
}
/// Loads all playlists from the root directory
pub async fn load_all(&self) -> Result<(), AppError>
where
N: Eq + Hash + Clone + From<String> + AsRef<str>,
V: FromSerialized<N, S>,
S: DeserializeOwned,
{
tokio::fs::read_dir(&self.root)
.await
.map(ReadDirStream::new)
.context("Unable to read playlists directory")?
.then(|entry| async {
// Ignore directories and non `.toml` files
let entry = entry.context("Unable to get entry")?;
let entry_path = entry.path();
if entry
.file_type()
.await
.context("Unable to get entry metadata")?
.is_dir() || entry_path.extension().and_then(OsStr::to_str) != Some("toml")
{
return Ok(());
}
// Then get the playlist name from the file
let playlist_name = entry_path
.file_stem()
.context("Entry path had no file stem")?
.to_os_string()
.into_string()
.map(N::from)
.map_err(|file_name| app_error!("Entry name was non-utf8: {file_name:?}"))?;
_ = self
.load(playlist_name)
.await
.with_context(|| format!("Unable to load file {entry_path:?}"))?;
Ok(())
})
.try_collect::<()>()
.await
}
/// Adds a new value

View File

@ -184,9 +184,29 @@ impl WinitApp {
let wgpu = Wgpu::new().await.context("Unable to initialize wgpu")?;
let panels_renderer_layouts = PanelsRendererShared::new(&wgpu);
let displays = Displays::new(config_dirs.displays().to_path_buf());
let playlists = Playlists::new(config_dirs.playlists().to_path_buf());
let profiles = Profiles::new(config_dirs.profiles().to_path_buf());
// Create and stat loading the displays
let displays = Displays::new(config_dirs.displays().to_path_buf())
.await
.context("Unable to create displays")?;
let displays = Arc::new(displays);
#[cloned(displays)]
self::spawn_task("Load displays", async move { displays.load_all().await });
// Create and stat loading the playlists
let playlists = Playlists::new(config_dirs.playlists().to_path_buf())
.await
.context("Unable to create playlists")?;
let playlists = Arc::new(playlists);
#[cloned(playlists)]
self::spawn_task("Load playlists", async move { playlists.load_all().await });
// Create and stat loading the profiles
let profiles = Profiles::new(config_dirs.profiles().to_path_buf())
.await
.context("Unable to create profiles")?;
let profiles = Arc::new(profiles);
#[cloned(profiles)]
self::spawn_task("Load profiles", async move { profiles.load_all().await });
// Shared state
let shared = Shared {
@ -196,9 +216,9 @@ impl WinitApp {
cursor_pos: AtomicCell::new(PhysicalPosition::new(0.0, 0.0)),
wgpu,
panels_renderer_shared: panels_renderer_layouts,
displays: Arc::new(displays),
playlists: Arc::new(playlists),
profiles: Arc::new(profiles),
displays,
playlists,
profiles,
panels: Mutex::new(vec![]),
};
let shared = Arc::new(shared);