1use std::sync::Arc;
2
3use async_trait::async_trait;
4use serde_json::Value;
5
6use crate::{
7 CodeModeExecuteRequest, ExecutionContext, GatewayExecuteRequest, GatewayPromptRoute,
8 GatewayReloadRequest, GatewayResourceRoute, GatewayRouteScope, GatewayToolRoute,
9 OpenApiExecuteRequest,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct PortError {
15 pub code: String,
17 pub message: String,
19 pub retryable: bool,
21 pub remediation: String,
23}
24
25impl PortError {
26 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
29 Self {
30 code: code.into(),
31 message: message.into(),
32 retryable: false,
33 remediation: "Check the engine configuration and retry.".to_owned(),
34 }
35 }
36}
37
38#[async_trait]
41pub trait GatewayPort: Send + Sync {
42 async fn status(&self, context: &ExecutionContext) -> Result<Value, PortError>;
44 async fn reload(
46 &self,
47 request: GatewayReloadRequest,
48 context: &ExecutionContext,
49 ) -> Result<Value, PortError>;
50 async fn execute(
52 &self,
53 request: GatewayExecuteRequest,
54 context: &ExecutionContext,
55 ) -> Result<Value, PortError>;
56
57 async fn list_mcp_tools(
59 &self,
60 scope: Option<&GatewayRouteScope>,
61 context: &ExecutionContext,
62 ) -> Result<Vec<GatewayToolRoute>, PortError>;
63
64 async fn call_mcp_tool(
66 &self,
67 name: &str,
68 params: Value,
69 scope: Option<&GatewayRouteScope>,
70 context: &ExecutionContext,
71 ) -> Result<Option<Value>, PortError>;
72
73 async fn list_mcp_resources(
75 &self,
76 scope: Option<&GatewayRouteScope>,
77 context: &ExecutionContext,
78 ) -> Result<Vec<GatewayResourceRoute>, PortError>;
79
80 async fn read_mcp_resource(
82 &self,
83 uri: &str,
84 scope: Option<&GatewayRouteScope>,
85 context: &ExecutionContext,
86 ) -> Result<Option<Value>, PortError>;
87
88 async fn list_mcp_prompts(
90 &self,
91 scope: Option<&GatewayRouteScope>,
92 context: &ExecutionContext,
93 ) -> Result<Vec<GatewayPromptRoute>, PortError>;
94
95 async fn get_mcp_prompt(
97 &self,
98 name: &str,
99 arguments: Option<serde_json::Map<String, Value>>,
100 scope: Option<&GatewayRouteScope>,
101 context: &ExecutionContext,
102 ) -> Result<Option<Value>, PortError>;
103}
104
105#[async_trait]
108pub trait CodeModePort: Send + Sync {
109 async fn execute(
111 &self,
112 request: CodeModeExecuteRequest,
113 context: &ExecutionContext,
114 ) -> Result<Value, PortError>;
115}
116
117#[async_trait]
119pub trait OpenApiPort: Send + Sync {
120 async fn execute(
122 &self,
123 request: OpenApiExecuteRequest,
124 context: &ExecutionContext,
125 ) -> Result<Value, PortError>;
126}
127
128pub struct ApplicationPorts {
130 pub gateway: Arc<dyn GatewayPort>,
132 pub codemode: Arc<dyn CodeModePort>,
134 pub openapi: Arc<dyn OpenApiPort>,
136}
137
138impl ApplicationPorts {
139 pub fn unavailable() -> Self {
141 let port = Arc::new(UnavailableEnginePort);
142 Self {
143 gateway: port.clone(),
144 codemode: port.clone(),
145 openapi: port,
146 }
147 }
148
149 pub fn with_gateway(mut self, gateway: Arc<dyn GatewayPort>) -> Self {
151 self.gateway = gateway;
152 self
153 }
154
155 pub fn with_codemode(mut self, codemode: Arc<dyn CodeModePort>) -> Self {
157 self.codemode = codemode;
158 self
159 }
160
161 pub fn with_openapi(mut self, openapi: Arc<dyn OpenApiPort>) -> Self {
163 self.openapi = openapi;
164 self
165 }
166}
167
168struct UnavailableEnginePort;
169
170impl UnavailableEnginePort {
171 fn error(engine: &str) -> PortError {
172 PortError::new(
173 "engine_unavailable",
174 format!("{engine} is not configured for this application instance"),
175 )
176 }
177}
178
179#[async_trait]
180impl GatewayPort for UnavailableEnginePort {
181 async fn status(&self, _context: &ExecutionContext) -> Result<Value, PortError> {
182 Err(Self::error("gateway"))
183 }
184
185 async fn reload(
186 &self,
187 _request: GatewayReloadRequest,
188 _context: &ExecutionContext,
189 ) -> Result<Value, PortError> {
190 Err(Self::error("gateway"))
191 }
192
193 async fn execute(
194 &self,
195 _request: GatewayExecuteRequest,
196 _context: &ExecutionContext,
197 ) -> Result<Value, PortError> {
198 Err(Self::error("gateway"))
199 }
200
201 async fn list_mcp_tools(
202 &self,
203 _scope: Option<&GatewayRouteScope>,
204 _context: &ExecutionContext,
205 ) -> Result<Vec<GatewayToolRoute>, PortError> {
206 Err(Self::error("gateway"))
207 }
208
209 async fn call_mcp_tool(
210 &self,
211 _name: &str,
212 _params: Value,
213 _scope: Option<&GatewayRouteScope>,
214 _context: &ExecutionContext,
215 ) -> Result<Option<Value>, PortError> {
216 Err(Self::error("gateway"))
217 }
218
219 async fn list_mcp_resources(
220 &self,
221 _scope: Option<&GatewayRouteScope>,
222 _context: &ExecutionContext,
223 ) -> Result<Vec<GatewayResourceRoute>, PortError> {
224 Err(Self::error("gateway"))
225 }
226
227 async fn read_mcp_resource(
228 &self,
229 _uri: &str,
230 _scope: Option<&GatewayRouteScope>,
231 _context: &ExecutionContext,
232 ) -> Result<Option<Value>, PortError> {
233 Err(Self::error("gateway"))
234 }
235
236 async fn list_mcp_prompts(
237 &self,
238 _scope: Option<&GatewayRouteScope>,
239 _context: &ExecutionContext,
240 ) -> Result<Vec<GatewayPromptRoute>, PortError> {
241 Err(Self::error("gateway"))
242 }
243
244 async fn get_mcp_prompt(
245 &self,
246 _name: &str,
247 _arguments: Option<serde_json::Map<String, Value>>,
248 _scope: Option<&GatewayRouteScope>,
249 _context: &ExecutionContext,
250 ) -> Result<Option<Value>, PortError> {
251 Err(Self::error("gateway"))
252 }
253}
254
255#[async_trait]
256impl CodeModePort for UnavailableEnginePort {
257 async fn execute(
258 &self,
259 _request: CodeModeExecuteRequest,
260 _context: &ExecutionContext,
261 ) -> Result<Value, PortError> {
262 Err(Self::error("Code Mode"))
263 }
264}
265
266#[async_trait]
267impl OpenApiPort for UnavailableEnginePort {
268 async fn execute(
269 &self,
270 _request: OpenApiExecuteRequest,
271 _context: &ExecutionContext,
272 ) -> Result<Value, PortError> {
273 Err(Self::error("OpenAPI"))
274 }
275}