dadk_user/executor/
cache.rs

1use std::{
2    path::PathBuf,
3    sync::{Arc, Once},
4};
5
6use log::info;
7
8use 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
17use super::ExecutorError;
18
19pub static CACHE_ROOT: Lazy<PathBuf> = Lazy::new();
20
21/// # 初始化缓存根目录
22///
23/// ## 参数
24///
25/// - `path` 缓存根目录的路径
26pub 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)]
93pub enum CacheDirType {
94    /// 构建缓存目录
95    Build,
96    /// 源码缓存目录
97    Source,
98    /// 每个任务执行数据缓存目录
99    TaskData,
100}
101
102#[derive(Debug, Clone)]
103pub struct CacheDir {
104    #[allow(dead_code)]
105    entity: Arc<SchedEntity>,
106    pub path: PathBuf,
107    pub cache_type: CacheDirType,
108}
109
110impl 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";
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
128    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
149    pub fn build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
150        return Ok(Self::new(entity.clone(), CacheDirType::Build)?.path);
151    }
152
153    pub fn source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
154        return Ok(Self::new(entity.clone(), CacheDirType::Source)?.path);
155    }
156
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
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
175    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
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    /// 判断缓存目录是否为空
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    /// 请注意,这会删除整个目录,包括目录下的所有文件和子目录
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)]
243pub struct TaskDataDir {
244    dir: CacheDir,
245}
246
247impl TaskDataDir {
248    const TASK_LOG_FILE_NAME: &'static str = "task_log.toml";
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    /// # 获取任务日志
255    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    /// # 设置任务日志
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}