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, Result};
4
5impl RouteDefinition {
6    pub async fn call(&self, mut request: BootRequest) -> Result<BootResponse> {
7        if request.method != self.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
36        for pipe in &self.pipes {
37            let context_request = request.clone();
38            request = match pipe.transform(request).await {
39                Ok(request) => request,
40                Err(error) => {
41                    return self
42                        .handle_error(self.execution_context(context_request), error)
43                        .await;
44                }
45            };
46        }
47
48        let context = self.execution_context(request.clone());
49
50        for guard in &self.guards {
51            let can_activate = match guard.can_activate(context.clone()).await {
52                Ok(can_activate) => can_activate,
53                Err(error) => return self.handle_error(context.clone(), error).await,
54            };
55
56            if !can_activate {
57                let message = format!("{} {}", context.method.as_str(), context.request_path);
58                return self
59                    .handle_error(context, BootError::Forbidden(message))
60                    .await;
61            }
62        }
63
64        for interceptor in &self.interceptors {
65            if let Err(error) = interceptor.before(context.clone()).await {
66                return self.handle_error(context.clone(), error).await;
67            }
68        }
69
70        let mut response = match self.handler.call(request).await {
71            Ok(response) => response,
72            Err(error) => return self.handle_error(context, error).await,
73        };
74
75        for interceptor in self.interceptors.iter().rev() {
76            response = match interceptor.after(context.clone(), response).await {
77                Ok(response) => response,
78                Err(error) => return self.handle_error(context.clone(), error).await,
79            };
80        }
81
82        Ok(response)
83    }
84
85    /// Dispatch a request through this route and convert unhandled errors into Boot HTTP responses.
86    pub async fn handle(&self, request: BootRequest) -> BootResponse {
87        match self.call(request).await {
88            Ok(response) => response,
89            Err(error) => BootResponse::from_error(&error),
90        }
91    }
92
93    pub(crate) fn matches_path_shape(&self, path: &str) -> bool {
94        self.matches_path(path)
95    }
96
97    pub(crate) fn path_shape_key(&self) -> String {
98        route_shape_key(&self.path)
99    }
100
101    pub(crate) fn path_specificity(&self) -> Vec<u8> {
102        route_specificity(&self.path)
103    }
104
105    fn execution_context(&self, request: BootRequest) -> ExecutionContext {
106        ExecutionContext::new(
107            request,
108            self.path.clone(),
109            self.module_name.clone(),
110            self.controller_prefix.clone(),
111        )
112    }
113
114    pub(crate) fn with_prefix(mut self, prefix: &str) -> Result<Self> {
115        self.path = join_paths(prefix, &self.path)?;
116        self.controller_prefix = Some(prefix.trim_end_matches('/').to_string());
117        Ok(self)
118    }
119
120    pub(crate) fn with_path_prefix(mut self, prefix: &str) -> Result<Self> {
121        self.path = join_paths(prefix, &self.path)?;
122        Ok(self)
123    }
124
125    pub(crate) fn with_module_name(mut self, module_name: &str) -> Self {
126        self.module_name = Some(module_name.to_string());
127        self
128    }
129
130    async fn handle_error(
131        &self,
132        context: ExecutionContext,
133        error: BootError,
134    ) -> Result<BootResponse> {
135        for filter in self.filters.iter().rev() {
136            if let Some(response) = filter
137                .catch(context.clone(), error.clone_for_filter())
138                .await?
139            {
140                return Ok(response);
141            }
142        }
143        Err(error)
144    }
145}