reqwest/config.rs
1//! The `config` module provides a generic mechanism for loading and managing
2//! request-scoped configuration.
3//!
4//! # Design Overview
5//!
6//! This module is centered around two abstractions:
7//!
8//! - The [`RequestConfigValue`] trait, used to associate a config key type with its value type.
9//! - The [`RequestConfig`] struct, which wraps an optional value of the type linked via [`RequestConfigValue`].
10//!
11//! Under the hood, the [`RequestConfig`] struct holds a single value for the associated config type.
12//! This value can be conveniently accessed, inserted, or mutated using [`http::Extensions`],
13//! enabling type-safe configuration storage and retrieval on a per-request basis.
14//!
15//! # Motivation
16//!
17//! The key design benefit is the ability to store multiple config types—potentially even with the same
18//! value type (e.g., [`Duration`])—without code duplication or ambiguity. By leveraging trait association,
19//! each config key is distinct at the type level, while code for storage and access remains totally generic.
20//!
21//! # Usage
22//!
23//! Implement [`RequestConfigValue`] for any marker type you wish to use as a config key,
24//! specifying the associated value type. Then use [`RequestConfig<T>`] in [`Extensions`]
25//! to set or retrieve config values for each key type in a uniform way.
26
27use std::any::type_name;
28use std::fmt::Debug;
29use std::time::Duration;
30
31use http::Extensions;
32
33/// This trait is empty and is only used to associate a configuration key type with its
34/// corresponding value type.
35pub(crate) trait RequestConfigValue: Copy + Clone + 'static {
36 type Value: Clone + Debug + Send + Sync + 'static;
37}
38
39/// RequestConfig carries a request-scoped configuration value.
40#[derive(Clone, Copy)]
41pub(crate) struct RequestConfig<T: RequestConfigValue>(Option<T::Value>);
42
43impl<T: RequestConfigValue> Default for RequestConfig<T> {
44 fn default() -> Self {
45 RequestConfig(None)
46 }
47}
48
49impl<T> RequestConfig<T>
50where
51 T: RequestConfigValue,
52{
53 pub(crate) fn new(v: Option<T::Value>) -> Self {
54 RequestConfig(v)
55 }
56
57 /// format request config value as struct field.
58 ///
59 /// We provide this API directly to avoid leak internal value to callers.
60 pub(crate) fn fmt_as_field(&self, f: &mut std::fmt::DebugStruct<'_, '_>) {
61 if let Some(v) = &self.0 {
62 f.field(type_name::<T>(), v);
63 }
64 }
65
66 /// Retrieve the value from the request-scoped configuration.
67 ///
68 /// If the request specifies a value, use that value; otherwise, attempt to retrieve it from the current instance (typically a client instance).
69 pub(crate) fn fetch<'client, 'request>(
70 &'client self,
71 ext: &'request Extensions,
72 ) -> Option<&'request T::Value>
73 where
74 'client: 'request,
75 {
76 ext.get::<RequestConfig<T>>()
77 .and_then(|v| v.0.as_ref())
78 .or(self.0.as_ref())
79 }
80
81 /// Retrieve the value from the request's Extensions.
82 pub(crate) fn get(ext: &Extensions) -> Option<&T::Value> {
83 ext.get::<RequestConfig<T>>().and_then(|v| v.0.as_ref())
84 }
85
86 /// Retrieve the mutable value from the request's Extensions.
87 pub(crate) fn get_mut(ext: &mut Extensions) -> &mut Option<T::Value> {
88 let cfg = ext.get_or_insert_default::<RequestConfig<T>>();
89 &mut cfg.0
90 }
91}
92
93// ================================
94//
95// The following sections are all configuration types
96// provided by reqwets.
97//
98// To add a new config:
99//
100// 1. create a new struct for the config key like `RequestTimeout`.
101// 2. implement `RequestConfigValue` for the struct, the `Value` is the config value's type.
102//
103// ================================
104
105#[derive(Clone, Copy)]
106pub(crate) struct RequestTimeout;
107
108impl RequestConfigValue for RequestTimeout {
109 type Value = Duration;
110}