1#[cfg(test)]
12mod tests;
13
14use std::collections::HashMap;
15use std::sync::Arc;
16use log::LevelFilter;
17use serde_json::Value;
18use thiserror::Error;
19
20use crate::config::{Config, ConfigError, ConfigProvider, EnvConfigProvider, FileConfigProvider};
21use crate::router::{FilterConfig, PredicateRouter, RouteConfig};
22use crate::{Filter, FilterFactory, ProxyError, ProxyServer, ServerConfig};
23use crate::core::ProxyCore;
24
25#[derive(Error, Debug)]
27pub enum LoaderError {
28 #[error("configuration error: {0}")]
30 ConfigError(#[from] ConfigError),
31
32 #[error("proxy error: {0}")]
34 ProxyError(#[from] ProxyError),
35
36 #[error("IO error: {0}")]
38 IoError(#[from] std::io::Error),
39
40 #[error("{0}")]
42 Other(String),
43}
44
45#[derive(Debug)]
47pub struct FoxyLoader {
48 config_builder: Option<Config>,
49 config_file_path: Option<String>,
50 use_env_vars: bool,
51 env_prefix: Option<String>,
52 custom_filters: Vec<Arc<dyn Filter>>,
53}
54
55impl Default for FoxyLoader {
56 fn default() -> Self {
57 Self {
58 config_builder: None,
59 config_file_path: None,
60 use_env_vars: false,
61 env_prefix: None,
62 custom_filters: Vec::new(),
63 }
64 }
65}
66
67impl FoxyLoader {
68 pub fn new() -> Self {
70 Self::default()
71 }
72
73 pub fn with_config(mut self, config: Config) -> Self {
75 self.config_builder = Some(config);
76 self
77 }
78
79 pub fn with_config_file(mut self, file_path: &str) -> Self {
81 self.config_file_path = Some(file_path.to_string());
82 self
83 }
84
85 pub fn with_env_vars(mut self) -> Self {
87 self.use_env_vars = true;
88 self
89 }
90
91 pub fn with_env_prefix(mut self, prefix: &str) -> Self {
93 self.env_prefix = Some(prefix.to_string());
94 self.use_env_vars = true;
95 self
96 }
97
98 pub fn with_provider<P: ConfigProvider + 'static>(self, provider: P) -> Self {
100 let config_builder = match self.config_builder {
101 Some(_) => Config::builder().with_provider(provider),
102 None => Config::builder().with_provider(provider),
103 };
104
105 Self {
106 config_builder: Some(config_builder.build()),
107 ..self
108 }
109 }
110
111 pub fn with_filter<F: Filter + 'static>(mut self, filter: F) -> Self {
113 self.custom_filters.push(Arc::new(filter));
114 self
115 }
116
117 pub async fn build(self) -> Result<Foxy, LoaderError> {
119 let config = if let Some(config) = self.config_builder {
121 config
122 } else {
123 let mut config_builder = Config::builder();
124
125 if self.use_env_vars {
127 let env_provider = match self.env_prefix {
128 Some(prefix) => EnvConfigProvider::new(&prefix),
129 None => EnvConfigProvider::default(),
130 };
131 config_builder = config_builder.with_provider(env_provider);
132 }
133
134 if let Some(file_path) = self.config_file_path {
136 match FileConfigProvider::new(&file_path) {
137 Ok(file_provider) => {
138 config_builder = config_builder.with_provider(file_provider);
139 },
140 Err(e) => {
141 return Err(LoaderError::ConfigError(e));
142 }
143 }
144 }
145
146 config_builder.build()
147 };
148
149 let config_arc = Arc::new(config);
150
151 #[cfg(feature = "opentelemetry")]
153 {
154 if let Ok(Some(otel_config)) = config_arc.get::<crate::opentelemetry::OpenTelemetryConfig>("proxy.opentelemetry") {
155 if let Err(e) = crate::opentelemetry::init_opentelemetry(&otel_config) {
156 return Err(LoaderError::Other(format!("OpenTelemetry initialization failed: {}", e)));
157 }
158 }
160 }
161
162 if env_logger::try_init().is_ok() {
164 env_logger::Builder::new()
165 .filter_level(LevelFilter::Trace)
166 .try_init()
167 .ok();
168 }
169
170 log::info!("Foxy starting up");
171
172 #[cfg(feature = "opentelemetry")]
173 {
174 if let Ok(Some(otel_config)) = config_arc.get::<crate::opentelemetry::OpenTelemetryConfig>("proxy.opentelemetry") {
175 log::info!("OpenTelemetry initialized with endpoint: {} and service name: {}",
176 otel_config.endpoint, otel_config.service_name);
177 }
178 }
179
180 let router = PredicateRouter::new(config_arc.clone()).await?;
182
183 let proxy_core = ProxyCore::new(config_arc.clone(), Arc::new(router)).await?;
185
186 #[cfg(feature = "opentelemetry")]
188 {
189 if let Ok(Some(otel_config)) = config_arc.get::<crate::opentelemetry::OpenTelemetryConfig>("proxy.opentelemetry") {
190 crate::opentelemetry::register_filter(&proxy_core, &otel_config).await
191 .map_err(|e| LoaderError::Other(format!("Failed to register OpenTelemetry filter: {}", e)))?;
192 log::info!("Registered OpenTelemetry filter with service name: {}", otel_config.service_name);
193 }
194 }
195
196 let global_filters_config: Option<Vec<FilterConfig>> = config_arc.get("proxy.global_filters")?;
198
199 if let Some(global_filters) = global_filters_config {
200 for filter_config in global_filters {
201 let filter = FilterFactory::create_filter(
202 &filter_config.type_,
203 filter_config.config.clone(),
204 )?;
205 proxy_core.add_global_filter(filter).await;
206
207 log::info!("Added global filter: {}", filter_config.type_);
208 }
209 }
210
211 for filter in self.custom_filters {
213 proxy_core.add_global_filter(filter).await;
214 }
215
216 let server_config: ServerConfig = config_arc.get_or_default("server", ServerConfig::default())?;
218
219 let proxy_server = ProxyServer::new(server_config, Arc::new(proxy_core));
221
222 Ok(Foxy {
224 config: config_arc,
225 server: proxy_server,
226 })
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct Foxy {
233 config: Arc<Config>,
234 server: ProxyServer,
235}
236
237impl Foxy {
238 pub fn loader() -> FoxyLoader {
240 FoxyLoader::new()
241 }
242
243 pub fn config(&self) -> &Config {
245 &self.config
246 }
247
248 pub async fn start(&self) -> Result<(), LoaderError> {
250 self.server.start().await.map_err(LoaderError::ProxyError)
251 }
252}