dbx_tools_auth/
bindings.rs1use crate::{CredentialStore, Error, Result, StorageLock, Token};
2use std::{sync::Arc, time::Duration};
3
4#[uniffi::export(with_foreign)]
5#[async_trait::async_trait]
6pub trait StorageAdapter: Send + Sync {
12 async fn load(&self, profile: String) -> BindingResult<Option<String>>;
14 async fn prepare_write(&self) -> BindingResult<()>;
16 async fn save(&self, profile: String, token: String) -> BindingResult<()>;
18 async fn remove(&self, profile: String) -> BindingResult<()>;
20 async fn acquire_lock(&self, profile: String, timeout_millis: u64) -> BindingResult<String>;
22 async fn release_lock(&self, lease: String) -> BindingResult<()>;
24 fn name(&self) -> String;
26}
27
28#[derive(Clone, uniffi::Record)]
30pub struct AccessToken {
31 pub access_token: String,
32 pub token_type: String,
33 pub expiry: Option<String>,
34 pub scopes: Vec<String>,
35}
36
37#[derive(Debug, thiserror::Error, uniffi::Error)]
38pub enum AuthError {
39 #[error("{message}")]
40 Failure { message: String },
41}
42
43impl From<uniffi::UnexpectedUniFFICallbackError> for AuthError {
44 fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self {
45 Self::Failure {
46 message: error.to_string(),
47 }
48 }
49}
50
51pub type BindingResult<T> = std::result::Result<T, AuthError>;
52
53pub struct ForeignStore {
54 pub storage: Arc<dyn StorageAdapter>,
55}
56
57#[derive(uniffi::Object)]
58pub struct StorageHandle {
60 pub store: Arc<dyn CredentialStore>,
61}
62
63#[uniffi::export]
64pub fn create_storage_handle(storage: Arc<dyn StorageAdapter>) -> Arc<StorageHandle> {
66 Arc::new(StorageHandle {
67 store: Arc::new(ForeignStore { storage }),
68 })
69}
70
71struct ForeignLock {
72 storage: Arc<dyn StorageAdapter>,
73 lease: String,
74}
75
76#[async_trait::async_trait]
77impl StorageLock for ForeignLock {
78 async fn release(self: Box<Self>) -> Result<()> {
79 self.storage
80 .release_lock(self.lease)
81 .await
82 .map_err(|error| Error::Storage(error.to_string()))
83 }
84}
85
86#[async_trait::async_trait]
87impl CredentialStore for ForeignStore {
88 async fn load(&self, profile: &str) -> Result<Option<Token>> {
89 self.storage
90 .load(profile.to_owned())
91 .await
92 .map_err(|error| Error::Storage(error.to_string()))?
93 .map(|token| serde_json::from_str(&token).map_err(Into::into))
94 .transpose()
95 }
96
97 async fn prepare_write(&self) -> Result<()> {
98 self.storage
99 .prepare_write()
100 .await
101 .map_err(|error| Error::Storage(error.to_string()))
102 }
103
104 async fn save(&self, profile: &str, token: &Token) -> Result<()> {
105 self.storage
106 .save(profile.to_owned(), serde_json::to_string(token)?)
107 .await
108 .map_err(|error| Error::Storage(error.to_string()))
109 }
110
111 async fn delete(&self, profile: &str) -> Result<()> {
112 self.storage
113 .remove(profile.to_owned())
114 .await
115 .map_err(|error| Error::Storage(error.to_string()))
116 }
117
118 async fn lock(&self, profile: &str, timeout: Duration) -> Result<Box<dyn StorageLock>> {
119 let timeout_millis = u64::try_from(timeout.as_millis()).unwrap_or(u64::MAX);
120 let lease = self
121 .storage
122 .acquire_lock(profile.to_owned(), timeout_millis)
123 .await
124 .map_err(|error| Error::Storage(error.to_string()))?;
125 Ok(Box::new(ForeignLock {
126 storage: Arc::clone(&self.storage),
127 lease,
128 }))
129 }
130
131 fn name(&self) -> &'static str {
132 "custom"
133 }
134}
135
136impl From<Token> for AccessToken {
137 fn from(token: Token) -> Self {
138 Self {
139 access_token: token.access_token,
140 token_type: token.token_type,
141 expiry: token.expires_at.map(|value| value.to_string()),
142 scopes: token.scopes,
143 }
144 }
145}