1use std::fmt::Debug;
2use std::path::{Path};
3use std::time::Duration;
4use once_cell::sync::{OnceCell};
5use tokio::fs::File;
6use tokio::io::AsyncReadExt;
7use tokio::sync::{RwLock, RwLockReadGuard};
8
9pub struct Artifact<T> {
58 data: OnceCell<RwLock<T>>,
59}
60
61impl<T: Debug + serde::de::DeserializeOwned + Send + Sync + 'static> Artifact<T> {
62 pub const fn new() -> Self {
64 Artifact {
65 data: OnceCell::new(),
66 }
67 }
68
69 pub async fn init(&self, artifact_file: &str) -> Result<(), ArtifactError> {
73 if self.data.get().is_none() {
74 let artifacts_path = get_env_or_default("ARTIFACTS_PATH", "artifacts".to_string());
75 let path = Path::new(&artifacts_path).join(artifact_file);
76
77 let data = get_data::<T>(&path).await.map_err(|err| ArtifactError::InitializationError(err.to_string()))?;
78
79 self.data.set(RwLock::new(data)).unwrap();
80 }
81 Ok(())
82 }
83
84 pub async fn get(&self) -> Result<RwLockReadGuard<'_, T>, ArtifactError> {
87 let data_lock = self.data.get().expect("Artifact is not initialized");
88 Ok(data_lock.read().await)
89 }
90
91 pub async fn update(&self, new_data: T) -> Result<(), ArtifactError> {
95 let data_lock = self.data.get().expect("Artifact is not initialized");
96 let mut data = data_lock.write().await;
97 *data = new_data;
98
99 Ok(())
100 }
101
102 pub async fn watch(&self, artifact_file: String, interval_secs: u64) -> Result<(), ArtifactError> {
105 let artifacts_path = get_env_or_default("ARTIFACTS_PATH", "artifacts".to_string());
106 let path = Path::new(&artifacts_path).join(artifact_file);
107
108 loop {
109 tokio::time::sleep(Duration::from_secs(interval_secs)).await;
110
111 match get_data::<T>(&path).await {
112 Ok(new_data) => {
113 if let Err(e) = self.update(new_data).await {
114 return Err(ArtifactError::UpdateError(e.to_string()));
115 }
116 }
117 Err(e) => {
118 return Err(ArtifactError::WatchError(e.to_string()));
119 }
120 }
121 }
122 }
123}
124
125async fn get_data<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T, ArtifactError> {
128 let mut file = File::open(path).await.map_err(|err| ArtifactError::IoError(err))?;
129 let mut contents = String::new();
130 file.read_to_string(&mut contents).await.map_err(|err| ArtifactError::IoError(err))?;
131 let data: T = serde_json::from_str(&contents).map_err(|err| ArtifactError::SerdeError(err))?;
132 Ok(data)
133}
134
135pub fn get_env_or_default(key: &str, default: String) -> String {
136 std::env::var(key).unwrap_or(default)
137}
138
139#[derive(Debug)]
140pub enum ArtifactError {
141 IoError(std::io::Error),
142 SerdeError(serde_json::Error),
143 InitializationError(String),
144 UpdateError(String),
145 WatchError(String),
146}
147
148impl std::fmt::Display for ArtifactError {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 match self {
151 ArtifactError::IoError(err) => write!(f, "I/O error: {}", err),
152 ArtifactError::SerdeError(err) => write!(f, "JSON serialization/deserialization error: {}", err),
153 ArtifactError::InitializationError(msg) => write!(f, "Initialization error: {}", msg),
154 ArtifactError::UpdateError(msg) => write!(f, "Update error: {}", msg),
155 ArtifactError::WatchError(msg) => write!(f, "Watch error: {}", msg),
156 }
157 }
158}
159
160impl std::error::Error for ArtifactError {}
161
162impl From<std::io::Error> for ArtifactError {
163 fn from(err: std::io::Error) -> Self {
164 ArtifactError::IoError(err)
165 }
166}
167
168impl From<serde_json::Error> for ArtifactError {
169 fn from(err: serde_json::Error) -> Self {
170 ArtifactError::SerdeError(err)
171 }
172}