noosphere_into/write/
native.rs1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use std::path::{Path, PathBuf};
4use tokio::fs::{create_dir_all, File};
5use tokio::io::{copy, AsyncRead, AsyncWriteExt};
6
7use super::target::WriteTarget;
8use super::WriteTargetConditionalSend;
9
10#[derive(Clone)]
14pub struct NativeFs {
15 pub root: PathBuf,
16}
17
18impl NativeFs {
19 fn assert_relative(path: &Path) -> Result<()> {
20 if path.is_absolute() || path.starts_with("..") {
21 Err(anyhow!(
22 "Only relative sub-paths allowed, but received: {:?}",
23 path
24 ))
25 } else {
26 Ok(())
27 }
28 }
29}
30
31#[async_trait]
32impl WriteTarget for NativeFs {
33 async fn exists(&self, path: &Path) -> Result<bool> {
34 Ok(self.root.join(path).exists())
35 }
36
37 async fn write<R>(&self, path: &Path, mut contents: R) -> Result<()>
38 where
39 R: AsyncRead + Unpin + WriteTargetConditionalSend,
40 {
41 NativeFs::assert_relative(path)?;
42
43 if let Some(parent) = path.parent() {
44 create_dir_all(self.root.join(parent)).await?;
45 }
46
47 let path = self.root.join(path);
48 let mut file = File::create(path).await?;
49
50 copy(&mut contents, &mut file).await?;
51
52 file.flush().await?;
53
54 Ok(())
55 }
56
57 async fn symlink(&self, src: &Path, dst: &Path) -> Result<()> {
58 NativeFs::assert_relative(src)?;
59 NativeFs::assert_relative(dst)?;
60
61 #[cfg(not(windows))]
62 let result = tokio::fs::symlink(self.root.join(src), self.root.join(dst)).await?;
63 #[cfg(windows)]
64 let result = tokio::fs::symlink_file(self.root.join(src), self.root.join(dst)).await?;
65
66 Ok(result)
67 }
68
69 async fn spawn<F>(future: F) -> Result<()>
70 where
71 F: futures::Future<Output = Result<()>> + WriteTargetConditionalSend + 'static,
72 {
73 tokio::spawn(future).await??;
74 Ok(())
75 }
76}