use std::collections::HashMap;
use async_trait::async_trait;
use std::sync::{Arc, Mutex, OnceLock};
pub mod device_code;
pub mod github_copilot;
#[derive(Debug, Clone)]
pub struct OAuthCredentials {
pub access: String,
pub refresh: String,
pub expires: i64, pub enterprise_url: Option<String>,
pub extra: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct DeviceCodeInfo {
pub user_code: String,
pub verification_uri: String,
pub interval_seconds: Option<u32>,
pub expires_in_seconds: Option<u32>,
}
#[derive(Debug, Clone)]
pub enum OAuthPrompt {
Text {
message: String,
placeholder: Option<String>,
allow_empty: bool,
},
}
pub struct OAuthLoginCallbacks<'a> {
pub on_device_code: Box<dyn FnMut(DeviceCodeInfo) + Send + 'a>,
pub on_prompt: Box<dyn FnMut(OAuthPrompt) -> Result<String, String> + Send + 'a>,
pub on_progress: Box<dyn FnMut(String) + Send + 'a>,
pub signal: Option<tokio_util::sync::CancellationToken>,
}
#[async_trait]
pub trait OAuthProvider: Send + Sync {
fn id(&self) -> &str;
fn name(&self) -> &str;
async fn login(
&self,
callbacks: &mut OAuthLoginCallbacks<'_>,
) -> Result<OAuthCredentials, String>;
async fn refresh_token(
&self,
credentials: &OAuthCredentials,
) -> Result<OAuthCredentials, String>;
fn get_api_key<'a>(&self, credentials: &'a OAuthCredentials) -> &'a str;
}
static BUILT_IN_PROVIDERS: &[&str] = &["github-copilot"];
static REGISTRY: OnceLock<Mutex<HashMap<String, Arc<dyn OAuthProvider>>>> = OnceLock::new();
fn registry() -> &'static Mutex<HashMap<String, Arc<dyn OAuthProvider>>> {
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn register(provider: Arc<dyn OAuthProvider>) {
registry()
.lock()
.unwrap()
.insert(provider.id().to_string(), provider);
}
pub fn get(id: &str) -> Option<Arc<dyn OAuthProvider>> {
registry().lock().unwrap().get(id).cloned()
}
pub fn list_ids() -> Vec<String> {
registry().lock().unwrap().keys().cloned().collect()
}
pub fn is_built_in(id: &str) -> bool {
BUILT_IN_PROVIDERS.contains(&id)
}
pub fn register_builtins() {
static INIT: std::sync::Once = std::sync::Once::new();
INIT.call_once(|| {
let gh = crate::provider::oauth::github_copilot::GitHubCopilotOAuth;
register(Arc::new(gh));
});
}