mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-27 01:54:47 -07:00
server: api doc part 5
This commit is contained in:
122
Cargo.lock
generated
122
Cargo.lock
generated
@@ -1636,7 +1636,7 @@ checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3"
|
||||
dependencies = [
|
||||
"dispatch",
|
||||
"nix",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1768,7 +1768,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1878,7 +1878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3760,13 +3760,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick_cache"
|
||||
version = "0.6.16"
|
||||
version = "0.6.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ad6644cb07b7f3488b9f3d2fde3b4c0a7fa367cafefb39dff93a659f76eb786"
|
||||
checksum = "ba15f5bccfb18c666351668b97bbff66da5093f96757ca15299e4e594fe1316e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"equivalent",
|
||||
"hashbrown 0.15.5",
|
||||
"hashbrown 0.16.0",
|
||||
"parking_lot 0.12.5",
|
||||
]
|
||||
|
||||
@@ -4080,7 +4080,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4197,7 +4197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4588,7 +4588,7 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5282,7 +5282,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5293,9 +5293,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.1"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49e6c4a1f363c8210c6f77ba24f645c61c6fb941eccf013da691f7e09515b8ac"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
@@ -5305,18 +5305,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123e712f464a8a60ce1a13f4c446d2d43ab06464cb5842ff68f5c71b6fb7852e"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.1"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
@@ -5327,9 +5327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f3db6b24b120200d649cd4811b4947188ed3a8d2626f7075146c5d178a9a4a"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
@@ -5338,9 +5338,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.1"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5349,9 +5349,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.2"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5360,15 +5360,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
@@ -5376,18 +5376,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
@@ -5416,14 +5416,14 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.4",
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.1"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
@@ -5446,26 +5446,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.4"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab47f085ad6932defa48855254c758cdd0e2f2d48e62a34118a268d8f345e118"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
@@ -5478,9 +5478,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -5490,9 +5490,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -5502,9 +5502,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
@@ -5514,9 +5514,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -5526,9 +5526,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -5538,9 +5538,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -5550,9 +5550,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -5562,9 +5562,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
||||
@@ -41,7 +41,7 @@ panic = "abort"
|
||||
debug-assertions = false
|
||||
|
||||
[workspace.dependencies]
|
||||
aide = { version = "0.15.1", features = ["axum-json", "axum-query"], package = "brk-aide" }
|
||||
aide = { version = "0.15.2", features = ["axum-json", "axum-query"], package = "brk-aide" }
|
||||
allocative = { version = "0.3.4", features = ["parking_lot"] }
|
||||
axum = "0.8.6"
|
||||
bitcoin = { version = "0.32.7", features = ["serde"] }
|
||||
@@ -70,7 +70,7 @@ jiff = "0.2.15"
|
||||
log = "0.4.28"
|
||||
minreq = { version = "2.14.1", features = ["https", "serde_json"] }
|
||||
parking_lot = "0.12.5"
|
||||
quick_cache = "0.6.16"
|
||||
quick_cache = "0.6.17"
|
||||
rayon = "1.11.0"
|
||||
schemars = "1.0.4"
|
||||
serde = "1.0.228"
|
||||
|
||||
@@ -25,9 +25,10 @@ const VERSION: Version = Version::ZERO;
|
||||
pub struct Vecs {
|
||||
starting_height: Option<Height>,
|
||||
|
||||
#[vecs(skip)]
|
||||
#[traversable(skip)]
|
||||
pub state: Option<AddressCohortState>,
|
||||
|
||||
#[traversable(flatten)]
|
||||
pub inner: common::Vecs,
|
||||
|
||||
pub height_to_addr_count: EagerVec<Height, StoredU64>,
|
||||
|
||||
@@ -17,9 +17,10 @@ use crate::{
|
||||
pub struct Vecs {
|
||||
state_starting_height: Option<Height>,
|
||||
|
||||
#[vecs(skip)]
|
||||
#[traversable(skip)]
|
||||
pub state: Option<UTXOCohortState>,
|
||||
|
||||
#[traversable(flatten)]
|
||||
pub inner: common::Vecs,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn init(path: Option<&Path>) -> io::Result<()> {
|
||||
});
|
||||
|
||||
Builder::from_env(Env::default().default_filter_or(
|
||||
"info,bitcoin=off,bitcoincore-rpc=off,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off,rmcp=off,brk_rmcp=off,tracing=off",
|
||||
"info,bitcoin=off,bitcoincore-rpc=off,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off,rmcp=off,brk_rmcp=off,tracing=off,aide=off,brk_aide=off",
|
||||
))
|
||||
.format(move |buf, record| {
|
||||
let date_time = Timestamp::now()
|
||||
|
||||
@@ -21,7 +21,7 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::VecIterator;
|
||||
|
||||
use crate::extended::TransformResponseExtended;
|
||||
use crate::extended::{ResponseExtended, TransformResponseExtended};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
@@ -140,10 +140,7 @@ async fn get_transaction_info(
|
||||
|
||||
let bytes = sonic_rs::to_vec(&tx_info).unwrap();
|
||||
|
||||
Ok(Response::builder()
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes.into())
|
||||
.unwrap())
|
||||
Ok(Response::new_json_from_bytes(bytes))
|
||||
}
|
||||
|
||||
fn get_transaction_info_docs(op: TransformOperation) -> TransformOperation {
|
||||
|
||||
@@ -91,16 +91,14 @@ fn req_to_response_res(
|
||||
match interface.format(vecs, ¶ms.rest)? {
|
||||
Output::CSV(s) => {
|
||||
if let GuardResult::Guard(g) = guard_res {
|
||||
g.insert(s.clone().into())
|
||||
.map_err(|_| Error::QuickCacheError)?;
|
||||
let _ = g.insert(s.clone().into());
|
||||
}
|
||||
s.into_response()
|
||||
}
|
||||
Output::Json(v) => {
|
||||
let json = v.to_vec();
|
||||
if let GuardResult::Guard(g) = guard_res {
|
||||
g.insert(json.clone().into())
|
||||
.map_err(|_| Error::QuickCacheError)?;
|
||||
let _ = g.insert(json.clone().into());
|
||||
}
|
||||
json.into_response()
|
||||
}
|
||||
|
||||
@@ -103,10 +103,7 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
|
||||
|
||||
let bytes = sonic_rs::to_vec(&app_state.interface.get_metrics_catalog()).unwrap();
|
||||
|
||||
let mut response = Response::builder()
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes.into())
|
||||
.unwrap();
|
||||
let mut response = Response::new_json_from_bytes(bytes);
|
||||
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
|
||||
@@ -6,7 +6,8 @@ use aide::{
|
||||
};
|
||||
use axum::{
|
||||
Extension, Json,
|
||||
response::{Html, Redirect},
|
||||
http::HeaderMap,
|
||||
response::{Html, Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
@@ -15,7 +16,7 @@ use serde::Serialize;
|
||||
use crate::{
|
||||
VERSION,
|
||||
api::{chain::ChainRoutes, metrics::ApiMetricsRoutes},
|
||||
extended::TransformResponseExtended,
|
||||
extended::{HeaderMapExtended, ResponseExtended, TransformResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
@@ -76,8 +77,26 @@ impl ApiRoutes for ApiRouter<AppState> {
|
||||
.route(
|
||||
"/api.json",
|
||||
get(
|
||||
async |Extension(api): Extension<Arc<OpenApi>>| -> Json<Arc<OpenApi>> {
|
||||
Json(api)
|
||||
async |headers: HeaderMap,
|
||||
Extension(api): Extension<Arc<OpenApi>>|
|
||||
-> Response {
|
||||
let etag = VERSION;
|
||||
|
||||
if headers
|
||||
.get_if_none_match()
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
{
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
|
||||
let mut response =
|
||||
Response::new_json_from_bytes(sonic_rs::to_vec(&api).unwrap());
|
||||
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
headers.insert_etag(etag);
|
||||
|
||||
response
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
fn new_not_modified() -> Self;
|
||||
fn new_json_from_bytes(bytes: Vec<u8>) -> Self;
|
||||
}
|
||||
|
||||
impl ResponseExtended for Response<Body> {
|
||||
@@ -20,4 +21,11 @@ impl ResponseExtended for Response<Body> {
|
||||
headers.insert_cors();
|
||||
response
|
||||
}
|
||||
|
||||
fn new_json_from_bytes(bytes: Vec<u8>) -> Self {
|
||||
Response::builder()
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes.into())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,263 +2,352 @@ use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
|
||||
|
||||
#[proc_macro_derive(Traversable, attributes(vecs))]
|
||||
#[proc_macro_derive(Traversable, attributes(traversable))]
|
||||
pub fn derive_traversable(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
let generics = &input.generics;
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
|
||||
let traverse_impl = match &input.data {
|
||||
Data::Struct(data) => {
|
||||
match &data.fields {
|
||||
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
||||
// Special case for single-field tuple structs - just delegate
|
||||
let generic_params: Vec<_> = generics.type_params().map(|p| &p.ident).collect();
|
||||
let original_predicates =
|
||||
&generics.where_clause.as_ref().map(|w| &w.predicates);
|
||||
|
||||
let where_clause =
|
||||
if original_predicates.is_some() || !generic_params.is_empty() {
|
||||
quote! {
|
||||
where
|
||||
#(#generic_params: Send + Sync,)*
|
||||
#original_predicates
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
return TokenStream::from(quote! {
|
||||
impl #impl_generics Traversable for #name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
self.0.to_tree_node()
|
||||
}
|
||||
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
self.0.iter_any_collectable()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// Normal struct with named fields
|
||||
let field_traversals = generate_field_traversals(&data.fields);
|
||||
let iterator_impl = generate_iterator_impl(&data.fields);
|
||||
|
||||
let generic_params: Vec<_> = generics.type_params().map(|p| &p.ident).collect();
|
||||
|
||||
let generics_needing_traversable =
|
||||
if let Fields::Named(named_fields) = &data.fields {
|
||||
let mut used = std::collections::BTreeSet::new();
|
||||
|
||||
for field in named_fields.named.iter() {
|
||||
if !should_process_field(field) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Type::Path(type_path) = &field.ty
|
||||
&& type_path.path.segments.len() == 1
|
||||
&& let Some(seg) = type_path.path.segments.first()
|
||||
&& seg.arguments.is_empty()
|
||||
&& let Some(pos) =
|
||||
generic_params.iter().position(|g| g == &&seg.ident)
|
||||
{
|
||||
used.insert(generic_params[pos]);
|
||||
}
|
||||
}
|
||||
used.into_iter().collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let original_predicates =
|
||||
&generics.where_clause.as_ref().map(|w| &w.predicates);
|
||||
|
||||
let where_clause = if !generics_needing_traversable.is_empty()
|
||||
|| original_predicates.is_some()
|
||||
|| !generic_params.is_empty()
|
||||
{
|
||||
quote! {
|
||||
where
|
||||
#(#generics_needing_traversable: brk_traversable::Traversable,)*
|
||||
#(#generic_params: Send + Sync,)*
|
||||
#original_predicates
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl #impl_generics Traversable for #name #ty_generics
|
||||
#where_clause
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
#field_traversals
|
||||
}
|
||||
|
||||
#iterator_impl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return syn::Error::new_spanned(
|
||||
&input.ident,
|
||||
"Traversable can only be derived for structs",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let Data::Struct(data) = &input.data else {
|
||||
return syn::Error::new_spanned(
|
||||
&input.ident,
|
||||
"Traversable can only be derived for structs",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
TokenStream::from(traverse_impl)
|
||||
}
|
||||
|
||||
fn should_process_field(field: &syn::Field) -> bool {
|
||||
matches!(field.vis, syn::Visibility::Public(_)) && !has_skip_attribute(field)
|
||||
}
|
||||
|
||||
fn generate_field_traversals(fields: &Fields) -> proc_macro2::TokenStream {
|
||||
match fields {
|
||||
Fields::Named(fields) => {
|
||||
let entries = fields.named.iter().filter_map(|f| {
|
||||
let field_name = f.ident.as_ref()?;
|
||||
let field_name_str = field_name.to_string();
|
||||
|
||||
if !should_process_field(f) {
|
||||
return None;
|
||||
// Handle single-field tuple struct delegation
|
||||
if let Fields::Unnamed(fields) = &data.fields
|
||||
&& fields.unnamed.len() == 1
|
||||
{
|
||||
let where_clause = build_where_clause(generics, &[]);
|
||||
return TokenStream::from(quote! {
|
||||
impl #impl_generics Traversable for #name #ty_generics #where_clause {
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
self.0.to_tree_node()
|
||||
}
|
||||
|
||||
if get_option_inner_type(&f.ty).is_some() {
|
||||
Some(quote! {
|
||||
self.#field_name.as_ref().map(|nested| (String::from(#field_name_str), nested.to_tree_node()))
|
||||
})
|
||||
} else {
|
||||
Some(quote! {
|
||||
Some((String::from(#field_name_str), self.#field_name.to_tree_node()))
|
||||
})
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
self.0.iter_any_collectable()
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
let collected: std::collections::BTreeMap<_, _> = [#(#entries,)*]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
// return if collected.len() == 1 {
|
||||
// collected.into_values().next().unwrap()
|
||||
// } else {
|
||||
brk_traversable::TreeNode::Branch(collected)
|
||||
// };
|
||||
}
|
||||
}
|
||||
_ => quote! {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn has_skip_attribute(field: &syn::Field) -> bool {
|
||||
field.attrs.iter().any(|attr| {
|
||||
attr.path().is_ident("vecs")
|
||||
&& attr
|
||||
.parse_args::<syn::Ident>()
|
||||
.map(|ident| ident == "skip")
|
||||
.unwrap_or(false)
|
||||
// Handle named fields
|
||||
let Fields::Named(named_fields) = &data.fields else {
|
||||
return TokenStream::from(quote! {
|
||||
impl #impl_generics Traversable for #name #ty_generics {
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(std::collections::BTreeMap::new())
|
||||
}
|
||||
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
std::iter::empty()
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let generic_params: Vec<_> = generics.type_params().map(|p| &p.ident).collect();
|
||||
|
||||
let (field_infos, generics_needing_traversable) = analyze_fields(named_fields, &generic_params);
|
||||
|
||||
let field_traversals = generate_field_traversals(&field_infos);
|
||||
let iterator_impl = generate_iterator_impl(&field_infos);
|
||||
let where_clause = build_where_clause(generics, &generics_needing_traversable);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl #impl_generics Traversable for #name #ty_generics #where_clause {
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
#field_traversals
|
||||
}
|
||||
|
||||
#iterator_impl
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_iterator_impl(fields: &Fields) -> proc_macro2::TokenStream {
|
||||
match fields {
|
||||
Fields::Named(fields) => {
|
||||
let mut regular_fields = Vec::new();
|
||||
let mut option_fields = Vec::new();
|
||||
enum FieldAttr {
|
||||
Normal,
|
||||
Flatten,
|
||||
}
|
||||
|
||||
for field in fields.named.iter() {
|
||||
if let Some(field_name) = &field.ident {
|
||||
if !should_process_field(field) {
|
||||
continue;
|
||||
}
|
||||
struct FieldInfo<'a> {
|
||||
name: &'a syn::Ident,
|
||||
is_option: bool,
|
||||
attr: FieldAttr,
|
||||
}
|
||||
|
||||
if get_option_inner_type(&field.ty).is_some() {
|
||||
option_fields.push(field_name);
|
||||
} else {
|
||||
regular_fields.push(field_name);
|
||||
}
|
||||
fn analyze_fields<'a>(
|
||||
fields: &'a syn::FieldsNamed,
|
||||
generic_params: &[&'a syn::Ident],
|
||||
) -> (Vec<FieldInfo<'a>>, Vec<&'a syn::Ident>) {
|
||||
let mut field_infos = Vec::new();
|
||||
let mut generics_set = std::collections::BTreeSet::new();
|
||||
|
||||
for field in &fields.named {
|
||||
let field_attr = get_field_attr(field);
|
||||
|
||||
// Skip attribute means don't process at all
|
||||
if field_attr.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !matches!(field.vis, syn::Visibility::Public(_)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(field_name) = &field.ident else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Type::Path(type_path) = &field.ty
|
||||
&& type_path.path.segments.len() == 1
|
||||
&& let Some(seg) = type_path.path.segments.first()
|
||||
&& seg.arguments.is_empty()
|
||||
&& let Some(¶m) = generic_params.iter().find(|&&g| g == &seg.ident)
|
||||
{
|
||||
generics_set.insert(param);
|
||||
}
|
||||
|
||||
field_infos.push(FieldInfo {
|
||||
name: field_name,
|
||||
is_option: is_option_type(&field.ty),
|
||||
attr: field_attr.unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
(field_infos, generics_set.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Returns None for skip, Some(attr) for normal/flatten
|
||||
fn get_field_attr(field: &syn::Field) -> Option<FieldAttr> {
|
||||
for attr in &field.attrs {
|
||||
if attr.path().is_ident("traversable")
|
||||
&& let Ok(ident) = attr.parse_args::<syn::Ident>()
|
||||
{
|
||||
return match ident.to_string().as_str() {
|
||||
"skip" => None,
|
||||
"flatten" => Some(FieldAttr::Flatten),
|
||||
_ => Some(FieldAttr::Normal),
|
||||
};
|
||||
}
|
||||
}
|
||||
Some(FieldAttr::Normal)
|
||||
}
|
||||
|
||||
fn is_option_type(ty: &Type) -> bool {
|
||||
matches!(
|
||||
ty,
|
||||
Type::Path(type_path)
|
||||
if type_path.path.segments.last()
|
||||
.is_some_and(|seg| seg.ident == "Option")
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_field_traversals(infos: &[FieldInfo]) -> proc_macro2::TokenStream {
|
||||
let has_flatten = infos.iter().any(|i| matches!(i.attr, FieldAttr::Flatten));
|
||||
let has_normal = infos.iter().any(|i| matches!(i.attr, FieldAttr::Normal));
|
||||
|
||||
if !has_flatten {
|
||||
// Fast path: no flatten, simple collection
|
||||
let entries = infos.iter().map(|info| {
|
||||
let field_name = info.name;
|
||||
let field_name_str = field_name.to_string();
|
||||
|
||||
if info.is_option {
|
||||
quote! {
|
||||
self.#field_name.as_ref().map(|nested| (String::from(#field_name_str), nested.to_tree_node()))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Some((String::from(#field_name_str), self.#field_name.to_tree_node()))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if regular_fields.is_empty() && option_fields.is_empty() {
|
||||
return quote! {
|
||||
let collected: std::collections::BTreeMap<_, _> = [#(#entries,)*]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
brk_traversable::TreeNode::Branch(collected)
|
||||
};
|
||||
}
|
||||
|
||||
// Has flatten fields
|
||||
if !has_normal {
|
||||
// Only flatten fields, no normal fields - need explicit type annotation
|
||||
let flatten_entries = infos.iter()
|
||||
.filter(|i| matches!(i.attr, FieldAttr::Flatten))
|
||||
.map(|info| {
|
||||
let field_name = info.name;
|
||||
|
||||
if info.is_option {
|
||||
quote! {
|
||||
if let Some(ref nested) = self.#field_name {
|
||||
if let brk_traversable::TreeNode::Branch(map) = nested.to_tree_node() {
|
||||
collected.extend(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
if let brk_traversable::TreeNode::Branch(map) = self.#field_name.to_tree_node() {
|
||||
collected.extend(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return quote! {
|
||||
let mut collected: std::collections::BTreeMap<String, brk_traversable::TreeNode> =
|
||||
std::collections::BTreeMap::new();
|
||||
|
||||
#(#flatten_entries)*
|
||||
|
||||
brk_traversable::TreeNode::Branch(collected)
|
||||
};
|
||||
}
|
||||
|
||||
// Has both normal and flatten fields
|
||||
let normal_entries = infos.iter()
|
||||
.filter(|i| matches!(i.attr, FieldAttr::Normal))
|
||||
.map(|info| {
|
||||
let field_name = info.name;
|
||||
let field_name_str = field_name.to_string();
|
||||
|
||||
if info.is_option {
|
||||
quote! {
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
std::iter::empty()
|
||||
self.#field_name.as_ref().map(|nested| (String::from(#field_name_str), nested.to_tree_node()))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Some((String::from(#field_name_str), self.#field_name.to_tree_node()))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let flatten_entries = infos.iter()
|
||||
.filter(|i| matches!(i.attr, FieldAttr::Flatten))
|
||||
.map(|info| {
|
||||
let field_name = info.name;
|
||||
|
||||
if info.is_option {
|
||||
quote! {
|
||||
if let Some(ref nested) = self.#field_name {
|
||||
if let brk_traversable::TreeNode::Branch(map) = nested.to_tree_node() {
|
||||
collected.extend(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let (init_part, chain_part) = if !regular_fields.is_empty() {
|
||||
let first = regular_fields.first().unwrap();
|
||||
let rest = ®ular_fields[1..];
|
||||
(
|
||||
quote! {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyCollectableVec>> =
|
||||
Box::new(self.#first.iter_any_collectable());
|
||||
},
|
||||
quote! {
|
||||
#(regular_iter = Box::new(regular_iter.chain(self.#rest.iter_any_collectable()));)*
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
quote! {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyCollectableVec>> =
|
||||
Box::new(std::iter::empty());
|
||||
},
|
||||
quote! {},
|
||||
)
|
||||
};
|
||||
|
||||
let option_part = if !option_fields.is_empty() {
|
||||
let chains = option_fields.iter().map(|f| {
|
||||
quote! {
|
||||
if let Some(ref x) = self.#f {
|
||||
regular_iter = Box::new(regular_iter.chain(x.iter_any_collectable()));
|
||||
}
|
||||
}
|
||||
});
|
||||
quote! { #(#chains)* }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
quote! {
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
#init_part
|
||||
#chain_part
|
||||
#option_part
|
||||
regular_iter
|
||||
if let brk_traversable::TreeNode::Branch(map) = self.#field_name.to_tree_node() {
|
||||
collected.extend(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => quote! {
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
std::iter::empty()
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
quote! {
|
||||
let mut collected: std::collections::BTreeMap<_, _> = [#(#normal_entries,)*]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
#(#flatten_entries)*
|
||||
|
||||
brk_traversable::TreeNode::Branch(collected)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_option_inner_type(ty: &Type) -> Option<&Type> {
|
||||
if let Type::Path(type_path) = ty
|
||||
&& let Some(segment) = type_path.path.segments.last()
|
||||
&& segment.ident == "Option"
|
||||
&& let syn::PathArguments::AngleBracketed(args) = &segment.arguments
|
||||
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first()
|
||||
{
|
||||
return Some(inner_ty);
|
||||
fn generate_iterator_impl(infos: &[FieldInfo]) -> proc_macro2::TokenStream {
|
||||
let regular_fields: Vec<_> = infos
|
||||
.iter()
|
||||
.filter(|i| !i.is_option)
|
||||
.map(|i| i.name)
|
||||
.collect();
|
||||
|
||||
let option_fields: Vec<_> = infos
|
||||
.iter()
|
||||
.filter(|i| i.is_option)
|
||||
.map(|i| i.name)
|
||||
.collect();
|
||||
|
||||
if regular_fields.is_empty() && option_fields.is_empty() {
|
||||
return quote! {
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
std::iter::empty()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let (init_part, chain_part) = if let Some((&first, rest)) = regular_fields.split_first() {
|
||||
(
|
||||
quote! {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyCollectableVec>> =
|
||||
Box::new(self.#first.iter_any_collectable());
|
||||
},
|
||||
quote! {
|
||||
#(regular_iter = Box::new(regular_iter.chain(self.#rest.iter_any_collectable()));)*
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
quote! {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn vecdb::AnyCollectableVec>> =
|
||||
Box::new(std::iter::empty());
|
||||
},
|
||||
quote! {},
|
||||
)
|
||||
};
|
||||
|
||||
let option_part = if !option_fields.is_empty() {
|
||||
let chains = option_fields.iter().map(|f| {
|
||||
quote! {
|
||||
if let Some(ref x) = self.#f {
|
||||
regular_iter = Box::new(regular_iter.chain(x.iter_any_collectable()));
|
||||
}
|
||||
}
|
||||
});
|
||||
quote! { #(#chains)* }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
quote! {
|
||||
fn iter_any_collectable(&self) -> impl Iterator<Item = &dyn vecdb::AnyCollectableVec> {
|
||||
#init_part
|
||||
#chain_part
|
||||
#option_part
|
||||
regular_iter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_where_clause(
|
||||
generics: &syn::Generics,
|
||||
generics_needing_traversable: &[&syn::Ident],
|
||||
) -> proc_macro2::TokenStream {
|
||||
let generic_params: Vec<_> = generics.type_params().map(|p| &p.ident).collect();
|
||||
let original_predicates = generics.where_clause.as_ref().map(|w| &w.predicates);
|
||||
|
||||
if generics_needing_traversable.is_empty()
|
||||
&& generic_params.is_empty()
|
||||
&& original_predicates.is_none()
|
||||
{
|
||||
return quote! {};
|
||||
}
|
||||
|
||||
quote! {
|
||||
where
|
||||
#(#generics_needing_traversable: brk_traversable::Traversable,)*
|
||||
#(#generic_params: Send + Sync,)*
|
||||
#original_predicates
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user