ggez_assets_manager/
source.rs1use std::{io, sync::Arc};
2
3use assets_manager::{
4 hot_reloading::{EventSender, FsWatcherBuilder},
5 source::{self, DirEntry, FileContent, Source},
6};
7
8#[derive(Debug, Clone)]
14pub struct GgezFileSystem {
15 resources: Option<source::FileSystem>,
16 zip: Option<Arc<source::Zip>>,
17 local: Option<source::FileSystem>,
18 config: Option<source::FileSystem>,
19}
20
21fn no_valid_source_error() -> io::Error {
22 io::Error::new(io::ErrorKind::Other, "Cannot find a valid source")
23}
24
25impl GgezFileSystem {
26 #[inline]
30 pub fn from_context(fs: &impl ggez::context::Has<ggez::filesystem::Filesystem>) -> Self {
31 fn inner(fs: &ggez::filesystem::Filesystem) -> GgezFileSystem {
32 let resources = source::FileSystem::new(fs.resources_dir()).ok();
33 let zip = source::Zip::open(fs.zip_dir()).ok().map(Arc::new);
34 let local = source::FileSystem::new(fs.user_data_dir()).ok();
35 let config = source::FileSystem::new(fs.user_config_dir()).ok();
36
37 GgezFileSystem {
38 resources,
39 zip,
40 local,
41 config,
42 }
43 }
44
45 inner(fs.retrieve())
46 }
47
48 #[deprecated = "use `GgezFileSystem::from_context` instead"]
53 pub fn new(game_id: &str, author: &str) -> Self {
54 let resources = source::FileSystem::new("resources").ok();
55 let zip = source::Zip::open("resources.zip").ok().map(Arc::new);
56
57 let (local, config) = match directories::ProjectDirs::from("", author, game_id) {
58 Some(project_dir) => (
59 source::FileSystem::new(project_dir.data_local_dir()).ok(),
60 source::FileSystem::new(project_dir.config_dir()).ok(),
61 ),
62 None => (None, None),
63 };
64
65 Self {
66 resources,
67 zip,
68 local,
69 config,
70 }
71 }
72}
73
74impl Source for GgezFileSystem {
75 fn read(&self, id: &str, ext: &str) -> io::Result<FileContent> {
76 let mut err = None;
77
78 if let Some(source) = &self.resources {
79 match source.read(id, ext) {
80 Err(e) => err = Some(e),
81 content => return content,
82 };
83 }
84 if let Some(source) = &self.zip {
85 match source.read(id, ext) {
86 Err(e) => err = Some(e),
87 content => return content,
88 }
89 }
90 if let Some(source) = &self.local {
91 match source.read(id, ext) {
92 Err(e) => err = Some(e),
93 content => return content,
94 }
95 }
96 if let Some(source) = &self.config {
97 match source.read(id, ext) {
98 Err(e) => err = Some(e),
99 content => return content,
100 }
101 }
102
103 Err(err.unwrap_or_else(no_valid_source_error))
104 }
105
106 fn read_dir(&self, id: &str, f: &mut dyn FnMut(DirEntry)) -> io::Result<()> {
107 let mut err = None;
108
109 if let Some(source) = &self.resources {
110 match source.read_dir(id, f) {
111 Err(e) => err = Some(e),
112 content => return content,
113 };
114 }
115 if let Some(source) = &self.zip {
116 match source.read_dir(id, f) {
117 Err(e) => err = Some(e),
118 content => return content,
119 }
120 }
121 if let Some(source) = &self.local {
122 match source.read_dir(id, f) {
123 Err(e) => err = Some(e),
124 content => return content,
125 }
126 }
127 if let Some(source) = &self.config {
128 match source.read_dir(id, f) {
129 Err(e) => err = Some(e),
130 content => return content,
131 }
132 }
133
134 Err(err.unwrap_or_else(no_valid_source_error))
135 }
136
137 fn exists(&self, entry: DirEntry) -> bool {
138 fn exists<S: Source>(s: &Option<S>, entry: DirEntry) -> bool {
139 s.as_ref().map_or(false, |s| s.exists(entry))
140 }
141
142 exists(&self.resources, entry)
143 || exists(&self.zip, entry)
144 || exists(&self.local, entry)
145 || exists(&self.config, entry)
146 }
147
148 fn configure_hot_reloading(
149 &self,
150 events: EventSender,
151 ) -> Result<(), assets_manager::BoxedError> {
152 let mut watcher = FsWatcherBuilder::new()?;
153 if let Some(res) = &self.resources {
154 watcher.watch(res.root().to_owned())?;
155 }
156 if let Some(res) = &self.local {
157 watcher.watch(res.root().to_owned())?;
158 }
159 if let Some(res) = &self.config {
160 watcher.watch(res.root().to_owned())?;
161 }
162 watcher.build(events);
163 Ok(())
164 }
165}