Skip to main content

seams_rs_std/
fs.rs

1//! `StdFileSystem` and `TokioFileSystem`: thin wrappers around
2//! `std::fs` and `tokio::fs` implementing the `seams-rs-core` port
3//! surface. Every method delegates directly with no logic of its own.
4
5use std::io::{self, Read, Seek, Write};
6use std::path::Path;
7
8use seams_rs_core::{
9    AsyncFileRead, AsyncFileSystem, AsyncFileWrite, BoxFuture, FileRead, FileSystem, FileWrite,
10    Metadata,
11};
12use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
13
14// ---------------- std-backed sync ----------------
15
16/// Production `FileSystem` backed by `std::fs`.
17#[derive(Debug, Default, Clone, Copy)]
18pub struct StdFileSystem;
19
20impl StdFileSystem {
21    /// Construct a new `StdFileSystem`.
22    pub fn new() -> Self {
23        Self
24    }
25}
26
27fn metadata_from_std(m: std::fs::Metadata) -> Metadata {
28    Metadata::new(m.len(), m.is_file(), m.is_dir(), m.modified().ok())
29}
30
31impl FileSystem for StdFileSystem {
32    fn create_dir_all(&self, path: &Path) -> io::Result<()> {
33        std::fs::create_dir_all(path)
34    }
35
36    fn remove_dir_all(&self, path: &Path) -> io::Result<()> {
37        std::fs::remove_dir_all(path)
38    }
39
40    fn try_exists(&self, path: &Path) -> io::Result<bool> {
41        std::fs::exists(path)
42    }
43
44    fn open_read(&self, path: &Path) -> io::Result<Box<dyn FileRead>> {
45        Ok(Box::new(StdFileRead(std::fs::File::open(path)?)))
46    }
47
48    fn open_write(&self, path: &Path) -> io::Result<Box<dyn FileWrite>> {
49        Ok(Box::new(StdFileWrite(std::fs::File::create(path)?)))
50    }
51
52    fn metadata(&self, path: &Path) -> io::Result<Metadata> {
53        Ok(metadata_from_std(std::fs::metadata(path)?))
54    }
55
56    fn rename(&self, from: &Path, to: &Path) -> io::Result<()> {
57        std::fs::rename(from, to)
58    }
59}
60
61struct StdFileRead(std::fs::File);
62
63impl FileRead for StdFileRead {
64    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
65        Read::read_to_end(&mut self.0, buf)
66    }
67    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
68        Read::read_exact(&mut self.0, buf)
69    }
70    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
71        Seek::seek(&mut self.0, pos)
72    }
73}
74
75struct StdFileWrite(std::fs::File);
76
77impl FileWrite for StdFileWrite {
78    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
79        Write::write_all(&mut self.0, buf)
80    }
81    fn flush(&mut self) -> io::Result<()> {
82        Write::flush(&mut self.0)
83    }
84    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
85        Seek::seek(&mut self.0, pos)
86    }
87}
88
89// ---------------- tokio-backed async ----------------
90
91/// Production `AsyncFileSystem` backed by `tokio::fs`.
92#[derive(Debug, Default, Clone, Copy)]
93pub struct TokioFileSystem;
94
95impl TokioFileSystem {
96    /// Construct a new `TokioFileSystem`.
97    pub fn new() -> Self {
98        Self
99    }
100}
101
102impl AsyncFileSystem for TokioFileSystem {
103    fn create_dir_all<'a>(&'a self, path: &'a Path) -> BoxFuture<'a, io::Result<()>> {
104        Box::pin(tokio::fs::create_dir_all(path))
105    }
106
107    fn remove_dir_all<'a>(&'a self, path: &'a Path) -> BoxFuture<'a, io::Result<()>> {
108        Box::pin(tokio::fs::remove_dir_all(path))
109    }
110
111    fn try_exists<'a>(&'a self, path: &'a Path) -> BoxFuture<'a, io::Result<bool>> {
112        Box::pin(tokio::fs::try_exists(path))
113    }
114
115    fn open_read<'a>(
116        &'a self,
117        path: &'a Path,
118    ) -> BoxFuture<'a, io::Result<Box<dyn AsyncFileRead>>> {
119        Box::pin(async move {
120            let f = tokio::fs::File::open(path).await?;
121            Ok(Box::new(TokioFileRead(f)) as Box<dyn AsyncFileRead>)
122        })
123    }
124
125    fn open_write<'a>(
126        &'a self,
127        path: &'a Path,
128    ) -> BoxFuture<'a, io::Result<Box<dyn AsyncFileWrite>>> {
129        Box::pin(async move {
130            let f = tokio::fs::File::create(path).await?;
131            Ok(Box::new(TokioFileWrite(f)) as Box<dyn AsyncFileWrite>)
132        })
133    }
134
135    fn metadata<'a>(&'a self, path: &'a Path) -> BoxFuture<'a, io::Result<Metadata>> {
136        Box::pin(async move {
137            let m = tokio::fs::metadata(path).await?;
138            Ok(Metadata::new(
139                m.len(),
140                m.is_file(),
141                m.is_dir(),
142                m.modified().ok(),
143            ))
144        })
145    }
146
147    fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, io::Result<()>> {
148        Box::pin(tokio::fs::rename(from, to))
149    }
150}
151
152struct TokioFileRead(tokio::fs::File);
153
154impl AsyncFileRead for TokioFileRead {
155    fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec<u8>) -> BoxFuture<'a, io::Result<usize>> {
156        Box::pin(self.0.read_to_end(buf))
157    }
158    fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> BoxFuture<'a, io::Result<()>> {
159        Box::pin(async move {
160            self.0.read_exact(buf).await?;
161            Ok(())
162        })
163    }
164    fn seek(&mut self, pos: io::SeekFrom) -> BoxFuture<'_, io::Result<u64>> {
165        Box::pin(self.0.seek(pos))
166    }
167}
168
169struct TokioFileWrite(tokio::fs::File);
170
171impl AsyncFileWrite for TokioFileWrite {
172    fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> BoxFuture<'a, io::Result<()>> {
173        Box::pin(self.0.write_all(buf))
174    }
175    fn flush(&mut self) -> BoxFuture<'_, io::Result<()>> {
176        Box::pin(self.0.flush())
177    }
178    fn seek(&mut self, pos: io::SeekFrom) -> BoxFuture<'_, io::Result<u64>> {
179        Box::pin(self.0.seek(pos))
180    }
181}