1use std::{num::NonZeroUsize, time::Duration};
6
7use reqwest::header::{CONTENT_TYPE, HeaderValue};
8
9use crate::{PrivyCreateError, generated::Client, get_auth_header, jwt_exchange::JwtExchange};
10
11const DEFAULT_BASE_URL: &str = "https://api.privy.io";
12const APP_ID_ENV_VAR: &str = "PRIVY_APP_ID";
13const APP_SECRET_ENV_VAR: &str = "PRIVY_APP_SECRET";
14const BASE_URL_ENV_VAR: &str = "PRIVY_BASE_URL";
15
16#[derive(Clone, Debug)]
27pub struct PrivyClient {
28 pub(crate) app_id: String,
29 #[allow(dead_code)]
30 pub(crate) app_secret: String,
31 pub(crate) base_url: String,
32 pub(crate) client: Client,
33
34 pub jwt_exchange: JwtExchange,
36}
37
38pub struct PrivyClientOptions {
40 pub cache_size: NonZeroUsize,
42 pub base_url: String,
44}
45
46impl Default for PrivyClientOptions {
47 fn default() -> Self {
48 Self {
49 cache_size: NonZeroUsize::new(1000).expect("non-zero"),
50 base_url: String::from(DEFAULT_BASE_URL),
51 }
52 }
53}
54
55impl PrivyClient {
56 pub fn new(app_id: String, app_secret: String) -> Result<Self, PrivyCreateError> {
73 Self::new_with_options(app_id, app_secret, PrivyClientOptions::default())
74 }
75
76 pub fn new_from_env() -> Result<Self, PrivyCreateError> {
83 let app_id = std::env::var(APP_ID_ENV_VAR).map_err(|_| PrivyCreateError::InvalidAppId)?;
84 let app_secret =
85 std::env::var(APP_SECRET_ENV_VAR).map_err(|_| PrivyCreateError::InvalidAppSecret)?;
86 Self::new_with_options(
87 app_id,
88 app_secret,
89 PrivyClientOptions {
90 base_url: std::env::var(BASE_URL_ENV_VAR)
91 .unwrap_or_else(|_| DEFAULT_BASE_URL.to_string()),
92 ..PrivyClientOptions::default()
93 },
94 )
95 }
96
97 pub fn new_with_options(
103 app_id: String,
104 app_secret: String,
105 options: PrivyClientOptions,
106 ) -> Result<Self, PrivyCreateError> {
107 let client_version = concat!("rust:", env!("CARGO_PKG_VERSION"));
108
109 tracing::debug!("Privy client version: {}", client_version);
110
111 let mut headers = reqwest::header::HeaderMap::new();
112 headers.insert(
113 reqwest::header::AUTHORIZATION,
114 HeaderValue::from_str(&get_auth_header(&app_id, &app_secret))?,
115 );
116 headers.insert("privy-app-id", HeaderValue::from_str(&app_id)?);
117 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
118 headers.insert("privy-client", HeaderValue::from_static(client_version));
119
120 let client_with_custom_defaults = reqwest::ClientBuilder::new()
121 .connect_timeout(Duration::from_secs(15))
122 .timeout(Duration::from_secs(15))
123 .default_headers(headers)
124 .build()?;
125
126 Ok(Self {
127 app_id,
128 app_secret,
129 client: Client::new_with_client(&options.base_url, client_with_custom_defaults),
130 base_url: options.base_url,
131 jwt_exchange: JwtExchange::new(options.cache_size),
132 })
133 }
134
135 #[must_use]
137 pub fn utils(&self) -> crate::utils::Utils {
138 crate::utils::Utils {
139 app_id: self.app_id.clone(),
140 }
141 }
142
143 pub fn app_id(&self) -> &str {
145 &self.app_id
146 }
147
148 pub fn base_url(&self) -> &str {
150 &self.base_url
151 }
152}