From 481f5c0a97e1b8dfd179b016df9092f340ec8cae Mon Sep 17 00:00:00 2001 From: nym21 Date: Fri, 27 Dec 2024 12:28:27 +0100 Subject: [PATCH] server: add support for dataset by timestamp --- CHANGELOG.md | 1 + src/parser/actions/iter_blocks.rs | 4 +- src/parser/actions/parse.rs | 2 +- src/parser/price/binance.rs | 2 +- src/parser/price/kraken.rs | 2 +- src/server/api/handlers/dataset.rs | 222 ++++++++++++++++++++-------- src/server/api/structs/extension.rs | 2 +- src/server/api/structs/kind.rs | 13 +- src/server/header_map.rs | 37 ++--- src/server/mod.rs | 1 + src/server/response.rs | 20 +++ src/server/website/handlers/file.rs | 13 +- src/structs/config.rs | 5 +- src/structs/date.rs | 2 +- src/structs/generic_map.rs | 4 +- src/structs/serialized_btreemap.rs | 3 +- src/structs/serialized_vec.rs | 6 + src/structs/timestamp.rs | 64 +++++++- src/utils/retry.rs | 4 - 19 files changed, 286 insertions(+), 121 deletions(-) create mode 100644 src/server/response.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d6696cd..da7793865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Reworked server's API code [#6ab0f46]( https://github.com/kibo-money/kibo/commit/6ab0f463119a902a1b7ca9691b54f61543bb8f2f) - New route format: `/api/date-to-realized-price` is now `/api/realized-price?kind=date` - Added status and timing to logs +- Updated website packages ## Git diff --git a/src/parser/actions/iter_blocks.rs b/src/parser/actions/iter_blocks.rs index 909a37ac9..de9b630b2 100644 --- a/src/parser/actions/iter_blocks.rs +++ b/src/parser/actions/iter_blocks.rs @@ -72,7 +72,7 @@ pub fn iter_blocks( if let Some((_current_block_height, current_block, _current_block_hash)) = current_block_opt { - let timestamp = Timestamp::wrap(current_block.header.time); + let timestamp = Timestamp::from(current_block.header.time); let current_block_date = timestamp.to_date(); let current_block_height: Height = height + blocks_loop_i; @@ -83,7 +83,7 @@ pub fn iter_blocks( } next_date_opt = next_block_opt.as_ref().map(|(_, next_block, _)| { - Timestamp::wrap(next_block.header.time).to_date() + Timestamp::from(next_block.header.time).to_date() }); // Always run for the first block of the loop diff --git a/src/parser/actions/parse.rs b/src/parser/actions/parse.rs index 39e5207e8..f3f94ed7a 100644 --- a/src/parser/actions/parse.rs +++ b/src/parser/actions/parse.rs @@ -61,7 +61,7 @@ pub fn parse( ) { // log(&format!("{height}")); - let timestamp = Timestamp::wrap(block.header.time); + let timestamp = Timestamp::from(block.header.time); // If false, expect that the code is flawless // or create a 0 value txid database diff --git a/src/parser/price/binance.rs b/src/parser/price/binance.rs index 3075f010d..304a5db4a 100644 --- a/src/parser/price/binance.rs +++ b/src/parser/price/binance.rs @@ -171,7 +171,7 @@ impl Binance { // [timestamp, open, high, low, close, volume, ...] let array = value.as_array().unwrap(); - let date = Timestamp::wrap( + let date = Timestamp::from( (array.first().unwrap().as_u64().unwrap() / 1_000) as u32, ) .to_date(); diff --git a/src/parser/price/kraken.rs b/src/parser/price/kraken.rs index dc7e403f4..d308b7f27 100644 --- a/src/parser/price/kraken.rs +++ b/src/parser/price/kraken.rs @@ -91,7 +91,7 @@ impl Kraken { .map(|value| { let array = value.as_array().unwrap(); - let date = Timestamp::wrap(array.first().unwrap().as_u64().unwrap() as u32) + let date = Timestamp::from(array.first().unwrap().as_u64().unwrap() as u32) .to_date(); let get_f32 = |index: usize| { diff --git a/src/server/api/handlers/dataset.rs b/src/server/api/handlers/dataset.rs index e547a44dd..b7cc4154e 100644 --- a/src/server/api/handlers/dataset.rs +++ b/src/server/api/handlers/dataset.rs @@ -1,6 +1,7 @@ use std::{fmt::Debug, path::PathBuf, time::Instant}; use axum::{ + body::Body, extract::{Path, Query, State}, http::HeaderMap, response::{IntoResponse, Response}, @@ -14,15 +15,19 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ server::{ api::{ - structs::{ChunkMetadata, DatasetRange, DatasetRangeChunk, Extension, Kind, Route}, + structs::{ + ChunkMetadata, DatasetRange, DatasetRangeChunk, Extension, Kind, Route, Routes, + }, API_URL_PREFIX, }, - header_map::HeaderMapUtils, - log_result, AppState, + header_map::{HeaderMapExtended, Modified}, + log_result, + response::ResponseExtended, + AppState, }, structs::{ - Date, GenericMap, Height, MapChunkId, MapKey, MapSerialized, MapValue, SerializedDateMap, - SerializedVec, OHLC, + Date, GenericMap, Height, HeightMapChunkId, MapChunkId, MapKey, MapSerialized, MapValue, + SerializedBTreeMap, SerializedDateMap, SerializedTimeMap, SerializedVec, Timestamp, OHLC, }, }; @@ -86,19 +91,17 @@ fn result_handler( let type_name = route.type_name.as_str(); Ok(match type_name { - "u8" => typed_handler::(headers, id, ext, query, route)?, - "u16" => typed_handler::(headers, id, ext, query, route)?, - "u32" => typed_handler::(headers, id, ext, query, route)?, - "u64" => typed_handler::(headers, id, ext, query, route)?, - "usize" => typed_handler::(headers, id, ext, query, route)?, - "f32" => typed_handler::(headers, id, ext, query, route)?, - "f64" => typed_handler::(headers, id, ext, query, route)?, - "OHLC" => typed_handler::(headers, id, ext, query, route)?, - "Date" => typed_handler::(headers, id, ext, query, route)?, - "Height" => typed_handler::(headers, id, ext, query, route)?, - // "Value" => { - // value_to_response::(Json::import(&route.file_path)?, extension) - // } + "u8" => typed_handler::(headers, id, ext, query, route, &routes)?, + "u16" => typed_handler::(headers, id, ext, query, route, &routes)?, + "u32" => typed_handler::(headers, id, ext, query, route, &routes)?, + "u64" => typed_handler::(headers, id, ext, query, route, &routes)?, + "usize" => typed_handler::(headers, id, ext, query, route, &routes)?, + "f32" => typed_handler::(headers, id, ext, query, route, &routes)?, + "f64" => typed_handler::(headers, id, ext, query, route, &routes)?, + "OHLC" => typed_handler::(headers, id, ext, query, route, &routes)?, + "Date" => typed_handler::(headers, id, ext, query, route, &routes)?, + "Height" => typed_handler::(headers, id, ext, query, route, &routes)?, + "Timestamp" => typed_handler::(headers, id, ext, query, route, &routes)?, _ => panic!("Incompatible type: {type_name}"), }) } @@ -109,6 +112,7 @@ fn typed_handler( ext: Option, query: &Query, route: &Route, + routes: &Routes, ) -> color_eyre::Result where T: Serialize + Debug + DeserializeOwned + Decode + MapValue, @@ -121,26 +125,97 @@ where let range = DatasetRange::try_from(query)?; let (mut response, date_modified) = match kind { - Kind::Date => map_to_response::>( - id, headers, route, &ext, range, query, - ), - Kind::Height => map_to_response::>( - id, headers, route, &ext, range, query, - ), Kind::Last => { let last_value: T = route.serialization.import(&route.path.join("last"))?; return Ok(axum::response::Json(last_value).into_response()); } - }?; + Kind::Date => match read_serialized::>( + id, &headers, route, &range, query, + )? { + ReadSerialized::DatasetAndDate((dataset, date, chunk_meta)) => { + (serialized_to_response(dataset, id, chunk_meta, ext), date) + } + ReadSerialized::NotModified => return Ok(Response::new_not_modified()), + ReadSerialized::_Phantom(_) => unreachable!(), + }, + Kind::Height => match read_serialized::>( + id, &headers, route, &range, query, + )? { + ReadSerialized::DatasetAndDate((dataset, date, chunk_meta)) => ( + serialized_to_response::>( + dataset, id, chunk_meta, ext, + ), + date, + ), + ReadSerialized::NotModified => return Ok(Response::new_not_modified()), + ReadSerialized::_Phantom(_) => unreachable!(), + }, + Kind::Timestamp => { + let (dataset, date, chunk_meta) = match read_serialized::>( + id, &headers, route, &range, query, + )? { + ReadSerialized::DatasetAndDate(tuple) => tuple, + ReadSerialized::NotModified => return Ok(Response::new_not_modified()), + ReadSerialized::_Phantom(_) => unreachable!(), + }; + + let (timestamp_dataset, _, _) = + match read_serialized::>( + "timestamp", + &headers, + routes.get("timestamp").unwrap(), + &range, + query, + )? { + ReadSerialized::DatasetAndDate(tuple) => tuple, + ReadSerialized::NotModified => return Ok(Response::new_not_modified()), + ReadSerialized::_Phantom(_) => unreachable!(), + }; + + let mut serialized_timemap: SerializedTimeMap = SerializedBTreeMap::default(); + + dataset + .map + .into_iter() + .enumerate() + .for_each(|(index, value)| { + serialized_timemap.map.insert( + timestamp_dataset + .get_index(index) + .cloned() + .unwrap_or(Timestamp::now()), + value, + ); + }); + + ( + serialized_to_response::>( + serialized_timemap, + id, + chunk_meta, + ext, + ), + date, + ) + + // let m = read_serialized::>( + // id, &headers, route, &range, query, + // )?; + // let t = read_serialized::>( + // "timestamp", + // &headers, + // routes.get("timestamp").unwrap(), + // &range, + // query, + // ); + // t + } + }; - let status_ok = response.status() == StatusCode::OK; let headers = response.headers_mut(); headers.insert_cors(); - - if status_ok { - headers.insert_last_modified(date_modified); - } + headers.insert_last_modified(date_modified); match ext { Some(extension) => { @@ -156,14 +231,51 @@ where Ok(response) } -fn map_to_response( +fn serialized_to_response( + dataset: Serialized, id: &str, - headers: HeaderMap, + chunk_meta: Option, + ext: Option, +) -> Response +where + Key: MapKey, + Value: MapValue, + ChunkId: MapChunkId, + Serialized: MapSerialized, +{ + if ext == Some(Extension::CSV) { + dataset.to_csv(id).into_response() + } else if let Some(chunk) = chunk_meta { + axum::response::Json(SerializedMapChunk { + chunk, + map: dataset.map(), + version: dataset.version(), + }) + .into_response() + } else { + axum::response::Json(dataset).into_response() + } +} + +enum ReadSerialized +where + Key: MapKey, + Value: MapValue, + ChunkId: MapChunkId, + Serialized: MapSerialized, +{ + DatasetAndDate((Serialized, DateTime, Option)), + NotModified, + _Phantom((Key, Value, ChunkId)), +} + +fn read_serialized( + id: &str, + headers: &HeaderMap, route: &Route, - ext: &Option, - range: DatasetRange, + range: &DatasetRange, query: &Query, -) -> color_eyre::Result<(Response, DateTime)> +) -> color_eyre::Result> where Key: MapKey, Value: MapValue, @@ -188,7 +300,7 @@ where .context("Last tuple of dataset directory")? .0 } - DatasetRangeChunk::Chunk(chunk) => ChunkId::from_usize(chunk), + DatasetRangeChunk::Chunk(chunk) => ChunkId::from_usize(*chunk), }; let chunk_path = datasets.get(&chunk_id); @@ -197,12 +309,11 @@ where } let chunk_path = chunk_path.unwrap(); - let (date, response) = headers.check_if_modified_since(chunk_path)?; - date_modified = date; - - if let Some(response) = response { - return Ok((response, date_modified)); + let (modified, date) = headers.check_if_modified_since(chunk_path)?; + if modified == Modified::NotModifiedSince { + return Ok(ReadSerialized::NotModified); } + date_modified = date; let to_url = |chunk: Option| { chunk.and_then(|chunk| { @@ -237,30 +348,21 @@ where }) .context("Expect to find newest file")?; - let (date, response) = headers.check_if_modified_since(newest_file)?; - date_modified = date; - - if let Some(response) = response { - return Ok((response, date_modified)); + let (modified, date) = headers.check_if_modified_since(newest_file)?; + if modified == Modified::NotModifiedSince { + return Ok(ReadSerialized::NotModified); } + date_modified = date; + Serialized::import_all(&folder_path, serialization) }; - let response = if *ext == Some(Extension::CSV) { - dataset.to_csv(id).into_response() - } else if let Some(chunk) = chunk_meta { - axum::response::Json(SerializedMapChunk { - chunk, - map: dataset.map(), - version: dataset.version(), - }) - .into_response() - } else { - axum::response::Json(dataset).into_response() - }; - - Ok((response, date_modified)) + Ok(ReadSerialized::DatasetAndDate(( + dataset, + date_modified, + chunk_meta, + ))) } #[derive(Serialize)] diff --git a/src/server/api/structs/extension.rs b/src/server/api/structs/extension.rs index 21a7477c4..1a4d5a33c 100644 --- a/src/server/api/structs/extension.rs +++ b/src/server/api/structs/extension.rs @@ -1,6 +1,6 @@ use std::path::Path; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone, Copy)] pub enum Extension { #[allow(clippy::upper_case_acronyms)] CSV, diff --git a/src/server/api/structs/kind.rs b/src/server/api/structs/kind.rs index ab25dc6f0..67456ceb5 100644 --- a/src/server/api/structs/kind.rs +++ b/src/server/api/structs/kind.rs @@ -9,6 +9,7 @@ use crate::structs::{AnyMap, Date, Height, MapKey}; pub enum Kind { Date, Height, + Timestamp, Last, } @@ -25,6 +26,7 @@ impl TryFrom<&String> for Kind { { 'd' => Self::Date, 'h' => Self::Height, + 't' => Self::Timestamp, 'l' => Self::Last, _ => return Err(eyre!("Bad kind")), }, @@ -40,6 +42,7 @@ impl From<&(dyn AnyMap + Send + Sync)> for BTreeSet { } if map.key_name() == Height::map_name() { s.insert(Kind::Height); + s.insert(Kind::Timestamp); } if map.last_value().is_some() { s.insert(Kind::Last); @@ -47,13 +50,3 @@ impl From<&(dyn AnyMap + Send + Sync)> for BTreeSet { s } } - -// impl std::fmt::Display for Kind { -// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { -// match *self { -// Self::Date => write!(f, "date"), -// Self::Height => write!(f, "height"), -// Self::Last => write!(f, "last"), -// } -// } -// } diff --git a/src/server/header_map.rs b/src/server/header_map.rs index 837ec6132..c9b63c42c 100644 --- a/src/server/header_map.rs +++ b/src/server/header_map.rs @@ -1,21 +1,20 @@ use std::path::Path; -use axum::{ - body::Body, - http::{header, HeaderMap, Response}, - response::IntoResponse, -}; +use axum::http::{header, HeaderMap}; use chrono::{DateTime, Timelike, Utc}; use log::info; -use reqwest::{ - header::{HOST, IF_MODIFIED_SINCE}, - StatusCode, -}; +use reqwest::header::{HOST, IF_MODIFIED_SINCE}; const STALE_IF_ERROR: u64 = 30_000_000; // 1 Year ish const MODIFIED_SINCE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT"; -pub trait HeaderMapUtils { +#[derive(PartialEq, Eq)] +pub enum Modified { + ModifiedSince, + NotModifiedSince, +} + +pub trait HeaderMapExtended { fn get_scheme(&self) -> &str; fn get_host(&self) -> &str; fn check_if_host_is_any_local(&self) -> bool; @@ -25,10 +24,8 @@ pub trait HeaderMapUtils { fn insert_cors(&mut self); fn get_if_modified_since(&self) -> Option>; - fn check_if_modified_since( - &self, - path: &Path, - ) -> color_eyre::Result<(DateTime, Option>)>; + fn check_if_modified_since(&self, path: &Path) + -> color_eyre::Result<(Modified, DateTime)>; fn insert_cache_control_immutable(&mut self); #[allow(unused)] @@ -52,7 +49,7 @@ pub trait HeaderMapUtils { fn insert_content_type_font_woff2(&mut self); } -impl HeaderMapUtils for HeaderMap { +impl HeaderMapExtended for HeaderMap { fn get_scheme(&self) -> &str { if self.check_if_host_is_any_local() { "http" @@ -114,22 +111,18 @@ impl HeaderMapUtils for HeaderMap { fn check_if_modified_since( &self, path: &Path, - ) -> color_eyre::Result<(DateTime, Option>)> { + ) -> color_eyre::Result<(Modified, DateTime)> { let time = path.metadata()?.modified()?; let date: DateTime = time.into(); let date = date.with_nanosecond(0).unwrap(); - let mut response_opt = None; if let Some(if_modified_since) = self.get_if_modified_since() { if if_modified_since == date { - let mut response = (StatusCode::NOT_MODIFIED, "").into_response(); - let headers = response.headers_mut(); - headers.insert_cors(); - response_opt.replace(response); + return Ok((Modified::NotModifiedSince, date)); } } - Ok((date, response_opt)) + Ok((Modified::ModifiedSince, date)) } fn get_if_modified_since(&self) -> Option> { diff --git a/src/server/mod.rs b/src/server/mod.rs index 9284ae71c..6cdd0422e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -13,6 +13,7 @@ use crate::structs::Config; pub mod api; mod header_map; +mod response; mod website; #[derive(Clone)] diff --git a/src/server/response.rs b/src/server/response.rs new file mode 100644 index 000000000..78f7c3bfc --- /dev/null +++ b/src/server/response.rs @@ -0,0 +1,20 @@ +use axum::{body::Body, http::Response, response::IntoResponse}; +use reqwest::StatusCode; + +use super::header_map::HeaderMapExtended; + +pub trait ResponseExtended +where + Self: Sized, +{ + fn new_not_modified() -> Self; +} + +impl ResponseExtended for Response { + fn new_not_modified() -> Response { + let mut response = (StatusCode::NOT_MODIFIED, "").into_response(); + let headers = response.headers_mut(); + headers.insert_cors(); + response + } +} diff --git a/src/server/website/handlers/file.rs b/src/server/website/handlers/file.rs index 125e7136c..c2788c951 100644 --- a/src/server/website/handlers/file.rs +++ b/src/server/website/handlers/file.rs @@ -13,7 +13,11 @@ use axum::{ use log::{error, info}; use reqwest::StatusCode; -use crate::server::{header_map::HeaderMapUtils, log_result}; +use crate::server::{ + header_map::{HeaderMapExtended, Modified}, + log_result, + response::ResponseExtended, +}; use super::minify_js; @@ -80,10 +84,9 @@ fn path_to_response(headers: &HeaderMap, path: &Path) -> Response { } fn _path_to_response(headers: &HeaderMap, path: &Path) -> color_eyre::Result { - let (date, response) = headers.check_if_modified_since(path)?; - - if let Some(response) = response { - return Ok(response); + let (modified, date) = headers.check_if_modified_since(path)?; + if modified == Modified::NotModifiedSince { + return Ok(Response::new_not_modified()); } let mut response; diff --git a/src/structs/config.rs b/src/structs/config.rs index f51af3a0d..89ac372e1 100644 --- a/src/structs/config.rs +++ b/src/structs/config.rs @@ -77,6 +77,7 @@ pub struct Config { impl Config { pub const DATASET_DIR_NAME: &str = "datasets"; + pub const DATABASES_DIR_NAME: &str = "databases"; pub fn import() -> color_eyre::Result { let path = Self::path_dot_kibo(); @@ -285,7 +286,7 @@ impl Config { } pub fn path_datasets(&self) -> MapPath { - MapPath::from(self.path_kibodir().join("datasets")) + MapPath::from(self.path_kibodir().join(Self::DATASET_DIR_NAME)) } pub fn path_datasets_last_values(&self) -> MapPath { @@ -297,7 +298,7 @@ impl Config { } pub fn path_databases(&self) -> PathBuf { - self.path_kibodir().join(Self::DATASET_DIR_NAME) + self.path_kibodir().join(Self::DATABASES_DIR_NAME) } pub fn path_states(&self) -> PathBuf { diff --git a/src/structs/date.rs b/src/structs/date.rs index fd3f0f77f..b99e933a4 100644 --- a/src/structs/date.rs +++ b/src/structs/date.rs @@ -49,7 +49,7 @@ impl Date { } pub fn to_timestamp(self) -> Timestamp { - Timestamp::wrap(NaiveDateTime::from(*self).and_utc().timestamp() as u32) + Timestamp::from(NaiveDateTime::from(*self).and_utc().timestamp() as u32) } /// Returns value between 0.0 and 1.0 depending on its completion diff --git a/src/structs/generic_map.rs b/src/structs/generic_map.rs index 6cdb099f6..f15c92e05 100644 --- a/src/structs/generic_map.rs +++ b/src/structs/generic_map.rs @@ -322,8 +322,8 @@ where fn id(&self, config: &Config) -> String { let path_to_string = |p: &Path| p.to_str().unwrap().to_owned(); path_to_string(self.path_parent()) - .replace(&path_to_string(&config.path_kibodir()), "") - .replace(&format!("/{}/", Config::DATASET_DIR_NAME), "") + .replace(&format!("{}/", path_to_string(&config.path_kibodir())), "") + .replace(&format!("{}/", Config::DATASET_DIR_NAME), "") .replace("/", "-") } diff --git a/src/structs/serialized_btreemap.rs b/src/structs/serialized_btreemap.rs index e70fb953d..3b5b730d4 100644 --- a/src/structs/serialized_btreemap.rs +++ b/src/structs/serialized_btreemap.rs @@ -6,9 +6,10 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::io::Serialization; -use super::{Date, DateMap, MapChunkId, MapKey, MapSerialized, MapValue}; +use super::{Date, DateMap, MapChunkId, MapKey, MapSerialized, MapValue, Timestamp}; pub type SerializedDateMap = SerializedBTreeMap; +pub type SerializedTimeMap = SerializedBTreeMap; #[derive(Debug, Default, Serialize, Deserialize, Encode, Decode, Allocative)] pub struct SerializedBTreeMap diff --git a/src/structs/serialized_vec.rs b/src/structs/serialized_vec.rs index f8dac4c1b..73efd3f3e 100644 --- a/src/structs/serialized_vec.rs +++ b/src/structs/serialized_vec.rs @@ -14,6 +14,12 @@ pub struct SerializedVec { pub map: Vec, } +impl SerializedVec { + pub fn get_index(&self, index: usize) -> Option<&Value> { + self.map.get(index) + } +} + impl MapSerialized for SerializedVec where Self: Debug + Serialize + DeserializeOwned + Encode + Decode, diff --git a/src/structs/timestamp.rs b/src/structs/timestamp.rs index e1ee2efeb..0d8cc88df 100644 --- a/src/structs/timestamp.rs +++ b/src/structs/timestamp.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::utils::{ONE_DAY_IN_S, ONE_HOUR_IN_S}; -use super::Date; +use super::{Date, HeightMapChunkId, MapKey}; #[derive( Debug, @@ -32,10 +32,6 @@ pub struct Timestamp(u32); impl Timestamp { pub const ZERO: Self = Self(0); - pub fn wrap(timestamp: u32) -> Self { - Self(timestamp) - } - pub fn now() -> Self { Self(chrono::offset::Utc::now().timestamp() as u32) } @@ -51,7 +47,7 @@ impl Timestamp { pub fn to_floored_seconds(self) -> Self { let date_time = Utc.timestamp_opt(i64::from(self.0), 0).unwrap(); - Self::wrap( + Self::from( NaiveDateTime::new( date_time.date_naive(), NaiveTime::from_hms_opt(date_time.hour(), date_time.minute(), 0).unwrap(), @@ -70,7 +66,7 @@ impl Timestamp { } pub fn older_by_1h_plus_than(&self, younger: Self) -> bool { - younger.checked_sub(**self).unwrap_or_default() > ONE_HOUR_IN_S as u32 + (*younger).checked_sub(**self).unwrap_or_default() > ONE_HOUR_IN_S as u32 } } @@ -78,7 +74,7 @@ impl Sub for Timestamp { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - Self::wrap(self.0 - rhs.0) + Self::from(self.0 - rhs.0) } } @@ -87,3 +83,55 @@ impl fmt::Display for Timestamp { write!(f, "{}", **self) } } + +impl From for Timestamp { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl MapKey for Timestamp { + fn to_chunk_id(&self) -> HeightMapChunkId { + unreachable!(); + } + + fn to_first_unsafe(&self) -> Option { + unreachable!(); + } + + fn to_serialized_key(&self) -> Self { + unreachable!(); + } + + fn is_out_of_bounds(&self) -> bool { + unreachable!(); + } + + fn is_first(&self) -> bool { + unreachable!(); + } + + fn checked_sub(&self, _: usize) -> Option { + unreachable!(); + } + + fn min_percentile_key() -> Self { + unreachable!(); + } + + fn iter_up_to(&self, other: &Self) -> impl Iterator { + (**self..=**other).map(Timestamp::from) + } + + fn map_name<'a>() -> &'a str { + "timestamp" + } + + fn to_usize(&self) -> usize { + (**self) as usize + } + + fn from_usize(t: usize) -> Self { + Self(t as u32) + } +} diff --git a/src/utils/retry.rs b/src/utils/retry.rs index 6f22e968c..f200ced47 100644 --- a/src/utils/retry.rs +++ b/src/utils/retry.rs @@ -5,10 +5,6 @@ pub fn retry( sleep_in_s: u64, retries: usize, ) -> color_eyre::Result { - if retries < 1 { - unreachable!() - } - let mut i = 0; loop {