chore: bootstrap rust service
This commit is contained in:
76
src/config.rs
Normal file
76
src/config.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::{env, net::SocketAddr, time::Duration};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppConfig {
|
||||
pub bind_addr: SocketAddr,
|
||||
pub pokeapi_base_url: String,
|
||||
pub translations_base_url: String,
|
||||
pub request_timeout: Duration,
|
||||
pub rate_limit_per_second: f64,
|
||||
pub rate_limit_burst: u32,
|
||||
pub service_name: String,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
pub fn from_env() -> Result<Self, ConfigError> {
|
||||
Ok(Self {
|
||||
bind_addr: env_or_parse("BIND_ADDR", default_bind_addr())?,
|
||||
pokeapi_base_url: env_or_string("POKEAPI_BASE_URL", "https://pokeapi.co/api/v2"),
|
||||
translations_base_url: env_or_string(
|
||||
"FUN_TRANSLATIONS_BASE_URL",
|
||||
"https://funtranslations.mercxry.me",
|
||||
),
|
||||
request_timeout: Duration::from_secs(env_or_parse("REQUEST_TIMEOUT_SECONDS", 5)?),
|
||||
rate_limit_per_second: env_or_parse("RATE_LIMIT_PER_SECOND", 20.0)?,
|
||||
rate_limit_burst: env_or_parse("RATE_LIMIT_BURST", 40)?,
|
||||
service_name: env_or_string("OTEL_SERVICE_NAME", env!("CARGO_PKG_NAME")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("invalid value for {name}: {source}")]
|
||||
InvalidValue {
|
||||
name: &'static str,
|
||||
source: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
}
|
||||
|
||||
fn env_or_string(name: &'static str, default: &str) -> String {
|
||||
env::var(name).unwrap_or_else(|_| default.to_owned())
|
||||
}
|
||||
|
||||
fn default_bind_addr() -> SocketAddr {
|
||||
SocketAddr::from(([0, 0, 0, 0], 5000))
|
||||
}
|
||||
|
||||
fn env_or_parse<T>(name: &'static str, default: T) -> Result<T, ConfigError>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
T::Err: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
env::var(name).map_or_else(
|
||||
|_| Ok(default),
|
||||
|value| {
|
||||
value.parse().map_err(|source| ConfigError::InvalidValue {
|
||||
name,
|
||||
source: Box::new(source),
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::AppConfig;
|
||||
|
||||
#[test]
|
||||
fn default_config_is_valid_for_local_development() {
|
||||
let config = AppConfig::from_env().expect("default env should parse");
|
||||
|
||||
assert_eq!(config.bind_addr.port(), 5000);
|
||||
assert_eq!(config.pokeapi_base_url, "https://pokeapi.co/api/v2");
|
||||
assert!((config.rate_limit_per_second - 20.0).abs() < f64::EPSILON);
|
||||
}
|
||||
}
|
||||
10
src/lib.rs
Normal file
10
src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
#![deny(warnings)]
|
||||
#![deny(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::multiple_crate_versions)]
|
||||
|
||||
pub mod config;
|
||||
|
||||
#[must_use]
|
||||
pub const fn crate_name() -> &'static str {
|
||||
env!("CARGO_PKG_NAME")
|
||||
}
|
||||
11
src/main.rs
Normal file
11
src/main.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#![deny(warnings)]
|
||||
#![deny(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::multiple_crate_versions)]
|
||||
|
||||
use pokedex_api::{config::AppConfig, crate_name};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let _config = AppConfig::from_env()?;
|
||||
println!("{name} bootstrap complete", name = crate_name());
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user