diff --git a/crates/brk_cli/Cargo.toml b/crates/brk_cli/Cargo.toml index 679aaa109..11e307448 100644 --- a/crates/brk_cli/Cargo.toml +++ b/crates/brk_cli/Cargo.toml @@ -16,3 +16,9 @@ brk_parser = { workspace = true } brk_query = { workspace = true } brk_server = { workspace = true } clap = { workspace = true } +color-eyre = { workspace = true } +log = { workspace = true } + +[[bin]] +name = "brk" +path = "src/main.rs" diff --git a/crates/brk_cli/src/main.rs b/crates/brk_cli/src/main.rs index 18e4361da..b0b91b5a0 100644 --- a/crates/brk_cli/src/main.rs +++ b/crates/brk_cli/src/main.rs @@ -1,4 +1,13 @@ -use clap::{Args, Parser, Subcommand}; +use std::{path::Path, thread::sleep, time::Duration}; + +use brk_computer::Computer; +use brk_exit::Exit; +use brk_indexer::Indexer; +use brk_parser::rpc::{self, RpcApi}; +use brk_query::{Index, Params as QueryParams, Query}; +use brk_server::tokio; +use clap::{Parser, Subcommand}; +use log::info; #[derive(Parser)] #[command(version, about)] @@ -8,33 +17,87 @@ struct Cli { command: Commands, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] enum Commands { Run(RunArgs), - Query(QueryArgs), + Query(QueryParams), } -#[derive(Args)] +#[derive(Parser, Debug)] struct RunArgs { name: Option, } -#[derive(Args)] -struct QueryArgs { - name: Option, -} +fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + + brk_logger::init(Some(Path::new(".log"))); -fn main() { let cli = Cli::parse(); - // You can check for the existence of subcommands, and if found use their - // matches just as you would the top level cmd + let outputs_dir = Path::new("../../_outputs"); + + let mut indexer = Indexer::import(&outputs_dir.join("indexed"))?; + + let mut computer = Computer::import(&outputs_dir.join("computed"))?; + match &cli.command { - Commands::Run(name) => { - println!("'myapp add' was used, name is: {:?}", name.name); + Commands::Run(args) => { + let data_dir = Path::new("../../../bitcoin"); + let rpc = Box::leak(Box::new(rpc::Client::new( + "http://localhost:8332", + rpc::Auth::CookieFile(Path::new(data_dir).join(".cookie")), + )?)); + let exit = Exit::new(); + + let parser = brk_parser::Parser::new(data_dir, rpc); + + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()? + .block_on(async { + let served_indexer = indexer.clone(); + let served_computer = computer.clone(); + + tokio::spawn(async move { + brk_server::main(served_indexer, served_computer).await.unwrap(); + }); + + loop { + let block_count = rpc.get_block_count()?; + + info!("{block_count} blocks found."); + + let starting_indexes = indexer.index(&parser, rpc, &exit)?; + + computer.compute(&mut indexer, starting_indexes, &exit)?; + + info!("Waiting for new blocks..."); + + while block_count == rpc.get_block_count()? { + sleep(Duration::from_secs(1)) + } + } + + #[allow(unreachable_code)] + Ok(()) + }) } - Commands::Query(name) => { - println!("'myapp add' was used, name is: {:?}", name.name); + Commands::Query(args) => { + let query = Query::build(&indexer, &computer); + + println!( + "{}", + query.search( + Index::try_from(args.index.as_str())?, + &args.values.iter().flat_map(|v| v.split(",")).collect::>(), + args.from, + args.to, + args.format + )? + ); + + Ok(()) } } } diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index 57740ca24..16a24ebc6 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -19,7 +19,7 @@ pub use brk_parser::*; use bitcoin::{Transaction, TxIn, TxOut}; use brk_exit::Exit; use color_eyre::eyre::{ContextCompat, eyre}; -use log::info; +use log::{debug, info}; use rayon::prelude::*; mod indexes; mod stores; @@ -41,7 +41,7 @@ impl Indexer { pub fn import(indexes_dir: &Path) -> color_eyre::Result { setrlimit()?; - info!("Importing indexes..."); + debug!("Importing indexes..."); let vecs = Vecs::import(&indexes_dir.join("vecs"))?; diff --git a/crates/brk_query/Cargo.toml b/crates/brk_query/Cargo.toml index d380e86fd..c885007fa 100644 --- a/crates/brk_query/Cargo.toml +++ b/crates/brk_query/Cargo.toml @@ -9,4 +9,9 @@ repository.workspace = true [dependencies] brk_computer = { workspace = true } brk_indexer = { workspace = true } +brk_vec = { workspace = true } clap = { workspace = true } +color-eyre = { workspace = true } +derive_deref = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/crates/brk_server/src/api/vecs/format.rs b/crates/brk_query/src/format.rs similarity index 86% rename from crates/brk_server/src/api/vecs/format.rs rename to crates/brk_query/src/format.rs index 1877c78d1..32a4b8e87 100644 --- a/crates/brk_server/src/api/vecs/format.rs +++ b/crates/brk_query/src/format.rs @@ -1,11 +1,13 @@ +use clap::ValueEnum; use color_eyre::eyre::eyre; +use serde::Deserialize; #[allow(clippy::upper_case_acronyms)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)] pub enum Format { + JSON, CSV, TSV, - JSON, } impl TryFrom> for Format { diff --git a/crates/brk_query/src/index.rs b/crates/brk_query/src/index.rs new file mode 100644 index 000000000..ec1c278d4 --- /dev/null +++ b/crates/brk_query/src/index.rs @@ -0,0 +1,86 @@ +use std::fmt::{self, Debug}; + +use color_eyre::eyre::eyre; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Index { + Addressindex, + Dateindex, + Height, + P2PK33index, + P2PK65index, + P2PKHindex, + P2SHindex, + P2TRindex, + P2WPKHindex, + P2WSHindex, + Txindex, + Txinindex, + Txoutindex, +} + +impl Index { + pub fn all() -> [Self; 13] { + [ + Self::Addressindex, + Self::Dateindex, + Self::Height, + Self::P2PK33index, + Self::P2PK65index, + Self::P2PKHindex, + Self::P2SHindex, + Self::P2TRindex, + Self::P2WPKHindex, + Self::P2WSHindex, + Self::Txindex, + Self::Txinindex, + Self::Txoutindex, + ] + } + + pub fn ids(&self) -> &[&str] { + match self { + Self::Dateindex => &["d", "date", "dateindex"], + Self::Height => &["h", "height"], + Self::Txindex => &["txi", "txindex"], + Self::Txinindex => &["txini", "txinindex"], + Self::Txoutindex => &["txouti", "txoutindex"], + Self::Addressindex => &["addri", "addressindex"], + Self::P2PK33index => &["p2pk33i", "p2pk33index"], + Self::P2PK65index => &["p2pk65i", "p2pk65index"], + Self::P2PKHindex => &["p2pkhi", "p2pkhindex"], + Self::P2SHindex => &["p2shi", "p2shindex"], + Self::P2TRindex => &["p2tri", "p2trindex"], + Self::P2WPKHindex => &["p2wpkhi", "p2wpkhindex"], + Self::P2WSHindex => &["p2wshi", "p2wshindex"], + } + } +} + +impl TryFrom<&str> for Index { + type Error = color_eyre::Report; + fn try_from(value: &str) -> Result { + Ok(match value { + v if (Self::Dateindex).ids().contains(&v) => Self::Dateindex, + v if (Self::Height).ids().contains(&v) => Self::Height, + v if (Self::Txindex).ids().contains(&v) => Self::Txindex, + v if (Self::Txinindex).ids().contains(&v) => Self::Txinindex, + v if (Self::Txoutindex).ids().contains(&v) => Self::Txoutindex, + v if (Self::Addressindex).ids().contains(&v) => Self::Addressindex, + v if (Self::P2PK33index).ids().contains(&v) => Self::P2PK33index, + v if (Self::P2PK65index).ids().contains(&v) => Self::P2PK65index, + v if (Self::P2PKHindex).ids().contains(&v) => Self::P2PKHindex, + v if (Self::P2SHindex).ids().contains(&v) => Self::P2SHindex, + v if (Self::P2TRindex).ids().contains(&v) => Self::P2TRindex, + v if (Self::P2WPKHindex).ids().contains(&v) => Self::P2WPKHindex, + v if (Self::P2WSHindex).ids().contains(&v) => Self::P2WSHindex, + _ => return Err(eyre!("Bad index")), + }) + } +} + +impl fmt::Display for Index { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, f) + } +} diff --git a/crates/brk_query/src/lib.rs b/crates/brk_query/src/lib.rs index 6c9eec308..eee21da87 100644 --- a/crates/brk_query/src/lib.rs +++ b/crates/brk_query/src/lib.rs @@ -2,3 +2,159 @@ #![doc = "\n## Example\n\n```rust"] #![doc = include_str!("main.rs")] #![doc = "```"] + +mod format; +mod index; +mod params; +mod tree; + +use std::fmt; + +use brk_computer::Computer; +use brk_indexer::Indexer; +pub use format::Format; +pub use index::Index; +pub use params::Params; +use serde::Serialize; +use tree::VecIdToIndexToVec; + +pub struct Query<'a> { + pub vecid_to_index_to_vec: VecIdToIndexToVec<'a>, + indexer: &'a Indexer, + computer: &'a Computer, +} + +impl<'a> Query<'a> { + pub fn build(indexer: &'a Indexer, computer: &'a Computer) -> Self { + let mut vecs = VecIdToIndexToVec::default(); + + indexer.vecs.as_any_vecs().into_iter().for_each(|vec| vecs.insert(vec)); + computer.vecs.as_any_vecs().into_iter().for_each(|vec| vecs.insert(vec)); + + Self { + vecid_to_index_to_vec: vecs, + indexer, + computer, + } + } + + pub fn search( + &self, + index: Index, + values: &[&str], + from: Option, + to: Option, + format: Option, + ) -> color_eyre::Result { + let ids = values + .iter() + .map(|s| { + ( + s.to_owned(), + self.vecid_to_index_to_vec.get(&s.to_lowercase().replace("_", "-")), + ) + }) + .filter(|(_, opt)| opt.is_some()) + .map(|(id, vec)| (id, vec.unwrap())) + .collect::>(); + + if ids.is_empty() { + return Ok(QueryResponse::default(format)); + } + + let mut values = ids + .iter() + .flat_map(|(_, i_to_v)| i_to_v.get(&index)) + .map(|vec| -> brk_vec::Result> { vec.collect_range_values(from, to) }) + .collect::>>()?; + + if values.is_empty() { + return Ok(QueryResponse::default(format)); + } + + let ids_last_i = ids.len() - 1; + + Ok(match format { + Some(Format::CSV) | Some(Format::TSV) => { + let delimiter = if format == Some(Format::CSV) { ',' } else { '\t' }; + + let mut text = ids + .into_iter() + .map(|(id, _)| id) + .collect::>() + .join(&delimiter.to_string()); + + text.push('\n'); + + let values_len = values.first().unwrap().len(); + + (0..values_len).for_each(|i| { + let mut line = "".to_string(); + values.iter().enumerate().for_each(|(id_i, v)| { + line += &v.get(i).unwrap().to_string(); + if id_i == ids_last_i { + line.push('\n'); + } else { + line.push(delimiter); + } + }); + text += &line; + }); + + if format == Some(Format::CSV) { + QueryResponse::CSV(text) + } else { + QueryResponse::TSV(text) + } + } + Some(Format::JSON) | None => { + if values.len() == 1 { + let mut values = values.pop().unwrap(); + if values.len() == 1 { + let value = values.pop().unwrap(); + QueryResponse::Json(Value::Single(value)) + } else { + QueryResponse::Json(Value::List(values)) + } + } else { + QueryResponse::Json(Value::Matrix(values)) + } + } + }) + } +} + +#[derive(Debug)] +pub enum QueryResponse { + Json(Value), + CSV(String), + TSV(String), +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum Value { + Matrix(Vec>), + List(Vec), + Single(serde_json::Value), +} + +impl QueryResponse { + fn default(format: Option) -> Self { + match format { + Some(Format::CSV) => QueryResponse::CSV("".to_string()), + Some(Format::TSV) => QueryResponse::TSV("".to_string()), + _ => QueryResponse::Json(Value::Single(serde_json::Value::Null)), + } + } +} + +impl fmt::Display for QueryResponse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Json(value) => write!(f, "{}", serde_json::to_string_pretty(value).unwrap()), + Self::CSV(string) => write!(f, "{}", string), + Self::TSV(string) => write!(f, "{}", string), + } + } +} diff --git a/crates/brk_query/src/main.rs b/crates/brk_query/src/main.rs index e7a11a969..b17f99da5 100644 --- a/crates/brk_query/src/main.rs +++ b/crates/brk_query/src/main.rs @@ -1,3 +1,23 @@ -fn main() { - println!("Hello, world!"); +use std::path::Path; + +use brk_computer::Computer; +use brk_indexer::Indexer; +use brk_query::{Index, Query}; + +pub fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + + let outputs_dir = Path::new("../../_outputs"); + + let indexer = Indexer::import(&outputs_dir.join("indexed"))?; + + let computer = Computer::import(&outputs_dir.join("computed"))?; + + let query = Query::build(&indexer, &computer); + + dbg!(query.search(Index::Height, &["date"], Some(-1), None, None)?); + dbg!(query.search(Index::Height, &["date"], Some(-10), None, None)?); + dbg!(query.search(Index::Height, &["date", "timestamp"], Some(-10), None, None)?); + + Ok(()) } diff --git a/crates/brk_query/src/params.rs b/crates/brk_query/src/params.rs new file mode 100644 index 000000000..a07f49465 --- /dev/null +++ b/crates/brk_query/src/params.rs @@ -0,0 +1,18 @@ +use clap::Parser; +use serde::Deserialize; + +use crate::Format; + +#[derive(Debug, Deserialize, Parser)] +pub struct Params { + #[clap(short, long)] + pub index: String, + #[clap(short, long, value_delimiter = ' ', num_args = 1..)] + pub values: Vec, + #[clap(short, long, allow_hyphen_values = true)] + pub from: Option, + #[clap(short, long, allow_hyphen_values = true)] + pub to: Option, + #[clap(long)] + pub format: Option, +} diff --git a/crates/brk_query/src/tree.rs b/crates/brk_query/src/tree.rs new file mode 100644 index 000000000..23f2d0cdf --- /dev/null +++ b/crates/brk_query/src/tree.rs @@ -0,0 +1,38 @@ +use std::collections::BTreeMap; + +use brk_vec::AnyStorableVec; +use derive_deref::{Deref, DerefMut}; + +use super::index::Index; + +#[derive(Default, Deref, DerefMut)] +pub struct VecIdToIndexToVec<'a>(BTreeMap>); + +impl<'a> VecIdToIndexToVec<'a> { + // Not the most performant or type safe but only built once so that's okay + pub fn insert(&mut self, vec: &'a dyn AnyStorableVec) { + let file_name = vec.file_name(); + let split = file_name.split("_to_").collect::>(); + if split.len() != 2 { + panic!(); + } + let str = vec.index_type_to_string().split("::").last().unwrap().to_lowercase(); + let index = Index::try_from(str.as_str()) + .inspect_err(|_| { + dbg!(str); + }) + .unwrap(); + if split[0] != index.to_string().to_lowercase() { + dbg!(split[0], index.to_string()); + panic!(); + } + let key = split[1].to_string().replace("_", "-"); + let prev = self.entry(key).or_default().insert(index, vec); + if prev.is_some() { + panic!() + } + } +} + +#[derive(Default, Deref, DerefMut)] +pub struct IndexToVec<'a>(BTreeMap); diff --git a/crates/brk_server/Cargo.toml b/crates/brk_server/Cargo.toml index 9d42eb6a9..8330a1359 100644 --- a/crates/brk_server/Cargo.toml +++ b/crates/brk_server/Cargo.toml @@ -13,6 +13,7 @@ brk_exit = { workspace = true } brk_indexer = { workspace = true } brk_logger = { workspace = true } brk_parser = { workspace = true } +brk_query = { workspace = true } brk_vec = { workspace = true } color-eyre = { workspace = true } derive_deref = { workspace = true } diff --git a/crates/brk_server/src/api/mod.rs b/crates/brk_server/src/api/mod.rs index 4f9025ec4..4ad69b941 100644 --- a/crates/brk_server/src/api/mod.rs +++ b/crates/brk_server/src/api/mod.rs @@ -1,11 +1,11 @@ -use axum::{routing::get, Router}; +use axum::{Router, routing::get}; use super::AppState; mod explorer; mod vecs; -pub use vecs::VecIdToIndexToVec; +pub use vecs::DTS; pub trait ApiRoutes { fn add_api_routes(self) -> Self; diff --git a/crates/brk_server/src/api/vecs/dts.rs b/crates/brk_server/src/api/vecs/dts.rs new file mode 100644 index 000000000..4324a21a8 --- /dev/null +++ b/crates/brk_server/src/api/vecs/dts.rs @@ -0,0 +1,50 @@ +use std::{fs, io}; + +use brk_query::{Index, Query}; + +use crate::WEBSITE_DEV_PATH; + +#[allow(clippy::upper_case_acronyms)] +pub trait DTS { + fn generate_dts_file(&self) -> io::Result<()>; +} + +impl DTS for Query<'static> { + fn generate_dts_file(&self) -> io::Result<()> { + if !fs::exists(WEBSITE_DEV_PATH)? { + return Ok(()); + } + + let path = format!("{WEBSITE_DEV_PATH}/scripts/types/vecid-to-indexes.d.ts"); + + let mut contents = Index::all() + .into_iter() + .enumerate() + .map(|(i_of_i, i)| format!("type {} = {};", i, i_of_i)) + .collect::>() + .join("\n"); + + contents += "\n\ninterface VecIdToIndexes {\n"; + + self.vecid_to_index_to_vec.iter().for_each(|(id, index_to_vec)| { + let indexes = index_to_vec + .keys() + .map(|i| i.to_string()) + .collect::>() + .join(", "); + + contents += &format!( + " {}: [{indexes}]\n", + if id.contains("-") { + format!("\"{id}\"") + } else { + id.to_owned() + } + ); + }); + + contents.push('}'); + + fs::write(path, contents) + } +} diff --git a/crates/brk_server/src/api/vecs/index.rs b/crates/brk_server/src/api/vecs/index.rs deleted file mode 100644 index ab439a075..000000000 --- a/crates/brk_server/src/api/vecs/index.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::fmt::{self, Debug}; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Index { - Addressindex, - Dateindex, - Height, - P2PK33index, - P2PK65index, - P2PKHindex, - P2SHindex, - P2TRindex, - P2WPKHindex, - P2WSHindex, - Txindex, - Txinindex, - Txoutindex, -} - -impl Index { - pub fn all() -> [Self; 13] { - [ - Self::Addressindex, - Self::Dateindex, - Self::Height, - Self::P2PK33index, - Self::P2PK65index, - Self::P2PKHindex, - Self::P2SHindex, - Self::P2TRindex, - Self::P2WPKHindex, - Self::P2WSHindex, - Self::Txindex, - Self::Txinindex, - Self::Txoutindex, - ] - } -} - -impl TryFrom<&str> for Index { - type Error = (); - fn try_from(value: &str) -> Result { - Ok(match value { - "d" | "date" | "dateindex" => Self::Dateindex, - "h" | "height" => Self::Height, - "txi" | "txindex" => Self::Txindex, - "txini" | "txinindex" => Self::Txinindex, - "txouti" | "txoutindex" => Self::Txoutindex, - "addri" | "addressindex" => Self::Addressindex, - "p2pk33i" | "p2pk33index" => Self::P2PK33index, - "p2pk65i" | "p2pk65index" => Self::P2PK65index, - "p2pkhi" | "p2pkhindex" => Self::P2PKHindex, - "p2shi" | "p2shindex" => Self::P2SHindex, - "p2tri" | "p2trindex" => Self::P2TRindex, - "p2wpkhi" | "p2wpkhindex" => Self::P2WPKHindex, - "p2wshi" | "p2wshindex" => Self::P2WSHindex, - _ => return Err(()), - }) - } -} - -impl fmt::Display for Index { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(self, f) - } -} diff --git a/crates/brk_server/src/api/vecs/mod.rs b/crates/brk_server/src/api/vecs/mod.rs index 7f07a6bbe..00c674137 100644 --- a/crates/brk_server/src/api/vecs/mod.rs +++ b/crates/brk_server/src/api/vecs/mod.rs @@ -2,10 +2,11 @@ use std::time::Instant; use axum::{ Json, - extract::{Query, State}, + extract::{Query as AxumQuery, State}, http::{HeaderMap, StatusCode, Uri}, response::{IntoResponse, Response}, }; +use brk_query::{Format, Index, Params}; use color_eyre::eyre::eyre; use serde_json::Value; @@ -13,20 +14,14 @@ use crate::{log_result, traits::HeaderMapExtended}; use super::AppState; -mod format; -mod index; -mod query; -mod tree; +mod dts; -use format::Format; -use index::Index; -use query::QueryS; -pub use tree::*; +pub use dts::*; pub async fn handler( headers: HeaderMap, uri: Uri, - query: Query, + query: AxumQuery, State(app_state): State, ) -> Response { let instant = Instant::now(); @@ -49,12 +44,16 @@ pub async fn handler( fn req_to_response_res( headers: HeaderMap, - Query(QueryS { format, from, i, to, v }): Query, - AppState { vecs, .. }: AppState, + AxumQuery(Params { + format, + from, + index, + to, + values, + }): AxumQuery, + AppState { query, .. }: AppState, ) -> color_eyre::Result { - let format = Format::try_from(format).ok(); - - let indexes = i + let indexes = index .to_lowercase() .split(",") .flat_map(|s| Index::try_from(s).ok()) @@ -66,10 +65,14 @@ fn req_to_response_res( return Err(eyre!("Unknown index")); } - let ids = v - .to_lowercase() - .split(",") - .map(|s| (s.to_owned(), vecs.get(&s.replace("_", "-")))) + let ids = values + .into_iter() + .map(|v| v.to_lowercase()) + .flat_map(|v| v.split(",").map(|v| v.to_owned()).collect::>()) + .map(|s| { + let opt = query.vecid_to_index_to_vec.get(&s.replace("_", "-")); + (s, opt) + }) .filter(|(_, opt)| opt.is_some()) .map(|(id, vec)| (id, vec.unwrap())) .collect::>(); diff --git a/crates/brk_server/src/api/vecs/query.rs b/crates/brk_server/src/api/vecs/query.rs deleted file mode 100644 index 6ad5db15e..000000000 --- a/crates/brk_server/src/api/vecs/query.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct QueryS { - pub i: String, - pub v: String, - pub from: Option, - pub to: Option, - pub format: Option, -} diff --git a/crates/brk_server/src/api/vecs/tree.rs b/crates/brk_server/src/api/vecs/tree.rs deleted file mode 100644 index 22c6c5b4d..000000000 --- a/crates/brk_server/src/api/vecs/tree.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::{collections::BTreeMap, fs, io}; - -use brk_vec::AnyStorableVec; -use derive_deref::{Deref, DerefMut}; - -use crate::WEBSITE_DEV_PATH; - -use super::index::Index; - -#[derive(Default, Deref, DerefMut)] -pub struct VecIdToIndexToVec(BTreeMap); - -impl VecIdToIndexToVec { - // Not the most performant or type safe but only built once so that's okay - pub fn insert(&mut self, vec: &'static dyn AnyStorableVec) { - let file_name = vec.file_name(); - let split = file_name.split("_to_").collect::>(); - if split.len() != 2 { - panic!(); - } - let str = vec.index_type_to_string().split("::").last().unwrap().to_lowercase(); - let index = Index::try_from(str.as_str()) - .inspect_err(|_| { - dbg!(str); - }) - .unwrap(); - if split[0] != index.to_string().to_lowercase() { - dbg!(split[0], index.to_string()); - panic!(); - } - let key = split[1].to_string().replace("_", "-"); - let prev = self.entry(key).or_default().insert(index, vec); - if prev.is_some() { - panic!() - } - } - - pub fn generate_dts_file(&self) -> io::Result<()> { - if !fs::exists(WEBSITE_DEV_PATH)? { - return Ok(()); - } - - let path = format!("{WEBSITE_DEV_PATH}/scripts/types/vecid-to-indexes.d.ts"); - - let mut contents = Index::all() - .into_iter() - .enumerate() - .map(|(i_of_i, i)| format!("type {} = {};", i, i_of_i)) - .collect::>() - .join("\n"); - - contents += "\n\ninterface VecIdToIndexes {\n"; - - self.iter().for_each(|(id, index_to_vec)| { - let indexes = index_to_vec - .keys() - .map(|i| i.to_string()) - .collect::>() - .join(", "); - - contents += &format!( - " {}: [{indexes}]\n", - if id.contains("-") { - format!("\"{id}\"") - } else { - id.to_owned() - } - ); - }); - - contents.push('}'); - - fs::write(path, contents) - } -} - -#[derive(Default, Deref, DerefMut)] -pub struct IndexToVec(BTreeMap); diff --git a/crates/brk_server/src/lib.rs b/crates/brk_server/src/lib.rs index 2b2cef39c..03490a340 100644 --- a/crates/brk_server/src/lib.rs +++ b/crates/brk_server/src/lib.rs @@ -5,13 +5,15 @@ use std::time::Instant; -use api::{ApiRoutes, VecIdToIndexToVec}; +use api::{ApiRoutes, DTS}; use axum::{Json, Router, http::StatusCode, routing::get, serve}; use brk_computer::Computer; use brk_indexer::Indexer; +use brk_query::Query; use color_eyre::owo_colors::OwoColorize; use files::FilesRoutes; use log::{error, info}; +pub use tokio; use tokio::net::TcpListener; use tower_http::compression::CompressionLayer; @@ -21,9 +23,7 @@ mod traits; #[derive(Clone)] pub struct AppState { - vecs: &'static VecIdToIndexToVec, - indexer: &'static Indexer, - computer: &'static Computer, + query: &'static Query<'static>, } pub const WEBSITE_DEV_PATH: &str = "../../websites/kibo.money/"; @@ -31,18 +31,11 @@ pub const WEBSITE_DEV_PATH: &str = "../../websites/kibo.money/"; pub async fn main(indexer: Indexer, computer: Computer) -> color_eyre::Result<()> { let indexer = Box::leak(Box::new(indexer)); let computer = Box::leak(Box::new(computer)); - let vecs = Box::leak(Box::new(VecIdToIndexToVec::default())); + let query = Box::leak(Box::new(Query::build(indexer, computer))); - indexer.vecs.as_any_vecs().into_iter().for_each(|vec| vecs.insert(vec)); - computer.vecs.as_any_vecs().into_iter().for_each(|vec| vecs.insert(vec)); + query.generate_dts_file()?; - vecs.generate_dts_file()?; - - let state = AppState { - vecs, - indexer, - computer, - }; + let state = AppState { query }; let compression_layer = CompressionLayer::new().br(true).deflate(true).gzip(true).zstd(true);