1use crate::{BootError, BootRequest, HttpMethod, Result, SerializationOptions};
2use serde::de::DeserializeOwned;
3use serde_json::Value;
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ExecutionProtocol {
9 Http,
10 WebSocket,
11 Transport,
12}
13
14impl ExecutionProtocol {
15 pub fn as_str(self) -> &'static str {
16 match self {
17 Self::Http => "http",
18 Self::WebSocket => "websocket",
19 Self::Transport => "transport",
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ExecutionTransportKind {
27 RequestResponse,
28 Event,
29}
30
31impl ExecutionTransportKind {
32 pub fn as_str(self) -> &'static str {
33 match self {
34 Self::RequestResponse => "request-response",
35 Self::Event => "event",
36 }
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct WebSocketExecutionContext {
43 pub gateway_path: String,
44 pub event: String,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct TransportExecutionContext {
50 pub pattern: String,
51 pub kind: ExecutionTransportKind,
52}
53
54#[derive(Debug, Clone)]
56pub struct ExecutionContext {
57 pub protocol: ExecutionProtocol,
58 pub method: HttpMethod,
59 pub request_path: String,
60 pub route_path: String,
61 pub module_name: Option<String>,
62 pub controller_prefix: Option<String>,
63 pub serialization: SerializationOptions,
64 pub metadata: BTreeMap<String, Value>,
65 pub request: BootRequest,
66 pub websocket: Option<WebSocketExecutionContext>,
67 pub transport: Option<TransportExecutionContext>,
68}
69
70impl ExecutionContext {
71 pub(crate) fn new(
72 request: BootRequest,
73 route_path: String,
74 module_name: Option<String>,
75 controller_prefix: Option<String>,
76 serialization: SerializationOptions,
77 metadata: BTreeMap<String, Value>,
78 ) -> Self {
79 Self {
80 protocol: ExecutionProtocol::Http,
81 method: request.method,
82 request_path: request.path.clone(),
83 route_path,
84 module_name,
85 controller_prefix,
86 serialization,
87 metadata,
88 request,
89 websocket: None,
90 transport: None,
91 }
92 }
93
94 pub(crate) fn websocket(
95 request: BootRequest,
96 gateway_path: String,
97 event: String,
98 module_name: Option<String>,
99 ) -> Self {
100 Self {
101 protocol: ExecutionProtocol::WebSocket,
102 method: request.method,
103 request_path: request.path.clone(),
104 route_path: gateway_path.clone(),
105 module_name,
106 controller_prefix: None,
107 serialization: SerializationOptions::default(),
108 metadata: BTreeMap::new(),
109 request,
110 websocket: Some(WebSocketExecutionContext {
111 gateway_path,
112 event,
113 }),
114 transport: None,
115 }
116 }
117
118 pub(crate) fn transport(
119 pattern: String,
120 kind: ExecutionTransportKind,
121 module_name: Option<String>,
122 ) -> Self {
123 Self {
124 protocol: ExecutionProtocol::Transport,
125 method: HttpMethod::Post,
126 request_path: pattern.clone(),
127 route_path: pattern.clone(),
128 module_name,
129 controller_prefix: None,
130 serialization: SerializationOptions::default(),
131 metadata: BTreeMap::new(),
132 request: BootRequest::new(HttpMethod::Post, "/__transport"),
133 websocket: None,
134 transport: Some(TransportExecutionContext { pattern, kind }),
135 }
136 }
137
138 pub fn protocol(&self) -> ExecutionProtocol {
139 self.protocol
140 }
141
142 pub fn websocket_context(&self) -> Option<&WebSocketExecutionContext> {
143 self.websocket.as_ref()
144 }
145
146 pub fn transport_context(&self) -> Option<&TransportExecutionContext> {
147 self.transport.as_ref()
148 }
149
150 pub fn metadata_value(&self, key: &str) -> Option<&Value> {
151 self.metadata.get(key)
152 }
153
154 pub fn metadata_as<T>(&self, key: &str) -> Result<Option<T>>
155 where
156 T: DeserializeOwned,
157 {
158 let Some(value) = self.metadata.get(key) else {
159 return Ok(None);
160 };
161
162 serde_json::from_value(value.clone())
163 .map(Some)
164 .map_err(|error| {
165 BootError::Internal(format!(
166 "failed to deserialize execution context metadata `{key}`: {error}"
167 ))
168 })
169 }
170}