use axum::Json;
use axum::extract::{Query, State};
use axum::http::StatusCode;
use serde::Deserialize;
use serde_json::{Value, json};
use crate::code::text::Change;
use crate::code::{AskError, Opened};
use crate::web::state::WebState;
type Refusal = (StatusCode, Json<Value>);
fn refusal(e: AskError) -> Refusal {
let message = e.to_string();
match e {
AskError::Stale(_) => (StatusCode::CONFLICT, Json(json!({ "error": message, "stale": true }))),
AskError::Resync(_) => (StatusCode::CONFLICT, Json(json!({ "error": message, "resync": true }))),
AskError::NotServed(_) => (StatusCode::BAD_REQUEST, Json(json!({ "error": message }))),
AskError::Unavailable(_) => (StatusCode::SERVICE_UNAVAILABLE, Json(json!({ "error": message }))),
AskError::Failed(_) => (StatusCode::UNPROCESSABLE_ENTITY, Json(json!({ "error": message }))),
}
}
fn failed(message: String) -> Refusal {
(StatusCode::UNPROCESSABLE_ENTITY, Json(json!({ "error": message })))
}
pub async fn status(State(state): State<WebState>) -> Json<Value> {
let omni = match state.code.omni() {
Ok(o) => json!({ "available": o }),
Err(why) => json!({ "unavailable": why, "message": why.to_string() }),
};
Json(json!({ "omni": omni, "roots": state.code.status().await }))
}
#[derive(Deserialize)]
pub struct Open {
client: String,
path: String,
version: i64,
text: String,
}
pub async fn open(State(state): State<WebState>, Json(b): Json<Open>) -> Result<Json<Opened>, Refusal> {
state.code.open(&b.client, &b.path, b.version, &b.text).await.map(Json).map_err(failed)
}
#[derive(Deserialize)]
pub struct Edit {
client: String,
path: String,
from: i64,
to: i64,
changes: Vec<Change>,
}
pub async fn change(State(state): State<WebState>, Json(b): Json<Edit>) -> Result<Json<Value>, Refusal> {
state.code.change(&b.client, &b.path, b.from, b.to, &b.changes).await.map_err(refusal)?;
Ok(Json(json!({})))
}
#[derive(Deserialize)]
pub struct Close {
client: String,
path: String,
}
pub async fn close(State(state): State<WebState>, Json(b): Json<Close>) -> Json<Value> {
state.code.close(&b.client, &b.path).await;
Json(json!({}))
}
#[derive(Deserialize)]
pub struct Ask {
client: String,
path: String,
version: i64,
feature: String,
#[serde(default = "empty")]
params: Value,
}
fn empty() -> Value {
json!({})
}
pub async fn ask(State(state): State<WebState>, Json(b): Json<Ask>) -> Result<Json<Value>, Refusal> {
if !b.params.is_object() {
return Err((StatusCode::BAD_REQUEST, Json(json!({ "error": "params must be an object" }))));
}
let result = state.code.ask(&b.client, &b.path, b.version, &b.feature, b.params).await.map_err(refusal)?;
Ok(Json(json!({ "result": result })))
}
#[derive(Deserialize)]
pub struct Restart {
root: String,
}
pub async fn restart(State(state): State<WebState>, Json(b): Json<Restart>) -> Result<Json<Value>, Refusal> {
state.code.restart(&b.root).await.map_err(|e| (StatusCode::SERVICE_UNAVAILABLE, Json(json!({ "error": e }))))?;
Ok(Json(json!({})))
}
pub async fn diagnostics(State(state): State<WebState>) -> Json<Value> {
Json(json!({ "events": state.code.diagnostics().await }))
}
#[derive(Deserialize)]
pub struct External {
token: String,
}
pub async fn external(State(state): State<WebState>, Query(q): Query<External>) -> Result<Json<Value>, Refusal> {
let (path, text) =
state.code.external(&q.token).await.map_err(|e| (StatusCode::NOT_FOUND, Json(json!({ "error": e }))))?;
Ok(Json(json!({ "path": path, "text": text })))
}