xbp_cli/
project_detector.rs1use colored::Colorize;
2use std::path::Path;
3
4use crate::utils::{preferred_pip_command, preferred_python_command};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum ProjectType {
8 Docker,
9 DockerCompose,
10 Python,
11 NodeJs,
12 Rust,
13 Railway,
14 Go,
15}
16
17impl ProjectType {
18 pub fn name(&self) -> &str {
19 match self {
20 ProjectType::Docker => "Docker",
21 ProjectType::DockerCompose => "Docker Compose",
22 ProjectType::Python => "Python",
23 ProjectType::NodeJs => "Node.js",
24 ProjectType::Rust => "Rust",
25 ProjectType::Railway => "Railway",
26 ProjectType::Go => "Go",
27 }
28 }
29
30 pub fn icon(&self) -> &str {
31 match self {
32 ProjectType::Docker => "🐳",
33 ProjectType::DockerCompose => "🐋",
34 ProjectType::Python => "🐍",
35 ProjectType::NodeJs => "📦",
36 ProjectType::Rust => "🦀",
37 ProjectType::Railway => "🚂",
38 ProjectType::Go => "🔷",
39 }
40 }
41}
42
43pub fn detect_project_types(path: &Path) -> Vec<ProjectType> {
44 let mut types = Vec::new();
45
46 if path.join("Dockerfile").exists() {
47 types.push(ProjectType::Docker);
48 }
49
50 if path.join("docker-compose.yml").exists()
51 || path.join("docker-compose.yaml").exists()
52 || path.join("compose.yml").exists()
53 || path.join("compose.yaml").exists()
54 {
55 types.push(ProjectType::DockerCompose);
56 }
57
58 if path.join("requirements.txt").exists()
59 || path.join("pyproject.toml").exists()
60 || path.join("setup.py").exists()
61 {
62 types.push(ProjectType::Python);
63 }
64
65 if path.join("package.json").exists() {
66 types.push(ProjectType::NodeJs);
67 }
68
69 if path.join("Cargo.toml").exists() {
70 types.push(ProjectType::Rust);
71 }
72
73 if path.join("railway.json").exists() || path.join("railway.toml").exists() {
74 types.push(ProjectType::Railway);
75 }
76
77 if path.join("go.mod").exists() {
78 types.push(ProjectType::Go);
79 }
80
81 types
82}
83
84pub fn suggest_commands(project_types: &[ProjectType]) -> Vec<String> {
85 let mut commands = Vec::new();
86
87 for project_type in project_types {
88 match project_type {
89 ProjectType::Docker => {
90 commands.push("docker build -t <image-name> .".to_string());
91 commands.push("docker run -p <port>:<port> <image-name>".to_string());
92 }
93 ProjectType::DockerCompose => {
94 commands.push("docker-compose up -d".to_string());
95 commands.push("docker-compose down".to_string());
96 }
97 ProjectType::Python => {
98 let pip = preferred_pip_command();
99 let python = preferred_python_command();
100 commands.push(format!("{} install -r requirements.txt", pip));
101 commands.push(format!("{} main.py", python));
102 }
103 ProjectType::NodeJs => {
104 commands.push("npm install".to_string());
105 commands.push("npm start".to_string());
106 }
107 ProjectType::Rust => {
108 commands.push("cargo build --release".to_string());
109 commands.push("cargo run".to_string());
110 }
111 ProjectType::Railway => {
112 commands.push("railway up".to_string());
113 commands.push("railway logs".to_string());
114 }
115 ProjectType::Go => {
116 commands.push("go build".to_string());
117 commands.push("go run .".to_string());
118 }
119 }
120 }
121
122 commands.dedup();
123 commands
124}
125
126pub fn display_project_types(project_types: &[ProjectType]) {
127 if project_types.is_empty() {
128 return;
129 }
130
131 let types_str = project_types
132 .iter()
133 .map(|t| format!("{} {}", t.icon(), t.name()))
134 .collect::<Vec<_>>()
135 .join(", ");
136
137 tracing::info!("{} {}", "Detected:".bright_blue(), types_str);
138}
139
140pub fn display_suggested_commands(project_types: &[ProjectType]) {
141 let commands = suggest_commands(project_types);
142
143 if commands.is_empty() {
144 return;
145 }
146
147 tracing::info!("{}", "Suggested commands:".bright_blue());
148 for cmd in commands.iter().take(3) {
149 tracing::info!(" {}", cmd.dimmed());
150 }
151}