spm_swift_package/presentation/
cli_controller.rs1use colored::Colorize;
2use demand::{DemandOption, Input, MultiSelect, Spinner, SpinnerStyle};
3use std::process::Command;
4
5use crate::domain::usecase::usecase::*;
6
7pub struct CliController;
8
9impl CliController {
10 pub async fn execute_flow() -> Result<(), String> {
11 let project_name = Self::project_name_input()?;
12 let file_selected = Self::multiselect_files()?;
13 let platform_selected = Self::multiselect_platform()?;
14
15 Self::loading().await?;
16 SpmUseCase::execute(&project_name, file_selected, platform_selected).await?;
17 Self::command_open_xcode(&project_name)?;
18 Ok(())
19 }
20
21 fn project_name_input() -> Result<String, String> {
24 let validation_empty = |s: &str| {
25 if s.is_empty() {
26 Err("Library name cannot be empty")
27 } else {
28 Ok(())
29 }
30 };
31
32 let input = Input::new("Library name")
33 .placeholder("Enter the library name")
34 .prompt("Library: ")
35 .validation(validation_empty);
36
37 input.run().map_err(|e| {
38 if e.kind() == std::io::ErrorKind::Interrupted {
39 "Operation interrupted by user.".to_string()
40 } else {
41 format!("Error getting library name: {}", e)
42 }
43 })
44 }
45
46 fn multiselect_options(
47 prompt: &str,
48 description: &str,
49 options: &[&'static str],
50 ) -> Result<Vec<&'static str>, String> {
51 loop {
52 let mut multiselect = MultiSelect::new(prompt)
53 .description(description)
54 .filterable(true);
55
56 for &option in options {
57 multiselect = multiselect.option(DemandOption::new(option));
58 }
59
60 let result = match multiselect.run() {
61 Ok(selection) => selection,
62 Err(e) => {
63 if e.kind() == std::io::ErrorKind::Interrupted {
64 return Err("Operation interrupted by user.".to_string());
65 } else {
66 return Err(format!("Error selecting options: {}", e));
67 }
68 }
69 };
70
71 let selected: Vec<&str> = result
72 .iter()
73 .filter(|opt| !opt.is_empty())
74 .copied()
75 .collect();
76
77 if selected.is_empty() {
78 println!(
79 "{}",
80 "You need to choose at least one option to continue".yellow()
81 );
82 continue;
83 }
84
85 return Ok(selected);
86 }
87 }
88
89 fn multiselect_files() -> Result<Vec<&'static str>, String> {
90 Self::multiselect_options(
91 "Add files",
92 "Do you want to add some of these files?",
93 &[
94 "Changelog",
95 "Swift Package Index",
96 "Readme",
97 "SwiftLint with mise",
98 ],
99 )
100 }
101
102 fn multiselect_platform() -> Result<Vec<&'static str>, String> {
103 Self::multiselect_options(
104 "Choose platform",
105 "Which platform do you want to choose?",
106 &["iOS", "macOS", "tvOS", "watchOS", "visionOS"],
107 )
108 }
109
110 async fn loading() -> Result<(), String> {
111 Spinner::new("Building the Package...")
112 .style(&SpinnerStyle::line())
113 .run(|_| {
114 std::thread::sleep(std::time::Duration::from_secs(5));
115 })
116 .map_err(|_| "Error running spinner".to_string())
117 }
118
119 fn command_open_xcode(project_name: &str) -> Result<(), String> {
120 let command = format!("cd {} && open Package.swift", project_name);
121 let mut child = Command::new("sh")
122 .arg("-c")
123 .arg(&command)
124 .spawn()
125 .map_err(|e| format!("Failed to open Xcode: {}", e))?;
126
127 child
128 .wait()
129 .map_err(|e| format!("Failed to wait for Xcode: {}", e))?;
130 Ok(())
131 }
132}