1use std::error::Error;
2use std::process::Command;
3use std::fs;
4use std::path::PathBuf;
5use std::collections::HashMap;
6
7fn get_config_dir() -> PathBuf {
8 let mut path = dirs::config_dir().unwrap_or_else(|| {
9 let mut fallback = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
10 fallback.push(".config");
11 fallback
12 });
13 path.push("fg");
14 path
15}
16
17fn get_config_file() -> PathBuf {
18 let mut path = get_config_dir();
19 path.push("config.json");
20 path
21}
22
23fn get_aliases_file() -> PathBuf {
24 let mut path = get_config_dir();
25 path.push("aliases.json");
26 path
27}
28
29#[derive(serde::Serialize, serde::Deserialize, Default)]
30struct Config {
31 mode: String, }
33
34#[derive(serde::Serialize, serde::Deserialize, Default)]
35struct Aliases {
36 commands: HashMap<String, Vec<String>>,
37}
38
39fn load_config() -> Config {
40 let config_file = get_config_file();
41 if let Ok(content) = fs::read_to_string(config_file) {
42 serde_json::from_str(&content).unwrap_or_default()
43 } else {
44 Config { mode: "git".to_string() }
45 }
46}
47
48fn save_config(config: &Config) -> Result<(), Box<dyn Error>> {
49 let config_dir = get_config_dir();
50 fs::create_dir_all(&config_dir)?;
51 let config_file = get_config_file();
52 let content = serde_json::to_string_pretty(config)?;
53 fs::write(config_file, content)?;
54 Ok(())
55}
56
57fn load_aliases() -> Aliases {
58 let aliases_file = get_aliases_file();
59 if let Ok(content) = fs::read_to_string(aliases_file) {
60 serde_json::from_str(&content).unwrap_or_default()
61 } else {
62 Aliases::default()
63 }
64}
65
66fn save_aliases(aliases: &Aliases) -> Result<(), Box<dyn Error>> {
67 let config_dir = get_config_dir();
68 fs::create_dir_all(&config_dir)?;
69 let aliases_file = get_aliases_file();
70 let content = serde_json::to_string_pretty(aliases)?;
71 fs::write(aliases_file, content)?;
72 Ok(())
73}
74
75pub fn set_mode(mode: &str) -> Result<(), Box<dyn Error>> {
76 if mode != "git" && mode != "gh" {
77 return Err("Mode must be 'git' or 'gh'".into());
78 }
79 let config = Config { mode: mode.to_string() };
80 save_config(&config)?;
81 println!("Mode set to: {}", mode);
82 Ok(())
83}
84
85pub fn create_alias(name: &str, commands: Vec<String>) -> Result<(), Box<dyn Error>> {
86 let mut aliases = load_aliases();
87 aliases.commands.insert(name.to_string(), commands.clone());
88 save_aliases(&aliases)?;
89 println!("Alias '{}' created with commands: {:?}", name, commands);
90 Ok(())
91}
92
93pub fn run_alias(name: &str) -> Result<(), Box<dyn Error>> {
94 let aliases = load_aliases();
95 if let Some(commands) = aliases.commands.get(name) {
96 let config = load_config();
97 for cmd in commands {
98 run_command(&config.mode, cmd)?;
99 }
100 Ok(())
101 } else {
102 Err(format!("Alias '{}' not found", name).into())
103 }
104}
105
106pub fn list_aliases() -> Result<(), Box<dyn Error>> {
107 let aliases = load_aliases();
108 if aliases.commands.is_empty() {
109 println!("No aliases configured");
110 } else {
111 println!("Configured aliases:");
112 for (name, commands) in &aliases.commands {
113 println!(" {} -> {:?}", name, commands);
114 }
115 }
116 Ok(())
117}
118
119pub fn get_current_mode() -> Result<(), Box<dyn Error>> {
120 let config = load_config();
121 println!("Current mode: {}", config.mode);
122 Ok(())
123}
124
125fn run_command(mode: &str, cmd: &str) -> Result<(), Box<dyn Error>> {
126 let status = Command::new(mode).args(cmd.split_whitespace()).status()?;
127 if status.success() {
128 println!("{} {} -> OK", mode, cmd);
129 Ok(())
130 } else {
131 Err(format!("{} {} failed (status: {})", mode, cmd, status).into())
132 }
133}
134
135pub fn git_init() -> Result<(), Box<dyn Error>> {
136 let config = load_config();
137 let status = Command::new(&config.mode).arg("init").status()?;
138 if status.success() {
139 println!("{} init -> OK", config.mode);
140 Ok(())
141 } else {
142 Err(format!("{} init failed (status: {})", config.mode, status).into())
143 }
144}
145
146pub fn git_add(archive: &str) -> Result<(), Box<dyn Error>> {
147 let config = load_config();
148 let path = if archive.is_empty() { "." } else { archive };
149 let status = Command::new(&config.mode).arg("add").arg(path).status()?;
150 if status.success() {
151 println!("{} add {} -> OK", config.mode, path);
152 Ok(())
153 } else {
154 Err(format!("{} add failed (status: {})", config.mode, status).into())
155 }
156}
157
158pub fn git_commit(text: &str) -> Result<(), Box<dyn Error>> {
159 let config = load_config();
160 let status = Command::new(&config.mode).arg("commit").arg("-m").arg(text).status()?;
161 if status.success() {
162 println!("{} commit -> OK ({})", config.mode, text);
163 Ok(())
164 } else {
165 Err(format!("{} commit failed (status: {})", config.mode, status).into())
166 }
167}
168
169pub fn git_pull(remote: &str) -> Result<(), Box<dyn Error>> {
170 let config = load_config();
171 let remote_arg = if remote.is_empty() { "origin" } else { remote };
172 let status = Command::new(&config.mode).arg("pull").arg(remote_arg).status()?;
173 if status.success() {
174 println!("{} pull {} -> OK", config.mode, remote_arg);
175 Ok(())
176 } else {
177 Err(format!("{} pull failed (status: {})", config.mode, status).into())
178 }
179}
180
181pub fn git_push(remote: &str) -> Result<(), Box<dyn Error>> {
182 let config = load_config();
183 let remote_arg = if remote.is_empty() { "origin" } else { remote };
184 let status = Command::new(&config.mode).arg("push").arg(remote_arg).status()?;
185 if status.success() {
186 println!("{} push {} -> OK", config.mode, remote_arg);
187 Ok(())
188 } else {
189 Err(format!("{} push failed (status: {})", config.mode, status).into())
190 }
191}
192
193pub fn git_set_branch(branch: &str) -> Result<(), Box<dyn Error>> {
194 let config = load_config();
195 let status = Command::new(&config.mode).arg("checkout").arg("-b").arg(branch).status()?;
196 if status.success() {
197 println!("{} setBranch {} -> OK", config.mode, branch);
198 Ok(())
199 } else {
200 Err(format!("{} setBranch failed (status: {})", config.mode, status).into())
201 }
202}
203
204pub fn git_ro(repository: &str) -> Result<(), Box<dyn Error>> {
205 let config = load_config();
206 let status = Command::new(&config.mode)
207 .arg("remote")
208 .arg("add")
209 .arg("origin")
210 .arg(repository)
211 .status()?;
212 if status.success() {
213 println!("{} remote add origin {} -> OK", config.mode, repository);
214 Ok(())
215 } else {
216 Err(format!("{} remote add failed (status: {})", config.mode, status).into())
217 }
218}
219
220pub fn git_info(target: &str) -> Result<(), Box<dyn Error>> {
221 let config = load_config();
222 let arg = if target.is_empty() { "." } else { target };
223 let status = Command::new(&config.mode).arg("status").arg(arg).status()?;
224 if status.success() {
225 println!("{} status {} -> OK", config.mode, arg);
226 Ok(())
227 } else {
228 Err(format!("{} status failed (status: {})", config.mode, status).into())
229 }
230}
231
232pub fn git_new(value: &str) -> Result<(), Box<dyn Error>> {
233 let config = load_config();
234 let status = Command::new(&config.mode).arg("checkout").arg("-b").arg(value).status()?;
235 if status.success() {
236 println!("{} checkout -b {} -> OK", config.mode, value);
237 Ok(())
238 } else {
239 Err(format!("{} checkout failed (status: {})", config.mode, status).into())
240 }
241}
242
243pub fn get_flag_value(args: &[String], flag: &str) -> Option<String> {
244 for (i, a) in args.iter().enumerate() {
245 if let Some(v) = a.strip_prefix(&format!("{}=", flag)) {
246 return Some(v.to_string());
247 }
248 if a == flag {
249 if let Some(next) = args.get(i + 1) {
250 if !next.starts_with('-') {
251 return Some(next.clone());
252 } else {
253 return Some(String::new());
254 }
255 } else {
256 return Some(String::new());
257 }
258 }
259 }
260 None
261}