payjp_client_core/
config.rs

1use std::fmt;
2use std::fmt::{Display, Formatter};
3
4
5use crate::RequestStrategy;
6
7/// This is meant for internal use when implementing compatible clients,
8/// so it may be more unstable with respect to semver.
9
10// The disclaimer above was written to justify the semver hazard of keeping all the fields here public.
11// This is not necessary, but writing accessors is tricky because configs using this
12// internally want to take ownership of each field to avoid unnecessary clones.
13#[derive(Clone)]
14pub struct SharedConfigBuilder {
15    /// The user-provided part of the user-agent we'll use.
16    pub app_info_str: Option<String>,
17    /// The default request strategy to use.,
18    pub request_strategy: Option<RequestStrategy>,
19    /// The secret key for authorizing requests.
20    pub secret: String,
21    /// The base URL to send requests to.
22    pub api_base: Option<String>,
23}
24
25impl SharedConfigBuilder {
26    /// Create a new `SharedConfigBuilder` with the given secret key.
27    pub fn new(secret: impl Into<String>) -> Self {
28        let secret = secret.into();
29
30        // some basic sanity checks
31        // TODO: maybe a full-blown type here rather than a warning?
32        if secret.trim() != secret || !secret.starts_with("sk_") {
33            tracing::warn!("suspiciously formatted secret key")
34        }
35
36        Self {
37            app_info_str: None,
38            request_strategy: None,
39            secret,
40            api_base: None,
41        }
42    }
43
44    /// Sets the default `RequestStrategy` used when making requests.
45    pub fn request_strategy(mut self, strategy: RequestStrategy) -> Self {
46        self.request_strategy = Some(strategy);
47        self
48    }
49
50    /// Create a new client pointed at a specific URL. This is useful for testing.
51    pub fn url(mut self, url: impl Into<String>) -> Self {
52        self.api_base = Some(url.into());
53        self
54    }
55
56    /// Set the application info for the client.
57    pub fn app_info(
58        mut self,
59        name: impl Into<String>,
60        version: Option<String>,
61        url: Option<String>,
62    ) -> Self {
63        self.app_info_str = Some(AppInfo { name: name.into(), url, version }.to_string());
64        self
65    }
66}
67
68struct AppInfo {
69    name: String,
70    url: Option<String>,
71    version: Option<String>,
72}
73
74impl Display for AppInfo {
75    /// Formats a plugin's 'App Info' into a string that can be added to the end of a User-Agent string.
76    ///
77    /// This formatting matches that of other libraries, and if changed then it should be changed everywhere.
78    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
79        let name = &self.name;
80        match (&self.version, &self.url) {
81            (Some(a), Some(b)) => write!(f, "{name}/{a} ({b})"),
82            (Some(a), None) => write!(f, "{name}/{a}"),
83            (None, Some(b)) => write!(f, "{name} ({b})"),
84            _ => f.write_str(name),
85        }
86    }
87}
88
89// Manual implementation so we don't print the secret!
90impl fmt::Debug for SharedConfigBuilder {
91    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92        let mut builder = f.debug_struct("SharedConfigBuilder");
93        builder.field("request_strategy", &self.request_strategy);
94        builder.field("app_info_str", &self.app_info_str);
95        if let Some(api_base) = &self.api_base {
96            builder.field("api_base", api_base);
97        }
98        builder.finish()
99    }
100}
101
102/// Per-request configuration overrides.
103#[derive(Debug)]
104#[non_exhaustive]
105pub struct ConfigOverride {
106    /// Use a particular `RequestStrategy`, instead of the client default.
107    pub request_strategy: Option<RequestStrategy>,
108}
109
110impl ConfigOverride {
111    pub(crate) fn new() -> Self {
112        Self { request_strategy: None }
113    }
114}