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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
aide = { version = "0.13.3", features = [
|
||||||
|
"axum",
|
||||||
|
"axum-extra",
|
||||||
|
"axum-extra-query",
|
||||||
|
"axum-ws",
|
||||||
|
"macros",
|
||||||
|
"scalar",
|
||||||
|
] }
|
||||||
anyhow = "1.0.82"
|
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"] }
|
clap = { version = "4.4.1", features = ["derive"] }
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
mpvipc = "1.3.0"
|
mpvipc = "1.3.0"
|
||||||
|
schemars = "0.8.16"
|
||||||
serde = { version = "1.0.188", features = ["derive"] }
|
serde = { version = "1.0.188", features = ["derive"] }
|
||||||
serde_json = "1.0.105"
|
serde_json = "1.0.105"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
tokio = { version = "1.32.0", features = ["full"] }
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
tower = { version = "0.4.13", features = ["full"] }
|
tower = { version = "0.4.13", features = ["full"] }
|
||||||
tower-http = { version = "0.4.3", 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::{
|
use axum::{
|
||||||
extract::{Query, State},
|
async_trait, extract::{rejection::{FailedToDeserializeQueryString, QueryRejection}, FromRequest, FromRequestParts, State}, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, routing::{delete, get, post}, Json, Router
|
||||||
http::StatusCode,
|
|
||||||
response::{IntoResponse, Response},
|
|
||||||
routing::{delete, get, post},
|
|
||||||
Json, Router,
|
|
||||||
};
|
};
|
||||||
use mpvipc::Mpv;
|
use mpvipc::Mpv;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use super::base;
|
use super::base;
|
||||||
|
|
||||||
pub fn rest_api_routes(mpv: Arc<Mutex<Mpv>>) -> Router {
|
// #[derive(FromRequest, OperationIo)]
|
||||||
Router::new()
|
// #[from_request(via(axum_jsonschema::Json), rejection(RestResponse))]
|
||||||
.route("/load", post(loadfile))
|
// #[aide(
|
||||||
.route("/play", get(play_get))
|
// input_with = "axum_jsonschema::Json<T>",
|
||||||
.route("/play", post(play_set))
|
// output_with = "axum_jsonschema::Json<T>",
|
||||||
.route("/volume", get(volume_get))
|
// json_schema
|
||||||
.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RestResponse(anyhow::Result<Value>);
|
pub struct RestResponse(anyhow::Result<Value>);
|
||||||
|
|
||||||
impl From<anyhow::Result<Value>> for RestResponse {
|
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 //
|
// Boilerplate galore //
|
||||||
// -------------------//
|
// -------------------//
|
||||||
|
|
||||||
// TODO: These could possibly be generated with a proc macro
|
// TODO: These could possibly be generated with a proc macro
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct LoadFileArgs {
|
struct LoadFileArgs {
|
||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[axum::debug_handler]
|
||||||
async fn loadfile(
|
async fn loadfile(
|
||||||
State(mpv): State<Arc<Mutex<Mpv>>>,
|
State(mpv): State<Arc<Mutex<Mpv>>>,
|
||||||
Query(query): Query<LoadFileArgs>,
|
Query(query): Query<LoadFileArgs>,
|
||||||
|
@ -79,11 +157,11 @@ async fn loadfile(
|
||||||
base::loadfile(mpv, &query.path).await.into()
|
base::loadfile(mpv, &query.path).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn play_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
async fn play_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> impl IntoApiResponse {
|
||||||
base::play_get(mpv).await.into()
|
RestResponse::from(base::play_get(mpv).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct PlaySetArgs {
|
struct PlaySetArgs {
|
||||||
play: String,
|
play: String,
|
||||||
}
|
}
|
||||||
|
@ -100,7 +178,7 @@ async fn volume_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
||||||
base::volume_get(mpv).await.into()
|
base::volume_get(mpv).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct VolumeSetArgs {
|
struct VolumeSetArgs {
|
||||||
volume: f64,
|
volume: f64,
|
||||||
}
|
}
|
||||||
|
@ -116,7 +194,7 @@ async fn time_get(State(mpv): State<Arc<Mutex<Mpv>>>) -> RestResponse {
|
||||||
base::time_get(mpv).await.into()
|
base::time_get(mpv).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct TimeSetArgs {
|
struct TimeSetArgs {
|
||||||
pos: Option<f64>,
|
pos: Option<f64>,
|
||||||
percent: 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()
|
base::playlist_previous(mpv).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct PlaylistGotoArgs {
|
struct PlaylistGotoArgs {
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
@ -153,7 +231,7 @@ async fn playlist_goto(
|
||||||
base::playlist_goto(mpv, query.index).await.into()
|
base::playlist_goto(mpv, query.index).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct PlaylistRemoveOrClearArgs {
|
struct PlaylistRemoveOrClearArgs {
|
||||||
index: Option<usize>,
|
index: Option<usize>,
|
||||||
}
|
}
|
||||||
|
@ -168,7 +246,7 @@ async fn playlist_remove_or_clear(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct PlaylistMoveArgs {
|
struct PlaylistMoveArgs {
|
||||||
index1: usize,
|
index1: usize,
|
||||||
index2: 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()
|
base::playlist_get_looping(mpv).await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, JsonSchema)]
|
||||||
struct PlaylistSetLoopingArgs {
|
struct PlaylistSetLoopingArgs {
|
||||||
r#loop: bool,
|
r#loop: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
|
mod mpv_broker;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
|
Loading…
Reference in New Issue