rust_ai/utils/
header.rs

1//!
2//! A module for manipulating additional HTTP headers especially when
3//! communicating with proxy servers.
4
5////////////////////////////////////////////////////////////////////////////////
6
7use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
8
9const ADDITIONAL_HEADERS_VAR: &'static str = "RUST_AI_ADDITIONAL_HEADERS";
10
11/// A type alias for (String, String), which denotes HTTP header/value pair.
12type RawHeader<'a> = (&'a str, &'a str);
13type Header = (String, String);
14
15/// This is the only interface to set/get runtime level headers when sending
16/// requests to endpoints such as OpenAI.
17///
18/// # Usage
19/// 1. Call `AdditionalHeaders::default()` to initialize an instance.
20/// 2. Call `set_header()` to set required headers.
21/// 3. Depends on the caller:
22///   - If you are using Rust-AI as an library, call `to_var()` to set related
23///     environment variable.
24///   - If you are calling from Rust-AI itself, you should call `provide()` to
25///     turn current instance into [`HeaderMap`].
26#[derive(serde::Serialize, serde::Deserialize, Debug)]
27pub struct AdditionalHeaders {
28    headers: Vec<Header>,
29}
30
31impl Default for AdditionalHeaders {
32    /// Create a new instance of [`AdditionalHeaders`] that doesn't contain any
33    /// headers.
34    fn default() -> Self {
35        Self { headers: vec![] }
36    }
37}
38
39impl AdditionalHeaders {
40    /// Try to initialize [`AdditionalHeaders`] instance from environment
41    /// variable. If no such environment variables available, then create an
42    /// empty instance.
43    pub fn from_var() -> Self {
44        if let Ok(c) = std::env::var(ADDITIONAL_HEADERS_VAR) {
45            if let Ok(deserialized) = serde_json::from_str::<Self>(&c) {
46                return deserialized;
47            }
48        }
49        Default::default()
50    }
51}
52
53impl AdditionalHeaders {
54    /// Set a new header pair. Header name and values should never include NUL
55    /// characters (`\0`).
56    pub fn set_header<'a>(&mut self, header: RawHeader<'a>) {
57        let header_name = header.0.to_string();
58        let header_value = header.1.to_string();
59        if header_name.contains("\0") || header_value.contains("\0") {
60            panic!("`\0` cannot present in any field of the header");
61        }
62        self.headers.push((header_name, header_value));
63    }
64
65    /// Turn the contained headers into [`HeaderMap`]. Will not consume current
66    /// instance.
67    pub fn provide(&self) -> HeaderMap {
68        let mut hm = HeaderMap::new();
69        self.headers.iter().for_each(|h| {
70            hm.append(
71                HeaderName::from_lowercase(h.0.to_lowercase().as_bytes()).unwrap(),
72                HeaderValue::from_str(&h.1).unwrap(),
73            );
74        });
75
76        hm
77    }
78
79    /// Try to serialize current instance into string form and set to specific
80    /// environment variable.
81    pub fn to_var(&self) {
82        let serialized = serde_json::to_string(self).unwrap();
83        std::env::set_var(ADDITIONAL_HEADERS_VAR, serialized);
84    }
85}