Skip to main content

lora_server/
app.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use axum::{
5    extract::State,
6    http::StatusCode,
7    response::IntoResponse,
8    routing::{get, post},
9    Json, Router,
10};
11use lora_database::{ExecuteOptions, QueryRunner, ResultFormat};
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Deserialize)]
15pub struct QueryRequest {
16    pub query: String,
17    #[serde(default)]
18    pub format: Option<QueryFormat>,
19}
20
21#[derive(Debug, Clone, Copy, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub enum QueryFormat {
24    Rows,
25    RowArrays,
26    Graph,
27    Combined,
28}
29
30impl From<QueryFormat> for ResultFormat {
31    fn from(value: QueryFormat) -> Self {
32        match value {
33            QueryFormat::Rows => ResultFormat::Rows,
34            QueryFormat::RowArrays => ResultFormat::RowArrays,
35            QueryFormat::Graph => ResultFormat::Graph,
36            QueryFormat::Combined => ResultFormat::Combined,
37        }
38    }
39}
40
41#[derive(Debug, Serialize)]
42pub struct ErrorResponse {
43    pub error: String,
44}
45
46#[derive(Debug, Serialize)]
47pub struct HealthResponse {
48    pub status: &'static str,
49}
50
51pub fn build_app<R>(db: Arc<R>) -> Router
52where
53    R: QueryRunner,
54{
55    Router::new()
56        .route("/health", get(health))
57        .route("/query", post(query::<R>))
58        .with_state(db)
59}
60
61pub async fn serve<R>(listener: tokio::net::TcpListener, db: Arc<R>) -> Result<()>
62where
63    R: QueryRunner,
64{
65    let app = build_app(db);
66    axum::serve(listener, app).await?;
67    Ok(())
68}
69
70async fn health() -> Json<HealthResponse> {
71    Json(HealthResponse { status: "ok" })
72}
73
74async fn query<R>(State(db): State<Arc<R>>, Json(req): Json<QueryRequest>) -> impl IntoResponse
75where
76    R: QueryRunner,
77{
78    let options = req.format.map(|format| ExecuteOptions {
79        format: format.into(),
80    });
81
82    match db.execute(&req.query, options) {
83        Ok(result) => (StatusCode::OK, Json(result)).into_response(),
84        Err(err) => (
85            StatusCode::BAD_REQUEST,
86            Json(ErrorResponse {
87                error: err.to_string(),
88            }),
89        )
90            .into_response(),
91    }
92}