pdk_classy/
user_agent.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! User agent calculation and injection for HTTP calls.
6
7use crate::extract::FromContext;
8use std::cell::RefCell;
9use std::convert::Infallible;
10use std::rc::Rc;
11
12const PDK_AGENT: &str = "PDK-HttpClient";
13const PDK_VERSION: &str = env!("CARGO_PKG_VERSION");
14
15thread_local! {
16    /// The user agent value is the same for all the filters. We can safely cache it once read to avoid
17    /// re-computation.
18    static USER_AGENT: RefCell<Option<Rc<UserAgent>>> = const { RefCell::new(None) };
19}
20
21impl<C> FromContext<C> for Rc<UserAgent> {
22    type Error = Infallible;
23
24    fn from_context(_context: &C) -> Result<Self, Self::Error> {
25        let agent = USER_AGENT
26            .with(|cell| cell.borrow().clone())
27            .unwrap_or_default();
28
29        Ok(Rc::clone(&agent))
30    }
31}
32
33/// Set the user agent globally with the given user agent value.
34/// All HttpClients constructed after setting the user agent will use this value.
35pub fn set_user_agent(user_agent: UserAgent) {
36    USER_AGENT.with(|cell| cell.replace(Some(Rc::new(user_agent))));
37}
38
39/// The user agent value to be used in HTTP calls.
40pub struct UserAgent {
41    value: String,
42}
43
44impl Default for UserAgent {
45    fn default() -> Self {
46        UserAgent::new(PDK_AGENT, PDK_VERSION)
47    }
48}
49
50impl UserAgent {
51    /// Create a new user agent with the format {key}/{version}.
52    pub fn new(key: &str, version: &str) -> Self {
53        UserAgent {
54            value: format!("{key}/{version}"),
55        }
56    }
57
58    /// The value that will be used for the User-Agent header.
59    pub fn value(&self) -> &str {
60        self.value.as_str()
61    }
62}
63
64#[cfg(test)]
65mod test {
66    use crate::user_agent::UserAgent;
67
68    #[test]
69    fn default() {
70        assert_eq!(
71            UserAgent::default().value(),
72            &format!("PDK-HttpClient/{}", env!("CARGO_PKG_VERSION"))
73        );
74    }
75}