bevy_asset/io/file/
file_asset.rs

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