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