Skip to content

Commit

Permalink
re structure the project
Browse files Browse the repository at this point in the history
  • Loading branch information
m4salah committed Dec 7, 2023
1 parent e3c2664 commit bc4639a
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 130 deletions.
15 changes: 15 additions & 0 deletions src/handlers/day0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use axum::{http::StatusCode, response::IntoResponse, routing::get};

pub fn router() -> axum::Router {
axum::Router::new()
.route("/", get(hello_world))
.route("/-1/error", get(internal_server_error))
}

pub async fn internal_server_error() -> impl IntoResponse {
StatusCode::INTERNAL_SERVER_ERROR
}

async fn hello_world() -> impl IntoResponse {
"Hello, World!"
}
25 changes: 25 additions & 0 deletions src/handlers/day1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get};
use std::str::FromStr;

pub fn router() -> axum::Router {
axum::Router::new().route("/1/*ids", get(packet_ids))
}

pub async fn packet_ids(Path(ids): Path<String>) -> impl IntoResponse {
let packet_ids: Vec<i32> = ids
.split('/')
// TODO: How to handle this gracefully?
.map(|id_str| i32::from_str(id_str).unwrap_or(0))
.collect();

// validate on the length of the ids
if packet_ids.len() > 20 {
return (
StatusCode::BAD_REQUEST,
"packet ids must be between 1 and 20 inclusive packets in a sled",
)
.into_response();
}
let result = packet_ids.iter().fold(0, |acc, prev| acc ^ prev).pow(3);
format!("{result}").into_response()
}
69 changes: 69 additions & 0 deletions src/handlers/day4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use axum::{http::StatusCode, response::IntoResponse, routing::post, Json};
use serde::{Deserialize, Serialize};

pub fn router() -> axum::Router {
axum::Router::new()
.route("/4/strength", post(sum_strength))
.route("/4/contest", post(contest))
}

#[derive(Debug, Serialize, Deserialize)]
struct Reindeer {
name: String,
strength: i32,
}
async fn sum_strength(Json(reindeers): Json<Vec<Reindeer>>) -> impl IntoResponse {
reindeers
.iter()
.fold(0, |acc, r| acc + r.strength)
.to_string()
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ContestReindeer {
pub name: String,
pub strength: i32,
pub speed: f32,
pub height: u32,
pub antler_width: u32,
pub snow_magic_power: i64,
pub favorite_food: String,
#[serde(alias = "cAnD13s_3ATeN-yesT3rdAy")]
pub candies: i32,
}

#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ContestResult {
pub fastest: String,
pub tallest: String,
pub magician: String,
pub consumer: String,
}
async fn contest(Json(reindeers): Json<Vec<ContestReindeer>>) -> impl IntoResponse {
let fastest = reindeers.iter().max_by(|a, b| a.strength.cmp(&b.strength));
let tallest = reindeers.iter().max_by(|a, b| a.height.cmp(&b.height));
let magician = reindeers
.iter()
.max_by(|a, b| a.snow_magic_power.cmp(&b.snow_magic_power));
let candiest = reindeers.iter().max_by(|a, b| a.candies.cmp(&b.candies));

match (fastest, tallest, magician, candiest) {
(Some(f), Some(t), Some(m), Some(c)) => Json(ContestResult {
fastest: format!(
"Speeding past the finish line with a strength of {} is {}",
f.strength, f.name
),
tallest: format!(
"{} is standing tall with his {} cm wide antlers",
t.name, t.antler_width
),
magician: format!(
"{} could blast you away with a snow magic power of {}",
m.name, m.snow_magic_power
),
consumer: format!("{} ate lots of candies, but also some grass", c.name),
})
.into_response(),
_ => (StatusCode::BAD_REQUEST, "Invalid contest").into_response(),
}
}
26 changes: 26 additions & 0 deletions src/handlers/day6.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use axum::{response::IntoResponse, routing::post, Json};
use serde::{Deserialize, Serialize};

pub fn router() -> axum::Router {
axum::Router::new().route("/6", post(elf_on_shelf))
}

#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ElfOnShelfResult {
pub elf: u32,
#[serde(alias = "elf on a shelf")]
pub elf_on_shelf: u32,
#[serde(alias = "shelf with no elf on it")]
pub shelf_with_no_elf: u32,
}

async fn elf_on_shelf(elf_text: String) -> impl IntoResponse {
let shelf = elf_text.matches("shelf").count() as u32;
let elf_on_shelf = elf_text.matches("elf on a shelf").count() as u32;
let elf = elf_text.matches("elf").count() as u32;
Json(ElfOnShelfResult {
elf,
elf_on_shelf,
shelf_with_no_elf: shelf - elf_on_shelf,
})
}
16 changes: 16 additions & 0 deletions src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mod day0;
mod day1;
mod day4;
mod day6;

pub use day4::{ContestReindeer, ContestResult};
pub use day6::ElfOnShelfResult;

pub fn router() -> axum::Router {
axum::Router::new()
.nest("/", day0::router())
.nest("/", day1::router())
// Days 2 and 3 are omitted due to being weekends
.nest("/", day4::router())
.nest("/", day6::router())
}
143 changes: 13 additions & 130 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,130 +1,9 @@
use std::str::FromStr;

use axum::{
extract::Path,
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use serde::{Deserialize, Serialize};

async fn hello_world() -> impl IntoResponse {
"Hello, World!"
}

async fn internal_server_error() -> impl IntoResponse {
StatusCode::INTERNAL_SERVER_ERROR
}

async fn packet_ids(Path(ids): Path<String>) -> impl IntoResponse {
let packet_ids: Vec<i32> = ids
.split('/')
// TODO: How to handle this gracefully?
.map(|id_str| i32::from_str(id_str).unwrap_or(0))
.collect();

// validate on the length of the ids
if packet_ids.len() > 20 {
return (
StatusCode::BAD_REQUEST,
"packet ids must be between 1 and 20 inclusive packets in a sled",
)
.into_response();
}
let result = packet_ids.iter().fold(0, |acc, prev| acc ^ prev).pow(3);
format!("{result}").into_response()
}

#[derive(Debug, Serialize, Deserialize)]
struct Reindeer {
name: String,
strength: i32,
}
async fn sum_strength(Json(reindeers): Json<Vec<Reindeer>>) -> impl IntoResponse {
reindeers
.iter()
.fold(0, |acc, r| acc + r.strength)
.to_string()
}

#[derive(Debug, Serialize, Deserialize)]
struct ContestReindeer {
name: String,
strength: i32,
speed: f32,
height: u32,
antler_width: u32,
snow_magic_power: i64,
favorite_food: String,
#[serde(alias = "cAnD13s_3ATeN-yesT3rdAy")]
candies: i32,
}

#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
struct ContestResult {
fastest: String,
tallest: String,
magician: String,
consumer: String,
}
async fn contest(Json(reindeers): Json<Vec<ContestReindeer>>) -> impl IntoResponse {
let fastest = reindeers.iter().max_by(|a, b| a.strength.cmp(&b.strength));
let tallest = reindeers.iter().max_by(|a, b| a.height.cmp(&b.height));
let magician = reindeers
.iter()
.max_by(|a, b| a.snow_magic_power.cmp(&b.snow_magic_power));
let candiest = reindeers.iter().max_by(|a, b| a.candies.cmp(&b.candies));

match (fastest, tallest, magician, candiest) {
(Some(f), Some(t), Some(m), Some(c)) => Json(ContestResult {
fastest: format!(
"Speeding past the finish line with a strength of {} is {}",
f.strength, f.name
),
tallest: format!(
"{} is standing tall with his {} cm wide antlers",
t.name, t.antler_width
),
magician: format!(
"{} could blast you away with a snow magic power of {}",
m.name, m.snow_magic_power
),
consumer: format!("{} ate lots of candies, but also some grass", c.name),
})
.into_response(),
_ => (StatusCode::BAD_REQUEST, "Invalid contest").into_response(),
}
}

#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
struct ElfOnShelfResult {
elf: u32,
#[serde(alias = "elf on a shelf")]
elf_on_shelf: u32,
#[serde(alias = "shelf with no elf on it")]
shelf_with_no_elf: u32,
}
async fn elf_on_shelf(elf_text: String) -> impl IntoResponse {
let shelf = elf_text.matches("shelf").count() as u32;
let elf_on_shelf = elf_text.matches("elf on a shelf").count() as u32;
let elf = elf_text.matches("elf").count() as u32;
Json(ElfOnShelfResult {
elf,
elf_on_shelf,
shelf_with_no_elf: shelf - elf_on_shelf,
})
}
use axum::Router;

mod handlers;
#[allow(dead_code)]
fn app() -> Router {
Router::new()
.route("/", get(hello_world))
.route("/-1/error", get(internal_server_error))
.route("/1/*ids", get(packet_ids))
.route("/4/strength", post(sum_strength))
.route("/4/contest", post(contest))
.route("/6", post(elf_on_shelf))
handlers::router()
}

#[shuttle_runtime::main]
Expand All @@ -135,6 +14,7 @@ async fn main() -> shuttle_axum::ShuttleAxum {
#[cfg(test)]
mod tests {
use super::*;
use axum::http::StatusCode;
use axum_test_helper::TestClient;
use serde_json::json;

Expand Down Expand Up @@ -265,13 +145,13 @@ mod tests {
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let expected = ContestResult {
let expected = handlers::ContestResult {
fastest: "Speeding past the finish line with a strength of 6 is Dancer".to_string(),
tallest: "Dasher is standing tall with his 36 cm wide antlers".to_string(),
magician: "Dasher could blast you away with a snow magic power of 9001".to_string(),
consumer: "Dancer ate lots of candies, but also some grass".to_string(),
};
assert_eq!(res.json::<ContestResult>().await, expected);
assert_eq!(res.json::<handlers::ContestResult>().await, expected);
}

#[tokio::test]
Expand Down Expand Up @@ -304,11 +184,14 @@ mod tests {
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let expected = ElfOnShelfResult {
let expected = handlers::ElfOnShelfResult {
elf: 4,
..Default::default()
};
assert_eq!(res.json::<ElfOnShelfResult>().await.elf, expected.elf);
assert_eq!(
res.json::<handlers::ElfOnShelfResult>().await.elf,
expected.elf
);
}
#[tokio::test]
async fn elf_on_shelf() {
Expand All @@ -322,11 +205,11 @@ mod tests {
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let expected = ElfOnShelfResult {
let expected = handlers::ElfOnShelfResult {
elf: 5,
shelf_with_no_elf: 1,
elf_on_shelf: 1,
};
assert_eq!(res.json::<ElfOnShelfResult>().await, expected);
assert_eq!(res.json::<handlers::ElfOnShelfResult>().await, expected);
}
}

0 comments on commit bc4639a

Please sign in to comment.