diff --git a/src/crawler/mod.rs b/src/crawler/mod.rs index 9669ac1..dcc173d 100644 --- a/src/crawler/mod.rs +++ b/src/crawler/mod.rs @@ -1,8 +1,8 @@ use std::include_str; use lazy_static::lazy_static; -use serde::Deserialize; use regex::Regex; +use serde::Deserialize; const RAW_CRAWLER_DATA: &str = include_str!("crawler-user-agents.json"); @@ -17,14 +17,17 @@ struct Crawler { } lazy_static! { - static ref CRAWLERS: Vec = serde_json::from_str::>(&RAW_CRAWLER_DATA).unwrap().into_iter().map(|crawler| Crawler { - pattern: Regex::new(&crawler.pattern).unwrap(), - }).collect(); + static ref CRAWLERS: Vec = serde_json::from_str::>(RAW_CRAWLER_DATA) + .unwrap() + .into_iter() + .map(|crawler| Crawler { + pattern: Regex::new(&crawler.pattern).unwrap(), + }) + .collect(); } pub fn is_crawler(user_agent: &str) -> bool { - CRAWLERS.iter().any(|crawler| { - crawler.pattern.is_match(user_agent) - }) + CRAWLERS + .iter() + .any(|crawler| crawler.pattern.is_match(user_agent)) } - diff --git a/src/dao/mod.rs b/src/dao/mod.rs index 5c3bc71..c51f1d8 100644 --- a/src/dao/mod.rs +++ b/src/dao/mod.rs @@ -1,7 +1,4 @@ -use std::iter; - use poem_openapi::Object; -use rand::Rng; use sqlx::prelude::FromRow; #[derive(thiserror::Error, Debug)] @@ -34,8 +31,7 @@ pub struct Link { pub image_url: String, } -const LINKS_SELECT_FIELDS: &str = - "id, link_path, title, description, image_url"; +const LINKS_SELECT_FIELDS: &str = "id, link_path, title, description, image_url"; pub async fn create_link( pool: &sqlx::Pool, @@ -101,10 +97,12 @@ pub async fn get_link( ) -> Result, Error> { tracing::info!("Getting link by id link_id={link_id}"); - let result = sqlx::query_as::<_, Link>(&format!("SELECT {LINKS_SELECT_FIELDS} FROM links WHERE id = $1")) - .bind(link_id) - .fetch_optional(pool) - .await; + let result = sqlx::query_as::<_, Link>(&format!( + "SELECT {LINKS_SELECT_FIELDS} FROM links WHERE id = $1" + )) + .bind(link_id) + .fetch_optional(pool) + .await; result.map_err(|err| { tracing::error!("Error whilst fetching link link_id={link_id} err={err}"); @@ -118,10 +116,12 @@ pub async fn get_link_by_link_path( ) -> Result, Error> { tracing::info!("Getting link by url path link_path={link_path}"); - let result = sqlx::query_as::<_, Link>(&format!("SELECT {LINKS_SELECT_FIELDS} FROM links WHERE link_path = $1")) - .bind(link_path) - .fetch_optional(pool) - .await; + let result = sqlx::query_as::<_, Link>(&format!( + "SELECT {LINKS_SELECT_FIELDS} FROM links WHERE link_path = $1" + )) + .bind(link_path) + .fetch_optional(pool) + .await; result.map_err(|err| { tracing::error!("Error whilst creating link err={err}"); diff --git a/src/link_router/mod.rs b/src/link_router/mod.rs index 3193e42..f500d16 100644 --- a/src/link_router/mod.rs +++ b/src/link_router/mod.rs @@ -1,11 +1,11 @@ -use std::env; - +use askama::Template; use poem::{ - get, handler, http::StatusCode, web::{Data, Html, Path, Redirect}, EndpointExt, FromRequest, IntoResponse, Request, RequestBody, Response, Result, Route + get, handler, + http::StatusCode, + web::{Data, Html, Path, Redirect}, + FromRequest, IntoResponse, Request, RequestBody, Response, Result, Route, }; use regex::Regex; -use askama::Template; -use url::Url; use crate::{crawler::is_crawler, dao, AppContext, FallbackData}; @@ -20,7 +20,7 @@ enum Platform { #[derive(Debug)] enum RequestActor { Crawler, - User(Platform) + User(Platform), } #[derive(Template)] @@ -50,7 +50,7 @@ fn get_platform_from_user_agent(user_agent: &str) -> Platform { { return Platform::Web; } - return Platform::Unknown; + Platform::Unknown } impl<'a> FromRequest<'a> for RequestActor { @@ -60,13 +60,11 @@ impl<'a> FromRequest<'a> for RequestActor { .get("User-Agent") .and_then(|value| value.to_str().ok()); - Ok( - match user_agent_header { - None => RequestActor::User(Platform::Unknown), - Some(header_value) if is_crawler(header_value) => RequestActor::Crawler, - Some(header_value) => RequestActor::User(get_platform_from_user_agent(header_value)), - } - ) + Ok(match user_agent_header { + None => RequestActor::User(Platform::Unknown), + Some(header_value) if is_crawler(header_value) => RequestActor::Crawler, + Some(header_value) => RequestActor::User(get_platform_from_user_agent(header_value)), + }) } } @@ -80,38 +78,33 @@ async fn link_handler( tracing::info!("Handling link link_path={link_path} request_actor={request_actor:?}"); match dao::get_link_by_link_path(&app_context.pool, &link_path).await { - Ok(Some(link)) => { - match request_actor { - RequestActor::Crawler => { - let response = CrawlerResponseTemplate { - og_title: link.title, - og_description: link.description, - og_url: link.link_path, - og_image_url: link.image_url, - og_type: "website".to_string(), - }; - Html(response.render().unwrap()).into_response() - }, - RequestActor::User(platform) => { - Redirect::temporary(match platform { - Platform::Android => &fallback_data.android_fallback, - Platform::Ios => &fallback_data.ios_fallback, - Platform::Web | Platform::Unknown => &fallback_data.web_fallback, - }).into_response() - } + Ok(Some(link)) => match request_actor { + RequestActor::Crawler => { + let response = CrawlerResponseTemplate { + og_title: link.title, + og_description: link.description, + og_url: link.link_path, + og_image_url: link.image_url, + og_type: "website".to_string(), + }; + Html(response.render().unwrap()).into_response() } - - } + RequestActor::User(platform) => Redirect::temporary(match platform { + Platform::Android => &fallback_data.android_fallback, + Platform::Ios => &fallback_data.ios_fallback, + Platform::Web | Platform::Unknown => &fallback_data.web_fallback, + }) + .into_response(), + }, Ok(None) => Response::builder() .status(StatusCode::NOT_FOUND) - .body(format!("Link not found")), + .body("Link not found".to_string()), Err(_) => Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(format!("Unknown error")), + .body("Unknown error".to_string()), } } pub fn create_link_router_service() -> Route { - Route::new() - .at("/*path", get(link_handler)) + Route::new().at("/*path", get(link_handler)) } diff --git a/src/main.rs b/src/main.rs index 32b8827..d578f8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,6 @@ use tracing::info; use url::Url; use sqlx::Postgres; -use tracing_subscriber; #[derive(Clone)] struct AppContext { @@ -29,8 +28,8 @@ struct FallbackData { } fn get_fallback_from_env(env_name: &str) -> Url { - let env_value = env::var(env_name).expect(&format!("{env_name} must be defined")); - Url::parse(&env_value).expect(&format!("{env_name} must be a valid url")) + let env_value = env::var(env_name).unwrap_or_else(|_| panic!("{env_name} must be defined")); + Url::parse(&env_value).unwrap_or_else(|_| panic!("{env_name} must be a valid url")) } #[tokio::main] @@ -57,7 +56,11 @@ async fn main() -> Result<(), Box> { let app = Route::new() .nest("/api", create_management_service(&base_url)) .nest("/", create_link_router_service()) - .data(AppContext { pool, base_url, api_key }) + .data(AppContext { + pool, + base_url, + api_key, + }) .data(FallbackData { web_fallback: get_fallback_from_env("WEB_FALLBACK_URL"), android_fallback: get_fallback_from_env("ANDROID_FALLBACK_URL"), diff --git a/src/management/mod.rs b/src/management/mod.rs index d220e3b..0198f06 100644 --- a/src/management/mod.rs +++ b/src/management/mod.rs @@ -5,7 +5,10 @@ use poem_openapi::{ }; use url::Url; -use crate::{dao::{self, update_link, LinkData}, AppContext}; +use crate::{ + dao::{self, update_link, LinkData}, + AppContext, +}; pub struct ManagementApi; @@ -32,7 +35,7 @@ impl dao::Link { } } - fn to_json(self, base_url: &Url) -> Json { + fn into_json(self, base_url: &Url) -> Json { Json(self.serialize(base_url)) } } @@ -76,15 +79,12 @@ enum UpdateLinkResponse { )] struct ApiKeyAuth(()); -async fn api_key_checker( - request: &Request, - api_key: ApiKey, -) -> Option<()> { +async fn api_key_checker(request: &Request, api_key: ApiKey) -> Option<()> { let app_context: &AppContext = request.data().unwrap(); if api_key.key == app_context.api_key { return Some(()); } - return None; + None } impl From for poem::Error { @@ -103,7 +103,10 @@ impl ManagementApi { _auth: ApiKeyAuth, ) -> Result { let links = dao::list_links(&app_context.pool).await?; - let links: Vec = links.into_iter().map(|item| item.serialize(&app_context.base_url)).collect(); + let links: Vec = links + .into_iter() + .map(|item| item.serialize(&app_context.base_url)) + .collect(); Ok(ListLinksResponse::Ok(Json(links))) } @@ -116,7 +119,7 @@ impl ManagementApi { _auth: ApiKeyAuth, ) -> Result { Ok(match dao::get_link(&app_context.pool, &link_id).await? { - Some(link) => GetLinkResponse::Ok(link.to_json(&app_context.base_url)), + Some(link) => GetLinkResponse::Ok(link.into_json(&app_context.base_url)), None => GetLinkResponse::NotFound, }) } @@ -131,7 +134,7 @@ impl ManagementApi { ) -> Result { let link = dao::create_link(&app_context.pool, input).await?; - Ok(CreateLinkResponse::Ok(link.to_json(&app_context.base_url))) + Ok(CreateLinkResponse::Ok(link.into_json(&app_context.base_url))) } /// Update an existing polylink @@ -143,22 +146,30 @@ impl ManagementApi { Data(app_context): Data<&AppContext>, _auth: ApiKeyAuth, ) -> Result { - Ok(match update_link(&app_context.pool, &link_id, input).await? { - Some(link) => UpdateLinkResponse::Ok(link.to_json(&app_context.base_url)), - None => UpdateLinkResponse::NotFound, - }) + Ok( + match update_link(&app_context.pool, &link_id, input).await? { + Some(link) => UpdateLinkResponse::Ok(link.into_json(&app_context.base_url)), + None => UpdateLinkResponse::NotFound, + }, + ) } } pub fn create_management_service(base_url: &Url) -> Route { let api_service = OpenApiService::new(ManagementApi, "PolyLink Management API", "0.0.1") - .server(base_url.join("/api").expect("Cannot join base url with api").to_string()); + .server( + base_url + .join("/api") + .expect("Cannot join base url with api") + .to_string(), + ); let ui = api_service.swagger_ui(); let json_spec_endpoint = api_service.spec_endpoint(); let yaml_spec_endpoint = api_service.spec_endpoint_yaml(); - Route::new().nest("/", api_service) + Route::new() + .nest("/", api_service) .nest("/docs", ui) .nest("/docs/spec.json", json_spec_endpoint) .nest("/docs/spec.yaml", yaml_spec_endpoint)