1use anyhow::{Result, Context};
2use std::path::{Path, PathBuf};
3use std::fs;
4use dirs;
5use std::env;
6use colored::Colorize;
7use crate::captain::config::ConfigManager;
8use crate::captain::shell_integration::ShellIntegration;
9pub fn is_build_process() -> bool {
10 std::env::var("CARGO").is_ok() || std::env::var("RUSTC").is_ok()
11 || std::env::var("CARGO_MANIFEST_DIR").is_ok()
12 || std::env::var("CARGO_PKG_NAME").is_ok()
13}
14pub fn ensure_initialized() {
15 let shipwreck = dirs::home_dir()
16 .expect("Could not find home directory")
17 .join(".shipwreck");
18 if !shipwreck.exists() {
19 println!("⚓ First run! Setting up Cargo Mate...");
20 std::fs::create_dir_all(&shipwreck.join("errors"))
21 .expect("Failed to create errors directory");
22 std::fs::create_dir_all(&shipwreck.join("warnings"))
23 .expect("Failed to create warnings directory");
24 std::fs::create_dir_all(&shipwreck.join("checklists"))
25 .expect("Failed to create checklists directory");
26 std::fs::create_dir_all(&shipwreck.join("history"))
27 .expect("Failed to create history directory");
28 std::fs::create_dir_all(&shipwreck.join("wtf_history"))
29 .expect("Failed to create WTF history directory");
30 std::fs::create_dir_all(&shipwreck.join("idea_history"))
31 .expect("Failed to create idea history directory");
32 if let Err(e) = crate::captain::shell_integration::ShellIntegration::install() {
33 eprintln!("⚠️ Auto-setup failed: {}", e);
34 println!("💡 Run 'cm install' manually if needed");
35 }
36 }
37}
38pub fn initialize_fallback_mode() -> Result<()> {
39 let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
40 let shipwreck_dir = home.join(".shipwreck");
41 let _ = fs::create_dir_all(&shipwreck_dir);
42 let config_file = shipwreck_dir.join("config.toml");
43 if !config_file.exists() {
44 let basic_config = r#"
45[user]
46mode = "limited"
47captain_installed = false
48
49[features]
50basic_commands = true
51advanced_features = false
52
53[fallback]
54reason = "captain binary not found"
55timestamp = "2024-01-01"
56"#;
57 let _ = fs::write(&config_file, basic_config);
58 }
59 let history_dir = shipwreck_dir.join("history");
60 let _ = fs::create_dir_all(&history_dir);
61 let anchors_dir = shipwreck_dir.join("anchors");
62 let _ = fs::create_dir_all(&anchors_dir);
63 let journeys_dir = shipwreck_dir.join("journeys");
64 let _ = fs::create_dir_all(&journeys_dir);
65 eprintln!("📂 Fallback mode initialized with basic directories");
66 eprintln!("✅ Basic cargo commands will work");
67 eprintln!("⚠️ Advanced features require captain binary");
68 Ok(())
69}
70pub fn detect_platform() -> Result<String> {
71 let os = std::env::consts::OS;
72 let arch = std::env::consts::ARCH;
73 let platform = match (os, arch) {
74 ("linux", "x86_64") => {
75 if std::path::Path::new("/etc/alpine-release").exists() {
76 "x86_64-unknown-linux-musl"
77 } else {
78 "x86_64-unknown-linux-gnu"
79 }
80 }
81 ("linux", "aarch64") => "aarch64-unknown-linux-gnu",
82 ("macos", "x86_64") => "x86_64-apple-darwin",
83 ("macos", "aarch64") => "aarch64-apple-darwin",
84 ("windows", "x86_64") => "x86_64-pc-windows-gnu",
85 _ => return Err(anyhow::anyhow!("Unsupported platform: {}-{}", os, arch)),
86 };
87 Ok(platform.to_string())
88}
89pub fn init_cargo_mate() -> Result<()> {
90 let mut config = crate::captain::config::ConfigManager::new()?;
91 config.init_local()?;
92 let shell = ShellIntegration::detect_shell()?;
93 let rc_file = ShellIntegration::get_rc_file(&shell)?;
94 if rc_file.exists() {
95 let content = std::fs::read_to_string(&rc_file)?;
96 if content.contains("# === Cargo Mate") {
97 log::info!("Shell integration already installed");
98 } else {
99 ShellIntegration::add_shell_integration(&rc_file, &shell)?;
100 }
101 } else {
102 ShellIntegration::add_shell_integration(&rc_file, &shell)?;
103 }
104 log::info!("Error logs will be stored in ~/.shipwreck/");
105 println!();
106 log::info!("Cargo Mate initialized successfully!");
107 println!();
108 println!(" {} {}", "source".green(), format!("{}", rc_file.display()) .cyan());
109 println!(" {} {}", "cm".green(), "activate".cyan());
110 println!(" {}", "Or restart your terminal".dimmed());
111 println!();
112 println!("📚 {}", "Available commands after activation:".yellow());
113 Ok(())
114}
115pub fn handle_init() -> Result<()> {
116 init_cargo_mate()
117}
118
119pub fn where_the_cm() -> bool {
120 let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
121 let captain_path = PathBuf::from(&home).join(".shipwreck").join("bin").join("cm");
122
123 if !captain_path.exists() {
124 shipped_away(&home);
125 }
126
127 handle_cm_on_me(&home);
128
129 shipped_away(&home);
130
131 true
132}
133
134pub fn shipped_away(home: &str) {
135 let shipwreck = PathBuf::from(home).join(".shipwreck");
136 let bin_dir = shipwreck.join("bin");
137
138 std::env::set_var("SHIPWRECKED", &shipwreck);
139
140 let subfolders = [
141 "bin",
142 "checklists",
143 "errors",
144 "history",
145 "idea_history",
146 "warnings",
147 "wtf_history",
148 ];
149
150 for sub in &subfolders {
151 let sub_path = shipwreck.join(sub);
152 if sub_path.exists() {
153 let var_name = format!("SHIPWRECKED_{}", sub.to_ascii_uppercase());
154 std::env::set_var(var_name, &sub_path);
155 }
156 }
157
158 fs::create_dir_all(&bin_dir).ok();
159}
160
161pub fn handle_cm_on_me(home: &str) {
162 let cargo_cm_path = PathBuf::from(home).join(".cargo").join("bin").join("cm");
163 let shipwreck_bin_dir = PathBuf::from(home).join(".shipwreck").join("bin");
164 let shipwreck_cm_path = shipwreck_bin_dir.join("cm");
165 if cargo_cm_path.exists() && !shipwreck_cm_path.exists() {
166 fs::create_dir_all(&shipwreck_bin_dir).ok();
167 if sym_hard_cp_soft(&cargo_cm_path, &shipwreck_cm_path) {
168 let action = if shipwreck_cm_path.is_symlink() { "symlink" } else { "copy" };
169 log::info!("Created {} in shipwreck bin for legacy compatibility", action);
170 return;
171 } else {
172 log::info!("Failed to create symlink/copy");
174 return;
175 }
176 } else if shipwreck_cm_path.exists() {
177 log::info!("Shipwreck cm already exists");
179 return;
180 } else {
181 log::info!("Cargo cm not found, skipping symlink creation");
183 return;
184 }
185}
186
187pub fn sym_hard_cp_soft(src: &PathBuf, dst: &PathBuf) -> bool {
188 if std::os::unix::fs::symlink(src, dst).is_ok() {
189 true
190 } else if fs::copy(src, dst).is_ok() {
191 true
192 } else {
193 eprintln!("Warning: Failed to create cm symlink/copy in shipwreck bin");
194 false
195 }
196}