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