example_webserver_rs/
handlers.rs

1//! In axum a “handler” is an async function that accepts zero or more “extractors”
2//! as arguments and returns something that can be converted into a response.
3use crate::structs::AppState;
4use crate::structs::{Country, CountryQueryParams, RequestJson, ResponseJson};
5use axum::{
6    extract::{Query, State},
7    http::StatusCode,
8    Json,
9};
10use rand::Rng;
11use serde_json::{json, Value};
12use std::sync::{Arc, Mutex};
13
14///Return arbitrary json object. `serde_json::Value` can be a valid json object of any kind.
15///Wrapping output around `axum::Json` as it implements `IntoResponse` trait.
16pub async fn get_json(State(s): State<Arc<Mutex<AppState>>>) -> Result<Json<Value>, StatusCode> {
17    match s.lock() {
18        Ok(mut x) => x.increment(),
19        Err(_e) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
20    }
21
22    Ok(Json(json!({
23        "data": 42,
24        "fields": ["a", "b", "c"],
25        "X": {"Y": 1, "z": 2},
26        "float_val": "1.34",
27    })))
28}
29
30///Return how many times `get_json` has been called. This introduces shared state implemented with
31/// Arc and Mutex.
32pub async fn get_json_counter(State(s): State<Arc<Mutex<AppState>>>) -> Result<String, StatusCode> {
33    match s.lock() {
34        Ok(x) => Ok(x.get_counter().to_string()),
35        Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR),
36    }
37}
38
39///Provide request json in form of `RequestJson` and outputs `ResponseJson`. Inside function
40/// body the input json will be parsed to `state` object.
41pub async fn append_to_string(Json(mut payload): Json<RequestJson>) -> Json<ResponseJson> {
42    payload.data.push_str(" world!");
43    Json(ResponseJson {
44        output: payload.data,
45    })
46}
47
48///Function generates single random number and returns it as `String`. `String` is viable output
49/// because `IntoResponse` trait is by default implemented for it.
50pub async fn get_random_number() -> String {
51    let mut rng = rand::thread_rng();
52    let y: f64 = rng.random();
53    y.to_string()
54}
55
56///Function calls external RestCountries API and extracts information about given country. Function
57/// expects query parameter in form of `?name=<country_name>`.
58pub async fn get_country(
59    query_params: Query<CountryQueryParams>,
60) -> Result<Json<Country>, StatusCode> {
61    let queried_name = query_params.0.name;
62
63    let response = match reqwest::get(format!(
64        "https://restcountries.com/v3.1/name/{queried_name}"
65    ))
66    .await
67    {
68        Ok(x) => x,
69        Err(_e) => return Err(StatusCode::SERVICE_UNAVAILABLE),
70    };
71
72    let mut parsed_vec = match response.json::<Vec<Country>>().await {
73        Ok(x) => x,
74        Err(_e) => return Err(StatusCode::UNPROCESSABLE_ENTITY),
75    };
76
77    match parsed_vec.pop() {
78        Some(x) => Ok(Json(x)),
79        None => Err(StatusCode::BAD_REQUEST),
80    }
81}