luminol_filesystem/
egui_bytes_loader.rs1use std::sync::Arc;
26
27use dashmap::{DashMap, DashSet};
28use egui::load::{Bytes, BytesPoll, LoadError};
29
30#[derive(Default)]
31pub struct Loader {
32 loaded_files: DashMap<camino::Utf8PathBuf, Arc<[u8]>>,
33 errored_files: DashMap<camino::Utf8PathBuf, color_eyre::Report>,
34 unloaded_files: DashSet<camino::Utf8PathBuf>,
35}
36
37pub const BYTES_LOADER_ID: &str = egui::load::generate_loader_id!(BytesLoader);
38
39pub const PROTOCOL: &str = "project://";
40
41fn supported_uri_to_path(uri: &str) -> Option<&camino::Utf8Path> {
42 uri.strip_prefix(PROTOCOL).map(camino::Utf8Path::new)
43}
44
45impl Loader {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn load_unloaded_files(&self, ctx: &egui::Context, filesystem: &impl crate::FileSystem) {
51 for path in self.unloaded_files.iter() {
53 let path = path.to_path_buf();
54 match filesystem.read(&path) {
55 Ok(bytes) => {
56 self.loaded_files.insert(path, bytes.into());
57 }
58 Err(err) => {
59 self.errored_files.insert(path, err);
60 }
61 };
62 }
63 if !self.unloaded_files.is_empty() {
64 ctx.request_repaint();
65 }
66 self.unloaded_files.clear();
67 }
68}
69
70impl egui::load::BytesLoader for Loader {
71 fn id(&self) -> &str {
72 BYTES_LOADER_ID
73 }
74
75 fn load(&self, _: &egui::Context, uri: &str) -> egui::load::BytesLoadResult {
76 let Some(path) = supported_uri_to_path(uri) else {
77 return Err(LoadError::NotSupported);
78 };
79
80 if let Some(bytes) = self.loaded_files.get(path) {
81 return Ok(BytesPoll::Ready {
82 size: None,
83 bytes: Bytes::Shared(bytes.clone()),
84 mime: None,
85 });
86 }
87
88 if let Some(error) = self.errored_files.get(path) {
89 return Err(LoadError::Loading(error.to_string()));
90 }
91
92 self.unloaded_files.insert(path.to_path_buf());
93 Ok(BytesPoll::Pending { size: None })
94 }
95
96 fn forget(&self, uri: &str) {
97 let Some(path) = supported_uri_to_path(uri) else {
98 return;
99 };
100
101 self.loaded_files.remove(path);
102 self.errored_files.remove(path);
103 self.unloaded_files.remove(path);
104 }
105
106 fn forget_all(&self) {
107 self.loaded_files.clear();
108 self.errored_files.clear();
109 self.unloaded_files.clear();
110 }
111
112 fn byte_size(&self) -> usize {
113 self.loaded_files.iter().map(|e| e.len()).sum()
114 }
115}