onepassword_async/
wrappers.rs1use crate::invoke;
2use onepassword_shared::types::{ClientConfig, Invocation, InvocationParameters, Item, Vault};
3use onepassword_sys::Error as FfiError;
4use secrecy::SecretString;
5use std::{ops::Deref, sync::Arc};
6
7type FfiResult<T> = Result<T, FfiError>;
8
9#[derive(Clone)]
10pub struct Client(Arc<ClientInner>);
11
12impl Deref for Client {
13 type Target = ClientInner;
14
15 fn deref(&self) -> &Self::Target {
16 &self.0
17 }
18}
19pub struct ClientInner {
20 pub(crate) id: u64,
21}
22
23impl Drop for ClientInner {
24 fn drop(&mut self) {
25 let id_str = self.id.to_string();
26 onepassword_sys::free_client(&id_str);
27 }
28}
29
30impl Client {
31 pub async fn new(config: ClientConfig) -> FfiResult<Client> {
32 let client = Arc::new(ClientInner {
33 id: Self::get_client_id(config).await?,
34 });
35
36 Ok(Client(client))
37 }
38
39 async fn get_client_id(config: ClientConfig) -> FfiResult<u64> {
40 onepassword_sys::validate_checksums();
41
42 let serialized_config = serde_json::to_string(&config).unwrap();
43 let id_buffer = onepassword_sys::get_client_id_buffer(&serialized_config).await?;
44
45 Ok(id_buffer.to_string().parse().unwrap())
46 }
47}
48
49impl Client {
50 pub async fn vaults(&self) -> FfiResult<Vec<VaultWrapper>> {
51 let vaults: Vec<Vault> = invoke(Invocation {
52 client_id: self.id,
53 parameters: InvocationParameters::VaultsList { _marker: () },
54 })
55 .await?;
56
57 let wrapped_vaults = vaults
58 .into_iter()
59 .map(|vault| VaultWrapper {
60 vault,
61 client: self.clone(),
62 })
63 .collect();
64
65 Ok(wrapped_vaults)
66 }
67
68 pub async fn get_vault_by_title(&self, title: &str) -> FfiResult<Option<VaultWrapper>> {
69 let vault = self.vaults().await?.into_iter().find(|v| v.title == title);
70 Ok(vault)
71 }
72}
73
74pub struct VaultWrapper {
75 pub vault: Vault,
76 client: Client,
77}
78
79impl std::fmt::Debug for VaultWrapper {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 f.debug_struct("VaultWrapper")
82 .field("vault", &self.vault)
83 .finish()
84 }
85}
86
87impl Deref for VaultWrapper {
88 type Target = Vault;
89
90 fn deref(&self) -> &Self::Target {
91 &self.vault
92 }
93}
94
95impl VaultWrapper {
96 pub async fn items(&self) -> FfiResult<Vec<ItemWrapper>> {
97 let items = invoke::<Vec<Item>>(Invocation {
98 client_id: self.client.id,
99 parameters: InvocationParameters::ItemsList {
100 vault_id: self.vault.id.clone(),
101 filters: vec![],
102 },
103 })
104 .await?;
105
106 let items = items
107 .into_iter()
108 .map(|item| ItemWrapper {
109 item,
110 client: self.client.clone(),
111 vault_id: self.vault.id.clone(),
112 })
113 .collect();
114
115 Ok(items)
116 }
117
118 pub async fn items_for_website(&self, website: &str) -> FfiResult<Vec<ItemWrapper>> {
119 let trim_protocol = !website.contains("://");
120 let items = self
121 .items()
122 .await?
123 .into_iter()
124 .filter(|it| {
125 it.websites.iter().any(|w| {
126 if trim_protocol {
127 w.url
128 .split_once("://")
129 .is_some_and(|(_, url)| website.starts_with(url))
130 } else {
131 website.starts_with(&w.url)
132 }
133 })
134 })
135 .collect();
136
137 Ok(items)
138 }
139}
140
141pub struct ItemWrapper {
142 pub item: Item,
143 client: Client,
144 vault_id: String,
145}
146
147impl std::fmt::Debug for ItemWrapper {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_struct("ItemWrapper")
150 .field("item", &self.item)
151 .field("vault_id", &self.vault_id)
152 .finish()
153 }
154}
155
156impl Deref for ItemWrapper {
157 type Target = Item;
158
159 fn deref(&self) -> &Self::Target {
160 &self.item
161 }
162}
163
164impl ItemWrapper {
165 fn construct_secret_ref(&self, field: &str) -> String {
166 format!("op://{}/{}/{field}", self.vault_id, self.item.id)
167 }
168}
169
170impl ItemWrapper {
171 pub async fn password(&self) -> FfiResult<Option<SecretString>> {
172 let secret_reference = self.construct_secret_ref("password");
173
174 let result = invoke::<SecretString>(Invocation {
175 client_id: self.client.id,
176 parameters: InvocationParameters::SecretsResolve { secret_reference },
177 })
178 .await;
179
180 match result {
181 Ok(secret) => Ok(Some(secret)),
182 Err(e) if e.code() == 133 => Ok(None),
183 Err(e) => Err(e),
184 }
185 }
186}