-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
149 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use crate::investment; | ||
use crate::investment_config; | ||
use crate::types; | ||
|
||
pub fn run_cli_simulation(config_file: String) { | ||
let config: investment_config::Configuration = config::Config::builder() | ||
.add_source(config::File::new(&config_file, config::FileFormat::Json)) | ||
.build() | ||
.expect("Error loading configuration file") | ||
.try_deserialize() | ||
.expect("Error deserializing the configuration"); | ||
|
||
let investment = investment::Investment::new( | ||
types::PositiveFloat::try_from(config.deposit as f64).unwrap(), | ||
config.years, | ||
config | ||
.annual_contributions | ||
.to_annual_contributions(config.years), | ||
config.interest_rates.to_interest_rates(config.years), | ||
); | ||
|
||
let investment_snapshots = investment.simulate().unwrap(); | ||
let investment_results: Vec<investment::InvestmentSnapshotResult> = investment_snapshots | ||
.iter() | ||
.map(|snapshot| snapshot.result()) | ||
.collect(); | ||
for (year, result) in investment_results.iter().enumerate() { | ||
println!( | ||
"Investment result year {}\n {}", | ||
year + 1, | ||
serde_json::to_string(result).unwrap() | ||
); | ||
} | ||
let investment_result = investment::get_investment_result(investment_results).unwrap(); | ||
println!( | ||
"Investment result\n {}", | ||
serde_json::to_string(&investment_result).unwrap() | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
use crate::types; | ||
|
||
#[derive(serde::Deserialize)] | ||
pub struct Configuration { | ||
pub deposit: usize, | ||
pub interest_rates: types::Interest, | ||
pub years: usize, | ||
pub annual_contributions: types::AnnualContribution, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,40 @@ | ||
use clap::{arg, command, Parser}; | ||
use config::{Config, File, FileFormat}; | ||
use clap::{arg, command, Parser, ValueEnum}; | ||
|
||
mod cli; | ||
mod distributions; | ||
mod error; | ||
mod investment; | ||
mod investment_config; | ||
mod server; | ||
mod types; | ||
|
||
use investment::{Investment, InvestmentSnapshotResult}; | ||
use types::{AnnualContribution, Interest, PositiveFloat}; | ||
#[derive(Clone, ValueEnum, Debug, PartialEq)] | ||
enum AppMode { | ||
Server, | ||
Cli, | ||
} | ||
|
||
#[derive(Parser)] | ||
#[derive(Parser, Debug)] | ||
#[command(about = "Simulate index funds behaviour!")] | ||
struct Args { | ||
#[arg(short, long, help = "Configuration file")] | ||
config_file: String, | ||
} | ||
|
||
#[derive(serde::Deserialize)] | ||
struct Configuration { | ||
deposit: usize, | ||
interest_rates: Interest, | ||
years: usize, | ||
annual_contributions: AnnualContribution, | ||
#[arg(short, long, help = "Application mode")] | ||
mode: AppMode, | ||
#[arg(short, long, help = "Configuration file", required = false)] | ||
config_file: Option<String>, | ||
} | ||
|
||
fn main() { | ||
#[tokio::main] | ||
async fn main() { | ||
let args = Args::parse(); | ||
let config: Configuration = Config::builder() | ||
.add_source(File::new(&args.config_file, FileFormat::Json)) | ||
.build() | ||
.expect("Error loading configuration file") | ||
.try_deserialize() | ||
.expect("Error deserializing the configuration"); | ||
if args.mode == AppMode::Cli && args.config_file.is_none() { | ||
eprintln!("Error: `config_file` is required when `mode` is set to `Cli`"); | ||
} | ||
|
||
let investment = Investment::new( | ||
PositiveFloat::try_from(config.deposit as f64).unwrap(), | ||
config.years, | ||
config | ||
.annual_contributions | ||
.to_annual_contributions(config.years), | ||
config.interest_rates.to_interest_rates(config.years), | ||
); | ||
let investment_snapshots = investment.simulate().unwrap(); | ||
let investment_results: Vec<InvestmentSnapshotResult> = investment_snapshots | ||
.iter() | ||
.map(|snapshot| snapshot.result()) | ||
.collect(); | ||
for (year, result) in investment_results.iter().enumerate() { | ||
println!( | ||
"Investment result year {}\n {}", | ||
year + 1, | ||
serde_json::to_string(result).unwrap() | ||
); | ||
match args.mode { | ||
AppMode::Cli => cli::run_cli_simulation(args.config_file.unwrap()), | ||
AppMode::Server => { | ||
let server = server::Server::new("0.0.0.0".to_string(), "3000".to_string()); | ||
server.serve().await; | ||
} | ||
} | ||
let investment_result = investment::get_investment_result(investment_results).unwrap(); | ||
println!( | ||
"Investment result\n {}", | ||
serde_json::to_string(&investment_result).unwrap() | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use axum::extract; | ||
use axum::response; | ||
use axum::routing::post; | ||
use axum::Router; | ||
|
||
use crate::investment; | ||
use crate::investment_config; | ||
use crate::types; | ||
|
||
pub struct Server { | ||
host: String, | ||
port: String, | ||
} | ||
|
||
impl Server { | ||
pub fn new(host: String, port: String) -> Self { | ||
Self { host, port } | ||
} | ||
|
||
pub async fn serve(&self) { | ||
let app = Router::new().route("/simulate", post(get_investment_result)); | ||
let listener = tokio::net::TcpListener::bind(format!("{}:{}", self.host, self.port)) | ||
.await | ||
.unwrap(); | ||
println!("Listening on {}", listener.local_addr().unwrap()); | ||
axum::serve(listener, app).await.unwrap(); | ||
println!("Exiting"); | ||
} | ||
} | ||
|
||
async fn get_investment_result( | ||
extract::Json(config): extract::Json<investment_config::Configuration>, | ||
) -> response::Json<investment::InvestmentResult> { | ||
let investment = investment::Investment::new( | ||
types::PositiveFloat::try_from(config.deposit as f64).unwrap(), | ||
config.years, | ||
config | ||
.annual_contributions | ||
.to_annual_contributions(config.years), | ||
config.interest_rates.to_interest_rates(config.years), | ||
); | ||
|
||
let investment_snapshots = investment.simulate().unwrap(); | ||
let investment_results: Vec<investment::InvestmentSnapshotResult> = investment_snapshots | ||
.iter() | ||
.map(|snapshot| snapshot.result()) | ||
.collect(); | ||
|
||
let investment_result = investment::get_investment_result(investment_results).unwrap(); | ||
|
||
response::Json(investment_result) | ||
} |