spm_swift_package/presentation/
cli_controller.rs

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