1use clap::Parser;
2use serde::{Deserialize, Serialize};
3use std::net::SocketAddr;
4
5#[derive(Parser, Debug, Clone)]
6#[command(name = "maple-proxy")]
7#[command(about = "Lightweight OpenAI-compatible proxy server for Maple/OpenSecret")]
8pub struct Config {
9 #[arg(long, env = "MAPLE_HOST", default_value = "127.0.0.1")]
11 pub host: String,
12
13 #[arg(short, long, env = "MAPLE_PORT", default_value = "8080")]
15 pub port: u16,
16
17 #[arg(
19 long,
20 env = "MAPLE_BACKEND_URL",
21 default_value = "https://enclave.trymaple.ai"
22 )]
23 pub backend_url: String,
24
25 #[arg(long, env = "MAPLE_API_KEY")]
27 pub default_api_key: Option<String>,
28
29 #[arg(short, long, env = "MAPLE_DEBUG")]
31 pub debug: bool,
32
33 #[arg(long, env = "MAPLE_ENABLE_CORS")]
35 pub enable_cors: bool,
36}
37
38impl Config {
39 pub fn socket_addr(&self) -> anyhow::Result<SocketAddr> {
40 let addr = format!("{}:{}", self.host, self.port);
41 addr.parse()
42 .map_err(|e| anyhow::anyhow!("Invalid socket address '{}': {}", addr, e))
43 }
44
45 pub fn load() -> Self {
46 let _ = dotenvy::dotenv();
48
49 Config::parse()
50 }
51
52 pub fn new(host: String, port: u16, backend_url: String) -> Self {
54 Self {
55 host,
56 port,
57 backend_url,
58 default_api_key: None,
59 debug: false,
60 enable_cors: false,
61 }
62 }
63
64 pub fn with_api_key(mut self, api_key: String) -> Self {
66 self.default_api_key = Some(api_key);
67 self
68 }
69
70 pub fn with_debug(mut self, debug: bool) -> Self {
72 self.debug = debug;
73 self
74 }
75
76 pub fn with_cors(mut self, enable_cors: bool) -> Self {
78 self.enable_cors = enable_cors;
79 self
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct OpenAIError {
85 pub error: OpenAIErrorDetails,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct OpenAIErrorDetails {
90 pub message: String,
91 #[serde(rename = "type")]
92 pub error_type: String,
93 pub param: Option<String>,
94 pub code: Option<String>,
95}
96
97impl OpenAIError {
98 pub fn new(message: impl Into<String>, error_type: impl Into<String>) -> Self {
99 Self {
100 error: OpenAIErrorDetails {
101 message: message.into(),
102 error_type: error_type.into(),
103 param: None,
104 code: None,
105 },
106 }
107 }
108
109 pub fn authentication_error(message: impl Into<String>) -> Self {
110 Self::new(message, "invalid_request_error")
111 }
112
113 pub fn server_error(message: impl Into<String>) -> Self {
114 Self::new(message, "server_error")
115 }
116}