1use crate::constants::{DAYS_TO_BACK_LOOK, DEFAULT_PAGE_SIZE, DEFAULT_SLEEP_TIME};
2use crate::storage::config::DatabaseConfig;
3use crate::utils::config::get_env_or_default;
4use dotenv::dotenv;
5use pretty_simple_display::{DebugPretty, DisplaySimple};
6use serde::{Deserialize, Serialize};
7use sqlx::postgres::PgPoolOptions;
8use std::env;
9use tracing::error;
10use tracing::log::debug;
11
12#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
13pub struct Credentials {
15 pub username: String,
17 pub password: String,
19 pub account_id: String,
21 pub api_key: String,
23 pub client_token: Option<String>,
25 pub account_token: Option<String>,
27}
28
29#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
30pub struct Config {
32 pub credentials: Credentials,
34 pub rest_api: RestApiConfig,
36 pub websocket: WebSocketConfig,
38 pub database: DatabaseConfig,
40 pub rate_limiter: RateLimiterConfig,
42 pub sleep_hours: u64,
44 pub page_size: u32,
46 pub days_to_look_back: i64,
48 pub api_version: Option<u8>,
50}
51
52#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
53pub struct RestApiConfig {
55 pub base_url: String,
57 pub timeout: u64,
59}
60
61#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
62pub struct WebSocketConfig {
64 pub url: String,
66 pub reconnect_interval: u64,
68}
69
70#[derive(DebugPretty, DisplaySimple, Serialize, Deserialize, Clone)]
71pub struct RateLimiterConfig {
73 pub max_requests: u32,
75 pub period_seconds: u64,
77 pub burst_size: u32,
79}
80
81impl Default for Config {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl Config {
88 pub fn new() -> Self {
99 match dotenv() {
101 Ok(_) => debug!("Successfully loaded .env file"),
102 Err(e) => debug!("Failed to load .env file: {e}"),
103 }
104
105 let username = get_env_or_default("IG_USERNAME", String::from("default_username"));
107 let password = get_env_or_default("IG_PASSWORD", String::from("default_password"));
108 let api_key = get_env_or_default("IG_API_KEY", String::from("default_api_key"));
109 let sleep_hours = get_env_or_default("TX_LOOP_INTERVAL_HOURS", DEFAULT_SLEEP_TIME);
110 let page_size = get_env_or_default("TX_PAGE_SIZE", DEFAULT_PAGE_SIZE);
111 let days_to_look_back = get_env_or_default("TX_DAYS_LOOKBACK", DAYS_TO_BACK_LOOK);
112
113 if username == "default_username" {
115 error!("IG_USERNAME not found in environment variables or .env file");
116 }
117 if password == "default_password" {
118 error!("IG_PASSWORD not found in environment variables or .env file");
119 }
120 if api_key == "default_api_key" {
121 error!("IG_API_KEY not found in environment variables or .env file");
122 }
123
124 Config {
125 credentials: Credentials {
126 username,
127 password,
128 account_id: get_env_or_default("IG_ACCOUNT_ID", String::from("default_account_id")),
129 api_key,
130 client_token: None,
131 account_token: None,
132 },
133 rest_api: RestApiConfig {
134 base_url: get_env_or_default(
135 "IG_REST_BASE_URL",
136 String::from("https://demo-api.ig.com/gateway/deal"),
137 ),
138 timeout: get_env_or_default("IG_REST_TIMEOUT", 30),
139 },
140 websocket: WebSocketConfig {
141 url: get_env_or_default(
142 "IG_WS_URL",
143 String::from("wss://demo-apd.marketdatasystems.com"),
144 ),
145 reconnect_interval: get_env_or_default("IG_WS_RECONNECT_INTERVAL", 5),
146 },
147 database: DatabaseConfig {
148 url: get_env_or_default(
149 "DATABASE_URL",
150 String::from("postgres://postgres:postgres@localhost/ig"),
151 ),
152 max_connections: get_env_or_default("DATABASE_MAX_CONNECTIONS", 5),
153 },
154 rate_limiter: RateLimiterConfig {
155 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),
158 },
159 sleep_hours,
160 page_size,
161 days_to_look_back,
162 api_version: env::var("IG_API_VERSION")
163 .ok()
164 .and_then(|v| v.parse::<u8>().ok())
165 .filter(|&v| v == 2 || v == 3)
166 .or(Some(3)), }
168 }
169
170 pub async fn pg_pool(&self) -> Result<sqlx::Pool<sqlx::Postgres>, sqlx::Error> {
176 PgPoolOptions::new()
177 .max_connections(self.database.max_connections)
178 .connect(&self.database.url)
179 .await
180 }
181}