Skip to main content

rama_ua/layer/emulate/
layer.rs

1use rama_core::Layer;
2use rama_http::HeaderName;
3
4use super::UserAgentSelectFallback;
5
6/// A layer that emulates a user agent profile.
7///
8/// See [`UserAgentEmulateService`] for more details.
9///
10/// This layer is used to emulate a user agent profile for a request.
11/// It makes use of a [`UserAgentProvider`] (`P`) to select a user agent profile.
12///
13/// [`UserAgentProvider`]: crate::layer::emulate::UserAgentProvider
14/// [`UserAgentEmulateService`]: crate::layer::emulate::UserAgentEmulateService
15#[derive(Debug, Clone)]
16pub struct UserAgentEmulateLayer<P> {
17    provider: P,
18    optional: bool,
19    try_auto_detect_user_agent: bool,
20    input_header_order: Option<HeaderName>,
21    select_fallback: Option<UserAgentSelectFallback>,
22}
23
24impl<P> UserAgentEmulateLayer<P> {
25    /// Create a new [`UserAgentEmulateLayer`] with the given provider.
26    pub fn new(provider: P) -> Self {
27        Self {
28            provider,
29            optional: false,
30            try_auto_detect_user_agent: false,
31            input_header_order: None,
32            select_fallback: None,
33        }
34    }
35
36    rama_utils::macros::generate_set_and_with! {
37        /// When no user agent profile was found it will
38        /// fail the request unless optional is true. In case of
39        /// the latter the service will do nothing.
40        pub fn is_optional(mut self, optional: bool) -> Self {
41            self.optional = optional;
42            self
43        }
44    }
45
46    rama_utils::macros::generate_set_and_with! {
47        /// If true, the layer will try to auto-detect the user agent from the request,
48        /// but only in case that info is not yet found in the context.
49        pub fn try_auto_detect_user_agent(mut self, try_auto_detect_user_agent: bool) -> Self {
50            self.try_auto_detect_user_agent = try_auto_detect_user_agent;
51            self
52        }
53    }
54
55    rama_utils::macros::generate_set_and_with! {
56        /// Define a header that if present is to contain a CSV header name list,
57        /// that allows you to define the desired header order for the (extra) headers
58        /// found in the input (http) request.
59        ///
60        /// Extra meaning any headers not considered a base header and already defined
61        /// by the (selected) User Agent Profile.
62        ///
63        /// This can be useful because your http client might not respect the header casing
64        /// and/or order of the headers taken together. Using this metadata allows you to
65        /// communicate this data through anyway. If however your http client does respect
66        /// casing and order, or you don't care about some of it, you might not need it.
67        pub fn input_header_order(mut self, name: Option<HeaderName>) -> Self {
68            self.input_header_order = name;
69            self
70        }
71    }
72
73    rama_utils::macros::generate_set_and_with! {
74        /// Choose what to do in case no profile could be selected
75        /// using the regular pre-conditions as specified by the provider.
76        pub fn select_fallback(mut self, fb: Option<UserAgentSelectFallback>) -> Self {
77            self.select_fallback = fb;
78            self
79        }
80    }
81}
82
83impl<S, P: Clone> Layer<S> for UserAgentEmulateLayer<P> {
84    type Service = super::UserAgentEmulateService<S, P>;
85
86    fn layer(&self, inner: S) -> Self::Service {
87        super::UserAgentEmulateService::new(inner, self.provider.clone())
88            .with_is_optional(self.optional)
89            .with_try_auto_detect_user_agent(self.try_auto_detect_user_agent)
90            .maybe_with_select_fallback(self.select_fallback)
91            .maybe_with_input_header_order(self.input_header_order.clone())
92    }
93
94    fn into_layer(self, inner: S) -> Self::Service {
95        super::UserAgentEmulateService::new(inner, self.provider)
96            .with_is_optional(self.optional)
97            .with_try_auto_detect_user_agent(self.try_auto_detect_user_agent)
98            .maybe_with_select_fallback(self.select_fallback)
99            .maybe_with_input_header_order(self.input_header_order)
100    }
101}