ionian-find/src/main.rs

116 lines
3.5 KiB
Rust

use clap::{Parser, Subcommand};
use std::path::PathBuf;
use std::process::Command;
mod platform;
mod config;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Register the deep link to execute this app.
Register,
/// Unregister the deep link.
Unregister,
/// Open a file explorer to the given path.
Open { path: String },
/// Set the file explorer and base path.
/// The explorer must be an executable in your PATH.
Setting {
#[arg(short, long)]
explorer: Option<String>,
#[arg(short, long)]
base_path: Option<String>,
},
}
fn main() {
let cli = Cli::parse();
let result = match &cli.command {
Commands::Register => platform::handle_register(),
Commands::Unregister => platform::handle_unregister(),
Commands::Open { path } => handle_open(path),
Commands::Setting { explorer, base_path } => handle_setting(explorer, base_path),
};
if let Err(e) = result {
eprintln!("Error: {}", e);
}
}
fn handle_open(path: &str) -> Result<(), String> {
let config = config::load_config().unwrap_or_else(|_| config::Config {
explorer: None,
base_path: None,
});
let path_without_scheme = path.strip_prefix("ionian-find:").unwrap_or(path);
let relative_path = path_without_scheme.trim_start_matches('/');
let mut full_path = PathBuf::new();
if let Some(base_path) = &config.base_path {
full_path.push(shellexpand::tilde(base_path).as_ref());
}
full_path.push(relative_path);
let result = full_path.canonicalize()
.expect("Failed to canonicalize path")
.to_str()
.ok_or("Failed to convert path to string")?
.to_string();
println!("Opening the file explorer to: {:?}", result);
let explorer = config.explorer.unwrap_or_else(|| "xdg-open".to_string());
Command::new(explorer)
.arg(result)
.output()
.map_err(|e| format!("Failed to execute file explorer: {}", e))?;
Ok(())
}
fn handle_setting(explorer: &Option<String>, base_path: &Option<String>) -> Result<(), String> {
let mut config = config::load_config().unwrap_or_else(|_| config::Config {
explorer: None,
base_path: None,
});
// config should be saved only if at least one setting is provided
// If no settings are provided, print the current configuration
// and do not save the config.
// TODO: config guard like ```rust
// defer {
// config::save_config(&config);
// }```
// This is possible with the `defer` crate, but not in the standard library.
// https://crates.io/crates/defer
let mut is_set = false;
if let Some(explorer) = explorer {
println!("Setting the file explorer to: {}", explorer);
config.explorer = Some(explorer.clone());
is_set = true;
}
if let Some(base_path) = base_path {
println!("Setting the base path to: {}", base_path);
config.base_path = Some(base_path.clone());
is_set = true;
}
if !is_set {
// If no settings were provided, print the current configuration
println!("Current configuration:");
println!("Explorer: {:?}", config.explorer.unwrap_or("Not set".into()));
println!("Base Path: {:?}", config.base_path.unwrap_or("Not set".into()));
return Ok(());
}
else {
config::save_config(&config)
}
}