bevy_asset/io/file/
file_asset.rs

1use crate::io::{
2    get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
3    Reader, ReaderNotSeekableError, SeekableReader, Writer,
4};
5use async_fs::{read_dir, File};
6use futures_lite::StreamExt;
7
8use alloc::{borrow::ToOwned, boxed::Box};
9use std::path::Path;
10
11use super::{FileAssetReader, FileAssetWriter};
12
13impl Reader for File {
14    fn seekable(&mut self) -> Result<&mut dyn SeekableReader, ReaderNotSeekableError> {
15        Ok(self)
16    }
17}
18
19impl AssetReader for FileAssetReader {
20    async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
21        let full_path = self.root_path.join(path);
22        File::open(&full_path).await.map_err(|e| {
23            if e.kind() == std::io::ErrorKind::NotFound {
24                AssetReaderError::NotFound(full_path)
25            } else {
26                e.into()
27            }
28        })
29    }
30
31    async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
32        let meta_path = get_meta_path(path);
33        let full_path = self.root_path.join(meta_path);
34        File::open(&full_path).await.map_err(|e| {
35            if e.kind() == std::io::ErrorKind::NotFound {
36                AssetReaderError::NotFound(full_path)
37            } else {
38                e.into()
39            }
40        })
41    }
42
43    async fn read_directory<'a>(
44        &'a self,
45        path: &'a Path,
46    ) -> Result<Box<PathStream>, AssetReaderError> {
47        let full_path = self.root_path.join(path);
48        match read_dir(&full_path).await {
49            Ok(read_dir) => {
50                let root_path = self.root_path.clone();
51                let mapped_stream = read_dir.filter_map(move |f| {
52                    f.ok().and_then(|dir_entry| {
53                        let path = dir_entry.path();
54                        // filter out meta files as they are not considered assets
55                        if let Some(ext) = path.extension().and_then(|e| e.to_str())
56                            && ext.eq_ignore_ascii_case("meta")
57                        {
58                            return None;
59                        }
60                        // filter out hidden files. they are not listed by default but are directly targetable
61                        if path
62                            .file_name()
63                            .and_then(|file_name| file_name.to_str())
64                            .map(|file_name| file_name.starts_with('.'))
65                            .unwrap_or_default()
66                        {
67                            return None;
68                        }
69                        let relative_path = path.strip_prefix(&root_path).unwrap();
70                        Some(relative_path.to_owned())
71                    })
72                });
73                let read_dir: Box<PathStream> = Box::new(mapped_stream);
74                Ok(read_dir)
75            }
76            Err(e) => {
77                if e.kind() == std::io::ErrorKind::NotFound {
78                    Err(AssetReaderError::NotFound(full_path))
79                } else {
80                    Err(e.into())
81                }
82            }
83        }
84    }
85
86    async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
87        let full_path = self.root_path.join(path);
88        let metadata = full_path
89            .metadata()
90            .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
91        Ok(metadata.file_type().is_dir())
92    }
93}
94
95impl AssetWriter for FileAssetWriter {
96    async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
97        let full_path = self.root_path.join(path);
98        if let Some(parent) = full_path.parent() {
99            async_fs::create_dir_all(parent).await?;
100        }
101        let file = File::create(&full_path).await?;
102        let writer: Box<Writer> = Box::new(file);
103        Ok(writer)
104    }
105
106    async fn write_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
107        let meta_path = get_meta_path(path);
108        let full_path = self.root_path.join(meta_path);
109        if let Some(parent) = full_path.parent() {
110            async_fs::create_dir_all(parent).await?;
111        }
112        let file = File::create(&full_path).await?;
113        let writer: Box<Writer> = Box::new(file);
114        Ok(writer)
115    }
116
117    async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
118        let full_path = self.root_path.join(path);
119        async_fs::remove_file(full_path).await?;
120        Ok(())
121    }
122
123    async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
124        let meta_path = get_meta_path(path);
125        let full_path = self.root_path.join(meta_path);
126        async_fs::remove_file(full_path).await?;
127        Ok(())
128    }
129
130    async fn rename<'a>(
131        &'a self,
132        old_path: &'a Path,
133        new_path: &'a Path,
134    ) -> Result<(), AssetWriterError> {
135        let full_old_path = self.root_path.join(old_path);
136        let full_new_path = self.root_path.join(new_path);
137        if let Some(parent) = full_new_path.parent() {
138            async_fs::create_dir_all(parent).await?;
139        }
140        async_fs::rename(full_old_path, full_new_path).await?;
141        Ok(())
142    }
143
144    async fn rename_meta<'a>(
145        &'a self,
146        old_path: &'a Path,
147        new_path: &'a Path,
148    ) -> Result<(), AssetWriterError> {
149        let old_meta_path = get_meta_path(old_path);
150        let new_meta_path = get_meta_path(new_path);
151        let full_old_path = self.root_path.join(old_meta_path);
152        let full_new_path = self.root_path.join(new_meta_path);
153        if let Some(parent) = full_new_path.parent() {
154            async_fs::create_dir_all(parent).await?;
155        }
156        async_fs::rename(full_old_path, full_new_path).await?;
157        Ok(())
158    }
159
160    async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
161        let full_path = self.root_path.join(path);
162        async_fs::create_dir_all(full_path).await?;
163        Ok(())
164    }
165
166    async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
167        let full_path = self.root_path.join(path);
168        async_fs::remove_dir_all(full_path).await?;
169        Ok(())
170    }
171
172    async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
173        let full_path = self.root_path.join(path);
174        async_fs::remove_dir(full_path).await?;
175        Ok(())
176    }
177
178    async fn remove_assets_in_directory<'a>(
179        &'a self,
180        path: &'a Path,
181    ) -> Result<(), AssetWriterError> {
182        let full_path = self.root_path.join(path);
183        async_fs::remove_dir_all(&full_path).await?;
184        async_fs::create_dir_all(&full_path).await?;
185        Ok(())
186    }
187}