diff --git a/crates/brk_cli/src/query.rs b/crates/brk_cli/src/query.rs index 68e0ca09f..50372c45e 100644 --- a/crates/brk_cli/src/query.rs +++ b/crates/brk_cli/src/query.rs @@ -22,7 +22,7 @@ pub fn query(params: QueryParams) -> color_eyre::Result<()> { let ids = params.values.iter().map(|s| s.as_str()).collect::>(); - let res = query.search(index, &ids, params.from, params.to, params.format)?; + let res = query.search_and_format(index, &ids, params.from, params.to, params.format)?; if params.format.is_some() { println!("{}", res); diff --git a/crates/brk_computer/src/storage/vecs/stats/generic.rs b/crates/brk_computer/src/storage/vecs/stats/generic.rs index 0b4856d08..953daae3f 100644 --- a/crates/brk_computer/src/storage/vecs/stats/generic.rs +++ b/crates/brk_computer/src/storage/vecs/stats/generic.rs @@ -193,6 +193,7 @@ where Ok(()) } + #[allow(clippy::wrong_self_convention)] pub fn from_aligned( &mut self, max_from: I, diff --git a/crates/brk_core/src/structs/timestamp.rs b/crates/brk_core/src/structs/timestamp.rs index 1d9adee47..419a4b2ca 100644 --- a/crates/brk_core/src/structs/timestamp.rs +++ b/crates/brk_core/src/structs/timestamp.rs @@ -1,10 +1,7 @@ use std::ops::{Add, Div}; use derive_deref::Deref; -use jiff::{ - civil::{Time, date}, - tz::TimeZone, -}; +use jiff::{civil::date, tz::TimeZone}; use serde::Serialize; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; diff --git a/crates/brk_indexer/src/stores/meta.rs b/crates/brk_indexer/src/stores/meta.rs index 5f7c13047..ccb3c3bbd 100644 --- a/crates/brk_indexer/src/stores/meta.rs +++ b/crates/brk_indexer/src/stores/meta.rs @@ -20,8 +20,8 @@ impl StoreMeta { pub fn checked_open(path: &Path, version: Version) -> color_eyre::Result { fs::create_dir_all(path)?; - let is_same_version = - Version::try_from(Self::path_version_(path).as_path()).is_ok_and(|prev_version| version == prev_version); + let is_same_version = Version::try_from(Self::path_version_(path).as_path()) + .is_ok_and(|prev_version| version == prev_version); if !is_same_version { Self::reset_(path)?; @@ -92,7 +92,7 @@ impl StoreMeta { fn write_length(&self) -> io::Result<()> { Self::write_length_(&self.pathbuf, self.len) } - fn write_length_(path: &Path, len: usize) -> Result<(), io::Error> { + fn write_length_(path: &Path, len: usize) -> io::Result<()> { fs::write(Self::path_length(path), len.as_bytes()) } fn path_length(path: &Path) -> PathBuf { diff --git a/crates/brk_query/examples/main.rs b/crates/brk_query/examples/main.rs index 7f8c5b42c..19dafe051 100644 --- a/crates/brk_query/examples/main.rs +++ b/crates/brk_query/examples/main.rs @@ -19,9 +19,9 @@ pub fn main() -> color_eyre::Result<()> { 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)?); + dbg!(query.search_and_format(Index::Height, &["date"], Some(-1), None, None)?); + dbg!(query.search_and_format(Index::Height, &["date"], Some(-10), None, None)?); + dbg!(query.search_and_format(Index::Height, &["date", "timestamp"], Some(-10), None, None)?); Ok(()) } diff --git a/crates/brk_query/src/lib.rs b/crates/brk_query/src/lib.rs index 74c7457bb..9ed1522d8 100644 --- a/crates/brk_query/src/lib.rs +++ b/crates/brk_query/src/lib.rs @@ -5,6 +5,7 @@ use brk_computer::Computer; use brk_indexer::Indexer; +use brk_vec::AnyStorableVec; use tabled::settings::Style; mod format; @@ -50,14 +51,7 @@ impl<'a> Query<'a> { } } - pub fn search( - &self, - index: Index, - ids: &[&str], - from: Option, - to: Option, - format: Option, - ) -> color_eyre::Result { + pub fn search(&self, index: Index, ids: &[&str]) -> Vec<(String, &&dyn AnyStorableVec)> { let tuples = ids .iter() .flat_map(|s| { @@ -84,14 +78,22 @@ impl<'a> Query<'a> { .map(|(id, vec)| (id, vec.unwrap())) .collect::>(); - if tuples.is_empty() { - return Ok(Output::default(format)); - } - - let mut values = tuples + tuples .iter() - .flat_map(|(_, i_to_v)| i_to_v.get(&index)) - .map(|vec| -> brk_vec::Result> { + .flat_map(|(str, i_to_v)| i_to_v.get(&index).map(|vec| (str.to_owned(), vec))) + .collect::>() + } + + pub fn format( + &self, + vecs: Vec<(String, &&dyn AnyStorableVec)>, + from: Option, + to: Option, + format: Option, + ) -> color_eyre::Result { + let mut values = vecs + .iter() + .map(|(_, vec)| -> brk_vec::Result> { vec.collect_range_values(from, to) }) .collect::>>()?; @@ -100,7 +102,7 @@ impl<'a> Query<'a> { return Ok(Output::default(format)); } - let ids_last_i = tuples.len() - 1; + let ids_last_i = vecs.len() - 1; Ok(match format { Some(Format::CSV) | Some(Format::TSV) => { @@ -110,9 +112,9 @@ impl<'a> Query<'a> { '\t' }; - let mut text = tuples - .into_iter() - .map(|(id, _)| id) + let mut text = vecs + .iter() + .map(|(id, _)| id.to_owned()) .collect::>() .join(&delimiter.to_string()); @@ -141,7 +143,7 @@ impl<'a> Query<'a> { } Some(Format::MD) => { let mut table = - values.to_table(tuples.iter().map(|(s, _)| s.to_owned()).collect::>()); + values.to_table(vecs.iter().map(|(s, _)| s.to_owned()).collect::>()); table.with(Style::markdown()); @@ -162,4 +164,15 @@ impl<'a> Query<'a> { } }) } + + pub fn search_and_format( + &self, + index: Index, + ids: &[&str], + from: Option, + to: Option, + format: Option, + ) -> color_eyre::Result { + self.format(self.search(index, ids), from, to, format) + } } diff --git a/crates/brk_server/src/api/query/mod.rs b/crates/brk_server/src/api/query/mod.rs index 5af0c5e56..5e093757c 100644 --- a/crates/brk_server/src/api/query/mod.rs +++ b/crates/brk_server/src/api/query/mod.rs @@ -8,7 +8,10 @@ use axum::{ }; use brk_query::{Format, Index, Output, Params}; -use crate::{log_result, traits::HeaderMapExtended}; +use crate::{ + log_result, + traits::{HeaderMapExtended, ModifiedState, ResponseExtended}, +}; use super::AppState; @@ -52,13 +55,32 @@ fn req_to_response_res( ) -> color_eyre::Result { let index = Index::try_from(index.as_str())?; - let output = query.search( + let vecs = query.search( index, &values.iter().map(|v| v.as_str()).collect::>(), - from, - to, - format, - )?; + ); + + let mut date_modified_opt = None; + + if to.is_none() { + let not_modified = vecs + .iter() + .map(|(_, vec)| headers.check_if_modified_since(&vec.path_vec())) + .all(|res| { + res.is_ok_and(|(modified, date_modified)| { + if date_modified_opt.is_none_or(|dm| dm > date_modified) { + date_modified_opt.replace(date_modified); + } + modified == ModifiedState::NotModifiedSince + }) + }); + + if not_modified { + return Ok(Response::new_not_modified()); + } + } + + let output = query.format(vecs, from, to, format)?; let mut response = match output { Output::CSV(s) => s.into_response(), @@ -74,7 +96,10 @@ fn req_to_response_res( let headers = response.headers_mut(); headers.insert_cors(); - // headers.insert_last_modified(date_modified); + + if let Some(date_modified) = date_modified_opt { + headers.insert_last_modified(date_modified); + } match format { Some(format) => { diff --git a/crates/brk_server/src/traits/header_map.rs b/crates/brk_server/src/traits/header_map.rs index 923155885..ab106c69f 100644 --- a/crates/brk_server/src/traits/header_map.rs +++ b/crates/brk_server/src/traits/header_map.rs @@ -26,7 +26,8 @@ pub trait HeaderMapExtended { fn insert_cors(&mut self); fn get_if_modified_since(&self) -> Option; - fn check_if_modified_since(&self, path: &Path) -> color_eyre::Result<(ModifiedState, DateTime)>; + fn check_if_modified_since(&self, path: &Path) + -> color_eyre::Result<(ModifiedState, DateTime)>; fn insert_cache_control_immutable(&mut self); #[allow(unused)] @@ -114,8 +115,15 @@ impl HeaderMapExtended for HeaderMap { self.insert(header::LAST_MODIFIED, formatted.parse().unwrap()); } - fn check_if_modified_since(&self, path: &Path) -> color_eyre::Result<(ModifiedState, DateTime)> { - let duration = path.metadata()?.modified()?.duration_since(time::UNIX_EPOCH).unwrap(); + fn check_if_modified_since( + &self, + path: &Path, + ) -> color_eyre::Result<(ModifiedState, DateTime)> { + let duration = path + .metadata()? + .modified()? + .duration_since(time::UNIX_EPOCH) + .unwrap(); let date = Timestamp::new(duration.as_secs() as i64, 0) .unwrap() .to_zoned(TimeZone::UTC) @@ -177,7 +185,10 @@ impl HeaderMapExtended for HeaderMap { } fn insert_content_type_application_javascript(&mut self) { - self.insert(header::CONTENT_TYPE, "application/javascript".parse().unwrap()); + self.insert( + header::CONTENT_TYPE, + "application/javascript".parse().unwrap(), + ); } fn insert_content_type_application_json(&mut self) { @@ -185,7 +196,10 @@ impl HeaderMapExtended for HeaderMap { } fn insert_content_type_application_manifest_json(&mut self) { - self.insert(header::CONTENT_TYPE, "application/manifest+json".parse().unwrap()); + self.insert( + header::CONTENT_TYPE, + "application/manifest+json".parse().unwrap(), + ); } fn insert_content_type_application_pdf(&mut self) { @@ -201,7 +215,10 @@ impl HeaderMapExtended for HeaderMap { } fn insert_content_type_text_tsv(&mut self) { - self.insert(header::CONTENT_TYPE, "text/tab-separated-values".parse().unwrap()); + self.insert( + header::CONTENT_TYPE, + "text/tab-separated-values".parse().unwrap(), + ); } fn insert_content_type_text_html(&mut self) { diff --git a/crates/brk_vec/src/traits/any.rs b/crates/brk_vec/src/traits/any.rs index ef5280910..1a7ccd843 100644 --- a/crates/brk_vec/src/traits/any.rs +++ b/crates/brk_vec/src/traits/any.rs @@ -1,4 +1,4 @@ -use std::io; +use std::{io, path::PathBuf}; use crate::{Result, StorableVec}; @@ -15,6 +15,7 @@ pub trait AnyStorableVec: Send + Sync { to: Option, ) -> Result>; fn flush(&mut self) -> io::Result<()>; + fn path_vec(&self) -> PathBuf; } impl AnyStorableVec for StorableVec @@ -53,4 +54,8 @@ where .map(|v| serde_json::to_value(v).unwrap()) .collect::>()) } + + fn path_vec(&self) -> PathBuf { + self.base().path_vec() + } }