use crate::constants::{DAYS_TO_BACK_LOOK, DEFAULT_PAGE_SIZE, DEFAULT_SLEEP_TIME};
use crate::storage::config::DatabaseConfig;
use crate::utils::config::get_env_or_default;
use dotenv::dotenv;
use pretty_simple_display::{DebugPretty, DisplaySimple};
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPoolOptions;
use std::env;
use tracing::error;
use tracing::log::debug;
#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
pub struct Credentials {
pub username: String,
pub password: String,
pub account_id: String,
pub api_key: String,
pub client_token: Option<String>,
pub account_token: Option<String>,
}
#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
pub struct Config {
pub credentials: Credentials,
pub rest_api: RestApiConfig,
pub websocket: WebSocketConfig,
pub database: DatabaseConfig,
pub rate_limiter: RateLimiterConfig,
pub sleep_hours: u64,
pub page_size: u32,
pub days_to_look_back: i64,
pub api_version: Option<u8>,
}
#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
pub struct RestApiConfig {
pub base_url: String,
pub timeout: u64,
}
#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
pub struct WebSocketConfig {
pub url: String,
pub reconnect_interval: u64,
}
#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
pub struct RateLimiterConfig {
pub max_requests: u32,
pub period_seconds: u64,
pub burst_size: u32,
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
impl Config {
pub fn new() -> Self {
match dotenv() {
Ok(_) => debug!("Successfully loaded .env file"),
Err(e) => debug!("Failed to load .env file: {e}"),
}
let username = get_env_or_default("IG_USERNAME", String::from("default_username"));
let password = get_env_or_default("IG_PASSWORD", String::from("default_password"));
let api_key = get_env_or_default("IG_API_KEY", String::from("default_api_key"));
let sleep_hours = get_env_or_default("TX_LOOP_INTERVAL_HOURS", DEFAULT_SLEEP_TIME);
let page_size = get_env_or_default("TX_PAGE_SIZE", DEFAULT_PAGE_SIZE);
let days_to_look_back = get_env_or_default("TX_DAYS_LOOKBACK", DAYS_TO_BACK_LOOK);
if username == "default_username" {
error!("IG_USERNAME not found in environment variables or .env file");
}
if password == "default_password" {
error!("IG_PASSWORD not found in environment variables or .env file");
}
if api_key == "default_api_key" {
error!("IG_API_KEY not found in environment variables or .env file");
}
Config {
credentials: Credentials {
username,
password,
account_id: get_env_or_default("IG_ACCOUNT_ID", String::from("default_account_id")),
api_key,
client_token: None,
account_token: None,
},
rest_api: RestApiConfig {
base_url: get_env_or_default(
"IG_REST_BASE_URL",
String::from("https://demo-api.ig.com/gateway/deal"),
),
timeout: get_env_or_default("IG_REST_TIMEOUT", 30),
},
websocket: WebSocketConfig {
url: get_env_or_default(
"IG_WS_URL",
String::from("wss://demo-apd.marketdatasystems.com"),
),
reconnect_interval: get_env_or_default("IG_WS_RECONNECT_INTERVAL", 5),
},
database: DatabaseConfig {
url: get_env_or_default(
"DATABASE_URL",
String::from("postgres://postgres:postgres@localhost/ig"),
),
max_connections: get_env_or_default("DATABASE_MAX_CONNECTIONS", 5),
},
rate_limiter: RateLimiterConfig {
max_requests: get_env_or_default("IG_RATE_LIMIT_MAX_REQUESTS", 4), period_seconds: get_env_or_default("IG_RATE_LIMIT_PERIOD_SECONDS", 12), burst_size: get_env_or_default("IG_RATE_LIMIT_BURST_SIZE", 3),
},
sleep_hours,
page_size,
days_to_look_back,
api_version: env::var("IG_API_VERSION")
.ok()
.and_then(|v| v.parse::<u8>().ok())
.filter(|&v| v == 2 || v == 3)
.or(Some(3)), }
}
pub async fn pg_pool(&self) -> Result<sqlx::Pool<sqlx::Postgres>, sqlx::Error> {
PgPoolOptions::new()
.max_connections(self.database.max_connections)
.connect(&self.database.url)
.await
}
}