116 lines
3.5 KiB
Rust
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)
|
|
}
|
|
}
|