bitwarden_core/client/from_client_part.rs
1//! Traits for extracting dependencies from a Client.
2//!
3//! This module provides:
4//! - [`FromClientPart`] trait which enables uniform extraction of dependencies from a [`Client`],
5//! facilitating macro-based generation of `from_client` methods for feature clients.
6//! - [`FromClient`] trait which can be derived using `#[derive(FromClient)]` from the
7//! `bitwarden_core_macro` crate to automatically implement `from_client`.
8
9use std::sync::Arc;
10
11use bitwarden_crypto::KeyStore;
12#[cfg(feature = "internal")]
13use bitwarden_state::repository::{Repository, RepositoryItem};
14
15use super::Client;
16use crate::{client::ApiConfigurations, key_management::KeySlotIds};
17
18/// Trait for types that can be constructed from a [`Client`].
19///
20/// This trait is typically derived using `#[derive(FromClient)]` from
21/// the `bitwarden_core_macro` crate, which generates the implementation
22/// by extracting all struct fields from the Client using [`FromClientPart`].
23///
24/// # Example
25///
26/// ```ignore
27/// use bitwarden_core::client::FromClient;
28/// use bitwarden_core_macro::FromClient;
29///
30/// #[derive(FromClient)]
31/// pub struct FoldersClient {
32/// key_store: KeyStore<KeySlotIds>,
33/// api_configurations: Arc<ApiConfigurations>,
34/// repository: Option<Arc<dyn Repository<Folder>>>,
35/// }
36///
37/// // Usage:
38/// let folders_client = FoldersClient::from_client(&client);
39/// ```
40pub trait FromClient: Sized {
41 /// Construct this type from a [`Client`] reference.
42 fn from_client(client: &Client) -> Self;
43}
44
45/// Trait for extracting parts/dependencies from a [`Client`].
46///
47/// Implemented by [`Client`] for each dependency type that can be extracted. Used internally by
48/// `#[derive(FromClient)]` - users should derive [`FromClient`] rather than using this trait
49/// directly.
50pub trait FromClientPart<T> {
51 /// Extract a dependency of type `T` from self.
52 fn get_part(&self) -> T;
53}
54
55/// Trait for trait objects that can be constructed from a [`Client`] as an [`Arc`].
56///
57/// Implementing this on `dyn FooTrait` makes `Arc<dyn FooTrait>` extractable via
58/// [`FromClientPart`], so a struct holding another feature client by trait object gets
59/// wired up by `#[derive(FromClient)]`:
60///
61/// ```ignore
62/// #[derive(FromClient)]
63/// struct OuterClient {
64/// inner: Arc<dyn FooTrait>,
65/// }
66/// ```
67///
68/// The impl is typically generated by `#[client_trait(via = ...)]` from the
69/// `bitwarden_core_macro` crate. Write one by hand only when the construction shape is unusual.
70///
71/// # Why this trait exists
72///
73/// Orphan-rule workaround. `impl FromClientPart<Arc<dyn FooTrait>> for Client` would be the
74/// natural way to wire this up, but Rust rejects it from any crate other than `bitwarden-core`:
75/// both [`FromClientPart`] and [`Client`] are foreign there, and `Arc<_>` isn't a fundamental
76/// wrapper. `impl FromClientShared for dyn FooTrait` works because `dyn FooTrait` is a local
77/// type; the blanket [`FromClientPart`] impl on [`Client`] bridges the two.
78pub trait FromClientShared {
79 /// Construct an `Arc<Self>` from a [`Client`].
80 fn from_client_shared(client: &Client) -> Arc<Self>;
81}
82
83impl<T: ?Sized + FromClientShared> FromClientPart<Arc<T>> for Client {
84 fn get_part(&self) -> Arc<T> {
85 T::from_client_shared(self)
86 }
87}
88
89impl FromClientPart<KeyStore<KeySlotIds>> for Client {
90 fn get_part(&self) -> KeyStore<KeySlotIds> {
91 self.internal.get_key_store().clone()
92 }
93}
94
95impl FromClientPart<Arc<ApiConfigurations>> for Client {
96 fn get_part(&self) -> Arc<ApiConfigurations> {
97 self.internal.get_api_configurations()
98 }
99}
100
101#[cfg(feature = "internal")]
102impl FromClientPart<reqwest::Client> for Client {
103 fn get_part(&self) -> reqwest::Client {
104 self.internal.get_http_client().clone()
105 }
106}
107
108#[cfg(feature = "internal")]
109impl<T: RepositoryItem> FromClientPart<Option<Arc<dyn Repository<T>>>> for Client {
110 fn get_part(&self) -> Option<Arc<dyn Repository<T>>> {
111 self.platform().state().get::<T>().ok()
112 }
113}