use std::{
env, fs,
io::{Error, ErrorKind},
path::PathBuf,
str::FromStr,
time::{SystemTime, UNIX_EPOCH},
};
use config::Config;
use constants::APP_NAME;
use filepicker::make_filepicker_command;
use log::{error, info};
use logger::{default_logger_path, setup_logger};
use terminals::run_command;
mod config;
mod constants;
mod filepicker;
mod logger;
mod terminals;
const NUM_ARGS: usize = 5;
#[derive(Debug, PartialEq, Clone)]
enum FileMode {
Read,
Write,
}
fn parse_bool(value: &str) -> bool {
value == "1"
}
fn last_selected_filepath() -> PathBuf {
let state_dir = dirs::state_dir()
.expect("could not determine state path")
.join(APP_NAME);
fs::create_dir_all(&state_dir).expect("could not create state dir");
state_dir.join("last-selected-path")
}
fn last_selected_path(use_default: bool) -> PathBuf {
let last_selected_file = last_selected_filepath();
let default_dir = dirs::download_dir()
.or(dirs::home_dir())
.expect("could not determine default dir");
if use_default {
return default_dir;
}
if !last_selected_file.exists() {
return default_dir;
}
let path = PathBuf::from_str(
fs::read_to_string(last_selected_file)
.expect("could not read last selected file")
.as_str(),
)
.or::<PathBuf>(Ok(default_dir.clone()))
.expect("could not determine last selected file");
if path.exists() && path.is_dir() {
return path;
}
default_dir
}
fn main() -> std::io::Result<()> {
setup_logger();
let log_path = default_logger_path()?;
let config = Config::load()?;
let args: Vec<String> = env::args().skip(1).collect();
info!("=============================");
info!("\tTerminal:\t\t{:?}", &config.terminal);
info!("\tFilepicker:\t\t{:?}", &config.filepicker);
info!("\tArguments:\t\t{:?}", &args);
info!("\tLog file:\t\t{:?}", log_path);
if args.len() < NUM_ARGS {
let msg = format!(
"Invalid number of arguments given, must be at least {}, {} were given.",
NUM_ARGS,
&args.len()
);
error!("{}", &msg);
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let is_multiple_files = parse_bool(&args[0]);
let is_directory = parse_bool(&args[1]);
let file_mode = if !parse_bool(&args[2]) {
FileMode::Read
} else {
FileMode::Write
};
let write_path = match file_mode {
FileMode::Read => None,
FileMode::Write => Some(PathBuf::from_str(&args[3]).expect("could not parse write path")),
};
let output_path = PathBuf::from_str(&args[4]).expect("could not parse output path");
info!("\tIs multiple files?\t{:?}", &is_multiple_files);
info!("\tIs directory?\t\t{:?}", &is_directory);
info!("\tFile Mode:\t\t{:?}", &file_mode);
info!("\tWrite Path:\t\t{:?}", &write_path);
info!("\tOutput Path:\t\t{:?}", &output_path);
let last_selected = last_selected_path(!config.start_at_last_selected_dir.unwrap_or(true));
info!("\tLast selected path:\t{:?}", &last_selected);
let old_write_path = write_path.clone();
let mut write_path = match &file_mode {
FileMode::Read => last_selected.clone(),
FileMode::Write => last_selected.join(write_path.unwrap().file_name().unwrap()),
};
if let Some(old_write_path) = old_write_path {
if old_write_path.exists() && file_mode == FileMode::Write {
if write_path.exists() {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
write_path.set_extension(if let Some(ext) = write_path.extension() {
format!(
"{}.{}",
&time,
ext.to_str().expect("couldt not convert OsStr to &str")
)
} else {
format!("{}", &time)
});
}
match fs::rename(&old_write_path, &write_path) {
Err(err) => error!(
"could not move file from {:?} to {:?}: {:?}",
old_write_path, write_path, err,
),
Ok(_) => {} }
}
}
let filepicker_command = make_filepicker_command(
config.filepicker.expect("no filepicker chosen"),
file_mode.clone(),
is_directory,
is_multiple_files,
&write_path,
&output_path,
last_selected,
);
info!("filepicker command: {:?}", filepicker_command);
run_command(
config.terminal.expect("no terminal chosen"),
&filepicker_command,
);
info!("command exited successfully!");
let output_selected_path = if output_path.exists() {
let data = fs::read_to_string(&output_path)?;
let data = data.trim();
if data.len() > 0 {
info!("Outfile content (selected path/s): {:?}", data);
PathBuf::from_str(data).map_or(None, Some)
} else {
None
}
} else {
None
};
if output_selected_path.is_some() {
let path = last_selected_filepath();
fs::write(
path,
output_selected_path
.unwrap()
.parent()
.expect("could not determine output parent dir")
.to_str()
.expect("could not convert OsStr to &str"),
)?;
}
if write_path.exists() {
if let Ok(data) = fs::read_to_string(&write_path) {
if data.starts_with("xdg-desktop-portal-termfilechooser saving files tutorial") {
info!("{:?}, appears to be unused deleting...", &write_path);
fs::remove_file(write_path).expect("could not delete write_path file");
}
}
}
Ok(())
}