1pub mod env;
2
3use anyhow::Error;
4use async_trait::async_trait;
5pub use env::FromEnv;
6use meio::LiteTask;
7use serde::{de::DeserializeOwned, Serialize};
8use std::fmt::Debug;
9use std::marker::PhantomData;
10use std::path::PathBuf;
11use tokio::fs::{remove_file, File};
12use tokio::io::{AsyncReadExt, AsyncWriteExt};
13
14pub trait Config: Debug + Send + 'static {}
15
16#[async_trait]
17pub trait ReadableConfig: Config + DeserializeOwned {
18 async fn read(path: PathBuf) -> Result<Self, Error> {
19 let mut file = File::open(path).await?;
20 let mut contents = Vec::new();
21 file.read_to_end(&mut contents).await?;
22 Self::parse(&contents)
23 }
24
25 fn parse(data: &[u8]) -> Result<Self, Error> {
26 toml::from_slice(data).map_err(Error::from)
27 }
28}
29
30pub struct ReadConfigFile<T> {
31 path: Option<PathBuf>,
32 default_path: &'static str,
33 _config: PhantomData<T>,
34}
35
36impl<T> ReadConfigFile<T> {
37 pub fn new(path: Option<PathBuf>, default_path: &'static str) -> Self {
38 Self {
39 path,
40 default_path,
41 _config: PhantomData,
42 }
43 }
44}
45
46#[async_trait]
47impl<T: ReadableConfig> LiteTask for ReadConfigFile<T> {
48 type Output = Option<T>;
49
50 async fn interruptable_routine(mut self) -> Result<Self::Output, Error> {
51 let config = {
52 if let Some(path) = self.path {
53 Some(T::read(path).await?)
54 } else {
55 let path = self.default_path.into();
56 T::read(path).await.ok()
57 }
58 };
59 log::trace!("Config ready: {:?}", config);
60 Ok(config)
61 }
62}
63
64#[async_trait]
65pub trait WritableConfig: Config + Serialize + Sync {
66 async fn write(&self, path: PathBuf) -> Result<(), Error> {
67 let mut file = File::create(path).await?;
68 let data = toml::to_vec(self)?;
69 file.write_all(&data).await?;
70 Ok(())
71 }
72
73 async fn drop_file(path: PathBuf) -> Result<(), Error> {
74 remove_file(path).await?;
75 Ok(())
76 }
77}
78
79pub struct WriteConfigFile<T> {
80 path: PathBuf,
81 config: T,
82}
83
84impl<T> WriteConfigFile<T> {
85 pub fn new(path: PathBuf, config: T) -> Self {
86 Self { path, config }
87 }
88}
89
90#[async_trait]
91impl<T: WritableConfig> LiteTask for WriteConfigFile<T> {
92 type Output = ();
93
94 async fn interruptable_routine(mut self) -> Result<Self::Output, Error> {
95 log::trace!("Storing config to {:?}: {:?}", self.path, self.config);
96 self.config.write(self.path).await?;
97 Ok(())
98 }
99}
100
101pub struct DropConfigFile<T> {
102 path: PathBuf,
103 _config: PhantomData<T>,
104}
105
106impl<T> DropConfigFile<T> {
107 pub fn new(path: PathBuf) -> Self {
108 Self {
109 path,
110 _config: PhantomData,
111 }
112 }
113}
114
115#[async_trait]
116impl<T: WritableConfig> LiteTask for DropConfigFile<T> {
117 type Output = ();
118
119 async fn interruptable_routine(mut self) -> Result<Self::Output, Error> {
120 log::trace!("Drop config file: {:?}", self.path);
121 T::drop_file(self.path).await?;
122 Ok(())
123 }
124}