xref: /DADK/src/console/new_config.rs (revision 9efba7eea2e1033e076a5a3d550ecf202616ed09)
1 use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc};
2 
3 use log::{debug, error, info};
4 
5 use crate::{
6     console::elements::{BoolInput, OptionalChoice, VecInput},
7     executor::{
8         cache::CacheDir,
9         source::{ArchiveSource, GitSource, LocalSource},
10     },
11     parser::task::{
12         BuildConfig, CleanConfig, CodeSource, DADKTask, Dependency, InstallConfig, PrebuiltSource,
13         TargetArch, TaskEnv, TaskType,
14     },
15 };
16 
17 use super::{
18     elements::{ChooseYesOrNo, Input},
19     interactive::{InputFunc, InteractiveCommand},
20     ConsoleError,
21 };
22 
23 #[derive(Debug)]
24 pub struct NewConfigCommand {
25     /// DADK任务配置文件所在目录
26     config_dir: Option<PathBuf>,
27 }
28 
29 impl InteractiveCommand for NewConfigCommand {
run(&mut self) -> Result<(), ConsoleError>30     fn run(&mut self) -> Result<(), ConsoleError> {
31         // 如果没有指定配置文件输出的目录,则使用当前目录
32         if self.config_dir.is_none() {
33             self.config_dir = Some(PathBuf::from("./"));
34         }
35 
36         println!("To create a new DADK task config, please follow the guidance below... \n");
37 
38         let mut dadk_task = self.build_dadk_task()?;
39         debug!("dadk_task: {:?}", dadk_task);
40 
41         // 校验
42         let check: Result<(), ConsoleError> = dadk_task.validate().map_err(|e| {
43             let msg = format!("Failed to validate DADKTask: {:?}", e);
44             ConsoleError::InvalidInput(msg)
45         });
46 
47         if check.is_err() {
48             error!("{:?}", check.unwrap_err());
49         }
50         // 不管校验是否通过,都写入文件
51         let config_file_path = self.write_dadk_config_file(&dadk_task)?;
52 
53         info!(
54             "DADK task config file created successfully! File:{}",
55             config_file_path.display()
56         );
57         return Ok(());
58     }
59 }
60 
61 impl NewConfigCommand {
new(config_dir: Option<PathBuf>) -> Self62     pub fn new(config_dir: Option<PathBuf>) -> Self {
63         Self { config_dir }
64     }
65 
write_dadk_config_file(&self, dadk_task: &DADKTask) -> Result<PathBuf, ConsoleError>66     fn write_dadk_config_file(&self, dadk_task: &DADKTask) -> Result<PathBuf, ConsoleError> {
67         let json = serde_json::to_string_pretty(&dadk_task).map_err(|e| {
68             let msg = format!("Failed to serialize DADKTask to json: {:?}", e);
69             error!("{}", msg);
70             ConsoleError::InvalidInput(msg)
71         })?;
72         info!("Complete DADK task config file:\n {}", json);
73 
74         // 创建路径
75         let config_dir = self.config_dir.as_ref().unwrap();
76         let filename = format!("{}.dadk", dadk_task.name_version());
77         let config_path = config_dir.join(filename);
78 
79         // 写入文件
80         std::fs::write(&config_path, json).map_err(|e| {
81             let msg = format!(
82                 "Failed to write config file to {}, error: {:?}",
83                 config_path.display(),
84                 e
85             );
86             error!("{}", msg);
87             ConsoleError::InvalidInput(msg)
88         })?;
89 
90         return Ok(config_path);
91     }
92 
build_dadk_task(&self) -> Result<DADKTask, ConsoleError>93     fn build_dadk_task(&self) -> Result<DADKTask, ConsoleError> {
94         let name = self.input_name()?;
95         let version = self.input_version()?;
96         let description = self.input_description()?;
97         debug!(
98             "name: {}, version: {}, description: {}",
99             name, version, description
100         );
101         let rust_target = self.input_rust_target()?;
102         let task_type: TaskType = TaskTypeInput::new().input()?;
103         debug!("task_type: {:?}", task_type);
104 
105         let dep: Vec<Dependency> = DependencyInput::new().input()?;
106         debug!("dep: {:?}", dep);
107         let build_config: BuildConfig = match &task_type {
108             TaskType::InstallFromPrebuilt(_) => BuildConfig::new(Option::Some("".to_string())),
109             TaskType::BuildFromSource(_) => BuildConfigInput::new().input()?,
110         };
111         debug!("build_config: {:?}", build_config);
112         let install_config: InstallConfig = InstallConfigInput::new().input()?;
113         debug!("install_config: {:?}", install_config);
114         let clean_config: CleanConfig = CleanConfigInput::new().input()?;
115         debug!("clean_config: {:?}", clean_config);
116 
117         let task_env: Option<Vec<TaskEnv>> = TaskEnvInput::new().input()?;
118         debug!("task_env: {:?}", task_env);
119 
120         let build_once = BoolInput::new("Run this task once?".to_string(), None).input()?;
121         debug!("build_once: {:?}", build_once);
122         let install_once = BoolInput::new("Install this task once?".to_string(), None).input()?;
123         debug!("install_once: {:?}", install_once);
124 
125         let target_arch = TargetArchInput::new().input()?;
126         debug!("target_arch: {:?}", target_arch);
127 
128         let mut dadk: DADKTask = DADKTask::new(
129             name,
130             version,
131             description,
132             rust_target,
133             task_type,
134             dep,
135             build_config,
136             install_config,
137             clean_config,
138             task_env,
139             build_once,
140             install_once,
141             target_arch,
142         );
143 
144         dadk.trim();
145 
146         return Ok(dadk);
147     }
148 
149     // 输入任务名称
input_name(&self) -> Result<String, ConsoleError>150     fn input_name(&self) -> Result<String, ConsoleError> {
151         let name = Input::new(
152             Some("Please input the [name] of the task:".to_string()),
153             None,
154         )
155         .input()?;
156         Ok(name)
157     }
158 
159     // 输入任务版本
input_version(&self) -> Result<String, ConsoleError>160     fn input_version(&self) -> Result<String, ConsoleError> {
161         let version = Input::new(
162             Some("Please input the [version] of the task:".to_string()),
163             None,
164         )
165         .input()?;
166 
167         return Ok(version);
168     }
169 
170     // 输入任务描述
input_description(&self) -> Result<String, ConsoleError>171     fn input_description(&self) -> Result<String, ConsoleError> {
172         let description = Input::new(
173             Some("Please input the [description] of the task:".to_string()),
174             None,
175         )
176         .input()?;
177 
178         return Ok(description);
179     }
180 
181     // 输入编译target
input_rust_target(&self) -> Result<Option<String>, ConsoleError>182     fn input_rust_target(&self) -> Result<Option<String>, ConsoleError> {
183         let choice = ChooseYesOrNo::new("Input rust_target?".to_string()).choose_until_valid()?;
184 
185         if choice {
186             let rust_target = Input::new(
187                 Some("Please input the [rust_target] of the task:".to_string()),
188                 None,
189             )
190             .input()?;
191             return Ok(Some(rust_target));
192         }
193 
194         return Ok(None);
195     }
196 }
197 
198 #[derive(Debug)]
199 struct TaskTypeInput;
200 
201 impl TaskTypeInput {
new() -> Self202     pub fn new() -> Self {
203         Self {}
204     }
205 }
206 
207 impl InputFunc<TaskType> for TaskTypeInput {
208     /// # 输入任务类型
input(&mut self) -> Result<TaskType, ConsoleError>209     fn input(&mut self) -> Result<TaskType, ConsoleError> {
210         const TASK_TYPE_BUILD_FROM_SOURCE: &str = "src";
211         const TASK_TYPE_INSTALL_FROM_PREBUILT: &str = "prebuilt";
212 
213         let mut task_type_choose =
214             OptionalChoice::new(Some("Please choose the [type] of the task:".to_string()));
215         task_type_choose.add_choice(
216             TASK_TYPE_BUILD_FROM_SOURCE.to_string(),
217             "Build from source".to_string(),
218         );
219         task_type_choose.add_choice(
220             TASK_TYPE_INSTALL_FROM_PREBUILT.to_string(),
221             "Install from prebuilt".to_string(),
222         );
223 
224         // 读取用户输入
225         let task_type = task_type_choose.choose_until_valid()?;
226 
227         // debug!("task type: {}", task_type);
228 
229         let mut task_type = match task_type.as_str() {
230             TASK_TYPE_BUILD_FROM_SOURCE => {
231                 TaskType::BuildFromSource(CodeSourceInput::new().input()?)
232             }
233             TASK_TYPE_INSTALL_FROM_PREBUILT => {
234                 TaskType::InstallFromPrebuilt(PrebuiltSourceInput::new().input()?)
235             }
236             _ => {
237                 let msg = format!("Invalid task type: {}", task_type);
238                 return Err(ConsoleError::InvalidInput(msg));
239             }
240         };
241 
242         // 验证输入
243         task_type.validate().map_err(|e| {
244             ConsoleError::InvalidInput(format!("Invalid task type: {}", e.to_string()))
245         })?;
246 
247         return Ok(task_type);
248     }
249 }
250 /// # 代码源输入
251 #[derive(Debug)]
252 struct CodeSourceInput;
253 
254 impl CodeSourceInput {
new() -> Self255     pub fn new() -> Self {
256         Self {}
257     }
258 
input(&self) -> Result<CodeSource, ConsoleError>259     pub fn input(&self) -> Result<CodeSource, ConsoleError> {
260         const CODE_SOURCE_GIT: &str = "git";
261         const CODE_SOURCE_LOCAL: &str = "local";
262         const CODE_SOURCE_ARCHIVE: &str = "archive";
263 
264         let mut code_source_choose = OptionalChoice::new(Some(
265             "Please choose the [code source] of the task:".to_string(),
266         ));
267         code_source_choose.add_choice(
268             CODE_SOURCE_GIT.to_string(),
269             "Build from git repository".to_string(),
270         );
271         code_source_choose.add_choice(
272             CODE_SOURCE_LOCAL.to_string(),
273             "Build from local directory".to_string(),
274         );
275         code_source_choose.add_choice(
276             CODE_SOURCE_ARCHIVE.to_string(),
277             "Build from archive file".to_string(),
278         );
279 
280         // 读取用户输入
281         let code_source: String = code_source_choose.choose_until_valid()?;
282         // debug!("code source: {}", code_source);
283 
284         let mut code_source: CodeSource = match code_source.as_str() {
285             CODE_SOURCE_GIT => CodeSource::Git(GitSourceInput::new().input_until_valid()?),
286             CODE_SOURCE_LOCAL => CodeSource::Local(LocalSourceInput::new().input_until_valid()?),
287             CODE_SOURCE_ARCHIVE => {
288                 CodeSource::Archive(ArchiveSourceInput::new().input_until_valid()?)
289             }
290             _ => {
291                 let msg = format!("Invalid code source: {}", code_source);
292                 return Err(ConsoleError::InvalidInput(msg));
293             }
294         };
295         code_source.trim();
296         code_source.validate().map_err(|e| {
297             ConsoleError::InvalidInput(format!("Invalid code source: {}", e.to_string()))
298         })?;
299 
300         return Ok(code_source);
301     }
302 }
303 
304 #[derive(Debug)]
305 struct PrebuiltSourceInput;
306 
307 impl PrebuiltSourceInput {
new() -> Self308     pub fn new() -> Self {
309         Self {}
310     }
311 }
312 
313 impl InputFunc<PrebuiltSource> for PrebuiltSourceInput {
input(&mut self) -> Result<PrebuiltSource, ConsoleError>314     fn input(&mut self) -> Result<PrebuiltSource, ConsoleError> {
315         const PREBUILT_SOURCE_LOCAL: &str = "local";
316         const PREBUILT_SOURCE_ARCHIVE: &str = "archive";
317 
318         let mut prebuilt_source_choose = OptionalChoice::new(Some(
319             "Please choose the [prebuilt source] of the task:".to_string(),
320         ));
321 
322         prebuilt_source_choose.add_choice(
323             PREBUILT_SOURCE_LOCAL.to_string(),
324             "Install from local directory".to_string(),
325         );
326         prebuilt_source_choose.add_choice(
327             PREBUILT_SOURCE_ARCHIVE.to_string(),
328             "Install from archive file".to_string(),
329         );
330 
331         // 读取用户输入
332         let prebuilt_source: String = prebuilt_source_choose.choose_until_valid()?;
333         // debug!("prebuilt source: {}", prebuilt_source);
334 
335         let mut prebuilt_source: PrebuiltSource = match prebuilt_source.as_str() {
336             PREBUILT_SOURCE_LOCAL => {
337                 PrebuiltSource::Local(LocalSourceInput::new().input_until_valid()?)
338             }
339             PREBUILT_SOURCE_ARCHIVE => {
340                 PrebuiltSource::Archive(ArchiveSourceInput::new().input_until_valid()?)
341             }
342             _ => {
343                 let msg = format!("Invalid prebuilt source: {}", prebuilt_source);
344                 return Err(ConsoleError::InvalidInput(msg));
345             }
346         };
347         prebuilt_source.trim();
348         prebuilt_source.validate().map_err(|e| {
349             ConsoleError::InvalidInput(format!("Invalid prebuilt source: {}", e.to_string()))
350         })?;
351 
352         return Ok(prebuilt_source);
353     }
354 }
355 
356 #[derive(Debug)]
357 struct GitSourceInput;
358 
359 impl InputFunc<GitSource> for GitSourceInput {
input(&mut self) -> Result<GitSource, ConsoleError>360     fn input(&mut self) -> Result<GitSource, ConsoleError> {
361         let url = self.input_url()?;
362 
363         // 选择分支还是指定的commit
364         const GIT_SOURCE_BRANCH: &str = "branch";
365         const GIT_SOURCE_REVISION: &str = "revision";
366 
367         let mut git_source_choose = OptionalChoice::new(Some(
368             "Please choose the [git source] of the task:".to_string(),
369         ));
370         git_source_choose.add_choice(GIT_SOURCE_BRANCH.to_string(), "branch name".to_string());
371         git_source_choose.add_choice(GIT_SOURCE_REVISION.to_string(), "revision hash".to_string());
372 
373         // 读取用户输入
374         let git_source = git_source_choose.choose_until_valid()?;
375         // debug!("git source: {}", git_source);
376 
377         let mut git_source: GitSource = match git_source.as_str() {
378             GIT_SOURCE_BRANCH => {
379                 let branch = self.input_branch()?;
380                 GitSource::new(url, Some(branch), None)
381             }
382             GIT_SOURCE_REVISION => {
383                 let revision = self.input_revision()?;
384                 GitSource::new(url, None, Some(revision))
385             }
386             _ => {
387                 let msg = format!("Invalid git source: {}", git_source);
388                 return Err(ConsoleError::InvalidInput(msg));
389             }
390         };
391         git_source.trim();
392         // 验证输入
393         git_source.validate().map_err(|e| {
394             ConsoleError::InvalidInput(format!("Invalid git source: {}", e.to_string()))
395         })?;
396 
397         return Ok(git_source);
398     }
399 }
400 
401 impl GitSourceInput {
new() -> Self402     pub fn new() -> Self {
403         Self {}
404     }
405 
input_url(&self) -> Result<String, ConsoleError>406     fn input_url(&self) -> Result<String, ConsoleError> {
407         let url = Input::new(
408             Some("Please input the [url] of the git repository:".to_string()),
409             None,
410         )
411         .input()?;
412         return Ok(url);
413     }
414 
input_branch(&self) -> Result<String, ConsoleError>415     fn input_branch(&self) -> Result<String, ConsoleError> {
416         let branch = Input::new(
417             Some("Please input the [branch name] of the git repository:".to_string()),
418             None,
419         )
420         .input()?;
421         return Ok(branch);
422     }
423 
input_revision(&self) -> Result<String, ConsoleError>424     fn input_revision(&self) -> Result<String, ConsoleError> {
425         let revision = Input::new(
426             Some("Please input the [revision hash] of the git repository:".to_string()),
427             None,
428         )
429         .input()?;
430         return Ok(revision);
431     }
432 }
433 
434 #[derive(Debug)]
435 struct LocalSourceInput;
436 
437 impl LocalSourceInput {
new() -> Self438     pub fn new() -> Self {
439         Self {}
440     }
441 
input_path(&self) -> Result<String, ConsoleError>442     fn input_path(&self) -> Result<String, ConsoleError> {
443         let path = Input::new(
444             Some("Please input the [path] of the local directory:".to_string()),
445             None,
446         )
447         .input()?;
448         return Ok(path);
449     }
450 }
451 impl InputFunc<LocalSource> for LocalSourceInput {
input(&mut self) -> Result<LocalSource, ConsoleError>452     fn input(&mut self) -> Result<LocalSource, ConsoleError> {
453         let path = self.input_path()?;
454         let path = PathBuf::from(path);
455         let mut local_source = LocalSource::new(path);
456 
457         local_source.trim();
458         // 验证输入
459         local_source.validate(None).map_err(|e| {
460             ConsoleError::InvalidInput(format!("Invalid local source: {}", e.to_string()))
461         })?;
462 
463         return Ok(local_source);
464     }
465 }
466 
467 #[derive(Debug)]
468 struct ArchiveSourceInput;
469 
470 impl ArchiveSourceInput {
new() -> Self471     pub fn new() -> Self {
472         Self {}
473     }
474 
input_url(&self) -> Result<String, ConsoleError>475     fn input_url(&self) -> Result<String, ConsoleError> {
476         let url = Input::new(
477             Some("Please input the [url] of the archive file:".to_string()),
478             None,
479         )
480         .input()?;
481         return Ok(url);
482     }
483 }
484 
485 impl InputFunc<ArchiveSource> for ArchiveSourceInput {
input(&mut self) -> Result<ArchiveSource, ConsoleError>486     fn input(&mut self) -> Result<ArchiveSource, ConsoleError> {
487         let url = self.input_url()?;
488         let mut archive_source = ArchiveSource::new(url);
489 
490         archive_source.trim();
491         // 验证输入
492         archive_source.validate().map_err(|e| {
493             ConsoleError::InvalidInput(format!("Invalid archive source: {}", e.to_string()))
494         })?;
495 
496         return Ok(archive_source);
497     }
498 }
499 
500 #[derive(Debug)]
501 struct DependencyInput;
502 
503 impl DependencyInput {
new() -> Self504     pub fn new() -> Self {
505         Self {}
506     }
507 }
508 
509 impl InputFunc<Vec<Dependency>> for DependencyInput {
input(&mut self) -> Result<Vec<Dependency>, ConsoleError>510     fn input(&mut self) -> Result<Vec<Dependency>, ConsoleError> {
511         const TIPS: &str = "Please input the [dependencies] of the task:";
512         println!();
513         println!("Please input the [dependencies] of the task:");
514         let dependency_reader: Rc<RefCell<DependencyInputOne>> =
515             Rc::new(RefCell::new(DependencyInputOne::new()));
516         let mut vecinput = VecInput::new(Some(TIPS.to_string()), dependency_reader);
517         vecinput.input()?;
518         return Ok(vecinput.results()?.clone());
519     }
520 }
521 
522 /// 读取一个dependency的读取器
523 #[derive(Debug)]
524 struct DependencyInputOne;
525 
526 impl InputFunc<Dependency> for DependencyInputOne {
input(&mut self) -> Result<Dependency, ConsoleError>527     fn input(&mut self) -> Result<Dependency, ConsoleError> {
528         return self.input_one();
529     }
530 }
531 
532 impl DependencyInputOne {
new() -> Self533     pub fn new() -> Self {
534         Self {}
535     }
536 
input_name(&self) -> Result<String, ConsoleError>537     fn input_name(&self) -> Result<String, ConsoleError> {
538         let name = Input::new(
539             Some("Please input the [name] of the dependency:".to_string()),
540             None,
541         )
542         .input()?;
543         return Ok(name);
544     }
545 
input_version(&self) -> Result<String, ConsoleError>546     fn input_version(&self) -> Result<String, ConsoleError> {
547         let version = Input::new(
548             Some("Please input the [version] of the dependency:".to_string()),
549             None,
550         )
551         .input()?;
552         return Ok(version);
553     }
554 
input_one(&self) -> Result<Dependency, ConsoleError>555     fn input_one(&self) -> Result<Dependency, ConsoleError> {
556         let name = self.input_name()?;
557         let version = self.input_version()?;
558         let mut dependency = Dependency::new(name, version);
559 
560         dependency.trim();
561         // 验证输入
562         dependency.validate().map_err(|e| {
563             ConsoleError::InvalidInput(format!("Invalid dependency: {}", e.to_string()))
564         })?;
565 
566         return Ok(dependency);
567     }
568 }
569 
570 #[derive(Debug)]
571 pub struct BuildConfigInput;
572 
573 impl BuildConfigInput {
new() -> Self574     pub fn new() -> Self {
575         Self {}
576     }
577 
input_command(&self) -> Result<String, ConsoleError>578     fn input_command(&self) -> Result<String, ConsoleError> {
579         println!("Please input the [build command] of the task:");
580         let tips = format!("\nNote:
581 \t1. The command will be executed in the root directory of the source code.
582 \t2. After the command is executed, all files need to install to DragonOS should be placed in: [{}_TASKNAME_VERSION]\n",
583  CacheDir::DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX);
584         println!("{}", tips);
585         let mut command = Input::new(Some("Build Command:".to_string()), None).input()?;
586         command = command.trim().to_string();
587 
588         return Ok(command);
589     }
590 }
591 
592 impl InputFunc<BuildConfig> for BuildConfigInput {
input(&mut self) -> Result<BuildConfig, ConsoleError>593     fn input(&mut self) -> Result<BuildConfig, ConsoleError> {
594         println!("\nPlease input the [build_config] of the task:");
595 
596         // 读取build_config
597         let command = self.input_command()?;
598         let command = if command.is_empty() {
599             None
600         } else {
601             Some(command)
602         };
603         let build_config = BuildConfig::new(command);
604         return Ok(build_config);
605     }
606 }
607 
608 #[derive(Debug)]
609 struct InstallConfigInput;
610 
611 impl InstallConfigInput {
new() -> Self612     pub fn new() -> Self {
613         Self {}
614     }
615 
input_install_dir(&self) -> Result<Option<PathBuf>, ConsoleError>616     fn input_install_dir(&self) -> Result<Option<PathBuf>, ConsoleError> {
617         let install_dir = Input::new(
618             Some("Please input the [dir to install in DragonOS] of the task:".to_string()),
619             None,
620         )
621         .input()?;
622         let install_dir = install_dir.trim().to_string();
623         let install_dir = if install_dir.is_empty() {
624             None
625         } else {
626             Some(PathBuf::from(install_dir))
627         };
628         return Ok(install_dir);
629     }
630 }
631 
632 impl InputFunc<InstallConfig> for InstallConfigInput {
input(&mut self) -> Result<InstallConfig, ConsoleError>633     fn input(&mut self) -> Result<InstallConfig, ConsoleError> {
634         println!("\nPlease input the [install_config] of the task:");
635 
636         // 读取install dir
637         let install_dir = self.input_install_dir()?;
638         let mut install_config = InstallConfig::new(install_dir);
639         install_config.trim();
640         return Ok(install_config);
641     }
642 }
643 
644 #[derive(Debug)]
645 struct CleanConfigInput;
646 
647 impl CleanConfigInput {
new() -> Self648     pub fn new() -> Self {
649         Self {}
650     }
651 
input_clean_command(&self) -> Result<Option<String>, ConsoleError>652     fn input_clean_command(&self) -> Result<Option<String>, ConsoleError> {
653         let clean_command = Input::new(
654             Some("Please input the [clean command] of the task:".to_string()),
655             None,
656         )
657         .input()?;
658         let clean_command = clean_command.trim().to_string();
659         let clean_command = if clean_command.is_empty() {
660             None
661         } else {
662             Some(clean_command)
663         };
664         return Ok(clean_command);
665     }
666 }
667 
668 impl InputFunc<CleanConfig> for CleanConfigInput {
input(&mut self) -> Result<CleanConfig, ConsoleError>669     fn input(&mut self) -> Result<CleanConfig, ConsoleError> {
670         println!("\nPlease configure the [clean_config] of the task:");
671 
672         // 读取clean command
673         let clean_command = self.input_clean_command()?;
674         let mut clean_config = CleanConfig::new(clean_command);
675         clean_config.trim();
676         return Ok(clean_config);
677     }
678 }
679 
680 #[derive(Debug)]
681 struct TaskEnvInput;
682 
683 impl TaskEnvInput {
new() -> Self684     pub fn new() -> Self {
685         Self {}
686     }
687 }
688 
689 impl InputFunc<Option<Vec<TaskEnv>>> for TaskEnvInput {
input(&mut self) -> Result<Option<Vec<TaskEnv>>, ConsoleError>690     fn input(&mut self) -> Result<Option<Vec<TaskEnv>>, ConsoleError> {
691         const TIPS: &str = "Please configure the [ environment variables ] of the task:";
692         println!();
693         println!("{TIPS}");
694         let env_reader: Rc<RefCell<TaskEnvInputOne>> =
695             Rc::new(RefCell::new(TaskEnvInputOne::new()));
696         let mut vecinput: VecInput<TaskEnv> = VecInput::new(Some(TIPS.to_string()), env_reader);
697         vecinput.input()?;
698         let result = vecinput.results()?.clone();
699         // 不管是否有输入,都返回Some
700         return Ok(Some(result));
701     }
702 }
703 
704 #[derive(Debug)]
705 struct TaskEnvInputOne;
706 
707 impl TaskEnvInputOne {
new() -> Self708     pub fn new() -> Self {
709         Self {}
710     }
711 
input_name(&self) -> Result<String, ConsoleError>712     fn input_name(&self) -> Result<String, ConsoleError> {
713         let name = Input::new(
714             Some("Please input the [name] of the env:".to_string()),
715             None,
716         )
717         .input()?;
718         return Ok(name);
719     }
720 
input_value(&self) -> Result<String, ConsoleError>721     fn input_value(&self) -> Result<String, ConsoleError> {
722         let value = Input::new(
723             Some("Please input the [value] of the env:".to_string()),
724             None,
725         )
726         .input()?;
727         return Ok(value);
728     }
729 
input_one(&self) -> Result<TaskEnv, ConsoleError>730     fn input_one(&self) -> Result<TaskEnv, ConsoleError> {
731         let name = self.input_name()?;
732         let value = self.input_value()?;
733         let mut env = TaskEnv::new(name, value);
734 
735         env.trim();
736         // 验证输入
737         env.validate()
738             .map_err(|e| ConsoleError::InvalidInput(format!("Invalid env: {}", e.to_string())))?;
739 
740         return Ok(env);
741     }
742 }
743 
744 impl InputFunc<TaskEnv> for TaskEnvInputOne {
input(&mut self) -> Result<TaskEnv, ConsoleError>745     fn input(&mut self) -> Result<TaskEnv, ConsoleError> {
746         let env = self.input_one()?;
747         return Ok(env);
748     }
749 }
750 
751 /// # 输入目标架构
752 ///
753 /// 可选值参考:[TargetArch](crate::parser::task::TargetArch::EXPECTED)
754 #[derive(Debug)]
755 struct TargetArchInput;
756 
757 impl TargetArchInput {
new() -> Self758     pub fn new() -> Self {
759         Self {}
760     }
761 }
762 
763 impl InputFunc<Option<Vec<TargetArch>>> for TargetArchInput {
input(&mut self) -> Result<Option<Vec<TargetArch>>, ConsoleError>764     fn input(&mut self) -> Result<Option<Vec<TargetArch>>, ConsoleError> {
765         const TIPS: &str = "Please configure the [ available target arch ] of the task:";
766         println!();
767         println!("{TIPS}");
768         let env_reader: Rc<RefCell<TargetArchInputOne>> =
769             Rc::new(RefCell::new(TargetArchInputOne::new()));
770         let mut vecinput: VecInput<TargetArch> = VecInput::new(Some(TIPS.to_string()), env_reader);
771         vecinput.input()?;
772         let result = vecinput.results()?.clone();
773 
774         if result.is_empty() {
775             return Ok(None);
776         }
777 
778         return Ok(Some(result));
779     }
780 }
781 
782 #[derive(Debug)]
783 struct TargetArchInputOne;
784 
785 impl TargetArchInputOne {
new() -> Self786     pub fn new() -> Self {
787         Self
788     }
789 
input_one(&self) -> Result<TargetArch, ConsoleError>790     fn input_one(&self) -> Result<TargetArch, ConsoleError> {
791         let s = Input::new(Some("Please input one target arch:".to_string()), None).input()?;
792 
793         let target_arch = TargetArch::try_from(s.as_str()).map_err(|e| {
794             ConsoleError::InvalidInput(format!("Invalid target arch: {}", e.to_string()))
795         })?;
796         return Ok(target_arch);
797     }
798 }
799 
800 impl InputFunc<TargetArch> for TargetArchInputOne {
input(&mut self) -> Result<TargetArch, ConsoleError>801     fn input(&mut self) -> Result<TargetArch, ConsoleError> {
802         let env = self.input_one()?;
803         return Ok(env);
804     }
805 }
806