dadk_user/parser/
task.rs

1use std::path::PathBuf;
2
3use crate::executor::source::{ArchiveSource, GitSource, LocalSource};
4use dadk_config::{
5    common::{
6        target_arch::TargetArch,
7        task::{
8            BuildConfig, CleanConfig, Dependency, InstallConfig, Source, TaskEnv, TaskSource,
9            TaskSourceType,
10        },
11    },
12    user::UserConfigFile,
13};
14use serde::{Deserialize, Serialize};
15
16use anyhow::{Ok, Result};
17
18// 对于生成的包名和版本号,需要进行替换的字符。
19pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [
20    (" ", "_"),
21    ("\t", "_"),
22    ("-", "_"),
23    (".", "_"),
24    ("+", "_"),
25    ("*", "_"),
26];
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct DADKTask {
30    /// 包名
31    pub name: String,
32    /// 版本
33    pub version: String,
34    /// 包的描述
35    pub description: String,
36    /// 任务类型
37    pub task_type: TaskType,
38    /// 依赖的包
39    pub depends: Vec<Dependency>,
40    /// 构建配置
41    pub build: BuildConfig,
42    /// 安装配置
43    pub install: InstallConfig,
44    /// 清理配置
45    pub clean: CleanConfig,
46    /// 环境变量
47    pub envs: Option<Vec<TaskEnv>>,
48
49    /// (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。
50    #[serde(default)]
51    pub build_once: bool,
52
53    /// (可选) 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装。
54    #[serde(default)]
55    pub install_once: bool,
56
57    #[serde(default = "DADKTask::default_target_arch_vec")]
58    pub target_arch: Vec<TargetArch>,
59}
60
61impl DADKTask {
62    #[allow(dead_code)]
63    pub fn new(
64        name: String,
65        version: String,
66        description: String,
67        task_type: TaskType,
68        depends: Vec<Dependency>,
69        build: BuildConfig,
70        install: InstallConfig,
71        clean: CleanConfig,
72        envs: Option<Vec<TaskEnv>>,
73        build_once: bool,
74        install_once: bool,
75        target_arch: Option<Vec<TargetArch>>,
76    ) -> Self {
77        Self {
78            name,
79            version,
80            description,
81            task_type,
82            depends,
83            build,
84            install,
85            clean,
86            envs,
87            build_once,
88            install_once,
89            target_arch: target_arch.unwrap_or_else(Self::default_target_arch_vec),
90        }
91    }
92
93    /// 默认的目标处理器架构
94    ///
95    /// 从环境变量`ARCH`中获取,如果没有设置,则默认为`x86_64`
96    pub fn default_target_arch() -> TargetArch {
97        let s = std::env::var("ARCH").unwrap_or("x86_64".to_string());
98        return TargetArch::try_from(s.as_str()).unwrap();
99    }
100
101    fn default_target_arch_vec() -> Vec<TargetArch> {
102        vec![Self::default_target_arch()]
103    }
104
105    pub fn validate(&mut self) -> Result<()> {
106        if self.name.is_empty() {
107            return Err(anyhow::Error::msg("name is empty"));
108        }
109        if self.version.is_empty() {
110            return Err(anyhow::Error::msg("version is empty"));
111        }
112        self.task_type.validate()?;
113        self.build.validate()?;
114        self.validate_build_type()?;
115        self.install.validate()?;
116        self.clean.validate()?;
117        self.validate_depends()?;
118        self.validate_envs()?;
119        self.validate_target_arch()?;
120
121        return Ok(());
122    }
123
124    pub fn trim(&mut self) {
125        self.name = self.name.trim().to_string();
126        self.version = self.version.trim().to_string();
127        self.description = self.description.trim().to_string();
128        self.task_type.trim();
129        self.build.trim();
130        self.install.trim();
131        self.clean.trim();
132        self.trim_depends();
133        self.trim_envs();
134    }
135
136    fn validate_depends(&self) -> Result<()> {
137        for depend in &self.depends {
138            depend.validate()?;
139        }
140        return Ok(());
141    }
142
143    fn trim_depends(&mut self) {
144        for depend in &mut self.depends {
145            depend.trim();
146        }
147    }
148
149    fn validate_envs(&self) -> Result<()> {
150        if let Some(envs) = &self.envs {
151            for env in envs {
152                env.validate()?;
153            }
154        }
155        return Ok(());
156    }
157
158    fn validate_target_arch(&self) -> Result<()> {
159        if self.target_arch.is_empty() {
160            return Err(anyhow::Error::msg("target_arch is empty"));
161        }
162        return Ok(());
163    }
164
165    fn trim_envs(&mut self) {
166        if let Some(envs) = &mut self.envs {
167            for env in envs {
168                env.trim();
169            }
170        }
171    }
172
173    /// 验证任务类型与构建配置是否匹配
174    fn validate_build_type(&self) -> Result<()> {
175        match &self.task_type {
176            TaskType::BuildFromSource(_) => {
177                if self.build.build_command.is_none() {
178                    return Err(anyhow::Error::msg("build command is empty"));
179                }
180            }
181            TaskType::InstallFromPrebuilt(_) => {
182                if self.build.build_command.is_some() {
183                    return Err(anyhow::Error::msg(
184                        "build command should be empty when install from prebuilt",
185                    ));
186                }
187            }
188        }
189        return Ok(());
190    }
191
192    pub fn name_version(&self) -> String {
193        let mut name_version = format!("{}-{}", self.name, self.version);
194        for (src, dst) in &NAME_VERSION_REPLACE_TABLE {
195            name_version = name_version.replace(src, dst);
196        }
197        return name_version;
198    }
199
200    pub fn name_version_env(&self) -> String {
201        return Self::name_version_uppercase(&self.name, &self.version);
202    }
203
204    pub fn name_version_uppercase(name: &str, version: &str) -> String {
205        let mut name_version = format!("{}-{}", name, version).to_ascii_uppercase();
206        for (src, dst) in &NAME_VERSION_REPLACE_TABLE {
207            name_version = name_version.replace(src, dst);
208        }
209        return name_version;
210    }
211
212    /// # 获取源码目录
213    ///
214    /// 如果从本地路径构建,则返回本地路径。否则返回None。
215    pub fn source_path(&self) -> Option<PathBuf> {
216        match &self.task_type {
217            TaskType::BuildFromSource(cs) => match cs {
218                CodeSource::Local(lc) => {
219                    return Some(lc.path().clone());
220                }
221                _ => {
222                    return None;
223                }
224            },
225            TaskType::InstallFromPrebuilt(ps) => match ps {
226                PrebuiltSource::Local(lc) => {
227                    return Some(lc.path().clone());
228                }
229                _ => {
230                    return None;
231                }
232            },
233        }
234    }
235}
236
237impl TryFrom<UserConfigFile> for DADKTask {
238    type Error = anyhow::Error;
239
240    fn try_from(user_config: UserConfigFile) -> Result<Self> {
241        Ok(DADKTask {
242            name: user_config.name,
243            version: user_config.version,
244            description: user_config.description,
245            task_type: TaskType::try_from(user_config.task_source)?,
246            depends: user_config.depends,
247            build: user_config.build,
248            install: user_config.install,
249            clean: user_config.clean,
250            envs: Some(user_config.envs),
251            build_once: user_config.build_once,
252            install_once: user_config.install_once,
253            target_arch: user_config.target_arch,
254        })
255    }
256}
257
258/// # 任务类型
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260pub enum TaskType {
261    /// 从源码构建
262    BuildFromSource(CodeSource),
263    /// 从预编译包安装
264    InstallFromPrebuilt(PrebuiltSource),
265}
266
267impl TaskType {
268    pub fn validate(&mut self) -> Result<()> {
269        match self {
270            TaskType::BuildFromSource(source) => source.validate(),
271            TaskType::InstallFromPrebuilt(source) => source.validate(),
272        }
273    }
274
275    pub fn trim(&mut self) {
276        match self {
277            TaskType::BuildFromSource(source) => source.trim(),
278            TaskType::InstallFromPrebuilt(source) => source.trim(),
279        }
280    }
281}
282
283impl TryFrom<TaskSource> for TaskType {
284    type Error = anyhow::Error;
285    fn try_from(task_source: TaskSource) -> Result<Self> {
286        match task_source.source_type {
287            TaskSourceType::BuildFromSource => match task_source.source {
288                Source::Git => Ok(TaskType::BuildFromSource(CodeSource::Git(GitSource::new(
289                    task_source.source_path,
290                    task_source.branch,
291                    task_source.revision,
292                )))),
293                Source::Local => Ok(TaskType::BuildFromSource(CodeSource::Local(
294                    LocalSource::new(PathBuf::from(task_source.source_path)),
295                ))),
296                Source::Archive => Ok(TaskType::BuildFromSource(CodeSource::Archive(
297                    ArchiveSource::new(task_source.source_path, task_source.archive_rootdir),
298                ))),
299            },
300            TaskSourceType::InstallFromPrebuilt => match task_source.source {
301                Source::Git => Err(anyhow::Error::msg(
302                    "InstallFromPrebuild doesn't support Git",
303                )),
304                Source::Local => Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Local(
305                    LocalSource::new(PathBuf::from(task_source.source_path)),
306                ))),
307                Source::Archive => Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Archive(
308                    ArchiveSource::new(task_source.source_path, task_source.archive_rootdir),
309                ))),
310            },
311        }
312    }
313}
314
315/// # 代码源
316#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
317pub enum CodeSource {
318    /// 从Git仓库获取
319    Git(GitSource),
320    /// 从本地目录获取
321    Local(LocalSource),
322    /// 从在线压缩包获取
323    Archive(ArchiveSource),
324}
325
326impl CodeSource {
327    pub fn validate(&mut self) -> Result<()> {
328        match self {
329            CodeSource::Git(source) => source.validate(),
330            CodeSource::Local(source) => source.validate(Some(false)),
331            CodeSource::Archive(source) => source.validate(),
332        }
333    }
334    pub fn trim(&mut self) {
335        match self {
336            CodeSource::Git(source) => source.trim(),
337            CodeSource::Local(source) => source.trim(),
338            CodeSource::Archive(source) => source.trim(),
339        }
340    }
341}
342
343/// # 预编译包源
344#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
345pub enum PrebuiltSource {
346    /// 从在线压缩包获取
347    Archive(ArchiveSource),
348    /// 从本地目录/文件获取
349    Local(LocalSource),
350}
351
352impl PrebuiltSource {
353    pub fn validate(&self) -> Result<()> {
354        match self {
355            PrebuiltSource::Archive(source) => source.validate(),
356            PrebuiltSource::Local(source) => source.validate(None),
357        }
358    }
359
360    pub fn trim(&mut self) {
361        match self {
362            PrebuiltSource::Archive(source) => source.trim(),
363            PrebuiltSource::Local(source) => source.trim(),
364        }
365    }
366}