Skip to main content

remowt_fs/
lib.rs

1use std::io::ErrorKind;
2use std::str::FromStr;
3use std::sync::Mutex;
4
5use bifrostlink::declarative::endpoints;
6use bifrostlink::Config;
7use camino::Utf8PathBuf;
8use serde::{Deserialize, Serialize};
9use tempfile::TempDir;
10
11#[derive(Default)]
12pub struct Fs {
13	tempdirs: Mutex<Vec<TempDir>>,
14}
15
16impl Fs {
17	pub fn new() -> Self {
18		Self::default()
19	}
20}
21
22#[derive(Serialize, Deserialize, Debug, thiserror::Error)]
23pub enum Error {
24	#[error("file not found")]
25	NotFound,
26	#[error("file name/contents is not utf8")]
27	InvalidUtf8,
28	#[error("unknown fs error")]
29	Unknown,
30}
31
32#[endpoints(ns = 1)]
33impl Fs {
34	#[endpoints(id = 1)]
35	async fn read_file_tiny(&self, path: Utf8PathBuf) -> Result<Vec<u8>, Error> {
36		match tokio::fs::read(path).await {
37			Ok(v) => Ok(v),
38			Err(e) if e.kind() == ErrorKind::NotFound => Err(Error::NotFound),
39			_ => Err(Error::Unknown),
40		}
41	}
42	#[endpoints(id = 2)]
43	async fn file_exists(&self, path: Utf8PathBuf) -> bool {
44		tokio::fs::try_exists(path).await.unwrap_or(false)
45	}
46	#[endpoints(id = 3)]
47	async fn read_dir_raw(&self, path: Utf8PathBuf) -> Result<Vec<Utf8PathBuf>, Error> {
48		let mut dir = match tokio::fs::read_dir(path).await {
49			Ok(dir) => dir,
50			Err(e) if e.kind() == ErrorKind::NotFound => return Err(Error::NotFound),
51			Err(_) => return Err(Error::Unknown),
52		};
53		let mut out = Vec::new();
54		while let Ok(Some(entry)) = dir.next_entry().await {
55			let name = Utf8PathBuf::try_from(entry.file_name()).map_err(|_| Error::InvalidUtf8)?;
56			out.push(name);
57		}
58		Ok(out)
59	}
60	#[endpoints(id = 4)]
61	async fn mktemp_dir_raw(&self) -> Result<Utf8PathBuf, Error> {
62		let dir = tempfile::Builder::new()
63			.prefix("remowt.")
64			.tempdir()
65			.map_err(|_| Error::Unknown)?;
66		let mut tempdirs = self.tempdirs.lock().expect("not poisoned");
67		let path = Utf8PathBuf::try_from(dir.path().to_owned()).map_err(|_| Error::InvalidUtf8);
68		tempdirs.push(dir);
69		path
70	}
71	#[endpoints(id = 5)]
72	async fn rm_file(&self, path: Utf8PathBuf) -> Result<(), Error> {
73		match tokio::fs::remove_file(path).await {
74			Ok(()) => Ok(()),
75			Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
76			Err(_) => Err(Error::Unknown),
77		}
78	}
79}
80
81impl<C: Config> FsClient<C> {
82	pub async fn read_file_text(&self, path: impl Into<Utf8PathBuf>) -> Result<String, Error> {
83		let v = self
84			.read_file_tiny(path.into())
85			.await
86			.map_err(|_| Error::Unknown)?;
87		let v = v?;
88		String::from_utf8(v).map_err(|_| Error::InvalidUtf8)
89	}
90	pub async fn read_file_value<T: FromStr>(
91		&self,
92		path: impl Into<Utf8PathBuf>,
93	) -> Result<Result<T, T::Err>, Error> {
94		let text = self.read_file_text(path).await?;
95		Ok(T::from_str(&text))
96	}
97	pub async fn mktemp_dir(&self) -> Result<Utf8PathBuf, Error> {
98		self.mktemp_dir_raw().await.map_err(|_| Error::Unknown)?
99	}
100	pub async fn read_dir(&self, path: impl Into<Utf8PathBuf>) -> Result<Vec<Utf8PathBuf>, Error> {
101		self.read_dir_raw(path.into())
102			.await
103			.map_err(|_| Error::Unknown)?
104	}
105}