xref: /DADK/dadk-user/src/parser/task.rs (revision cfb7b78ff5dff2a09cba93336ba222be89cbd3e1)
1 use std::path::PathBuf;
2 
3 use crate::executor::source::{ArchiveSource, GitSource, LocalSource};
4 use 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 };
14 use serde::{Deserialize, Serialize};
15 
16 use anyhow::{Ok, Result};
17 
18 // 对于生成的包名和版本号,需要进行替换的字符。
19 pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [
20     (" ", "_"),
21     ("\t", "_"),
22     ("-", "_"),
23     (".", "_"),
24     ("+", "_"),
25     ("*", "_"),
26 ];
27 
28 #[derive(Debug, Clone, Serialize, Deserialize)]
29 pub 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 
61 impl 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 
237 impl 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)]
260 pub enum TaskType {
261     /// 从源码构建
262     BuildFromSource(CodeSource),
263     /// 从预编译包安装
264     InstallFromPrebuilt(PrebuiltSource),
265 }
266 
267 impl 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 
283 impl 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),
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),
309                 ))),
310             },
311         }
312     }
313 }
314 
315 /// # 代码源
316 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
317 pub enum CodeSource {
318     /// 从Git仓库获取
319     Git(GitSource),
320     /// 从本地目录获取
321     Local(LocalSource),
322     /// 从在线压缩包获取
323     Archive(ArchiveSource),
324 }
325 
326 impl 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)]
345 pub enum PrebuiltSource {
346     /// 从在线压缩包获取
347     Archive(ArchiveSource),
348     /// 从本地目录/文件获取
349     Local(LocalSource),
350 }
351 
352 impl 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 }
367