1use fluent_templates::{static_loader, Loader};
2use std::env;
3use std::ffi::OsString;
4use std::fs;
5use std::io::{self, Write};
6use std::path::{Path, PathBuf};
7use std::process::Command;
8use std::process::ExitStatus;
9use std::usize;
10use unic_langid::{langid, LanguageIdentifier};
11
12use input::*;
13
14mod input;
15
16static_loader! {
17 static LOCALES = {
18 locales: "locales",
19 fallback_language: "en-US",
20 customise: |bundle| bundle.set_use_isolating(true),
23 };
24}
25
26pub struct Config {
27 list_all: bool,
28 language: LanguageIdentifier,
29}
30impl Config {
31 pub fn read_conf() -> Self {
33 let as_u8: u8 = env::var("LIST_ALL")
35 .unwrap_or_else(|_err| "0".to_string())
36 .trim()
37 .parse()
38 .unwrap_or(0);
39 let mut list_all: bool = match as_u8 {
40 0 => false,
41 _ => true,
42 };
43
44 if env::args_os().nth(1).unwrap_or(OsString::from("0")) == *"--list-all" {
45 list_all = true;
46 };
47
48 let mut as_string = String::from("");
50 for i in env::var("LANG").unwrap_or("en_US".into()).chars() {
52 if i == '.' {
53 break;
54 }
55 as_string += &String::from(i);
56 }
57 let language = as_string.parse().unwrap_or_else(|err| {
58 eprintln!("{}", err);
59 langid!("en_US")
60 });
61
62 Self { list_all, language }
63 }
64}
65
66fn open_file(file: &Path, lang: &LanguageIdentifier) -> io::Result<ExitStatus> {
72 print!("{}", LOCALES.lookup(lang, "ask-for-program"));
73 io::stdout().flush().unwrap();
74 let mut input = String::new();
75 io::stdin()
76 .read_line(&mut input)
77 .unwrap_or_else(|_| panic!("{}", LOCALES.lookup(lang, "input-error")));
78
79 Command::new(input.trim()).arg(file).status()
80}
81
82fn print_dirs(paths: &Vec<PathBuf>) -> usize {
86 println!("0: ..");
87 let mut i: usize = 0;
88 for path in paths {
89 i += 1;
90 println!("{}. {}", i, path.file_name().unwrap().to_str().unwrap());
91 }
92 i
93}
94
95fn filter_elements(elements: Vec<PathBuf>) -> Vec<PathBuf> {
99 let mut result: Vec<PathBuf> = vec![];
100 for i in elements {
101 if !i.file_name().unwrap().to_str().unwrap().starts_with(".") {
102 result.push(i);
103 }
104 }
105 result
106}
107
108fn copy_dir(from: &Path, to: &Path, lang: &LanguageIdentifier) {
110 if from.is_dir() {
111 if !to.exists() {
112 fs::create_dir_all(to).unwrap_or_else(|err| handle_err(err, lang));
113 }
114 let paths = match fs::read_dir(from) {
115 Ok(paths) => paths,
116 Err(err) => {
117 handle_err(err, lang);
118 return;
119 }
120 };
121 for dir in paths {
122 copy_dir(
123 dir.as_ref().unwrap().path().as_path(),
124 &to.join(dir.unwrap().file_name()),
125 lang,
126 );
127 }
128 } else {
129 fs::copy(from, to).unwrap_or_else(|err| {
130 eprintln!("{err}");
131 0
132 });
133 }
134}
135
136fn handle_err(err: io::Error, lang: &LanguageIdentifier) {
138 eprintln!(
139 "{}",
140 match err.kind() {
141 io::ErrorKind::NotFound => LOCALES.lookup(lang, "err-not-found"),
142 io::ErrorKind::PermissionDenied => LOCALES.lookup(lang, "err-permission-denied"),
143 io::ErrorKind::AlreadyExists => LOCALES.lookup(lang, "err-already-exists"),
144 _ => err.kind().to_string(),
145 }
146 )
147}
148
149pub fn run(config: Config) -> Result<(), String> {
151 let mut list_all_once = false;
152 loop {
153 let dir = env::current_dir().unwrap();
154 let mut dir = dir.as_path();
155
156 let mut paths = dir
157 .read_dir()
158 .unwrap()
159 .map(|res| res.map(|e| e.path()))
160 .collect::<Result<Vec<_>, io::Error>>()
161 .unwrap();
162
163 if !config.list_all && !list_all_once {
164 paths = filter_elements(paths);
165 }
166 list_all_once = false;
167
168 println!("\n{}:", dir.to_str().unwrap());
169 let paths_num = print_dirs(&paths);
170
171 let input = match Input::get_input(&config.language) {
172 Ok(input) => match input {
173 Input::Choose(size) => {
174 if size > paths_num {
175 eprintln!("{}", LOCALES.lookup(&config.language, "no-valid-option"));
176 continue;
177 }
178 size
179 }
180 Input::DirName(name) => {
181 if Path::new(&name.trim()).is_dir() {
182 env::set_current_dir(name.trim())
183 .unwrap_or_else(|err| handle_err(err, &config.language));
184 } else {
185 println!(
186 "{}",
187 if let Err(err) = open_file(Path::new(&name), &config.language) {
188 if err.kind() == io::ErrorKind::NotFound {
189 LOCALES.lookup(&config.language, "program-doesnt-exist")
190 } else {
191 err.kind().to_string()
192 }
193 } else {
194 String::from("")
195 }
196 );
197 }
198 continue;
199 }
200 Input::Copy(from, to) => {
201 copy_dir(Path::new(&from), Path::new(&to), &config.language);
202 continue;
203 }
204 Input::NewDir(name) => {
205 fs::create_dir(name).unwrap_or_else(|err| handle_err(err, &config.language));
206 continue;
207 }
208 Input::Rm(name) => {
209 let as_path = Path::new(name.trim());
210 if as_path.is_dir() {
211 fs::remove_dir_all(name.trim())
212 .unwrap_or_else(|err| handle_err(err, &config.language));
213 } else {
214 fs::remove_file(name.trim())
215 .unwrap_or_else(|err| handle_err(err, &config.language));
216 }
217 continue;
218 }
219 Input::Command(cmd) => {
220 if let Err(err) = Command::new("sh").arg("-c").arg(cmd).status() {
221 handle_err(err, &config.language);
222 }
223 continue;
224 }
225 Input::ListAll => {
226 list_all_once = true;
227 continue;
228 }
229 Input::Quit => return Ok(()),
230 },
231 Err(string) => {
232 println!("{string}");
233 continue;
234 }
235 };
236
237 dir = if input == 0 {
238 if let Some(path) = dir.parent() {
239 path
240 } else {
241 eprintln!("{}", LOCALES.lookup(&config.language, "no-root-dir"));
242 continue;
243 }
244 } else {
245 Path::new(&paths[input - 1])
246 };
247 if dir.is_file() {
248 println!(
249 "{}",
250 if let Err(err) = open_file(dir, &config.language) {
251 if err.kind() == io::ErrorKind::NotFound {
252 LOCALES.lookup(&config.language, "program-doesnt-exist")
253 } else {
254 err.kind().to_string()
255 }
256 } else {
257 String::from("")
258 }
259 );
260 continue;
261 }
262 env::set_current_dir(dir).unwrap_or_else(|err| handle_err(err, &config.language));
263 }
264}