WIP: aide OpenAPI docs
This commit is contained in:
parent
3d370d255f
commit
9838d8e8f0
14
Cargo.toml
14
Cargo.toml
|
@ -6,14 +6,26 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aide = { version = "0.13.3", features = [
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"axum-extra-query",
|
||||
"axum-ws",
|
||||
"macros",
|
||||
"scalar",
|
||||
] }
|
||||
anyhow = "1.0.82"
|
||||
axum = { version = "0.6.20", features = ["macros"] }
|
||||
axum = { version = "0.6.20", features = ["macros", "ws"] }
|
||||
axum-jsonschema = { version = "0.8.0", features = ["aide"] }
|
||||
axum-macros = "0.4.1"
|
||||
clap = { version = "4.4.1", features = ["derive"] }
|
||||
env_logger = "0.10.0"
|
||||
log = "0.4.20"
|
||||
mpvipc = "1.3.0"
|
||||
schemars = "0.8.16"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0.105"
|
||||
serde_urlencoded = "0.7.1"
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tower = { version = "0.4.13", features = ["full"] }
|
||||
tower-http = { version = "0.4.3", features = ["full"] }
|
||||
|
|
|
@ -1,39 +1,26 @@
|
|||
use std::sync::Arc;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use aide::{axum::IntoApiResponse, operation::OperationIo, OperationOutput};
|
||||
use axum_jsonschema::JsonSchemaRejection;
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
async_trait, extract::{rejection::{FailedToDeserializeQueryString, QueryRejection}, FromRequest, FromRequestParts, State}, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, routing::{delete, get, post}, Json, Router
|
||||
};
|
||||
use mpvipc::Mpv;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::base;
|
||||
|
||||
pub fn rest_api_routes(mpv: Arc<Mutex<Mpv>>) -> Router {
|
||||
Router::new()
|
||||
.route("/load", post(loadfile))
|
||||
.route("/play", get(play_get))
|
||||
.route("/play", post(play_set))
|
||||
.route("/volume", get(volume_get))
|
||||
.route("/volume", post(volume_set))
|
||||
.route("/time", get(time_get))
|
||||
.route("/time", post(time_set))
|
||||
.route("/playlist", get(playlist_get))
|
||||
.route("/playlist/next", post(playlist_next))
|
||||
.route("/playlist/previous", post(playlist_previous))
|
||||
.route("/playlist/goto", post(playlist_goto))
|
||||
.route("/playlist", delete(playlist_remove_or_clear))
|
||||
.route("/playlist/move", post(playlist_move))
|
||||
.route("/playlist/shuffle", post(shuffle))
|
||||
.route("/playlist/loop", get(playlist_get_looping))
|
||||
.route("/playlist/loop", post(playlist_set_looping))
|
||||
.with_state(mpv)
|
||||
}
|
||||
|
||||
// #[derive(FromRequest, OperationIo)]
|
||||
// #[from_request(via(axum_jsonschema::Json), rejection(RestResponse))]
|
||||
// #[aide(
|
||||
// input_with = "axum_jsonschema::Json<T>",
|
||||
// output_with = "axum_jsonschema::Json<T>",
|
||||
// json_schema
|
||||
// )]
|
||||
pub struct RestResponse(anyhow::Result<Value>);
|
||||
|
||||
impl From<anyhow::Result<Value>> for RestResponse {
|
||||
|
@ -61,17 +48,108 @@ impl IntoResponse for RestResponse {
|
|||
}
|
||||
}
|
||||
|
||||
impl aide::OperationOutput for RestResponse {
|
||||
type Inner = anyhow::Result<Value>;
|
||||
}
|
||||
|
||||
/// -------
|
||||
|
||||
// impl<T> aide::OperationInput for Query<T> {}
|
||||
|
||||
// #[derive(FromRequest, OperationIo)]
|
||||
// #[from_request(via(axum_jsonschema::Json), rejection(RestResponse))]
|
||||
// #[aide(
|
||||
// input_with = "axum_jsonschema::Json<T>",
|
||||
// output_with = "axum_jsonschema::Json<T>",
|
||||
// json_schema
|
||||
// )]
|
||||
// pub struct Json<T>(pub T);
|
||||
|
||||
// impl<T> IntoResponse for Json<T>
|
||||
// where
|
||||
// T: Serialize,
|
||||
// {
|
||||
// fn into_response(self) -> axum::response::Response {
|
||||
// axum::Json(self.0).into_response()
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(OperationIo)]
|
||||
#[aide(json_schema)]
|
||||
pub struct Query<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl <T, S> FromRequestParts<S> for Query<T>
|
||||
where
|
||||
T: JsonSchema + DeserializeOwned,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = QueryRejection;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
let axum::extract::Query(query) = axum::extract::Query::try_from_uri(&parts.uri)?;
|
||||
Ok(Query(query))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Query<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rest_api_route_docs(mpv: Arc<Mutex<Mpv>>) -> Router {
|
||||
use aide::axum::ApiRouter;
|
||||
use aide::axum::routing::{delete, get, post};
|
||||
|
||||
let mut api = aide::openapi::OpenApi::default();
|
||||
|
||||
let x = ApiRouter::new()
|
||||
// .api_route("/load", get(loadfile))
|
||||
.api_route("/play", get(play_get))
|
||||
.finish_api(&mut api);
|
||||
// .with_state(mpv);
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
pub fn rest_api_routes(mpv: Arc<Mutex<Mpv>>) -> Router {
|
||||
Router::new()
|
||||
.route("/load", post(loadfile))
|
||||
.route("/play", get(play_get))
|
||||
.route("/play", post(play_set))
|
||||
.route("/volume", get(volume_get))
|
||||
.route("/volume", post(volume_set))
|
||||
.route("/time", get(time_get))
|
||||
.route("/time", post(time_set))
|
||||
.route("/playlist", get(playlist_get))
|
||||
.route("/playlist/next", post(playlist_next))
|
||||
.route("/playlist/previous", post(playlist_previous))
|
||||
.route("/playlist/goto", post(playlist_goto))
|
||||
.route("/playlist", delete(playlist_remove_or_clear))
|
||||
.route("/playlist/move", post(playlist_move))
|
||||
.route("/playlist/shuffle", post(shuffle))
|
||||
.route("/playlist/loop", get(playlist_get_looping))
|
||||
.route("/playlist/loop", post(playlist_set_looping))
|
||||
.with_state(mpv)
|
||||
}
|
||||
|
||||
// -------------------//
|
||||
// Boilerplate galore //
|
||||
// -------------------//
|
||||
|
||||
// TODO: These could possibly be generated with a proc macro
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct LoadFileArgs {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
async fn loadfile(
|
||||
State(mpv): State<Arc<Mutex<Mpv>>>,
|
||||
Query(query): Query<LoadFileArgs>,
|
||||
|
@ -79,11 +157,11 @@ async fn loadfile(
|
|||
base::loadfile(mpv, &query.path).await.into()
|
||||
}
|
||||
|
||||
async fn play_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
||||
base::play_get(mpv).await.into()
|
||||
async fn play_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> impl IntoApiResponse {
|
||||
RestResponse::from(base::play_get(mpv).await)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct PlaySetArgs {
|
||||
play: String,
|
||||
}
|
||||
|
@ -100,7 +178,7 @@ async fn volume_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
|||
base::volume_get(mpv).await.into()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct VolumeSetArgs {
|
||||
volume: f64,
|
||||
}
|
||||
|
@ -116,7 +194,7 @@ async fn time_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
|||
base::time_get(mpv).await.into()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct TimeSetArgs {
|
||||
pos: Option<f64>,
|
||||
percent: Option<f64>,
|
||||
|
@ -141,7 +219,7 @@ async fn playlist_previous(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
|||
base::playlist_previous(mpv).await.into()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct PlaylistGotoArgs {
|
||||
index: usize,
|
||||
}
|
||||
|
@ -153,7 +231,7 @@ async fn playlist_goto(
|
|||
base::playlist_goto(mpv, query.index).await.into()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct PlaylistRemoveOrClearArgs {
|
||||
index: Option<usize>,
|
||||
}
|
||||
|
@ -168,7 +246,7 @@ async fn playlist_remove_or_clear(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct PlaylistMoveArgs {
|
||||
index1: usize,
|
||||
index2: usize,
|
||||
|
@ -191,7 +269,7 @@ async fn playlist_get_looping(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestRespons
|
|||
base::playlist_get_looping(mpv).await.into()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[derive(serde::Deserialize, JsonSchema)]
|
||||
struct PlaylistSetLoopingArgs {
|
||||
r#loop: bool,
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use tokio::{
|
|||
};
|
||||
|
||||
mod api;
|
||||
mod mpv_broker;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
|
|
Loading…
Reference in New Issue