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}