1
use std::{
2
    io::{Read, Seek, Write},
3
    path::Path,
4
};
5

            
6
use crate::io::{
7
    fs::{OpenStdFile, StdFile, StdFileManager},
8
    memory::{MemoryFile, MemoryFileManager, OpenMemoryFile},
9
    FileManager, FileOp, IntoPathId, ManagedFile, ManagedFileOpener, OpenableFile, OperableFile,
10
    PathId,
11
};
12

            
13
/// A file that can be either a [`StdFile`] or [`MemoryFile`].
14
#[derive(Debug)]
15
pub enum AnyFile {
16
    /// A file backed by a filesystem.
17
    Std(StdFile),
18
    /// A simulated file backed by memory.
19
    Memory(MemoryFile),
20
}
21

            
22
impl ManagedFile for AnyFile {
23
    type Manager = AnyFileManager;
24
}
25

            
26
impl super::File for AnyFile {
27
1036501
    fn id(&self) -> &PathId {
28
1036501
        match self {
29
824794
            Self::Std(file) => file.id(),
30
211707
            Self::Memory(file) => file.id(),
31
        }
32
1036501
    }
33

            
34
    fn length(&self) -> Result<u64, crate::Error> {
35
        match self {
36
            Self::Std(file) => file.length(),
37
            Self::Memory(file) => file.length(),
38
        }
39
    }
40

            
41
    fn close(self) -> Result<(), crate::Error> {
42
        match self {
43
            Self::Std(file) => file.close(),
44
            Self::Memory(file) => file.close(),
45
        }
46
    }
47

            
48
1764
    fn synchronize(&mut self) -> Result<(), crate::Error> {
49
1764
        match self {
50
1360
            Self::Std(file) => file.synchronize(),
51
404
            Self::Memory(file) => file.synchronize(),
52
        }
53
1764
    }
54
}
55

            
56
impl Write for AnyFile {
57
8110
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
58
8110
        match self {
59
6385
            Self::Std(file) => file.write(buf),
60
1725
            Self::Memory(file) => file.write(buf),
61
        }
62
8110
    }
63

            
64
    fn flush(&mut self) -> std::io::Result<()> {
65
        match self {
66
            Self::Std(file) => file.flush(),
67
            Self::Memory(file) => file.flush(),
68
        }
69
    }
70
}
71

            
72
impl Read for AnyFile {
73
124
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
74
124
        match self {
75
62
            Self::Std(file) => file.read(buf),
76
62
            Self::Memory(file) => file.read(buf),
77
        }
78
124
    }
79
}
80

            
81
impl Seek for AnyFile {
82
84
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
83
84
        match self {
84
42
            Self::Std(file) => file.seek(pos),
85
42
            Self::Memory(file) => file.seek(pos),
86
        }
87
84
    }
88
}
89

            
90
/// A file manager that can either be a [`StdFileManager`] or a [`MemoryFileManager`].
91
46388
#[derive(Debug, Clone)]
92
pub enum AnyFileManager {
93
    /// A file manager that uses the filesystem.
94
    Std(StdFileManager),
95
    /// A simulated file system backed by memory.
96
    Memory(MemoryFileManager),
97
}
98

            
99
impl AnyFileManager {
100
    /// Returns a new filesystem-backed manager.
101
    #[must_use]
102
13
    pub fn std() -> Self {
103
13
        Self::Std(StdFileManager::default())
104
13
    }
105

            
106
    /// Returns a new memory-backed manager.
107
    #[must_use]
108
13
    pub fn memory() -> Self {
109
13
        Self::Memory(MemoryFileManager::default())
110
13
    }
111
}
112

            
113
impl FileManager for AnyFileManager {
114
    type File = AnyFile;
115
    type FileHandle = AnyFileHandle;
116

            
117
4
    fn resolve_path(&self, path: impl AsRef<Path>, create_if_not_found: bool) -> Option<PathId> {
118
4
        match self {
119
2
            Self::Std(manager) => manager.resolve_path(path, create_if_not_found),
120
2
            Self::Memory(manager) => manager.resolve_path(path, create_if_not_found),
121
        }
122
4
    }
123

            
124
8062
    fn read(&self, path: impl IntoPathId) -> Result<Self::FileHandle, crate::Error> {
125
8062
        match self {
126
4509
            Self::Std(manager) => manager.read(path).map(AnyFileHandle::Std),
127
3553
            Self::Memory(manager) => manager.read(path).map(AnyFileHandle::Memory),
128
        }
129
8062
    }
130

            
131
14362
    fn append(&self, path: impl IntoPathId) -> Result<Self::FileHandle, crate::Error> {
132
14362
        match self {
133
7181
            Self::Std(manager) => manager.append(path).map(AnyFileHandle::Std),
134
7181
            Self::Memory(manager) => manager.append(path).map(AnyFileHandle::Memory),
135
        }
136
14362
    }
137

            
138
    fn close_handles<F: FnOnce(PathId)>(&self, path: impl IntoPathId, publish_callback: F) {
139
        match self {
140
            Self::Std(manager) => manager.close_handles(path, publish_callback),
141
            Self::Memory(manager) => manager.close_handles(path, publish_callback),
142
        }
143
    }
144

            
145
    fn delete(&self, path: impl IntoPathId) -> Result<bool, crate::Error> {
146
        match self {
147
            Self::Std(manager) => manager.delete(path),
148
            Self::Memory(manager) => manager.delete(path),
149
        }
150
    }
151

            
152
    fn delete_directory(&self, path: impl AsRef<Path>) -> Result<(), crate::Error> {
153
        match self {
154
            Self::Std(manager) => manager.delete_directory(path),
155
            Self::Memory(manager) => manager.delete_directory(path),
156
        }
157
    }
158

            
159
4
    fn exists(&self, path: impl IntoPathId) -> Result<bool, crate::Error> {
160
4
        match self {
161
2
            Self::Std(manager) => manager.exists(path),
162
2
            Self::Memory(manager) => manager.exists(path),
163
        }
164
4
    }
165

            
166
2080
    fn file_length(&self, path: impl IntoPathId) -> Result<u64, crate::Error> {
167
2080
        match self {
168
1040
            Self::Std(manager) => manager.file_length(path),
169
1040
            Self::Memory(manager) => manager.file_length(path),
170
        }
171
2080
    }
172
}
173

            
174
impl ManagedFileOpener<AnyFile> for AnyFileManager {
175
40
    fn open_for_read(&self, path: impl IntoPathId + Send) -> Result<AnyFile, crate::Error> {
176
40
        match self {
177
20
            Self::Std(manager) => manager.open_for_read(path).map(AnyFile::Std),
178
20
            Self::Memory(manager) => manager.open_for_read(path).map(AnyFile::Memory),
179
        }
180
40
    }
181

            
182
1764
    fn open_for_append(&self, path: impl IntoPathId + Send) -> Result<AnyFile, crate::Error> {
183
1764
        match self {
184
1360
            Self::Std(manager) => manager.open_for_append(path).map(AnyFile::Std),
185
404
            Self::Memory(manager) => manager.open_for_append(path).map(AnyFile::Memory),
186
        }
187
1764
    }
188
}
189

            
190
impl Default for AnyFileManager {
191
    fn default() -> Self {
192
        Self::Std(StdFileManager::default())
193
    }
194
}
195

            
196
/// A handle to an open file that could be either an [`OpenStdFile`] or an [`OpenMemoryFile`].
197
#[derive(Debug)]
198
pub enum AnyFileHandle {
199
    /// An open file on the filesystem.
200
    Std(OpenStdFile),
201
    /// An open file in memory.
202
    Memory(OpenMemoryFile),
203
}
204

            
205
impl OpenableFile<AnyFile> for AnyFileHandle {
206
18204
    fn id(&self) -> &PathId {
207
18204
        match self {
208
9580
            Self::Std(file) => file.id(),
209
8624
            Self::Memory(file) => file.id(),
210
        }
211
18204
    }
212

            
213
    fn replace_with<C: FnOnce(PathId)>(
214
        self,
215
        replacement: AnyFile,
216
        manager: &<AnyFile as ManagedFile>::Manager,
217
        publish_callback: C,
218
    ) -> Result<Self, crate::Error> {
219
1764
        match (self, replacement, manager) {
220
1360
            (Self::Std(file), AnyFile::Std(replacement), AnyFileManager::Std(manager)) => file
221
1360
                .replace_with(replacement, manager, publish_callback)
222
1360
                .map(AnyFileHandle::Std),
223
404
            (Self::Memory(file), AnyFile::Memory(replacement), AnyFileManager::Memory(manager)) => {
224
404
                file.replace_with(replacement, manager, publish_callback)
225
404
                    .map(AnyFileHandle::Memory)
226
            }
227
            _ => Err(crate::Error::from("incompatible file and manager")),
228
        }
229
1764
    }
230

            
231
2000
    fn close(self) -> Result<(), crate::Error> {
232
2000
        match self {
233
1000
            Self::Std(file) => file.close(),
234
1000
            Self::Memory(file) => file.close(),
235
        }
236
2000
    }
237
}
238

            
239
impl OperableFile<AnyFile> for AnyFileHandle {
240
78747
    fn execute<Output, Op: FileOp<Output>>(&mut self, operator: Op) -> Output {
241
78747
        match self {
242
40487
            Self::Std(file) => file.execute(operator),
243
38260
            Self::Memory(file) => file.execute(operator),
244
        }
245
78747
    }
246
}