From cfbe772b33eb6201ef324c32902f7ec56076aedb Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 1 Apr 2026 22:59:49 +0200 Subject: [PATCH] WIP: working on OpenAPI docs --- Cargo.lock | 345 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 +- src/args.rs | 60 +++++++- src/daemon.rs | 131 ++++++++++++++---- src/daemon/api.rs | 11 +- src/lib.rs | 14 +- src/main.rs | 8 +- 7 files changed, 536 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3617d71..94e6098 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -173,6 +182,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bimap" version = "0.6.3" @@ -185,6 +200,15 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -196,6 +220,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + [[package]] name = "bytes" version = "1.11.1" @@ -305,6 +335,76 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.60.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -357,6 +457,7 @@ dependencies = [ "tracing-subscriber", "utoipa", "utoipa-axum", + "utoipa-swagger-ui", "which", ] @@ -388,6 +489,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "miniz_oxide", + "zlib-rs", +] + [[package]] name = "foldhash" version = "0.2.0" @@ -445,6 +556,27 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.32.3" @@ -760,6 +892,15 @@ version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -815,6 +956,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -822,6 +973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -878,6 +1030,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "owo-colors" version = "4.3.0" @@ -995,6 +1153,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.12.3" @@ -1030,6 +1199,40 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -1067,6 +1270,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1139,6 +1351,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1164,6 +1387,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "slab" version = "0.4.12" @@ -1288,6 +1517,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -1441,6 +1690,18 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1545,12 +1806,55 @@ dependencies = [ "utoipa-config", ] +[[package]] +name = "utoipa-swagger-ui" +version = "9.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55" +dependencies = [ + "axum", + "base64", + "dirs", + "mime_guess", + "regex", + "rust-embed", + "serde", + "serde_json", + "sha2", + "url", + "utoipa", + "utoipa-swagger-ui-vendored", + "zip", +] + +[[package]] +name = "utoipa-swagger-ui-vendored" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2eebbbfe4093922c2b6734d7c679ebfebd704a0d7e56dfcb0d05818ce28977d" + [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1566,6 +1870,15 @@ dependencies = [ "libc", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows" version = "0.61.3" @@ -1849,8 +2162,40 @@ dependencies = [ "syn", ] +[[package]] +name = "zip" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index cbc0c6e..ccd023c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,10 @@ name = "dynix" path = "src/lib.rs" [features] -default = ["regex-full"] +default = ["regex-full", "vendored-swagger"] regex-full = ["dep:regex"] regex-lite = ["dep:regex-lite"] +vendored-swagger = ["utoipa-swagger-ui/vendored"] [dependencies] axum = { version = "0.8.8", features = ["macros"] } @@ -54,11 +55,12 @@ tracing-human-layer = "0.2.1" tracing-subscriber = { version = "0.3.22", default-features = false, features = ["std", "env-filter", "fmt", "ansi", "registry", "parking_lot"] } utoipa = { version = "5.4.0", features = ["axum_extras", "config", "debug", "indexmap", "preserve_order", "preserve_path_order", "repr", "time", "url"] } utoipa-axum = { version = "0.2.0", features = ["debug"] } +utoipa-swagger-ui = { version = "9.0.2", features = ["axum", "cache", "debug"] } which = "8.0.2" [profile.dev] opt-level = 1 -lto = "thin" +#lto = "thin" [profile.release] debug = true diff --git a/src/args.rs b/src/args.rs index fb77cba..9662c47 100644 --- a/src/args.rs +++ b/src/args.rs @@ -9,7 +9,7 @@ use std::{ sync::{Arc, LazyLock}, }; -use clap::ColorChoice; +use clap::{ArgAction, ColorChoice}; use crate::prelude::*; @@ -23,6 +23,49 @@ pub struct AppendCmd { pub value: Arc, } +#[derive(Debug, Clone, PartialEq, Default)] +pub enum UnixPathOutput { + #[default] + Stdout, + Path(PathBuf), +} +impl UnixPathOutput { + pub fn is_stdout(&self) -> bool { + use UnixPathOutput::*; + match self { + Stdout => true, + Path(_) => false, + } + } +} +impl From<&OsStr> for UnixPathOutput { + fn from(other: &OsStr) -> UnixPathOutput { + use UnixPathOutput::*; + if other == OsStr::new("-") { + return Stdout; + } + Path(PathBuf::from(other)) + } +} +impl Display for UnixPathOutput { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + use UnixPathOutput::*; + match self { + Stdout => write!(f, "-")?, + Path(path) => { + if path.to_str().is_none() { + warn!( + "clap might complain that this path doesn't roundtrip through Display: {}", + path.display(), + ); + } + write!(f, "{}", path.display())? + }, + } + Ok(()) + } +} + /// Accept commands over (default stdin). #[derive(Debug, Clone, PartialEq, clap::Parser)] #[command(long_about = None)] @@ -30,6 +73,20 @@ pub struct DaemonCmd { /// Specify the bind address. #[arg(long, default_value = "0.0.0.0:42420")] pub bind: SocketAddr, + + /// Output generated OpenAPI docs, and exit. + #[arg(long)] + #[arg(default_missing_value = "-")] + #[arg(value_name = "dest")] + //#[arg(default_value = None)] + #[arg(action = ArgAction::Set)] + #[arg(num_args = 0..=1)] + //#[arg(require_equals = true)] + pub apidocs: Option, + + /// Host a swagger server at 0.0.0.0:42421. + #[arg(long)] + pub api_server: bool, } #[derive(Debug, Clone, PartialEq, clap::Parser)] @@ -45,7 +102,6 @@ pub enum Subcommand { Append(AppendCmd), Init(InitCmd), Daemon(DaemonCmd), - OpenApiDocs, } pub static DEFAULT_PATH: LazyLock> = LazyLock::new(|| { diff --git a/src/daemon.rs b/src/daemon.rs index da08960..d038266 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,7 +1,7 @@ use std::{ net::SocketAddr, process::{Output, Stdio}, - sync::LazyLock, + sync::{Arc, LazyLock}, }; use axum::{ @@ -11,8 +11,9 @@ use axum::{ routing::post, }; use tokio::{net::TcpListener, process::Command}; -//use utoipa::{OpenApi as _, ToSchema, openapi::OpenApi}; -//use utoipa_axum::router::{OpenApiRouter, UtoipaMethodRouterExt}; +use utoipa::{OpenApi as _, ToSchema, openapi::OpenApi}; +use utoipa_axum::router::{OpenApiRouter, UtoipaMethodRouterExt}; +use utoipa_swagger_ui::SwaggerUi; use serde::{Deserialize, Serialize}; @@ -46,17 +47,30 @@ pub static NIX: LazyLock<&'static Path> = LazyLock::new(|| { .unwrap_or(Path::new("/run/current-system/sw/bin/nix")) }); +pub const API_JSON_EP: &str = "/api-docs/openapi.json"; + pub async fn run(config: Config) { let addr = config.addr.clone(); - let router = Router::new() - .route("/set", post(ep_set_post)) - // `.with_state()` has to be last for the type inference to work. - .with_state(config); - //let (router, api): (Router, OpenApi) = OpenApiRouter::with_openapi(ApiDoc::openapi()) - // .routes(utoipa_axum::routes!(ep_set_post)) - // // `.with_state()` has to be last for the type inference works. - // .with_state(config) - // .split_for_parts(); + + let (router, api): (Router, OpenApi) = OpenApiRouter::with_openapi(DaemonApiDocs::openapi()) + .routes(utoipa_axum::routes!(ep_set_post)) + // `.with_state()` has to be last for the type inference works. + .with_state(config) + .split_for_parts(); + + let swagger = SwaggerUi::new("/swagger-ui").url(API_JSON_EP, DaemonApiDocs::openapi()); + let router = router.merge(Router::from(swagger)); + + //trace!("Router constructed; API is {api:?}"); + debug!( + "Router constructed, OpenAPI has {} path(s)", + api.paths.paths.len(), + ); + + //let router = Router::new() + // .route("/set", post(ep_set_post)) + // // `.with_state()` has to be last for the type inference to work. + // .with_state(config); let listener = TcpListener::bind(addr).await.unwrap(); @@ -67,36 +81,95 @@ pub async fn run(config: Config) { pub struct Config { pub config_file: SourceFile, pub addr: SocketAddr, - pub token: Option, + pub token: Arc>, } #[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Deserialize, Serialize)] -//#[derive(ToSchema)] +#[derive(ToSchema)] pub struct SetParams { + /// The name of the NixOS option to set, either as a dotted string, or a list of attribute path parts. pub name: ConvenientAttrPath, pub value: NixLiteral, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Deserialize, Serialize)] -//#[derive(ToSchema)] +#[derive(ToSchema)] +#[schema(examples( + json!({ + "status": 0, + "msg": null, + }), + json!({ + "status": -1, + "msg": "No such file or directory", + }), +))] pub struct SetResponse { - /// Will be 0 if everything is okay. + /// The exit code of the `nix` command that applied the configuration, + /// or a negative errno value if there was an error elsewhere. /// /// Will be -1 for an error with no code. pub status: i64, + + /// General remarks. If an error occurred, this field is used for the error message. pub msg: Option, } #[axum::debug_handler] -//#[utoipa::path( -// post, -// path = "/set", -// responses( -// (status = 200, description = "Request was valid", body = SetResponse) -// ), -//)] +#[utoipa::path( + post, + path = "/set", + request_body( + content = inline(SetParams), + examples( + ("Gotoscial" = ( + value = json!({ + "name": "services.gotosocial.settings.application-name", + "value": "My awesome Fedi instance!", + }), + description = "Set's Gotosocial's ActivityPub instance name to \"My awesome Fedi instance!\"", + )), + ("Harmonia" = ( + value = json!({ + "name": ["services", "harmonia", "settings", "workers"], + "value": 20, + }), + description = "Configures Harmonia to use 20 workers for building", + )) + ), + ), + responses( + // This syntax is wild. + ( + status = OK, + body = inline(SetResponse), + description = indoc::indoc!{" + Request processed successfully. Check `status` for if the *operation* as a whole succeeded. + "}, + examples( + ("1. Success" = ( + value = json!({ + "status": 0, + "msg": null, + }), + description = "What you should see for normal success", + )), + ("2. Command failure" = ( + value = json!({ + "status": 1, + "msg": "Stderr: error: cannot add a string to an integer", + }) + )) + ) + ), + (status = UNAUTHORIZED, description = indoc::indoc!{" + Server configured a required Authorization `token` and you provided the wrong one (or none at all) +
  • NOTE: this is currently unimplemented
  • + "}), + ), +)] async fn ep_set_post( State(config): State, headers: HeaderMap, @@ -104,7 +177,7 @@ async fn ep_set_post( ) -> Result, StatusCode> { debug!("POST /set with name={name:?}, value={value:?}"); - if let Some(token) = &config.token { + if let Some(token) = config.token.as_deref() { let Some(auth) = headers.get(http::header::AUTHORIZATION) else { // FIXME: technically RFC9110 requires us to respond with a // `WWW-Authenticate` header. @@ -224,8 +297,8 @@ async fn nix_run_apply(config: &Config) -> Result { Ok(output) } -//#[derive(Copy)] -//#[derive(Debug, Clone, PartialEq)] -//#[derive(utoipa::OpenApi)] -//#[openapi(paths(ep_set_post))] -//pub struct ApiDoc; +#[derive(Copy)] +#[derive(Debug, Clone, PartialEq)] +#[derive(utoipa::OpenApi)] +#[openapi(paths(ep_set_post))] +pub struct DaemonApiDocs; diff --git a/src/daemon/api.rs b/src/daemon/api.rs index 6708939..123d7d0 100644 --- a/src/daemon/api.rs +++ b/src/daemon/api.rs @@ -12,10 +12,19 @@ mod impls; /// This type does not provide a [`Default`] impl, however. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Deserialize, Serialize)] -#[derive(ToSchema)] #[serde(untagged)] +#[derive(ToSchema)] +#[schema(as = AttrPath, description = "Nix attribute path", title = "attrpath")] pub enum ConvenientAttrPath { + #[schema( + title = "Dotted", + example = "services.gotosocial.settings.application-name" + )] Dotted(Box), + #[schema( + title = "Split", + example = json!(["services", "gotosocial", "settings", "application-name"]), + )] Split(Box<[Box]>), } diff --git a/src/lib.rs b/src/lib.rs index c365f0f..8f36482 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,18 @@ pub fn do_append(args: Arc, append_args: AppendCmd) -> Result<(), BoxDynEr //#[tracing::instrument(level = "debug")] pub fn do_daemon(args: Arc, daemon_args: DaemonCmd) -> Result<(), BoxDynError> { + if let Some(dest) = daemon_args.apidocs { + let openapi = ::openapi(); + let as_json = openapi.to_pretty_json().unwrap(); + if dest.is_stdout() { + println!("{}", as_json); + } + + todo!("Generate API docs"); + info!("Generated API docs"); + return Ok(()); + } + let config_file: Arc = Arc::clone(&args.file); // FIXME: make configurable? @@ -150,7 +162,7 @@ pub fn do_daemon(args: Arc, daemon_args: DaemonCmd) -> Result<(), BoxDynEr config_file: SourceFile::new(config_file).unwrap(), addr: daemon_args.bind, // FIXME - token: None, + token: Arc::new(None), }; rt.block_on(async move { diff --git a/src/main.rs b/src/main.rs index 46ac99b..829fac7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,10 +45,10 @@ fn main_wrapped() -> Result<(), Box> { Append(append_args) => dynix::do_append(args.clone(), append_args.clone())?, Daemon(daemon_args) => dynix::do_daemon(args.clone(), daemon_args.clone())?, Init(init_args) => dynix::do_init(args.clone(), init_args.clone())?, - OpenApiDocs => { - //let api = dynix::ApiDoc::openapi(); - //dbg!(api); - }, + //OpenApiDocs => { + // //let api = dynix::ApiDoc::openapi(); + // //dbg!(api); + //}, }; }