diff --git a/src/application/mod.rs b/src/application/mod.rs new file mode 100644 index 0000000..ee52769 --- /dev/null +++ b/src/application/mod.rs @@ -0,0 +1,3 @@ +mod pokedex; + +pub use pokedex::{PokedexService, translation_kind_for}; diff --git a/src/service.rs b/src/application/pokedex.rs similarity index 93% rename from src/service.rs rename to src/application/pokedex.rs index 766442c..cf375eb 100644 --- a/src/service.rs +++ b/src/application/pokedex.rs @@ -1,9 +1,8 @@ use crate::{ - domain::PokemonInfo, + clients::{funtranslations::TranslationClient, pokeapi::PokeApiClient}, + domain::{PokemonInfo, TranslationKind}, error::AppError, - pokemon::PokeApiClient, telemetry::AppMetrics, - translation::{TranslationClient, TranslationKind}, }; use std::time::Instant; use tracing::{debug, instrument}; @@ -46,7 +45,7 @@ impl PokedexService { .await; self.metrics.record_upstream_request( "funtranslations", - translation_kind.endpoint(), + translation_operation(translation_kind), translation.is_ok(), translation_start.elapsed(), ); @@ -72,6 +71,13 @@ impl PokedexService { } } +const fn translation_operation(kind: TranslationKind) -> &'static str { + match kind { + TranslationKind::Shakespeare => "shakespeare", + TranslationKind::Yoda => "yoda", + } +} + #[must_use] pub fn translation_kind_for(pokemon: &PokemonInfo) -> TranslationKind { if pokemon.needs_yoda_translation() { @@ -85,8 +91,9 @@ pub fn translation_kind_for(pokemon: &PokemonInfo) -> TranslationKind { mod tests { use super::{PokedexService, translation_kind_for}; use crate::{ - domain::PokemonInfo, pokemon::PokeApiClient, telemetry::Telemetry, - translation::TranslationClient, translation::TranslationKind, + clients::{funtranslations::TranslationClient, pokeapi::PokeApiClient}, + domain::{PokemonInfo, TranslationKind}, + telemetry::Telemetry, }; use std::time::Duration; use wiremock::{Mock, MockServer, ResponseTemplate, matchers}; diff --git a/src/translation.rs b/src/clients/funtranslations.rs similarity index 87% rename from src/translation.rs rename to src/clients/funtranslations.rs index 691744a..c67c74d 100644 --- a/src/translation.rs +++ b/src/clients/funtranslations.rs @@ -1,4 +1,4 @@ -use crate::error::AppError; +use crate::{domain::TranslationKind, error::AppError}; use serde::Deserialize; use std::time::Duration; use tracing::instrument; @@ -9,22 +9,6 @@ pub struct TranslationClient { http: reqwest::Client, } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum TranslationKind { - Shakespeare, - Yoda, -} - -impl TranslationKind { - #[must_use] - pub const fn endpoint(self) -> &'static str { - match self { - Self::Shakespeare => "shakespeare", - Self::Yoda => "yoda", - } - } -} - impl TranslationClient { pub fn new(base_url: impl Into, timeout: Duration) -> Result { let http = reqwest::Client::builder() @@ -40,7 +24,7 @@ impl TranslationClient { #[instrument(skip(self, text), fields(translation.kind = ?kind))] pub async fn translate(&self, kind: TranslationKind, text: &str) -> Result { - let url = format!("{}/translate/{}.json", self.base_url, kind.endpoint()); + let url = format!("{}/translate/{}.json", self.base_url, endpoint_for(kind)); let response = self .http .post(url) @@ -74,6 +58,13 @@ struct TranslationContents { translated: String, } +const fn endpoint_for(kind: TranslationKind) -> &'static str { + match kind { + TranslationKind::Shakespeare => "shakespeare", + TranslationKind::Yoda => "yoda", + } +} + fn trim_trailing_slash(value: &str) -> String { value.trim_end_matches('/').to_owned() } @@ -88,14 +79,15 @@ fn map_reqwest_error(error: &reqwest::Error) -> AppError { #[cfg(test)] mod tests { - use super::{TranslationClient, TranslationKind}; + use super::{TranslationClient, endpoint_for}; + use crate::domain::TranslationKind; use std::time::Duration; use wiremock::{Mock, MockServer, ResponseTemplate, matchers}; #[test] fn translation_kind_resolves_funtranslations_endpoint() { - assert_eq!(TranslationKind::Yoda.endpoint(), "yoda"); - assert_eq!(TranslationKind::Shakespeare.endpoint(), "shakespeare"); + assert_eq!(endpoint_for(TranslationKind::Yoda), "yoda"); + assert_eq!(endpoint_for(TranslationKind::Shakespeare), "shakespeare"); } #[tokio::test] diff --git a/src/clients/mod.rs b/src/clients/mod.rs new file mode 100644 index 0000000..535801b --- /dev/null +++ b/src/clients/mod.rs @@ -0,0 +1,2 @@ +pub mod funtranslations; +pub mod pokeapi; diff --git a/src/pokemon.rs b/src/clients/pokeapi.rs similarity index 100% rename from src/pokemon.rs rename to src/clients/pokeapi.rs diff --git a/src/domain.rs b/src/domain/mod.rs similarity index 79% rename from src/domain.rs rename to src/domain/mod.rs index 9413331..09a9242 100644 --- a/src/domain.rs +++ b/src/domain/mod.rs @@ -15,3 +15,9 @@ impl PokemonInfo { self.is_legendary || self.habitat.eq_ignore_ascii_case("cave") } } + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TranslationKind { + Shakespeare, + Yoda, +} diff --git a/src/http/mod.rs b/src/http/mod.rs index e2d5531..0337c5c 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -3,8 +3,11 @@ mod middleware; mod rate_limit; use crate::{ - config::AppConfig, error::AppError, pokemon::PokeApiClient, service::PokedexService, - telemetry::Telemetry, translation::TranslationClient, + application::PokedexService, + clients::{funtranslations::TranslationClient, pokeapi::PokeApiClient}, + config::AppConfig, + error::AppError, + telemetry::Telemetry, }; use axum::{Router, extract::Request, middleware as axum_middleware, routing::get}; use std::sync::Arc; diff --git a/src/lib.rs b/src/lib.rs index d83d5a9..669c597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,14 +2,13 @@ #![deny(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] #![allow(clippy::missing_errors_doc, clippy::multiple_crate_versions)] +pub mod application; +pub mod clients; pub mod config; pub mod domain; pub mod error; pub mod http; -pub mod pokemon; -pub mod service; pub mod telemetry; -pub mod translation; #[must_use] pub const fn crate_name() -> &'static str { diff --git a/tests/app.rs b/tests/app.rs index 1269ce2..14efe58 100644 --- a/tests/app.rs +++ b/tests/app.rs @@ -3,11 +3,10 @@ use axum::{ http::{Request, StatusCode, header::HeaderName}, }; use pokedex_api::{ + application::PokedexService, + clients::{funtranslations::TranslationClient, pokeapi::PokeApiClient}, http::{AppState, RateLimiter, create_app}, - pokemon::PokeApiClient, - service::PokedexService, telemetry::Telemetry, - translation::TranslationClient, }; use serde_json::Value; use std::time::Duration;