diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e512471 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/**/* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5e81e62..a5bb422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,6 +157,7 @@ dependencies = [ "serde", "serde_json", "shellexpand", + "winreg", ] [[package]] @@ -474,3 +475,13 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/Cargo.toml b/Cargo.toml index 7224e63..a7e42ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ dirs = "5.0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" shellexpand = "3.1.0" +[target.'cfg(windows)'.dependencies] +winreg = "0.52" diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..065bbeb --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,41 @@ +# Ionian Find + +Ionian Find는 딥링크를 사용하여 파일 탐색기를 여는 간단한 Rust 애플리케이션입니다. + +## 주요 기능 + +- **딥링크 등록:** `ionian-find://`와 같은 사용자 정의 딥링크를 시스템에 등록합니다. +- **경로 기반 파일 탐색기 열기:** 딥링크에 포함된 경로를 기반으로 파일 탐색기를 엽니다. 예를 들어, `ionian-find://C:/Users`는 `C:/Users` 폴더를 엽니다. + +## 설정 + +- base_path: 딥링크가 열 때 사용할 기본 경로를 설정할 수 있습니다. 이 경로는 딥링크에 포함된 경로와 결합되어 최종 경로를 생성합니다. +- explore: 딥링크를 통해 파일 탐색기를 열 때 사용할 명령어를 설정할 수 있습니다. 기본적으로 Windows에서는 `explorer`가, Linux에서는 `xdg-open`이 사용됩니다. + +## 사용법 + +1. **설치:** + ```bash + cargo install --path . + ``` + +2. **딥링크 등록:** + ```bash + ionian-find register + ``` + +3. **딥링크 사용:** + 브라우저나 다른 애플리케이션에서 `ionian-find://<경로>` 형식의 딥링크를 엽니다. + +## 기술 스택 + +- **Rust:** 핵심 애플리케이션 로직 +- **clap:** 명령줄 인수 파싱 +- **dirs:** 사용자 디렉토리 경로 확인 +- **serde/serde_json:** 설정 파일 직렬화/역직렬화 +- **shellexpand:** 셸 환경 변수 확장 + +## 플랫폼 지원 + +- Windows +- Linux diff --git a/src/main.rs b/src/main.rs index d65a833..20b9726 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,12 +60,17 @@ fn handle_open(path: &str) -> Result<(), String> { } full_path.push(relative_path); - println!("Opening the file explorer to: {:?}", full_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(full_path) + .arg(result) .output() .map_err(|e| format!("Failed to execute file explorer: {}", e))?; @@ -87,18 +92,18 @@ fn handle_setting(explorer: &Option, base_path: &Option) -> Resu // This is possible with the `defer` crate, but not in the standard library. // https://crates.io/crates/defer - let mut isSet = false; + let mut is_set = false; if let Some(explorer) = explorer { println!("Setting the file explorer to: {}", explorer); config.explorer = Some(explorer.clone()); - isSet = true; + 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()); - isSet = true; + is_set = true; } - if !isSet { + if !is_set { // If no settings were provided, print the current configuration println!("Current configuration:"); println!("Explorer: {:?}", config.explorer.unwrap_or("Not set".into())); diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 378414c..ef1400c 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,7 +1,55 @@ +use std::env; +use winreg::enums::*; +use winreg::RegKey; + +const PATH: &str = r"Software\Classes\ionian-find"; + pub fn handle_register() -> Result<(), String> { - unimplemented!("Register is not yet implemented for Windows."); + println!("Registering the deep link for Windows..."); + + let exe_path = env::current_exe() + .map_err(|e| format!("Error getting current executable path: {}", e))? + .to_str() + .ok_or("Invalid UTF-8 in executable path")? + .to_string(); + + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + let (key, disp) = hkcu.create_subkey(&PATH) + .map_err(|e| format!("Failed to create subkey: {}", e))?; + + match disp { + REG_CREATED_NEW_KEY => println!("Created new registry key"), + REG_OPENED_EXISTING_KEY => println!("Opened existing registry key"), + } + + key.set_value("", &"URL:ionian-find") + .map_err(|e| format!("Failed to set value: {}", e))?; + key.set_value("URL Protocol", &"") + .map_err(|e| format!("Failed to set value: {}", e))?; + + let (shell_key, _) = key.create_subkey("shell") + .map_err(|e| format!("Failed to create subkey: {}", e))?; + let (open_key, _) = shell_key.create_subkey("open") + .map_err(|e| format!("Failed to create subkey: {}", e))?; + let (command_key, _) = open_key.create_subkey("command") + .map_err(|e| format!("Failed to create subkey: {}", e))?; + + let command_value = format!(r#""{}" "open" "%1""#, exe_path); + command_key.set_value("", &command_value) + .map_err(|e| format!("Failed to set value: {}", e))?; + + println!("Deep link registered successfully!"); + Ok(()) } pub fn handle_unregister() -> Result<(), String> { - unimplemented!("Unregister is not yet implemented for Windows."); -} + println!("Unregistering the deep link for Windows..."); + + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + + hkcu.delete_subkey_all(PATH) + .map_err(|e| format!("Failed to delete subkey: {}", e))?; + + println!("Deep link unregistered successfully!"); + Ok(()) +} \ No newline at end of file