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_options(
55        prompt: &str,
56        description: &str,
57        options: &[&'static str],
58    ) -> Vec<&'static str> {
59        loop {
60            let mut multiselect = MultiSelect::new(prompt)
61                .description(description)
62                .filterable(true);
63
64            for &option in options {
65                multiselect = multiselect.option(DemandOption::new(option));
66            }
67
68            let result = match multiselect.run() {
69                Ok(selection) => selection,
70                Err(e) => {
71                    if e.kind() == std::io::ErrorKind::Interrupted {
72                        println!("Operation interrupted.");
73                        exit(0);
74                    } else {
75                        panic!("Error: {}", e);
76                    }
77                }
78            };
79
80            let selected: Vec<&str> = result
81                .iter()
82                .filter(|opt| !opt.is_empty())
83                .copied()
84                .collect();
85
86            if selected.is_empty() {
87                println!("{}", "You need to choose in order to follow".yellow());
88                continue;
89            }
90
91            return selected;
92        }
93    }
94
95    fn multiselect_files() -> Vec<&'static str> {
96        Self::multiselect_options(
97            "Add files",
98            "Do you want to add some of these files?",
99            &["Changelog", "Swift Package Index", "Readme", "SwiftLint with mise"]
100        )
101    }
102
103    fn multiselect_platform() -> Vec<&'static str> {
104        Self::multiselect_options(
105            "Choose platform",
106            "Which platform do you want to choose?",
107            &["iOS", "macOS", "tvOS", "watchOS", "visionOS"]
108        )
109    }
110
111    fn loading() {
112        Spinner::new("Building the Package...")
113            .style(&SpinnerStyle::line())
114            .run(|_| {
115                sleep(Duration::from_secs(5));
116            })
117            .expect("error running spinner");
118    }
119
120    fn command_open_xcode(project_name: String) {
121        let command = format!("cd {} && open Package.swift", project_name);
122        let mut child = Command::new("sh")
123            .arg("-c")
124            .arg(&command)
125            .spawn()
126            .expect("Failed to open Xcode");
127        
128        child.wait().expect("Failed to wait on child");
129    }    
130}