vibe_workspace/uri/
handler.rs1use anyhow::Result;
2use async_trait::async_trait;
3use std::sync::Arc;
4
5use crate::git::{CloneCommand, GitConfig, SearchCommand};
6use crate::uri::{parse_vibe_uri, VibeUri};
7use crate::workspace::manager::WorkspaceManager;
8
9#[async_trait]
10pub trait UriHandler: Send + Sync {
11 fn can_handle(&self, uri: &VibeUri) -> bool;
12 async fn handle(&self, uri: &VibeUri) -> Result<()>;
13}
14
15pub struct UriRouter {
16 handlers: Vec<Box<dyn UriHandler>>,
17}
18
19impl UriRouter {
20 pub fn new() -> Self {
21 Self {
22 handlers: Vec::new(),
23 }
24 }
25
26 pub fn add_handler(&mut self, handler: Box<dyn UriHandler>) {
27 self.handlers.push(handler);
28 }
29
30 pub async fn handle_uri(&self, uri_str: &str) -> Result<()> {
31 let uri = parse_vibe_uri(uri_str)?;
32
33 for handler in &self.handlers {
34 if handler.can_handle(&uri) {
35 return handler.handle(&uri).await;
36 }
37 }
38
39 anyhow::bail!("No handler found for URI: {}", uri_str)
40 }
41}
42
43pub struct GitHubUriHandler {
45 workspace_manager: Arc<tokio::sync::Mutex<WorkspaceManager>>,
46 git_config: GitConfig,
47}
48
49impl GitHubUriHandler {
50 pub fn new(
51 workspace_manager: Arc<tokio::sync::Mutex<WorkspaceManager>>,
52 git_config: GitConfig,
53 ) -> Self {
54 Self {
55 workspace_manager,
56 git_config,
57 }
58 }
59}
60
61#[async_trait]
62impl UriHandler for GitHubUriHandler {
63 fn can_handle(&self, uri: &VibeUri) -> bool {
64 uri.action == "github"
65 }
66
67 async fn handle(&self, uri: &VibeUri) -> Result<()> {
68 match uri.command.as_str() {
69 "install" => {
70 if let Some(path) = uri.params.get("path") {
71 let url = format!("https://github.com/{path}");
72 let mut manager = self.workspace_manager.lock().await;
73 CloneCommand::execute(url, None, false, false, &mut manager, &self.git_config)
74 .await?;
75 } else {
76 anyhow::bail!("Missing repository path in URI");
77 }
78 }
79 "search" => {
80 let mut manager = self.workspace_manager.lock().await;
82 SearchCommand::execute_interactive(&mut manager, &self.git_config).await?;
83 }
84 _ => anyhow::bail!("Unknown GitHub command: {}", uri.command),
85 }
86
87 Ok(())
88 }
89}
90
91pub fn register_uri_scheme(scheme: &str) -> Result<()> {
93 #[cfg(target_os = "macos")]
94 {
95 macos::register_uri_scheme(scheme)
96 }
97
98 #[cfg(target_os = "linux")]
99 {
100 linux::register_uri_scheme(scheme)
101 }
102
103 #[cfg(target_os = "windows")]
104 {
105 windows::register_uri_scheme(scheme)
106 }
107
108 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
109 {
110 anyhow::bail!("URI scheme registration not supported on this platform")
111 }
112}
113
114#[cfg(target_os = "macos")]
116mod macos {
117 use anyhow::Result;
118
119 pub fn register_uri_scheme(scheme: &str) -> Result<()> {
120 eprintln!("To register the '{scheme}' URI scheme on macOS:");
125 eprintln!("1. Add the following to your Info.plist:");
126 eprintln!(" <key>CFBundleURLTypes</key>");
127 eprintln!(" <array>");
128 eprintln!(" <dict>");
129 eprintln!(" <key>CFBundleURLSchemes</key>");
130 eprintln!(" <array>");
131 eprintln!(" <string>{scheme}</string>");
132 eprintln!(" </array>");
133 eprintln!(" </dict>");
134 eprintln!(" </array>");
135 eprintln!("2. Rebuild and reinstall the application");
136
137 Ok(())
138 }
139}
140
141#[cfg(target_os = "linux")]
143mod linux {
144 use anyhow::Result;
145
146 pub fn register_uri_scheme(scheme: &str) -> Result<()> {
147 eprintln!(
149 "Linux URI scheme registration not yet implemented for '{}'",
150 scheme
151 );
152 Ok(())
153 }
154}
155
156#[cfg(target_os = "windows")]
158mod windows {
159 use anyhow::Result;
160
161 pub fn register_uri_scheme(scheme: &str) -> Result<()> {
162 eprintln!(
164 "Windows URI scheme registration not yet implemented for '{}'",
165 scheme
166 );
167 Ok(())
168 }
169}