1 use std::{
2 path::PathBuf,
3 sync::{Arc, Once},
4 };
5
6 use log::info;
7
8 use crate::{
9 parser::{
10 task::{CodeSource, DADKTask, TaskType},
11 task_log::TaskLog,
12 },
13 scheduler::SchedEntity,
14 utils::{lazy_init::Lazy, path::abs_path},
15 };
16
17 use super::ExecutorError;
18
19 pub static CACHE_ROOT: Lazy<PathBuf> = Lazy::new();
20
21 /// # 初始化缓存根目录
22 ///
23 /// ## 参数
24 ///
25 /// - `path` 缓存根目录的路径
cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError>26 pub fn cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError> {
27 let cache_root: String;
28 if path.is_none() {
29 // 查询环境变量,是否有设置缓存根目录
30 let env = std::env::var("DADK_CACHE_ROOT");
31 if env.is_ok() {
32 cache_root = env.unwrap();
33 } else {
34 // 如果没有设置环境变量,则使用默认值
35 // 默认值为当前目录下的.cache目录
36 let cwd = std::env::current_dir().map_err(|e| ExecutorError::IoError(e.to_string()))?;
37 let cwd = cwd.to_str();
38
39 if cwd.is_none() {
40 return Err(ExecutorError::IoError(
41 std::io::Error::new(
42 std::io::ErrorKind::Other,
43 "Current dir is not a valid unicode string",
44 )
45 .to_string(),
46 ));
47 }
48 let cwd = cwd.unwrap();
49
50 cache_root = format!("{}/dadk_cache", cwd);
51 }
52 } else {
53 // 如果有设置缓存根目录,则使用设置的值
54 let path = path.unwrap();
55 let x = path.to_str().ok_or(ExecutorError::IoError(
56 std::io::Error::new(
57 std::io::ErrorKind::Other,
58 "Cache root dir is not a valid unicode string",
59 )
60 .to_string(),
61 ))?;
62 cache_root = x.to_string();
63 }
64
65 let cache_root = PathBuf::from(cache_root);
66
67 // 如果缓存根目录不存在,则创建
68 if !cache_root.exists() {
69 info!("Cache root dir not exists, create it: {:?}", cache_root);
70 std::fs::create_dir_all(&cache_root).map_err(|e| ExecutorError::IoError(e.to_string()))?;
71 } else if !cache_root.is_dir() {
72 // 如果缓存根目录不是目录,则报错
73 return Err(ExecutorError::IoError(
74 std::io::Error::new(
75 std::io::ErrorKind::NotADirectory,
76 format!("Cache root dir is not a directory: {:?}", cache_root),
77 )
78 .to_string(),
79 ));
80 }
81
82 // 初始化缓存根目录
83 static CACHE_ROOT_INIT_ONCE: Once = Once::new();
84 CACHE_ROOT_INIT_ONCE.call_once(|| CACHE_ROOT.init(cache_root));
85
86 // 设置环境变量
87 std::env::set_var("DADK_CACHE_ROOT", CACHE_ROOT.get().to_str().unwrap());
88 info!("Cache root dir: {:?}", CACHE_ROOT.get());
89 return Ok(());
90 }
91
92 #[derive(Debug, Clone, Copy)]
93 pub enum CacheDirType {
94 /// 构建缓存目录
95 Build,
96 /// 源码缓存目录
97 Source,
98 /// 每个任务执行数据缓存目录
99 TaskData,
100 }
101
102 #[derive(Debug, Clone)]
103 pub struct CacheDir {
104 #[allow(dead_code)]
105 entity: Arc<SchedEntity>,
106 pub path: PathBuf,
107 pub cache_type: CacheDirType,
108 }
109
110 impl CacheDir {
111 pub const DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_BUILD_CACHE_DIR";
112 pub const DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_SOURCE_CACHE_DIR";
new(entity: Arc<SchedEntity>, cache_type: CacheDirType) -> Result<Self, ExecutorError>113 pub fn new(entity: Arc<SchedEntity>, cache_type: CacheDirType) -> Result<Self, ExecutorError> {
114 let task = entity.task();
115 let path = Self::get_path(&task, cache_type);
116
117 let result = Self {
118 entity,
119 path,
120 cache_type,
121 };
122
123 result.create()?;
124
125 return Ok(result);
126 }
127
get_path(task: &DADKTask, cache_type: CacheDirType) -> PathBuf128 fn get_path(task: &DADKTask, cache_type: CacheDirType) -> PathBuf {
129 let cache_root = CACHE_ROOT.get();
130 let name_version = task.name_version();
131 let cache_dir = match cache_type {
132 CacheDirType::Build => {
133 format!("{}/build/{}", cache_root.to_str().unwrap(), name_version)
134 }
135 CacheDirType::Source => {
136 format!("{}/source/{}", cache_root.to_str().unwrap(), name_version)
137 }
138 CacheDirType::TaskData => {
139 format!(
140 "{}/task_data/{}",
141 cache_root.to_str().unwrap(),
142 name_version
143 )
144 }
145 };
146 abs_path(&PathBuf::from(cache_dir))
147 }
148
build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError>149 pub fn build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
150 return Ok(Self::new(entity.clone(), CacheDirType::Build)?.path);
151 }
152
source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError>153 pub fn source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
154 return Ok(Self::new(entity.clone(), CacheDirType::Source)?.path);
155 }
156
build_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError>157 pub fn build_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
158 let name_version_env = entity.task().name_version_env();
159 return Ok(format!(
160 "{}_{}",
161 Self::DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX,
162 name_version_env
163 ));
164 }
165
source_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError>166 pub fn source_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
167 let name_version_env = entity.task().name_version_env();
168 return Ok(format!(
169 "{}_{}",
170 Self::DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX,
171 name_version_env
172 ));
173 }
174
need_source_cache(entity: &Arc<SchedEntity>) -> bool175 pub fn need_source_cache(entity: &Arc<SchedEntity>) -> bool {
176 let task_type = &entity.task().task_type;
177
178 if let TaskType::BuildFromSource(cs) = task_type {
179 match cs {
180 CodeSource::Git(_) | CodeSource::Archive(_) => {
181 return true;
182 }
183 CodeSource::Local(_) => {
184 return false;
185 }
186 }
187 } else if let TaskType::InstallFromPrebuilt(ps) = task_type {
188 match ps {
189 crate::parser::task::PrebuiltSource::Archive(_) => return false,
190 crate::parser::task::PrebuiltSource::Local(_) => return false,
191 }
192 }
193 unimplemented!("Not fully implemented task type: {:?}", task_type);
194 }
195
create(&self) -> Result<(), ExecutorError>196 pub fn create(&self) -> Result<(), ExecutorError> {
197 if !self.path.exists() {
198 info!("Cache dir not exists, create it: {:?}", self.path);
199 std::fs::create_dir_all(&self.path)
200 .map_err(|e| ExecutorError::IoError(e.to_string()))?;
201 info!("Cache dir: [{:?}] created.", self.path);
202 } else if !self.path.is_dir() {
203 // 如果路径类别不是目录,则报错
204 return Err(ExecutorError::IoError(
205 std::io::Error::new(
206 std::io::ErrorKind::NotADirectory,
207 format!("Cache dir is not a directory: {:?}", self.path),
208 )
209 .to_string(),
210 ));
211 }
212
213 return Ok(());
214 }
215
216 /// 判断缓存目录是否为空
is_empty(&self) -> Result<bool, ExecutorError>217 pub fn is_empty(&self) -> Result<bool, ExecutorError> {
218 let x = self
219 .path
220 .read_dir()
221 .map_err(|e| ExecutorError::IoError(e.to_string()))?;
222 for _ in x {
223 return Ok(false);
224 }
225
226 return Ok(true);
227 }
228
229 /// # 递归删除自身目录
230 /// 递归删除自身目录,如果目录不存在,则忽略
231 ///
232 /// 请注意,这会删除整个目录,包括目录下的所有文件和子目录
remove_self_recursive(&self) -> Result<(), ExecutorError>233 pub fn remove_self_recursive(&self) -> Result<(), ExecutorError> {
234 let path = &self.path;
235 if path.exists() {
236 std::fs::remove_dir_all(path).map_err(|e| ExecutorError::IoError(e.to_string()))?;
237 }
238 return Ok(());
239 }
240 }
241
242 #[derive(Debug, Clone)]
243 pub struct TaskDataDir {
244 dir: CacheDir,
245 }
246
247 impl TaskDataDir {
248 const TASK_LOG_FILE_NAME: &'static str = "task_log.toml";
new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError>249 pub fn new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError> {
250 let dir = CacheDir::new(entity.clone(), CacheDirType::TaskData)?;
251 return Ok(Self { dir });
252 }
253
254 /// # 获取任务日志
task_log(&self) -> TaskLog255 pub fn task_log(&self) -> TaskLog {
256 let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
257 if path.exists() {
258 let content = std::fs::read_to_string(&path).unwrap();
259 let task_log: TaskLog = toml::from_str(&content).unwrap();
260 return task_log;
261 } else {
262 return TaskLog::new();
263 }
264 }
265
266 /// # 设置任务日志
save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError>267 pub fn save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError> {
268 let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
269 let content = toml::to_string(task_log).unwrap();
270 std::fs::write(&path, content).map_err(|e| ExecutorError::IoError(e.to_string()))?;
271 return Ok(());
272 }
273 }
274