xref: /DADK/dadk-user/src/parser/task.rs (revision eaa67f3cf8881c221a744937c6318444b068a801)
1 use std::path::PathBuf;
2 
3 use dadk_config::common::target_arch::TargetArch;
4 use serde::{de::Error, Deserialize, Serialize};
5 
6 use crate::executor::source::{ArchiveSource, GitSource, LocalSource};
7 
8 use super::{
9     config::{
10         DADKUserBuildConfig, DADKUserCleanConfig, DADKUserConfigKey, DADKUserInstallConfig,
11         DADKUserTaskType,
12     },
13     InnerParserError, ParserError,
14 };
15 
16 // 对于生成的包名和版本号,需要进行替换的字符。
17 pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [
18     (" ", "_"),
19     ("\t", "_"),
20     ("-", "_"),
21     (".", "_"),
22     ("+", "_"),
23     ("*", "_"),
24 ];
25 
26 #[derive(Debug, Clone, Serialize, Deserialize)]
27 pub struct DADKTask {
28     /// 包名
29     pub name: String,
30     /// 版本
31     pub version: String,
32     /// 包的描述
33     pub description: String,
34     /// 编译target
35     pub rust_target: Option<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         rust_target: Option<String>,
68         task_type: TaskType,
69         depends: Vec<Dependency>,
70         build: BuildConfig,
71         install: InstallConfig,
72         clean: CleanConfig,
73         envs: Option<Vec<TaskEnv>>,
74         build_once: bool,
75         install_once: bool,
76         target_arch: Option<Vec<TargetArch>>,
77     ) -> Self {
78         Self {
79             name,
80             version,
81             description,
82             rust_target,
83             task_type,
84             depends,
85             build,
86             install,
87             clean,
88             envs,
89             build_once,
90             install_once,
91             target_arch: target_arch.unwrap_or_else(Self::default_target_arch_vec),
92         }
93     }
94 
95     /// 默认的目标处理器架构
96     ///
97     /// 从环境变量`ARCH`中获取,如果没有设置,则默认为`x86_64`
98     pub fn default_target_arch() -> TargetArch {
99         let s = std::env::var("ARCH").unwrap_or("x86_64".to_string());
100         return TargetArch::try_from(s.as_str()).unwrap();
101     }
102 
103     fn default_target_arch_vec() -> Vec<TargetArch> {
104         vec![Self::default_target_arch()]
105     }
106 
107     pub fn validate(&mut self) -> Result<(), String> {
108         if self.name.is_empty() {
109             return Err("name is empty".to_string());
110         }
111         if self.version.is_empty() {
112             return Err("version is empty".to_string());
113         }
114         self.task_type.validate()?;
115         self.build.validate()?;
116         self.validate_build_type()?;
117         self.install.validate()?;
118         self.clean.validate()?;
119         self.validate_depends()?;
120         self.validate_envs()?;
121         self.validate_target_arch()?;
122 
123         return Ok(());
124     }
125 
126     pub fn trim(&mut self) {
127         self.name = self.name.trim().to_string();
128         self.version = self.version.trim().to_string();
129         self.description = self.description.trim().to_string();
130         if let Some(target) = &self.rust_target {
131             self.rust_target = Some(target.trim().to_string());
132         };
133         self.task_type.trim();
134         self.build.trim();
135         self.install.trim();
136         self.clean.trim();
137         self.trim_depends();
138         self.trim_envs();
139     }
140 
141     fn validate_depends(&self) -> Result<(), String> {
142         for depend in &self.depends {
143             depend.validate()?;
144         }
145         return Ok(());
146     }
147 
148     fn trim_depends(&mut self) {
149         for depend in &mut self.depends {
150             depend.trim();
151         }
152     }
153 
154     fn validate_envs(&self) -> Result<(), String> {
155         if let Some(envs) = &self.envs {
156             for env in envs {
157                 env.validate()?;
158             }
159         }
160         return Ok(());
161     }
162 
163     fn validate_target_arch(&self) -> Result<(), String> {
164         if self.target_arch.is_empty() {
165             return Err("target_arch is empty".to_string());
166         }
167         return Ok(());
168     }
169 
170     fn trim_envs(&mut self) {
171         if let Some(envs) = &mut self.envs {
172             for env in envs {
173                 env.trim();
174             }
175         }
176     }
177 
178     /// 验证任务类型与构建配置是否匹配
179     fn validate_build_type(&self) -> Result<(), String> {
180         match &self.task_type {
181             TaskType::BuildFromSource(_) => {
182                 if self.build.build_command.is_none() {
183                     return Err("build command is empty".to_string());
184                 }
185             }
186             TaskType::InstallFromPrebuilt(_) => {
187                 if self.build.build_command.is_some() {
188                     return Err(
189                         "build command should be empty when install from prebuilt".to_string()
190                     );
191                 }
192             }
193         }
194         return Ok(());
195     }
196 
197     pub fn name_version(&self) -> String {
198         let mut name_version = format!("{}-{}", self.name, self.version);
199         for (src, dst) in &NAME_VERSION_REPLACE_TABLE {
200             name_version = name_version.replace(src, dst);
201         }
202         return name_version;
203     }
204 
205     pub fn name_version_env(&self) -> String {
206         return Self::name_version_uppercase(&self.name, &self.version);
207     }
208 
209     pub fn name_version_uppercase(name: &str, version: &str) -> String {
210         let mut name_version = format!("{}-{}", name, version).to_ascii_uppercase();
211         for (src, dst) in &NAME_VERSION_REPLACE_TABLE {
212             name_version = name_version.replace(src, dst);
213         }
214         return name_version;
215     }
216 
217     /// # 获取源码目录
218     ///
219     /// 如果从本地路径构建,则返回本地路径。否则返回None。
220     pub fn source_path(&self) -> Option<PathBuf> {
221         match &self.task_type {
222             TaskType::BuildFromSource(cs) => match cs {
223                 CodeSource::Local(lc) => {
224                     return Some(lc.path().clone());
225                 }
226                 _ => {
227                     return None;
228                 }
229             },
230             TaskType::InstallFromPrebuilt(ps) => match ps {
231                 PrebuiltSource::Local(lc) => {
232                     return Some(lc.path().clone());
233                 }
234                 _ => {
235                     return None;
236                 }
237             },
238         }
239     }
240 }
241 
242 impl PartialEq for DADKTask {
243     fn eq(&self, other: &Self) -> bool {
244         self.name == other.name
245             && self.version == other.version
246             && self.description == other.description
247             && self.rust_target == other.rust_target
248             && self.build_once == other.build_once
249             && self.install_once == other.install_once
250             && self.target_arch == other.target_arch
251             && self.task_type == other.task_type
252             && self.build == other.build
253             && self.install == other.install
254             && self.clean == other.clean
255             && self.depends == other.depends
256             && self.envs == other.envs
257     }
258 }
259 
260 /// @brief 构建配置
261 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
262 pub struct BuildConfig {
263     /// 构建命令
264     pub build_command: Option<String>,
265 }
266 
267 impl BuildConfig {
268     #[allow(dead_code)]
269     pub fn new(build_command: Option<String>) -> Self {
270         Self { build_command }
271     }
272 
273     pub fn validate(&self) -> Result<(), String> {
274         return Ok(());
275     }
276 
277     pub fn trim(&mut self) {
278         if let Some(build_command) = &mut self.build_command {
279             *build_command = build_command.trim().to_string();
280         }
281     }
282 }
283 
284 impl From<DADKUserBuildConfig> for BuildConfig {
285     fn from(value: DADKUserBuildConfig) -> Self {
286         return BuildConfig {
287             build_command: value.build_command,
288         };
289     }
290 }
291 
292 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
293 pub struct InstallConfig {
294     /// 安装到DragonOS内的目录
295     pub in_dragonos_path: Option<PathBuf>,
296 }
297 
298 impl InstallConfig {
299     #[allow(dead_code)]
300     pub fn new(in_dragonos_path: Option<PathBuf>) -> Self {
301         Self { in_dragonos_path }
302     }
303 
304     pub fn validate(&self) -> Result<(), String> {
305         if self.in_dragonos_path.is_none() {
306             return Ok(());
307         }
308         if self.in_dragonos_path.as_ref().unwrap().is_relative() {
309             return Err("InstallConfig: in_dragonos_path should be an Absolute path".to_string());
310         }
311         return Ok(());
312     }
313 
314     pub fn trim(&mut self) {}
315 }
316 
317 impl From<DADKUserInstallConfig> for InstallConfig {
318     fn from(value: DADKUserInstallConfig) -> Self {
319         return InstallConfig {
320             in_dragonos_path: (value.in_dragonos_path),
321         };
322     }
323 }
324 
325 /// # 清理配置
326 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
327 pub struct CleanConfig {
328     /// 清理命令
329     pub clean_command: Option<String>,
330 }
331 
332 impl CleanConfig {
333     #[allow(dead_code)]
334     pub fn new(clean_command: Option<String>) -> Self {
335         Self { clean_command }
336     }
337 
338     pub fn validate(&self) -> Result<(), String> {
339         return Ok(());
340     }
341 
342     pub fn trim(&mut self) {
343         if let Some(clean_command) = &mut self.clean_command {
344             *clean_command = clean_command.trim().to_string();
345         }
346     }
347 }
348 
349 impl From<DADKUserCleanConfig> for CleanConfig {
350     fn from(value: DADKUserCleanConfig) -> Self {
351         return CleanConfig {
352             clean_command: value.clean_command,
353         };
354     }
355 }
356 
357 /// @brief 依赖项
358 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
359 pub struct Dependency {
360     pub name: String,
361     pub version: String,
362 }
363 
364 impl Dependency {
365     #[allow(dead_code)]
366     pub fn new(name: String, version: String) -> Self {
367         Self { name, version }
368     }
369 
370     pub fn validate(&self) -> Result<(), String> {
371         if self.name.is_empty() {
372             return Err("name is empty".to_string());
373         }
374         if self.version.is_empty() {
375             return Err("version is empty".to_string());
376         }
377         return Ok(());
378     }
379 
380     pub fn trim(&mut self) {
381         self.name = self.name.trim().to_string();
382         self.version = self.version.trim().to_string();
383     }
384 
385     pub fn name_version(&self) -> String {
386         return format!("{}-{}", self.name, self.version);
387     }
388 }
389 
390 /// # 任务类型
391 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
392 pub enum TaskType {
393     /// 从源码构建
394     BuildFromSource(CodeSource),
395     /// 从预编译包安装
396     InstallFromPrebuilt(PrebuiltSource),
397 }
398 
399 impl TaskType {
400     pub fn validate(&mut self) -> Result<(), String> {
401         match self {
402             TaskType::BuildFromSource(source) => source.validate(),
403             TaskType::InstallFromPrebuilt(source) => source.validate(),
404         }
405     }
406 
407     pub fn trim(&mut self) {
408         match self {
409             TaskType::BuildFromSource(source) => source.trim(),
410             TaskType::InstallFromPrebuilt(source) => source.trim(),
411         }
412     }
413 }
414 
415 impl TryFrom<DADKUserTaskType> for TaskType {
416     type Error = ParserError;
417     fn try_from(dadk_user_task_type: DADKUserTaskType) -> Result<Self, Self::Error> {
418         let task_type = DADKUserConfigKey::try_from(dadk_user_task_type.task_type.as_str())
419             .map_err(|mut e| {
420                 e.config_file = Some(dadk_user_task_type.config_file.clone());
421                 e
422             })?;
423 
424         let source =
425             DADKUserConfigKey::try_from(dadk_user_task_type.source.as_str()).map_err(|mut e| {
426                 e.config_file = Some(dadk_user_task_type.config_file.clone());
427                 e
428             })?;
429 
430         match task_type {
431             DADKUserConfigKey::BuildFromSource => match source {
432                 DADKUserConfigKey::Git => {
433                     Ok(TaskType::BuildFromSource(CodeSource::Git(GitSource::new(
434                         dadk_user_task_type.source_path,
435                         dadk_user_task_type.branch,
436                         dadk_user_task_type.revision,
437                     ))))
438                 }
439                 DADKUserConfigKey::Local => Ok(TaskType::BuildFromSource(CodeSource::Local(
440                     LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)),
441                 ))),
442                 DADKUserConfigKey::Archive => Ok(TaskType::BuildFromSource(CodeSource::Archive(
443                     ArchiveSource::new(dadk_user_task_type.source_path),
444                 ))),
445                 _ => Err(ParserError {
446                     config_file: Some(dadk_user_task_type.config_file),
447                     error: InnerParserError::TomlError(toml::de::Error::custom(format!(
448                         "Unknown source: {}",
449                         dadk_user_task_type.source
450                     ))),
451                 }),
452             },
453             DADKUserConfigKey::InstallFromPrebuilt => match source {
454                 DADKUserConfigKey::Local => {
455                     Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Local(
456                         LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)),
457                     )))
458                 }
459                 DADKUserConfigKey::Archive => Ok(TaskType::InstallFromPrebuilt(
460                     PrebuiltSource::Archive(ArchiveSource::new(dadk_user_task_type.source_path)),
461                 )),
462                 _ => Err(ParserError {
463                     config_file: Some(dadk_user_task_type.config_file),
464                     error: InnerParserError::TomlError(toml::de::Error::custom(format!(
465                         "Unknown source: {}",
466                         dadk_user_task_type.source
467                     ))),
468                 }),
469             },
470             _ => Err(ParserError {
471                 config_file: Some(dadk_user_task_type.config_file),
472                 error: InnerParserError::TomlError(toml::de::Error::custom(format!(
473                     "Unknown task type: {}",
474                     dadk_user_task_type.task_type
475                 ))),
476             }),
477         }
478     }
479 }
480 
481 /// # 代码源
482 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
483 pub enum CodeSource {
484     /// 从Git仓库获取
485     Git(GitSource),
486     /// 从本地目录获取
487     Local(LocalSource),
488     /// 从在线压缩包获取
489     Archive(ArchiveSource),
490 }
491 
492 impl CodeSource {
493     pub fn validate(&mut self) -> Result<(), String> {
494         match self {
495             CodeSource::Git(source) => source.validate(),
496             CodeSource::Local(source) => source.validate(Some(false)),
497             CodeSource::Archive(source) => source.validate(),
498         }
499     }
500     pub fn trim(&mut self) {
501         match self {
502             CodeSource::Git(source) => source.trim(),
503             CodeSource::Local(source) => source.trim(),
504             CodeSource::Archive(source) => source.trim(),
505         }
506     }
507 }
508 
509 /// # 预编译包源
510 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
511 pub enum PrebuiltSource {
512     /// 从在线压缩包获取
513     Archive(ArchiveSource),
514     /// 从本地目录/文件获取
515     Local(LocalSource),
516 }
517 
518 impl PrebuiltSource {
519     pub fn validate(&self) -> Result<(), String> {
520         match self {
521             PrebuiltSource::Archive(source) => source.validate(),
522             PrebuiltSource::Local(source) => source.validate(None),
523         }
524     }
525 
526     pub fn trim(&mut self) {
527         match self {
528             PrebuiltSource::Archive(source) => source.trim(),
529             PrebuiltSource::Local(source) => source.trim(),
530         }
531     }
532 }
533 
534 /// # 任务环境变量
535 ///
536 /// 任务执行时的环境变量.这个环境变量是在当前任务执行时设置的,不会影响到其他任务
537 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
538 pub struct TaskEnv {
539     pub key: String,
540     pub value: String,
541 }
542 
543 impl TaskEnv {
544     #[allow(dead_code)]
545     pub fn new(key: String, value: String) -> Self {
546         Self { key, value }
547     }
548 
549     pub fn key(&self) -> &str {
550         &self.key
551     }
552 
553     pub fn value(&self) -> &str {
554         &self.value
555     }
556 
557     pub fn trim(&mut self) {
558         self.key = self.key.trim().to_string();
559         self.value = self.value.trim().to_string();
560     }
561 
562     pub fn validate(&self) -> Result<(), String> {
563         if self.key.is_empty() {
564             return Err("Env: key is empty".to_string());
565         }
566         return Ok(());
567     }
568 }
569