mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-29 19:29:27 -07:00
server: cleanup
This commit is contained in:
21
pricer/src/retry.rs
Normal file
21
pricer/src/retry.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
pub fn retry<T>(
|
||||
function: impl Fn(usize) -> color_eyre::Result<T>,
|
||||
sleep_in_s: u64,
|
||||
retries: usize,
|
||||
) -> color_eyre::Result<T> {
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
let res = function(i);
|
||||
|
||||
if i == retries || res.is_ok() {
|
||||
return res;
|
||||
} else {
|
||||
sleep(Duration::from_secs(sleep_in_s));
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
0
server/src/api/explorer/mod.rs
Normal file
0
server/src/api/explorer/mod.rs
Normal file
@@ -1,26 +1,11 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use color_eyre::eyre::eyre;
|
||||
use reqwest::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use structs::{Format, Index};
|
||||
|
||||
use crate::{log_result, traits::HeaderMapExtended};
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
// mod handlers;
|
||||
pub mod structs;
|
||||
mod explorer;
|
||||
mod vecs;
|
||||
|
||||
pub const VECS_URL_PREFIX: &str = "/api/vecs";
|
||||
pub use vecs::VecIdToIndexToVec;
|
||||
|
||||
pub trait ApiRoutes {
|
||||
fn add_api_routes(self) -> Self;
|
||||
@@ -28,147 +13,6 @@ pub trait ApiRoutes {
|
||||
|
||||
impl ApiRoutes for Router<AppState> {
|
||||
fn add_api_routes(self) -> Self {
|
||||
self.route(VECS_URL_PREFIX, get(handler))
|
||||
self.route("/api/vecs", get(vecs::handler))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct DatasetParams {
|
||||
pub i: String,
|
||||
pub v: String,
|
||||
pub from: Option<i64>,
|
||||
pub to: Option<i64>,
|
||||
pub format: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn handler(headers: HeaderMap, query: Query<DatasetParams>, State(app_state): State<AppState>) -> Response {
|
||||
let instant = Instant::now();
|
||||
|
||||
let path = format!(
|
||||
"{VECS_URL_PREFIX}?i={}&v={}{}{}",
|
||||
query.i,
|
||||
query.v,
|
||||
query.from.map_or("".to_string(), |from| format!("&from={from}")),
|
||||
query.to.map_or("".to_string(), |to| format!("&to={to}")),
|
||||
);
|
||||
|
||||
match req_to_response_res(headers, query, app_state) {
|
||||
Ok(response) => {
|
||||
log_result(response.status(), &path, instant);
|
||||
response
|
||||
}
|
||||
Err(error) => {
|
||||
let mut response = (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
|
||||
log_result(response.status(), &path, instant);
|
||||
response.headers_mut().insert_cors();
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn req_to_response_res(
|
||||
headers: HeaderMap,
|
||||
Query(DatasetParams { format, from, i, to, v }): Query<DatasetParams>,
|
||||
AppState { vecs, .. }: AppState,
|
||||
) -> color_eyre::Result<Response> {
|
||||
let format = Format::try_from(format).ok();
|
||||
|
||||
let indexes = i
|
||||
.to_lowercase()
|
||||
.split(",")
|
||||
.flat_map(|s| Index::try_from(s).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if indexes.len() > 1 {
|
||||
return Err(eyre!("Multiple indexes aren't supported"));
|
||||
} else if indexes.is_empty() {
|
||||
return Err(eyre!("Unknown index"));
|
||||
}
|
||||
|
||||
let ids = v
|
||||
.to_lowercase()
|
||||
.split(",")
|
||||
.map(|s| (s.to_owned(), vecs.get(&s.replace("_", "-"))))
|
||||
.filter(|(_, opt)| opt.is_some())
|
||||
.map(|(id, vec)| (id, vec.unwrap()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if ids.is_empty() {
|
||||
return Ok(Json(()).into_response());
|
||||
}
|
||||
|
||||
let values = ids
|
||||
.iter()
|
||||
.flat_map(|(_, i_to_v)| i_to_v.get(indexes.first().unwrap()))
|
||||
.map(|vec| -> storable_vec::Result<Vec<Value>> { vec.collect_range_values(from, to) })
|
||||
.collect::<storable_vec::Result<Vec<_>>>()?;
|
||||
|
||||
if ids.is_empty() {
|
||||
return Ok(Json(()).into_response());
|
||||
}
|
||||
|
||||
let ids_last_i = ids.len() - 1;
|
||||
|
||||
let mut response = match format {
|
||||
Some(Format::CSV) | Some(Format::TSV) => {
|
||||
let delimiter = if format == Some(Format::CSV) { ',' } else { '\t' };
|
||||
|
||||
let mut csv = ids
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<Vec<_>>()
|
||||
.join(&delimiter.to_string());
|
||||
|
||||
csv.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);
|
||||
}
|
||||
});
|
||||
csv += &line;
|
||||
});
|
||||
|
||||
csv.into_response()
|
||||
}
|
||||
Some(Format::JSON) | None => {
|
||||
if values.len() == 1 {
|
||||
let values = values.first().unwrap();
|
||||
if values.len() == 1 {
|
||||
let value = values.first().unwrap();
|
||||
Json(value).into_response()
|
||||
} else {
|
||||
Json(values).into_response()
|
||||
}
|
||||
} else {
|
||||
Json(values).into_response()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let headers = response.headers_mut();
|
||||
|
||||
headers.insert_cors();
|
||||
// headers.insert_last_modified(date_modified);
|
||||
|
||||
match format {
|
||||
Some(format) => {
|
||||
headers.insert_content_disposition_attachment();
|
||||
match format {
|
||||
Format::CSV => headers.insert_content_type_text_csv(),
|
||||
Format::TSV => headers.insert_content_type_text_tsv(),
|
||||
Format::JSON => headers.insert_content_type_application_json(),
|
||||
}
|
||||
}
|
||||
_ => headers.insert_content_type_application_json(),
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// mod chunk_metadata;
|
||||
mod format;
|
||||
mod index;
|
||||
// mod kind;
|
||||
// mod range;
|
||||
// mod route;
|
||||
// mod routes;
|
||||
|
||||
// pub use chunk_metadata::*;
|
||||
pub use format::*;
|
||||
pub use index::*;
|
||||
// pub use kind::*;
|
||||
// pub use range::*;
|
||||
// pub use route::*;
|
||||
// pub use routes::*;
|
||||
38
server/src/api/vecs/id_to_i_to_vec.rs
Normal file
38
server/src/api/vecs/id_to_i_to_vec.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use storable_vec::AnyJsonStorableVec;
|
||||
|
||||
use super::index::Index;
|
||||
|
||||
#[derive(Default, Deref, DerefMut)]
|
||||
pub struct VecIdToIndexToVec(BTreeMap<String, IndexToVec>);
|
||||
|
||||
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 AnyJsonStorableVec) {
|
||||
let file_name = vec.file_name();
|
||||
let split = file_name.split("_to_").collect::<Vec<_>>();
|
||||
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(BTreeMap<Index, &'static dyn AnyJsonStorableVec>);
|
||||
156
server/src/api/vecs/mod.rs
Normal file
156
server/src/api/vecs/mod.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, Uri},
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use color_eyre::eyre::eyre;
|
||||
use reqwest::StatusCode;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{log_result, traits::HeaderMapExtended};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
mod format;
|
||||
mod id_to_i_to_vec;
|
||||
mod index;
|
||||
mod query;
|
||||
|
||||
use format::Format;
|
||||
pub use id_to_i_to_vec::*;
|
||||
use index::Index;
|
||||
use query::QueryS;
|
||||
|
||||
pub async fn handler(
|
||||
headers: HeaderMap,
|
||||
uri: Uri,
|
||||
query: Query<QueryS>,
|
||||
State(app_state): State<AppState>,
|
||||
) -> Response {
|
||||
let instant = Instant::now();
|
||||
|
||||
let path = uri.path();
|
||||
|
||||
match req_to_response_res(headers, query, app_state) {
|
||||
Ok(response) => {
|
||||
log_result(response.status(), path, instant);
|
||||
response
|
||||
}
|
||||
Err(error) => {
|
||||
let mut response = (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
|
||||
log_result(response.status(), path, instant);
|
||||
response.headers_mut().insert_cors();
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn req_to_response_res(
|
||||
headers: HeaderMap,
|
||||
Query(QueryS { format, from, i, to, v }): Query<QueryS>,
|
||||
AppState { vecs, .. }: AppState,
|
||||
) -> color_eyre::Result<Response> {
|
||||
let format = Format::try_from(format).ok();
|
||||
|
||||
let indexes = i
|
||||
.to_lowercase()
|
||||
.split(",")
|
||||
.flat_map(|s| Index::try_from(s).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if indexes.len() > 1 {
|
||||
return Err(eyre!("Multiple indexes aren't supported"));
|
||||
} else if indexes.is_empty() {
|
||||
return Err(eyre!("Unknown index"));
|
||||
}
|
||||
|
||||
let ids = v
|
||||
.to_lowercase()
|
||||
.split(",")
|
||||
.map(|s| (s.to_owned(), vecs.get(&s.replace("_", "-"))))
|
||||
.filter(|(_, opt)| opt.is_some())
|
||||
.map(|(id, vec)| (id, vec.unwrap()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if ids.is_empty() {
|
||||
return Ok(Json(()).into_response());
|
||||
}
|
||||
|
||||
let values = ids
|
||||
.iter()
|
||||
.flat_map(|(_, i_to_v)| i_to_v.get(indexes.first().unwrap()))
|
||||
.map(|vec| -> storable_vec::Result<Vec<Value>> { vec.collect_range_values(from, to) })
|
||||
.collect::<storable_vec::Result<Vec<_>>>()?;
|
||||
|
||||
if ids.is_empty() {
|
||||
return Ok(Json(()).into_response());
|
||||
}
|
||||
|
||||
let ids_last_i = ids.len() - 1;
|
||||
|
||||
let mut response = match format {
|
||||
Some(Format::CSV) | Some(Format::TSV) => {
|
||||
let delimiter = if format == Some(Format::CSV) { ',' } else { '\t' };
|
||||
|
||||
let mut csv = ids
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<Vec<_>>()
|
||||
.join(&delimiter.to_string());
|
||||
|
||||
csv.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);
|
||||
}
|
||||
});
|
||||
csv += &line;
|
||||
});
|
||||
|
||||
csv.into_response()
|
||||
}
|
||||
Some(Format::JSON) | None => {
|
||||
if values.len() == 1 {
|
||||
let values = values.first().unwrap();
|
||||
if values.len() == 1 {
|
||||
let value = values.first().unwrap();
|
||||
Json(value).into_response()
|
||||
} else {
|
||||
Json(values).into_response()
|
||||
}
|
||||
} else {
|
||||
Json(values).into_response()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let headers = response.headers_mut();
|
||||
|
||||
headers.insert_cors();
|
||||
// headers.insert_last_modified(date_modified);
|
||||
|
||||
match format {
|
||||
Some(format) => {
|
||||
headers.insert_content_disposition_attachment();
|
||||
match format {
|
||||
Format::CSV => headers.insert_content_type_text_csv(),
|
||||
Format::TSV => headers.insert_content_type_text_tsv(),
|
||||
Format::JSON => headers.insert_content_type_application_json(),
|
||||
}
|
||||
}
|
||||
_ => headers.insert_content_type_application_json(),
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
10
server/src/api/vecs/query.rs
Normal file
10
server/src/api/vecs/query.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct QueryS {
|
||||
pub i: String,
|
||||
pub v: String,
|
||||
pub from: Option<i64>,
|
||||
pub to: Option<i64>,
|
||||
pub format: Option<String>,
|
||||
}
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
traits::{HeaderMapExtended, ModifiedState, ResponseExtended},
|
||||
};
|
||||
|
||||
use super::minify_js;
|
||||
use super::minify::minify_js;
|
||||
|
||||
const WEBSITE_DEV_PATH: &str = "../website/";
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
mod handlers;
|
||||
|
||||
use handlers::{file_handler, index_handler};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
pub trait WebsiteRoutes {
|
||||
mod file;
|
||||
mod minify;
|
||||
|
||||
use file::{file_handler, index_handler};
|
||||
|
||||
pub trait FilesRoutes {
|
||||
fn add_website_routes(self) -> Self;
|
||||
}
|
||||
|
||||
impl WebsiteRoutes for Router<AppState> {
|
||||
impl FilesRoutes for Router<AppState> {
|
||||
fn add_website_routes(self) -> Self {
|
||||
self.route("/{*path}", get(file_handler)).route("/", get(index_handler))
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
use std::{collections::BTreeMap, time::Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
use api::{structs::Index, ApiRoutes};
|
||||
use api::{ApiRoutes, VecIdToIndexToVec};
|
||||
use axum::{routing::get, serve, Json, Router};
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use computer::Computer;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use files::FilesRoutes;
|
||||
use indexer::Indexer;
|
||||
use logger::{error, info};
|
||||
use reqwest::StatusCode;
|
||||
use storable_vec::{AnyJsonStorableVec, STATELESS};
|
||||
use storable_vec::STATELESS;
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
use website::WebsiteRoutes;
|
||||
|
||||
mod api;
|
||||
mod files;
|
||||
mod traits;
|
||||
mod website;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
@@ -24,40 +23,6 @@ pub struct AppState {
|
||||
computer: &'static Computer<STATELESS>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deref, DerefMut)]
|
||||
pub struct VecIdToIndexToVec(BTreeMap<String, IndexToVec>);
|
||||
|
||||
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 AnyJsonStorableVec) {
|
||||
let file_name = vec.file_name();
|
||||
let split = file_name.split("_to_").collect::<Vec<_>>();
|
||||
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 {
|
||||
pub index_to_vec: BTreeMap<Index, &'static dyn AnyJsonStorableVec>,
|
||||
}
|
||||
|
||||
pub async fn main(indexer: Indexer<STATELESS>, computer: Computer<STATELESS>) -> color_eyre::Result<()> {
|
||||
// pub async fn main(routes: Routes, config: Config) -> color_eyre::Result<()> {
|
||||
// routes.generate_dts_file();
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
mod file;
|
||||
mod minify;
|
||||
|
||||
pub use file::*;
|
||||
use minify::*;
|
||||
139
src/io/binary.rs
139
src/io/binary.rs
@@ -1,139 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs::{self, File},
|
||||
io::{BufReader, BufWriter, Cursor},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use bincode::{
|
||||
config, decode_from_slice, decode_from_std_read, encode_into_std_write, Decode, Encode,
|
||||
};
|
||||
use zstd::decode_all;
|
||||
|
||||
const ZST_EXTENSION: &str = "zst";
|
||||
|
||||
pub const BIN_EXTENSION: &str = "bin";
|
||||
pub const COMPRESSED_BIN_EXTENSION: &str = "bin.zst";
|
||||
|
||||
enum BinaryType {
|
||||
Raw,
|
||||
Compressed,
|
||||
}
|
||||
|
||||
pub struct Binary;
|
||||
|
||||
impl Binary {
|
||||
pub fn import<T>(path: &Path) -> color_eyre::Result<T>
|
||||
where
|
||||
T: Decode,
|
||||
{
|
||||
match Self::type_from_path(path) {
|
||||
BinaryType::Compressed => Self::import_compressed(path),
|
||||
BinaryType::Raw => Self::import_raw(path),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_raw<T>(path: &Path) -> color_eyre::Result<T>
|
||||
where
|
||||
T: Decode,
|
||||
{
|
||||
let config = config::standard();
|
||||
|
||||
let file = File::open(path)?;
|
||||
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
let decoded = decode_from_std_read(&mut reader, config)?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
fn import_compressed<T>(path: &Path) -> color_eyre::Result<T>
|
||||
where
|
||||
T: Decode,
|
||||
{
|
||||
let file = File::open(path).unwrap();
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
let decompressed = decode_all(reader).unwrap();
|
||||
|
||||
let config = config::standard();
|
||||
|
||||
let decoded = decode_from_slice::<T, _>(&decompressed, config)?.0;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
pub fn export<T>(path: &Path, value: &T) -> color_eyre::Result<()>
|
||||
where
|
||||
T: Debug + Encode,
|
||||
{
|
||||
// log(&format!("Exporting: {:?}", path));
|
||||
|
||||
match Self::type_from_path(path) {
|
||||
BinaryType::Compressed => Self::export_compressed(path, value),
|
||||
BinaryType::Raw => Self::export_raw(path, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn export_raw<T>(path: &Path, value: &T) -> color_eyre::Result<()>
|
||||
where
|
||||
T: Debug + Encode,
|
||||
{
|
||||
let config = config::standard();
|
||||
|
||||
let file = File::create(path).inspect_err(|_| {
|
||||
dbg!(path, value);
|
||||
})?;
|
||||
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
encode_into_std_write(value, &mut writer, config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn export_compressed<T>(path: &Path, value: &T) -> color_eyre::Result<()>
|
||||
where
|
||||
T: Debug + Encode,
|
||||
{
|
||||
let config = config::standard();
|
||||
|
||||
let encoded = bincode::encode_to_vec(value, config).unwrap();
|
||||
|
||||
let cursor = Cursor::new(encoded);
|
||||
|
||||
let compressed = zstd::encode_all(cursor, 0).unwrap();
|
||||
|
||||
fs::write(path, compressed).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_correct_extension(path: &Path) -> bool {
|
||||
let path = path.to_str().unwrap();
|
||||
path.ends_with(BIN_EXTENSION) || path.ends_with(COMPRESSED_BIN_EXTENSION)
|
||||
}
|
||||
|
||||
fn type_from_path(path: &Path) -> BinaryType {
|
||||
let extension = path.extension();
|
||||
|
||||
if extension.is_none() {
|
||||
panic!("Should have extension");
|
||||
}
|
||||
|
||||
if !Self::has_correct_extension(path) {
|
||||
dbg!(path);
|
||||
panic!("Wrong extension")
|
||||
}
|
||||
|
||||
let extension = extension.unwrap();
|
||||
|
||||
if extension == ZST_EXTENSION {
|
||||
BinaryType::Compressed
|
||||
} else {
|
||||
BinaryType::Raw
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
pub struct Json;
|
||||
|
||||
pub const JSON_EXTENSION: &str = "json";
|
||||
pub const HAR_EXTENSION: &str = "har";
|
||||
|
||||
impl Json {
|
||||
pub fn import<T>(path: &Path) -> color_eyre::Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
if !Self::has_correct_extension(path) {
|
||||
panic!("Wrong extension");
|
||||
}
|
||||
|
||||
let file = File::open(path)?;
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
Ok(serde_json::from_reader(reader)?)
|
||||
}
|
||||
|
||||
pub fn export<T>(path: &Path, value: &T) -> color_eyre::Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
if !Self::has_correct_extension(path) {
|
||||
dbg!(path);
|
||||
panic!("Wrong extension");
|
||||
}
|
||||
|
||||
let file = File::create(path).unwrap_or_else(|_| {
|
||||
dbg!(&path);
|
||||
panic!("No such file or directory")
|
||||
});
|
||||
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
serde_json::to_writer_pretty(&mut writer, value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn has_correct_extension(path: &Path) -> bool {
|
||||
let path = path.to_str().unwrap();
|
||||
path.ends_with(JSON_EXTENSION) || path.ends_with(HAR_EXTENSION)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
mod binary;
|
||||
mod json;
|
||||
mod serialization;
|
||||
|
||||
pub use binary::*;
|
||||
pub use json::*;
|
||||
pub use serialization::*;
|
||||
@@ -1,128 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use allocative::Allocative;
|
||||
use bincode::{Decode, Encode};
|
||||
use color_eyre::eyre::eyre;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::io::{Binary, Json};
|
||||
|
||||
use super::{BIN_EXTENSION, COMPRESSED_BIN_EXTENSION, HAR_EXTENSION, JSON_EXTENSION};
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy, Default, Allocative)]
|
||||
pub enum Serialization {
|
||||
#[default]
|
||||
Binary,
|
||||
Json,
|
||||
}
|
||||
|
||||
impl Serialization {
|
||||
pub fn is_serializable(&self, path: &Path) -> bool {
|
||||
let path = path.to_str().unwrap();
|
||||
match self {
|
||||
Self::Binary => {
|
||||
path.ends_with(BIN_EXTENSION) || path.ends_with(COMPRESSED_BIN_EXTENSION)
|
||||
}
|
||||
Self::Json => path.ends_with(JSON_EXTENSION) || path.ends_with(HAR_EXTENSION),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import<T>(&self, path: &Path) -> color_eyre::Result<T>
|
||||
where
|
||||
T: Debug + DeserializeOwned + Decode,
|
||||
{
|
||||
match self {
|
||||
Serialization::Binary => {
|
||||
if self.is_serializable(path) {
|
||||
Binary::import(path)
|
||||
} else {
|
||||
let path = path.to_str().unwrap();
|
||||
let bin_path_str = format!("{path}.{BIN_EXTENSION}");
|
||||
let bin_path = Path::new(&bin_path_str);
|
||||
|
||||
if bin_path.exists() {
|
||||
return Binary::import(bin_path);
|
||||
}
|
||||
|
||||
let compressed_bin_path_str = format!("{path}.{COMPRESSED_BIN_EXTENSION}");
|
||||
let compressed_bin_path = Path::new(&compressed_bin_path_str);
|
||||
|
||||
if compressed_bin_path.exists() {
|
||||
return Binary::import(compressed_bin_path);
|
||||
}
|
||||
|
||||
Err(eyre!("Wrong path or no file"))
|
||||
}
|
||||
}
|
||||
Serialization::Json => {
|
||||
if self.is_serializable(path) {
|
||||
Json::import(path)
|
||||
} else {
|
||||
let path = path.to_str().unwrap();
|
||||
let json_path_str = format!("{path}.{JSON_EXTENSION}");
|
||||
let json_path = Path::new(&json_path_str);
|
||||
|
||||
if json_path.exists() {
|
||||
return Json::import(json_path);
|
||||
}
|
||||
|
||||
Err(eyre!("Wrong path or no file"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export<T>(&self, path: &Path, value: &T) -> color_eyre::Result<()>
|
||||
where
|
||||
T: Debug + Serialize + Encode,
|
||||
{
|
||||
match self {
|
||||
Serialization::Binary => {
|
||||
if self.is_serializable(path) {
|
||||
Binary::export(path, value)
|
||||
} else {
|
||||
let path = path.to_str().unwrap();
|
||||
|
||||
let res = Binary::export(
|
||||
Path::new(&format!("{}.{COMPRESSED_BIN_EXTENSION}", path)),
|
||||
value,
|
||||
);
|
||||
|
||||
if res.is_ok() {
|
||||
let _ = fs::remove_file(Path::new(&format!("{}.{BIN_EXTENSION}", path)));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
Serialization::Json => {
|
||||
if self.is_serializable(path) {
|
||||
Json::export(path, value)
|
||||
} else {
|
||||
Json::export(
|
||||
Path::new(&format!("{}.{JSON_EXTENSION}", path.to_str().unwrap())),
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PathBuf> for Serialization {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
|
||||
let extension = path.extension().ok_or(())?.to_str().unwrap();
|
||||
|
||||
match extension {
|
||||
BIN_EXTENSION | COMPRESSED_BIN_EXTENSION => Ok(Self::Binary),
|
||||
JSON_EXTENSION => Ok(Self::Json),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ pub struct StorableVec<I, T, const MODE: u8> {
|
||||
|
||||
/// In bytes
|
||||
const MAX_PAGE_SIZE: usize = 4 * 4096;
|
||||
const ONE_MB: usize = 1000 * 1024;
|
||||
// const ONE_MB: usize = 1000 * 1024;
|
||||
const MAX_CACHE_SIZE: usize = usize::MAX;
|
||||
// const MAX_CACHE_SIZE: usize = 100 * ONE_MB;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user