Skip to main content

a3s_boot/routing/route/
execution.rs

1use super::definition::RouteDefinition;
2use crate::routing::path::{join_paths, route_shape_key, route_specificity};
3use crate::{BootError, BootRequest, BootResponse, ExecutionContext, MiddlewareOutcome, Result};
4
5impl RouteDefinition {
6    pub async fn call(&self, mut request: BootRequest) -> Result<BootResponse> {
7        if !self.method.matches(request.method) {
8            let message = format!("{} {}", request.method.as_str(), request.path);
9            return self
10                .handle_error(
11                    self.execution_context(request),
12                    BootError::MethodNotAllowed(message),
13                )
14                .await;
15        }
16
17        let params = match self.path_params(&request.path) {
18            Ok(Some(params)) => params,
19            Ok(None) => {
20                let message = format!("{} {}", request.method.as_str(), request.path);
21                return self
22                    .handle_error(
23                        self.execution_context(request),
24                        BootError::NotFound(message),
25                    )
26                    .await;
27            }
28            Err(error) => {
29                return self
30                    .handle_error(self.execution_context(request), error)
31                    .await;
32            }
33        };
34        request = request.with_path_params(params);
35        let host_params = match self.host_params(request.host()) {
36            Ok(Some(params)) => params,
37            Ok(None) => {
38                let message = format!("{} {}", request.method.as_str(), request.path);
39                return self
40                    .handle_error(
41                        self.execution_context(request),
42                        BootError::NotFound(message),
43                    )
44                    .await;
45            }
46            Err(error) => {
47                return self
48                    .handle_error(self.execution_context(request), error)
49                    .await;
50            }
51        };
52        request = request.with_host_params(host_params);
53        if let Some(module_ref) = &self.module_ref {
54            request = request.with_module_ref(module_ref.request_scope());
55        }
56
57        #[cfg(feature = "request-context")]
58        {
59            let context = crate::RequestContext::from_route_request(
60                &request,
61                self.path.clone(),
62                self.module_name.clone(),
63                self.controller_prefix.clone(),
64                self.metadata.clone(),
65            );
66            return crate::RequestContext::scope(context, self.call_pipeline(request)).await;
67        }
68
69        #[cfg(not(feature = "request-context"))]
70        {
71            self.call_pipeline(request).await
72        }
73    }
74
75    async fn call_pipeline(&self, mut request: BootRequest) -> Result<BootResponse> {
76        for middleware in &self.middleware {
77            let context_request = request.clone();
78            request = match middleware.handle(request).await {
79                Ok(MiddlewareOutcome::Continue(request)) => request,
80                Ok(MiddlewareOutcome::Respond(response)) => return Ok(response),
81                Err(error) => {
82                    return self
83                        .handle_error(self.execution_context(context_request), error)
84                        .await;
85                }
86            };
87        }
88
89        let context = self.execution_context(request.clone());
90
91        for guard in &self.guards {
92            let can_activate = match guard.inner().can_activate(context.clone()).await {
93                Ok(can_activate) => can_activate,
94                Err(error) => return self.handle_error(context.clone(), error).await,
95            };
96
97            if !can_activate {
98                let message = format!("{} {}", context.method.as_str(), context.request_path);
99                return self
100                    .handle_error(context, BootError::Forbidden(message))
101                    .await;
102            }
103        }
104
105        for (index, interceptor) in self.interceptors.iter().enumerate() {
106            if let Err(error) = interceptor.inner().before(context.clone()).await {
107                return self.handle_error(context.clone(), error).await;
108            }
109
110            let mut response = match interceptor.inner().short_circuit(context.clone()).await {
111                Ok(Some(response)) => response,
112                Ok(None) => continue,
113                Err(error) => return self.handle_error(context.clone(), error).await,
114            };
115
116            for interceptor in self.interceptors[..index].iter().rev() {
117                response = match interceptor.inner().after(context.clone(), response).await {
118                    Ok(response) => response,
119                    Err(error) => return self.handle_error(context.clone(), error).await,
120                };
121            }
122
123            return Ok(response);
124        }
125
126        for pipe in &self.pipes {
127            let context_request = request.clone();
128            request = match pipe.inner().transform(request).await {
129                Ok(request) => request,
130                Err(error) => {
131                    return self
132                        .handle_error(self.execution_context(context_request), error)
133                        .await;
134                }
135            };
136        }
137
138        if self.validation_enabled {
139            for validator in &self.validators {
140                let context_request = request.clone();
141                request = match validator(request, self.validation_options) {
142                    Ok(request) => request,
143                    Err(error) => {
144                        return self
145                            .handle_error(self.execution_context(context_request), error)
146                            .await;
147                    }
148                };
149            }
150        }
151
152        let mut response = match self.handler.call(request).await {
153            Ok(response) => response,
154            Err(error) => return self.handle_error(context, error).await,
155        };
156
157        for interceptor in self.interceptors.iter().rev() {
158            response = match interceptor.inner().after(context.clone(), response).await {
159                Ok(response) => response,
160                Err(error) => return self.handle_error(context.clone(), error).await,
161            };
162        }
163
164        Ok(response)
165    }
166
167    /// Dispatch a request through this route and convert unhandled errors into Boot HTTP responses.
168    pub async fn handle(&self, request: BootRequest) -> BootResponse {
169        match self.call(request).await {
170            Ok(response) => response,
171            Err(error) => BootResponse::from_error(&error),
172        }
173    }
174
175    pub(crate) fn matches_path_shape(&self, path: &str) -> bool {
176        self.matches_path(path)
177    }
178
179    pub(crate) fn path_shape_key(&self) -> String {
180        route_shape_key(&self.path)
181    }
182
183    pub(crate) fn path_specificity(&self) -> Vec<u8> {
184        route_specificity(&self.path)
185    }
186
187    fn execution_context(&self, request: BootRequest) -> ExecutionContext {
188        ExecutionContext::new(
189            request,
190            self.path.clone(),
191            self.module_name.clone(),
192            self.controller_prefix.clone(),
193            self.serialization.clone(),
194            self.metadata.clone(),
195        )
196    }
197
198    pub(crate) fn with_prefix(mut self, prefix: &str) -> Result<Self> {
199        self.path = join_paths(prefix, &self.path)?;
200        self.controller_prefix = Some(prefix.trim_end_matches('/').to_string());
201        Ok(self)
202    }
203
204    pub(crate) fn with_path_prefix(mut self, prefix: &str) -> Result<Self> {
205        self.path = join_paths(prefix, &self.path)?;
206        Ok(self)
207    }
208
209    pub(crate) fn with_module_name(mut self, module_name: &str) -> Self {
210        self.module_name = Some(module_name.to_string());
211        self
212    }
213
214    pub(crate) fn with_module_ref(mut self, module_ref: crate::ModuleRef) -> Self {
215        self.module_ref = Some(module_ref);
216        self
217    }
218
219    pub(crate) fn with_default_module_ref(mut self, module_ref: crate::ModuleRef) -> Self {
220        if self.module_ref.is_none() {
221            self.module_ref = Some(module_ref);
222        }
223        self
224    }
225
226    async fn handle_error(
227        &self,
228        context: ExecutionContext,
229        error: BootError,
230    ) -> Result<BootResponse> {
231        for filter in self.filters.iter().rev() {
232            if let Some(response) = filter
233                .inner()
234                .catch(context.clone(), error.clone_for_filter())
235                .await?
236            {
237                return Ok(response);
238            }
239        }
240        Err(error)
241    }
242}