use crate::cli::{ConfigCommand, ConfigSetArgs, DirectoryAddArgs, DirectoryRemoveArgs};
use crate::config::{Config, DirectoryEntry};
use crate::error::Result;
use rust_embed::RustEmbed;
use std::fs;
use std::io::{self};
use std::path::PathBuf;
#[derive(RustEmbed)]
#[folder = "assets/"]
struct Asset;
fn config_path() -> PathBuf {
let mut dir = dirs_next::config_dir().expect("Cannot get config directory");
dir.push("prefixload");
if !dir.exists() {
fs::create_dir_all(&dir).expect("Cannot create config directory");
}
dir.push("config.yml");
dir
}
fn ensure_config_exists(path: &PathBuf) -> Result<()> {
if !path.exists() {
let bytes = Asset::get("config.yml")
.expect("Embedded config.yaml not found")
.data;
std::fs::write(path, bytes)?;
println!("Default config.yml written to {}", path.display());
}
Ok(())
}
fn backup_config(path: &PathBuf) -> Result<()> {
if path.exists() {
let mut backup_path = path.clone();
backup_path.set_extension("yml.bak"); std::fs::copy(path, &backup_path)?;
println!("Backup created at {}", backup_path.display());
}
Ok(())
}
#[cfg(windows)]
fn default_editor() -> String {
"notepad".to_string()
}
#[cfg(not(windows))]
fn default_editor() -> String {
"nano".to_string()
}
fn edit_config_in_editor(path: &PathBuf) -> io::Result<()> {
let editor = std::env::var("EDITOR").unwrap_or_else(|_| default_editor());
std::process::Command::new(editor).arg(path).status()?;
Ok(())
}
fn handle_config_show(path: &PathBuf) -> Result<()> {
let content = fs::read_to_string(&path)?;
println!("{content}");
Ok(())
}
fn handle_config_edit(path: &PathBuf) -> Result<()> {
let backup_path = path.clone();
backup_config(&backup_path)?;
edit_config_in_editor(&path)?;
Ok(())
}
fn handle_config_set(args: &ConfigSetArgs, path: &PathBuf) -> Result<()> {
let backup_path = path.clone();
backup_config(&backup_path)?;
let mut config: Config = Config::load(&path)?;
if let Some(val) = &args.endpoint {
config.endpoint = val.to_string();
}
if let Some(val) = &args.backet {
config.backet = val.to_string();
}
if let Some(val) = &args.part_size {
config.part_size = *val;
}
if let Some(val) = &args.local_directory_path {
config.local_directory_path = val.to_string();
}
config.save(&path)?;
println!("Config updated.");
Ok(())
}
fn handle_config_dir_add(args: &DirectoryAddArgs, path: &PathBuf) -> Result<()> {
let backup_path = path.clone();
backup_config(&backup_path)?;
let mut config: Config = Config::load(&path)?;
if config
.directory_struct
.iter()
.any(|entry| entry.prefix_file == args.prefix_file)
{
println!("Entry with this prefix_file already exists.");
} else {
config.directory_struct.push(DirectoryEntry {
prefix_file: args.prefix_file.to_string(),
cloud_dir: args.cloud_dir.to_string(),
});
config.save(&path)?;
println!("Directory entry added.");
}
Ok(())
}
fn handle_config_dir_rm(args: &DirectoryRemoveArgs, path: &PathBuf) -> Result<()> {
let backup_path = path.clone();
backup_config(&backup_path)?;
let mut config: Config = Config::load(&path)?;
let old_len = config.directory_struct.len();
config
.directory_struct
.retain(|entry| entry.prefix_file != args.prefix_file);
if config.directory_struct.len() < old_len {
config.save(&path)?;
println!("Directory entry removed.");
} else {
println!("No entry with such prefix_file found.");
}
Ok(())
}
pub fn run(cmd: ConfigCommand) -> Result<String> {
let path = config_path();
ensure_config_exists(&path)?;
match cmd {
ConfigCommand::Show => handle_config_show(&path)?,
ConfigCommand::Edit => handle_config_edit(&path)?,
ConfigCommand::Set(args) => handle_config_set(&args, &path)?,
ConfigCommand::DirAdd(args) => handle_config_dir_add(&args, &path)?,
ConfigCommand::DirRm(args) => handle_config_dir_rm(&args, &path)?,
}
Ok("".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
fn test_config_path(tmpdir: &tempfile::TempDir) -> PathBuf {
tmpdir.path().join("config.yml")
}
fn write_embedded_default(path: &PathBuf) {
let bytes = Asset::get("config.yml").expect("No embedded config").data;
fs::write(path, bytes).unwrap();
}
#[test]
fn test_ensure_config_exists_creates_file() {
let dir = tempdir().unwrap();
let path = test_config_path(&dir);
assert!(!path.exists());
ensure_config_exists(&path).unwrap();
assert!(path.exists());
fs::write(&path, b"test").unwrap();
ensure_config_exists(&path).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert_eq!(content, "test");
}
#[test]
fn test_handle_config_set_updates_fields() {
let dir = tempdir().unwrap();
let path = test_config_path(&dir);
write_embedded_default(&path);
let args = ConfigSetArgs {
endpoint: Some("custom-endpoint".into()),
backet: None,
part_size: Some(4242),
local_directory_path: None,
};
handle_config_set(&args, &path).unwrap();
let updated: Config = {
let s = fs::read_to_string(&path).unwrap();
serde_yaml::from_str(&s).unwrap()
};
assert_eq!(updated.endpoint, "custom-endpoint");
assert_eq!(updated.part_size, 4242);
}
#[test]
fn test_handle_config_dir_add_and_rm() {
let dir = tempdir().unwrap();
let path = test_config_path(&dir);
write_embedded_default(&path);
let initial_config: Config = {
let s = fs::read_to_string(&path).unwrap();
serde_yaml::from_str(&s).unwrap()
};
let initial_len = initial_config.directory_struct.len();
let add_args = DirectoryAddArgs {
prefix_file: "prefix_test".into(),
cloud_dir: "cloud_dir_test".into(),
};
handle_config_dir_add(&add_args, &path).unwrap();
let config: Config = {
let s = fs::read_to_string(&path).unwrap();
serde_yaml::from_str(&s).unwrap()
};
assert_eq!(config.directory_struct.len(), initial_len + 1);
assert!(
config
.directory_struct
.iter()
.any(|d| d.prefix_file == "prefix_test")
);
let rm_args = DirectoryRemoveArgs {
prefix_file: "prefix_test".into(),
};
handle_config_dir_rm(&rm_args, &path).unwrap();
let config: Config = {
let s = fs::read_to_string(&path).unwrap();
serde_yaml::from_str(&s).unwrap()
};
assert_eq!(config.directory_struct.len(), initial_len);
}
}