diff --git a/Cargo.lock b/Cargo.lock index 32e9a2050..7a6c9395c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,6 +653,7 @@ dependencies = [ "brk_types", "brk_website", "brotli", + "color-eyre", "derive_more", "flate2", "jiff", diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index e13e707c5..a0583db10 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -8,7 +8,7 @@ use brk_reader::Reader; use brk_traversable::Traversable; use brk_types::Version; use tracing::info; -use vecdb::{Exit, Ro, Rw, StorageMode}; +use vecdb::{AnyExportableVec, Exit, Ro, Rw, StorageMode}; mod blocks; mod cointime; @@ -493,41 +493,27 @@ impl Computer { } } -impl Computer { - /// Iterate over all exportable vecs with their database name. - pub fn iter_named_exportable( - &self, - ) -> impl Iterator { - use brk_traversable::Traversable; - - macro_rules! named { - ($($field:ident),+ $(,)?) => { +macro_rules! impl_iter_named_exportable { + ($($field:ident),+ $(,)?) => { + impl_iter_named_exportable!(@mode Ro, $($field),+); + impl_iter_named_exportable!(@mode Rw, $($field),+); + }; + (@mode $mode:ty, $($field:ident),+) => { + impl Computer<$mode> { + pub fn iter_named_exportable( + &self, + ) -> impl Iterator { + use brk_traversable::Traversable; std::iter::empty() $(.chain(self.$field.iter_any_exportable().map(|v| ($field::DB_NAME, v))))+ - }; + } } - - named!( - blocks, - mining, - transactions, - scripts, - positions, - cointime, - constants, - indicators, - indexes, - market, - pools, - prices, - distribution, - supply, - inputs, - outputs, - ) - } + }; } +impl_iter_named_exportable!(blocks, mining, transactions, scripts, positions, cointime, + constants, indicators, indexes, market, pools, prices, distribution, supply, inputs, outputs); + fn timed(label: &str, f: impl FnOnce() -> T) -> T { let start = Instant::now(); let result = f(); diff --git a/crates/brk_query/examples/list.rs b/crates/brk_query/examples/list.rs index eaef9c1dd..b5c041998 100644 --- a/crates/brk_query/examples/list.rs +++ b/crates/brk_query/examples/list.rs @@ -6,7 +6,6 @@ use brk_query::Vecs; use vecdb::ReadOnlyClone; pub fn main() -> brk_error::Result<()> { - let tmp = env::temp_dir().join("brk_search_gen"); fs::create_dir_all(&tmp)?; diff --git a/crates/brk_query/src/vecs.rs b/crates/brk_query/src/vecs.rs index d70dec2b8..c2f8774eb 100644 --- a/crates/brk_query/src/vecs.rs +++ b/crates/brk_query/src/vecs.rs @@ -8,7 +8,7 @@ use brk_types::{ }; use derive_more::{Deref, DerefMut}; use quickmatch::{QuickMatch, QuickMatchConfig}; -use vecdb::{AnyExportableVec, Ro}; +use vecdb::AnyExportableVec; #[derive(Default)] pub struct Vecs<'a> { @@ -25,17 +25,34 @@ pub struct Vecs<'a> { } impl<'a> Vecs<'a> { - pub fn build(indexer: &'a Indexer, computer: &'a Computer) -> Self { + pub fn build(indexer: &'a Indexer, computer: &'a Computer) -> Self { + Self::build_from( + indexer.vecs.iter_any_exportable(), + indexer.vecs.to_tree_node(), + computer.iter_named_exportable(), + computer.to_tree_node(), + ) + } + + pub fn build_rw(indexer: &'a Indexer, computer: &'a Computer) -> Self { + Self::build_from( + indexer.vecs.iter_any_exportable(), + indexer.vecs.to_tree_node(), + computer.iter_named_exportable(), + computer.to_tree_node(), + ) + } + + fn build_from( + indexed_vecs: impl Iterator, + indexed_tree: TreeNode, + computed_vecs: impl Iterator, + computed_tree: TreeNode, + ) -> Self { let mut this = Vecs::default(); - indexer - .vecs - .iter_any_exportable() - .for_each(|vec| this.insert(vec, "indexed")); - - computer - .iter_named_exportable() - .for_each(|(db, vec)| this.insert(vec, db)); + indexed_vecs.for_each(|vec| this.insert(vec, "indexed")); + computed_vecs.for_each(|(db, vec)| this.insert(vec, db)); let mut ids = this .metric_to_index_to_vec @@ -97,8 +114,8 @@ impl<'a> Vecs<'a> { this.catalog.replace( TreeNode::Branch( [ - ("indexed".to_string(), indexer.vecs.to_tree_node()), - ("computed".to_string(), computer.to_tree_node()), + ("indexed".to_string(), indexed_tree), + ("computed".to_string(), computed_tree), ] .into_iter() .collect(), diff --git a/crates/brk_server/Cargo.toml b/crates/brk_server/Cargo.toml index 3ec358b91..bced25360 100644 --- a/crates/brk_server/Cargo.toml +++ b/crates/brk_server/Cargo.toml @@ -41,4 +41,6 @@ tower-http = { workspace = true } tower-layer = { workspace = true } [dev-dependencies] +brk_bindgen = { workspace = true } brk_mempool = { workspace = true } +color-eyre = { workspace = true } diff --git a/crates/brk_server/examples/bindgen.rs b/crates/brk_server/examples/bindgen.rs new file mode 100644 index 000000000..79f713c55 --- /dev/null +++ b/crates/brk_server/examples/bindgen.rs @@ -0,0 +1,37 @@ +use std::{env, fs, path::PathBuf}; + +use aide::axum::ApiRouter; +use brk_computer::Computer; +use brk_indexer::Indexer; +use brk_query::Vecs; +use brk_server::{ApiRoutes, finish_openapi, generate_bindings}; + +pub fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + + let tmp = env::temp_dir().join("brk_bindgen"); + fs::create_dir_all(&tmp)?; + + let indexer = Indexer::forced_import(&tmp)?; + let computer = Computer::forced_import(&tmp, &indexer)?; + let vecs = Vecs::build_rw(&indexer, &computer); + + let (_, openapi) = finish_openapi(ApiRouter::new().add_api_routes()); + + let workspace_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(|p| p.parent()) + .unwrap() + .to_path_buf(); + + let output_paths = brk_bindgen::ClientOutputPaths::new() + .javascript(workspace_root.join("website/scripts/modules/brk-client/index.js")); + + generate_bindings(&vecs, &openapi, &output_paths)?; + + fs::remove_dir_all(&tmp)?; + + eprintln!("Done"); + + Ok(()) +} diff --git a/crates/brk_server/src/api/openapi/mod.rs b/crates/brk_server/src/api/openapi/mod.rs index 9a7b1932b..a3b408de0 100644 --- a/crates/brk_server/src/api/openapi/mod.rs +++ b/crates/brk_server/src/api/openapi/mod.rs @@ -164,4 +164,4 @@ All errors return structured JSON with a consistent format: tags, ..OpenApi::default() } -} +} \ No newline at end of file diff --git a/crates/brk_server/src/lib.rs b/crates/brk_server/src/lib.rs index 060e67825..2310cfc85 100644 --- a/crates/brk_server/src/lib.rs +++ b/crates/brk_server/src/lib.rs @@ -39,6 +39,7 @@ mod extended; mod state; use api::*; +pub use api::ApiRoutes; pub use brk_types::Port; pub use brk_website::Website; pub use cache::{CacheParams, CacheStrategy}; @@ -253,8 +254,7 @@ impl Server { info!("Starting server on port {port}..."); - let mut openapi = create_openapi(); - let router = router.finish_api(&mut openapi); + let (router, openapi) = finish_openapi(router); #[cfg(feature = "bindgen")] { @@ -269,10 +269,8 @@ impl Server { .javascript(workspace_root.join("modules/brk-client/index.js")) .python(workspace_root.join("packages/brk_client/brk_client/__init__.py")); - let openapi_json = serde_json::to_string(&openapi).unwrap(); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - brk_bindgen::generate_clients(vecs, &openapi_json, &output_paths) + generate_bindings(vecs, &openapi, &output_paths) })); match result { @@ -300,3 +298,23 @@ impl Server { Ok(()) } } + +/// Finalize a router and extract the OpenAPI spec. +pub fn finish_openapi( + router: ApiRouter, +) -> (axum::Router, aide::openapi::OpenApi) { + let mut openapi = create_openapi(); + let router = router.finish_api(&mut openapi); + (router, openapi) +} + +#[cfg(feature = "bindgen")] +pub fn generate_bindings( + vecs: &brk_query::Vecs, + openapi: &aide::openapi::OpenApi, + output_paths: &brk_bindgen::ClientOutputPaths, +) -> std::io::Result<()> { + let openapi_json = serde_json::to_string(openapi) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; + brk_bindgen::generate_clients(vecs, &openapi_json, output_paths) +}