use std::sync::Arc;
use log::LevelFilter;
use thiserror::Error;
use crate::config::{Config, ConfigError, ConfigProvider, EnvConfigProvider, FileConfigProvider};
use crate::router::PredicateRouter;
use crate::{ProxyError, ProxyServer, ServerConfig};
use crate::core::ProxyCore;
#[derive(Error, Debug)]
pub enum LoaderError {
#[error("configuration error: {0}")]
ConfigError(#[from] ConfigError),
#[error("proxy error: {0}")]
ProxyError(#[from] ProxyError),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
#[derive(Debug)]
pub struct FoxyLoader {
config_builder: Option<Config>,
config_file_path: Option<String>,
use_env_vars: bool,
env_prefix: Option<String>,
}
impl Default for FoxyLoader {
fn default() -> Self {
Self {
config_builder: None,
config_file_path: None,
use_env_vars: false,
env_prefix: None,
}
}
}
impl FoxyLoader {
pub fn new() -> Self {
Self::default()
}
pub fn with_config(mut self, config: Config) -> Self {
self.config_builder = Some(config);
self
}
pub fn with_config_file(mut self, file_path: &str) -> Self {
self.config_file_path = Some(file_path.to_string());
self
}
pub fn with_env_vars(mut self) -> Self {
self.use_env_vars = true;
self
}
pub fn with_env_prefix(mut self, prefix: &str) -> Self {
self.env_prefix = Some(prefix.to_string());
self.use_env_vars = true;
self
}
pub fn with_provider<P: ConfigProvider + 'static>(self, provider: P) -> Self {
let config_builder = match self.config_builder {
Some(_) => Config::builder().with_provider(provider),
None => Config::builder().with_provider(provider),
};
Self {
config_builder: Some(config_builder.build()),
..self
}
}
pub async fn build(self) -> Result<Foxy, LoaderError> {
env_logger::Builder::new()
.filter_level(LevelFilter::Trace) .init();
log::info!("Foxy starting up");
let config = if let Some(config) = self.config_builder {
config
} else {
let mut config_builder = Config::builder();
if self.use_env_vars {
let env_provider = match self.env_prefix {
Some(prefix) => EnvConfigProvider::new(&prefix),
None => EnvConfigProvider::default(),
};
config_builder = config_builder.with_provider(env_provider);
}
if let Some(file_path) = self.config_file_path {
match FileConfigProvider::new(&file_path) {
Ok(file_provider) => {
config_builder = config_builder.with_provider(file_provider);
},
Err(e) => {
return Err(LoaderError::ConfigError(e));
}
}
}
config_builder.build()
};
let config_arc = Arc::new(config);
let router = PredicateRouter::new(config_arc.clone()).await?;
let proxy_core = ProxyCore::new(config_arc.clone(), Arc::new(router))?;
let server_config: ServerConfig = config_arc.get_or_default("server", ServerConfig::default())?;
let proxy_server = ProxyServer::new(server_config, Arc::new(proxy_core));
log::info!("Foxy started up");
Ok(Foxy {
config: config_arc,
server: proxy_server,
})
}
}
#[derive(Debug, Clone)]
pub struct Foxy {
config: Arc<Config>,
server: ProxyServer,
}
impl Foxy {
pub fn loader() -> FoxyLoader {
FoxyLoader::new()
}
pub fn config(&self) -> &Config {
&self.config
}
pub async fn start(&self) -> Result<(), LoaderError> {
self.server.start().await.map_err(LoaderError::ProxyError)
}
}