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