feat(config): add config file

This commit is contained in:
Kristofers Solo 2024-03-23 16:36:46 +02:00
parent 5ce9b4a766
commit 938bb7d071
10 changed files with 1366 additions and 49 deletions

1285
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,9 @@ name = "zero2prod"
[dependencies] [dependencies]
axum = "0.7" axum = "0.7"
config = "0.14"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "tls-rustls", "macros", "postgres", "uuid", "chrono", "migrate"] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
[dev-dependencies] [dev-dependencies]

38
src/configuation.rs Normal file
View File

@ -0,0 +1,38 @@
use std::fmt::Display;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct Settings {
pub database: DatabaseSettings,
pub application_port: u16,
}
#[derive(Debug, Deserialize)]
pub struct DatabaseSettings {
pub username: String,
pub password: String,
pub port: u16,
pub host: String,
pub database_name: String,
}
impl Display for DatabaseSettings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"postgres://{}:{}@{}:{}/{}",
self.username, self.password, self.host, self.port, self.database_name
)
}
}
pub fn get_configuration() -> Result<Settings, config::ConfigError> {
let settings = config::Config::builder()
.add_source(config::File::new(
"configuration.toml",
config::FileFormat::Toml,
))
.build()?;
settings.try_deserialize::<Settings>()
}

View File

@ -1,37 +1,3 @@
use axum::{ pub mod configuation;
extract::Path, pub mod routes;
http::StatusCode, pub mod startup;
response::IntoResponse,
routing::{get, post},
Form, Router,
};
use serde::Deserialize;
pub fn app() -> Router {
Router::new()
.route("/", get(root))
.route("/:name", get(greet))
.route("/health_check", get(health_check))
.route("/subscribtions", post(subscribe))
}
async fn root() -> impl IntoResponse {
"Hello, world!"
}
async fn greet(Path(name): Path<String>) -> impl IntoResponse {
format!("Hello, {name}!")
}
async fn health_check() -> impl IntoResponse {
StatusCode::OK
}
#[derive(Deserialize)]
struct FormData {
name: String,
email: String,
}
async fn subscribe(Form(form): Form<FormData>) -> impl IntoResponse {
StatusCode::OK
}

View File

@ -1,12 +1,14 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use zero2prod::{configuation::get_configuration, routes::route};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), std::io::Error> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8000)); let configuation = get_configuration().expect("Failed to read configuation.");
let addr = SocketAddr::from(([127, 0, 0, 1], configuation.application_port));
let listener = TcpListener::bind(addr) let listener = TcpListener::bind(addr)
.await .await
.expect("Failed to bind random port"); .expect("Failed to bind random port");
axum::serve(listener, zero2prod::app()).await.unwrap(); axum::serve(listener, route()).await
} }

View File

@ -0,0 +1,5 @@
use axum::{http::StatusCode, response::IntoResponse};
pub async fn health_check() -> impl IntoResponse {
StatusCode::OK
}

15
src/routes/mod.rs Normal file
View File

@ -0,0 +1,15 @@
mod health_check;
mod subscibtions;
use axum::{
routing::{get, post},
Router,
};
pub use health_check::*;
pub use subscibtions::*;
pub fn route() -> Router {
Router::new()
.route("/health_check", get(health_check))
.route("/subscribtions", post(subscribe))
}

View File

@ -0,0 +1,11 @@
use axum::{http::StatusCode, response::IntoResponse, Form};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct FormData {
name: String,
email: String,
}
pub async fn subscribe(Form(form): Form<FormData>) -> impl IntoResponse {
StatusCode::OK
}

2
src/startup.rs Normal file
View File

@ -0,0 +1,2 @@

View File

@ -1,5 +1,7 @@
use reqwest::Client; use reqwest::Client;
use sqlx::{Connection, PgConnection};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use zero2prod::{configuation::get_configuration, routes::route};
#[tokio::test] #[tokio::test]
async fn health_check() { async fn health_check() {
@ -19,6 +21,11 @@ async fn health_check() {
#[tokio::test] #[tokio::test]
async fn subscribe_returns_200_for_valid_form_data() { async fn subscribe_returns_200_for_valid_form_data() {
let address = spawn_app().await; let address = spawn_app().await;
let configuration = get_configuration().expect("Failed to read configuration.");
let db_url = configuration.database.to_string();
let connection = PgConnection::connect(&db_url)
.await
.expect("Failed to connect to Postgres.");
let client = Client::new(); let client = Client::new();
let body = "name=kristofers%20solo&email=dev%40kristofers.solo"; let body = "name=kristofers%20solo&email=dev%40kristofers.solo";
@ -67,6 +74,6 @@ async fn spawn_app() -> String {
.await .await
.expect("Failed to bind random port"); .expect("Failed to bind random port");
let port = listener.local_addr().unwrap().port(); let port = listener.local_addr().unwrap().port();
let _ = tokio::spawn(async move { axum::serve(listener, zero2prod::app()).await.unwrap() }); let _ = tokio::spawn(async move { axum::serve(listener, route()).await.unwrap() });
format!("http://127.0.0.1:{}", port) format!("http://127.0.0.1:{}", port)
} }