Skip to main content

eduboardapi/api/
fetch.rs

1use std::sync::Arc;
2
3use axum::extract::{Query, State};
4use axum::http::StatusCode;
5use axum::response::{IntoResponse, Response};
6use axum::routing::get;
7use axum::{Json, Router};
8use serde::Deserialize;
9use tracing::instrument;
10
11use crate::exceptions::AppError;
12use crate::models::RequestData;
13use crate::services::{HttpClient, fetch_result};
14
15#[derive(Clone)]
16pub struct ApiState {
17    pub client: Arc<HttpClient>,
18}
19
20#[derive(Debug, Deserialize)]
21pub struct FetchPayload {
22    pub exam: String,
23    pub year: String,
24    pub board: String,
25    pub roll: String,
26    pub reg: String,
27}
28
29impl From<FetchPayload> for RequestData {
30    fn from(value: FetchPayload) -> Self {
31        Self {
32            exam: value.exam,
33            year: value.year,
34            board: value.board,
35            roll: value.roll,
36            reg: value.reg,
37        }
38    }
39}
40
41pub fn router(state: ApiState) -> Router {
42    Router::new()
43        .route("/fetch", get(fetch_get).post(fetch_post))
44        .with_state(state)
45}
46
47#[instrument(skip(state))]
48async fn fetch_get(State(state): State<ApiState>, Query(payload): Query<FetchPayload>) -> Response {
49    handle_fetch(state, payload).await
50}
51
52#[instrument(skip(state))]
53async fn fetch_post(State(state): State<ApiState>, Json(payload): Json<FetchPayload>) -> Response {
54    handle_fetch(state, payload).await
55}
56
57fn map_error(err: AppError) -> Response {
58    match err {
59        AppError::Network(msg) => (StatusCode::BAD_GATEWAY, msg).into_response(),
60        AppError::Parse(msg) => (StatusCode::BAD_GATEWAY, msg).into_response(),
61        AppError::Captcha(msg) => (StatusCode::BAD_REQUEST, msg).into_response(),
62    }
63}
64
65#[instrument(skip(state), fields(exam = %payload.exam, year = %payload.year, board = %payload.board, roll = %payload.roll))]
66async fn handle_fetch(state: ApiState, payload: FetchPayload) -> Response {
67    let request: RequestData = payload.into();
68    match fetch_result(state.client.as_ref(), &request).await {
69        Ok(result) => Json(result).into_response(),
70        Err(err) => map_error(err),
71    }
72}