Import the Dashboard app #51

Merged
savanni merged 5 commits from dashboard into main 2023-08-07 19:21:27 +00:00
15 changed files with 10526 additions and 88 deletions

1763
dashboard/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

7262
dashboard/Cargo.nix Normal file

File diff suppressed because it is too large Load Diff

26
dashboard/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "dashboard"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
fluent = { version = "0.16" }
fluent-ergonomics = { path = "../fluent-ergonomics/" }
futures = { version = "0.3" }
geo-types = { path = "../geo-types/" }
lazy_static = { version = "1.4" }
unic-langid = { version = "0.9" }
horrorshow = { version = "0.8" }
http = { version = "*" }
international-fixed-calendar = { path = "../ifc/" }
memorycache = { path = "../memory_cache" }
reqwest = { version = "0.11", features = ["json"] }
serde_derive = { version = "1" }
serde_json = { version = "1" }
serde = { version = "1" }
tokio = { version = "1", features = ["full"] }
warp = { version = "0.3" }

210
dashboard/src/main.rs Normal file
View File

@ -0,0 +1,210 @@
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate horrorshow;
#[macro_use]
extern crate lazy_static;
use chrono::{Datelike, Local, TimeZone, Utc};
use fluent::{FluentArgs, FluentValue};
use geo_types::{Latitude, Longitude};
use horrorshow::helper::doctype;
use horrorshow::prelude::*;
use international_fixed_calendar as IFC;
use std::{
borrow::Cow,
net::{IpAddr, Ipv4Addr, SocketAddr},
sync::Arc,
};
use warp::{reply::Html, Filter, Rejection};
mod soluna_client;
use soluna_client::{SolunaClient, SunMoon};
mod solstices;
use solstices::{Event, EVENTS};
/*
const EO_TEXT: &'static str = "
day = {$day ->
*[Sunday] Dimanĉo
[Monday] Lundo
[Tuesday] Mardo
[Wednesday] Merkredo
[Thursday] Ĵaŭdo
[Friday] Vendredo
[Saturday] Sabato
[LeapDay] Leap Day
[YearDay] Year Day
}
month = {$month ->
*[January] Januaro
[February] Februaro
[March] Marto
[April] Aprilo
[May] Mayo
[June] Junio
[Sol] Solo
[July] Julio
[August] Aŭgusto
[September] Septembro
[October] Oktobro
[November] Novembro
[December] Decembro
}
spring_equinox = Printempa Ekvinokso
summer_solstice = Somera Solstico
autumn_equinox = Aŭtuna Ekvinokso
winter_solstice = Vintra Solstico
";
fn date(fluent: Arc<fluent_ergonomics::FluentErgo>, today: IFC::IFC) -> impl Render {
let mut day_args = FluentArgs::new();
day_args.set(
"day",
FluentValue::String(Cow::Owned(String::from(today.weekday_ifc()))),
);
let tago = fluent.tr("tago", Some(&day_args)).unwrap();
let mut month_args = FluentArgs::new();
month_args.set(
"month",
FluentValue::String(Cow::Owned(String::from(today.month_ifc()))),
);
let monato = fluent.tr("month", Some(&month_args)).unwrap();
owned_html! {p : format!("{}, {} {}, {}", tago, monato, today.day(), today.year());}
}
*/
/*
fn soluna_desegno<Tz: TimeZone>(sun_moon: SunMoon<Tz>) -> impl Render {
owned_html! {
table {
tr {
th : "Sunleviĝo";
th : "Sunfalo";
th : "Lunleviĝo";
th : "Lunfalo";
}
tr {
td : format!("{}", sun_moon.sunrise.format("%H:%M"));
td : format!("{}", sun_moon.sunset.format("%H:%M"));
td : sun_moon.moonrise.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string());
td : sun_moon.moonset.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string());
td : format!("{:?}", sun_moon.moon_phase);
}
}
}
}
*/
/*
fn astronomia_eventa_desegno(
fluent: Arc<fluent_ergonomics::FluentErgo>,
event: Option<Event>,
) -> impl Render {
let s = match event {
None => "".to_owned(),
Some(event) => {
let eventa_str = match event {
Event::SpringEquinox(_) => fluent.tr("spring_equinox", None),
Event::SummerSolstice(_) => fluent.tr("summer_solstice", None),
Event::AutumnEquinox(_) => fluent.tr("autumn_equinox", None),
Event::WinterSolstice(_) => fluent.tr("winter_solstice", None),
}
.unwrap();
format!("{} {}", eventa_str, event.date().format("%Y-%m-%d"))
}
};
owned_html! {
div : s.clone();
}
}
*/
/*
fn page_template(
fluent: Arc<fluent_ergonomics::FluentErgo>,
today: IFC::IFC,
sun_moon: SunMoon,
event: Option<solstices::Event>,
) -> String {
format!(
"{}",
html! {
: doctype::HTML;
html {
head {
title: "Superrigardo";
}
body {
h1(id="heading", class="title") : date(fluent.clone(), today);
div : soluna_desegno(sun_moon.clone());
div : astronomia_eventa_desegno(fluent.clone(), event);
// div : cirklo();
}
}
}
)
}
async fn page(
fluent: Arc<fluent_ergonomics::FluentErgo>,
solar_client: Arc<SolunaClient>,
latitude: Latitude,
longitude: Longitude,
) -> Result<Html<String>, Rejection> {
let now = Utc::now();
let d = IFC::IFC::from(now.with_timezone(&New_York).date());
let sun_moon = solar_client
.request(latitude, longitude, now.with_timezone(&New_York).date())
.await;
let next_event = EVENTS.next_event(now);
Ok(warp::reply::html(page_template(
fluent, d, sun_moon, next_event,
)))
}
*/
#[tokio::main]
pub async fn main() {
let now = Local::now();
let ifc = IFC::IFC::from(now.date_naive().with_year(12023).unwrap());
let next_event = EVENTS.next_event(now.with_timezone(&Utc)).unwrap();
println!(
"{:?}, {:?} {}, {}",
ifc.weekday(),
ifc.month(),
ifc.day(),
ifc.year()
);
println!("{:?}", next_event);
let latitude = Latitude::from(41.78);
let longitude = Longitude::from(-71.41);
let soluna_client = SolunaClient::new();
let sun_moon = soluna_client
.request(latitude, longitude, Local::now())
.await;
println!(
"Solar Transit: {} -> {}",
sun_moon.sunrise.format("%H:%M").to_string(),
sun_moon.sunset.format("%H:%M").to_string()
);
println!(
"Lunar Transit: {} -> {} [{:?}]",
sun_moon
.moonrise
.map(|time| time.format("%H:%M").to_string())
.unwrap_or(" -- ".to_owned()),
sun_moon
.moonset
.map(|time| time.format("%H:%M").to_string())
.unwrap_or(" -- ".to_owned()),
sun_moon.moon_phase
);
}

14
dashboard/src/moon.rs Normal file
View File

@ -0,0 +1,14 @@
use svg::{
node::element::{Circle, Image},
Document,
};
pub fn moon() -> Document {
/*
svg(width="100%", height="100%", xmlns="http://www.w3.org/2000/svg") {
circle(cx="50", cy="50", r="50", stroke="green", fill="none");
}
*/
let img = Image::new().set("href", "/moon-small.png");
Document::new().add(img)
}

179
dashboard/src/solstices.rs Normal file
View File

@ -0,0 +1,179 @@
use chrono;
use chrono::prelude::*;
use serde_derive::{Deserialize, Serialize};
use serde_json;
use std::collections::HashMap;
// http://astropixels.com/ephemeris/soleq2001.html
const SOLSTICE_TEXT: &'static str = "
2001 Mar 20 13:31 Jun 21 07:38 Sep 22 23:05 Dec 21 19:22
2002 Mar 20 19:16 Jun 21 13:25 Sep 23 04:56 Dec 22 01:15
2003 Mar 21 01:00 Jun 21 19:11 Sep 23 10:47 Dec 22 07:04
2004 Mar 20 06:49 Jun 21 00:57 Sep 22 16:30 Dec 21 12:42
2005 Mar 20 12:34 Jun 21 06:46 Sep 22 22:23 Dec 21 18:35
2006 Mar 20 18:25 Jun 21 12:26 Sep 23 04:04 Dec 22 00:22
2007 Mar 21 00:07 Jun 21 18:06 Sep 23 09:51 Dec 22 06:08
2008 Mar 20 05:49 Jun 21 00:00 Sep 22 15:45 Dec 21 12:04
2009 Mar 20 11:44 Jun 21 05:45 Sep 22 21:18 Dec 21 17:47
2010 Mar 20 17:32 Jun 21 11:28 Sep 23 03:09 Dec 21 23:38
2011 Mar 20 23:21 Jun 21 17:16 Sep 23 09:05 Dec 22 05:30
2012 Mar 20 05:15 Jun 20 23:08 Sep 22 14:49 Dec 21 11:12
2013 Mar 20 11:02 Jun 21 05:04 Sep 22 20:44 Dec 21 17:11
2014 Mar 20 16:57 Jun 21 10:52 Sep 23 02:30 Dec 21 23:03
2015 Mar 20 22:45 Jun 21 16:38 Sep 23 08:20 Dec 22 04:48
2016 Mar 20 04:31 Jun 20 22:35 Sep 22 14:21 Dec 21 10:45
2017 Mar 20 10:29 Jun 21 04:25 Sep 22 20:02 Dec 21 16:29
2018 Mar 20 16:15 Jun 21 10:07 Sep 23 01:54 Dec 21 22:22
2019 Mar 20 21:58 Jun 21 15:54 Sep 23 07:50 Dec 22 04:19
2020 Mar 20 03:50 Jun 20 21:43 Sep 22 13:31 Dec 21 10:03
2021 Mar 20 09:37 Jun 21 03:32 Sep 22 19:21 Dec 21 15:59
2022 Mar 20 15:33 Jun 21 09:14 Sep 23 01:04 Dec 21 21:48
2023 Mar 20 21:25 Jun 21 14:58 Sep 23 06:50 Dec 22 03:28
2024 Mar 20 03:07 Jun 20 20:51 Sep 22 12:44 Dec 21 09:20
2025 Mar 20 09:02 Jun 21 02:42 Sep 22 18:20 Dec 21 15:03
2026 Mar 20 14:46 Jun 21 08:25 Sep 23 00:06 Dec 21 20:50
2027 Mar 20 20:25 Jun 21 14:11 Sep 23 06:02 Dec 22 02:43
2028 Mar 20 02:17 Jun 20 20:02 Sep 22 11:45 Dec 21 08:20
2029 Mar 20 08:01 Jun 21 01:48 Sep 22 17:37 Dec 21 14:14
2030 Mar 20 13:51 Jun 21 07:31 Sep 22 23:27 Dec 21 20:09
2031 Mar 20 19:41 Jun 21 13:17 Sep 23 05:15 Dec 22 01:56
2032 Mar 20 01:23 Jun 20 19:09 Sep 22 11:11 Dec 21 07:57
2033 Mar 20 07:23 Jun 21 01:01 Sep 22 16:52 Dec 21 13:45
2034 Mar 20 13:18 Jun 21 06:45 Sep 22 22:41 Dec 21 19:35
2035 Mar 20 19:03 Jun 21 12:33 Sep 23 04:39 Dec 22 01:31
2036 Mar 20 01:02 Jun 20 18:31 Sep 22 10:23 Dec 21 07:12
2037 Mar 20 06:50 Jun 21 00:22 Sep 22 16:13 Dec 21 13:08
2038 Mar 20 12:40 Jun 21 06:09 Sep 22 22:02 Dec 21 19:01
2039 Mar 20 18:32 Jun 21 11:58 Sep 23 03:50 Dec 22 00:41
2040 Mar 20 00:11 Jun 20 17:46 Sep 22 09:44 Dec 21 06:33
2041 Mar 20 06:07 Jun 20 23:37 Sep 22 15:27 Dec 21 12:19
2042 Mar 20 11:53 Jun 21 05:16 Sep 22 21:11 Dec 21 18:04
2043 Mar 20 17:29 Jun 21 10:59 Sep 23 03:07 Dec 22 00:02
2044 Mar 19 23:20 Jun 20 16:50 Sep 22 08:47 Dec 21 05:43
2045 Mar 20 05:08 Jun 20 22:34 Sep 22 14:33 Dec 21 11:36
2046 Mar 20 10:58 Jun 21 04:15 Sep 22 20:22 Dec 21 17:28
2047 Mar 20 16:52 Jun 21 10:02 Sep 23 02:07 Dec 21 23:07
2048 Mar 19 22:34 Jun 20 15:54 Sep 22 08:01 Dec 21 05:02
2049 Mar 20 04:28 Jun 20 21:47 Sep 22 13:42 Dec 21 10:51
2050 Mar 20 10:20 Jun 21 03:33 Sep 22 19:29 Dec 21 16:39
";
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct YearlyEvents {
pub year: i32,
pub spring_equinox: chrono::DateTime<chrono::Utc>,
pub summer_solstice: chrono::DateTime<chrono::Utc>,
pub autumn_equinox: chrono::DateTime<chrono::Utc>,
pub winter_solstice: chrono::DateTime<chrono::Utc>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Event {
SpringEquinox(chrono::DateTime<chrono::Utc>),
SummerSolstice(chrono::DateTime<chrono::Utc>),
AutumnEquinox(chrono::DateTime<chrono::Utc>),
WinterSolstice(chrono::DateTime<chrono::Utc>),
}
impl Event {
pub fn date(&self) -> chrono::DateTime<chrono::Utc> {
match *self {
Event::SpringEquinox(d) => d,
Event::SummerSolstice(d) => d,
Event::AutumnEquinox(d) => d,
Event::WinterSolstice(d) => d,
}
}
}
fn parse_time<'a>(
jaro: &str,
iter: impl Iterator<Item = &'a str>,
) -> chrono::DateTime<chrono::Utc> {
let partoj = iter.collect::<Vec<&str>>();
let p = format!("{} {} {} {}", jaro, partoj[0], partoj[1], partoj[2]);
chrono::Utc.datetime_from_str(&p, "%Y %b %d %H:%M").unwrap()
}
fn parse_line(year: &str, rest: &[&str]) -> YearlyEvents {
let spring = parse_time(year, rest.iter().take(3).cloned());
let summer = parse_time(year, rest.iter().skip(3).take(3).cloned());
let autumn = parse_time(year, rest.iter().skip(6).take(3).cloned());
let winter = parse_time(year, rest.iter().skip(9).take(3).cloned());
YearlyEvents {
year: year.parse::<i32>().unwrap(),
spring_equinox: spring,
summer_solstice: summer,
autumn_equinox: autumn,
winter_solstice: winter,
}
}
fn parse_events() -> Vec<Option<YearlyEvents>> {
SOLSTICE_TEXT
.lines()
.map(|line| {
match line
.split(" ")
.filter(|elem| !elem.is_empty())
.collect::<Vec<&str>>()
.as_slice()
{
[year, rest @ ..] => Some(parse_line(year, rest)),
_ => None,
}
})
.collect()
}
pub struct Solstices(HashMap<i32, YearlyEvents>);
impl Solstices {
pub fn akiru(&self, year: i32) -> Option<YearlyEvents> {
self.0.get(&year).map(|c| c.clone())
}
pub fn next_event(&self, date: chrono::DateTime<chrono::Utc>) -> Option<Event> {
let year_events = self.0.get(&date.year());
match year_events {
Some(year_events) => {
if date <= year_events.spring_equinox {
Some(Event::SpringEquinox(year_events.spring_equinox.clone()))
} else if date <= year_events.summer_solstice {
Some(Event::SummerSolstice(year_events.summer_solstice.clone()))
} else if date <= year_events.autumn_equinox {
Some(Event::AutumnEquinox(year_events.autumn_equinox.clone()))
} else if date <= year_events.winter_solstice {
Some(Event::WinterSolstice(year_events.winter_solstice.clone()))
} else {
self.0
.get(&(date.year() + 1))
.map(|events| Event::SpringEquinox(year_events.spring_equinox.clone()))
}
}
None => None,
}
}
}
impl From<Vec<Option<YearlyEvents>>> for Solstices {
fn from(event_list: Vec<Option<YearlyEvents>>) -> Self {
Solstices(event_list.iter().fold(HashMap::new(), |mut m, record| {
match record {
Some(record) => {
m.insert(record.year, record.clone());
}
None => (),
}
m
}))
}
}
lazy_static! {
pub static ref EVENTS: Solstices = Solstices::from(parse_events());
}

View File

@ -0,0 +1,147 @@
// 41.78, -71.41
// https://api.solunar.org/solunar/41.78,-71.41,20211029,-4
use chrono::{
DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, Utc,
};
use geo_types::{Latitude, Longitude};
use memorycache::MemoryCache;
use reqwest;
const ENDPOINT: &str = "https://api.solunar.org/solunar";
#[derive(Clone, Debug, PartialEq)]
pub struct SunMoon {
pub sunrise: NaiveTime,
pub sunset: NaiveTime,
pub moonrise: Option<NaiveTime>,
pub moonset: Option<NaiveTime>,
pub moon_phase: LunarPhase,
}
impl SunMoon {
fn from_js(val: SunMoonJs) -> Self {
fn parse_time(val: String) -> Option<NaiveTime> {
NaiveTime::parse_from_str(&val, "%H:%M").ok()
}
let sunrise = parse_time(val.sunrise).unwrap();
let sunset = parse_time(val.sunset).unwrap();
let moonrise = val.moonrise.and_then(|v| parse_time(v));
let moonset = val.moonset.and_then(|v| parse_time(v));
Self {
sunrise,
sunset,
moonrise,
moonset,
moon_phase: val.moon_phase,
}
}
}
#[derive(Clone, Debug, Deserialize)]
pub(crate) struct SunMoonJs {
#[serde(alias = "sunRise")]
sunrise: String,
#[serde(alias = "sunSet")]
sunset: String,
#[serde(alias = "moonRise")]
moonrise: Option<String>,
#[serde(alias = "moonSet")]
moonset: Option<String>,
#[serde(alias = "moonPhase")]
moon_phase: LunarPhase,
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub enum LunarPhase {
#[serde(alias = "New Moon")]
NewMoon,
#[serde(alias = "Waxing Crescent")]
WaxingCrescent,
#[serde(alias = "First Quarter")]
FirstQuarter,
#[serde(alias = "Waxing Gibbous")]
WaxingGibbous,
#[serde(alias = "Full Moon")]
FullMoon,
#[serde(alias = "Waning Gibbous")]
WaningGibbous,
#[serde(alias = "Last Quarter")]
LastQuarter,
#[serde(alias = "Waning Crescent")]
WaningCrescent,
}
pub struct SolunaClient {
client: reqwest::Client,
memory_cache: MemoryCache<SunMoonJs>,
}
impl SolunaClient {
pub fn new() -> Self {
Self {
client: reqwest::Client::new(),
memory_cache: MemoryCache::new(),
}
}
pub async fn request<Tz: TimeZone>(
&self,
latitude: Latitude,
longitude: Longitude,
day: DateTime<Tz>,
) -> SunMoon {
let date = day.date_naive().format("%Y%m%d");
let url = format!(
"{}/{},{},{},{}",
ENDPOINT,
latitude,
longitude,
date,
day.offset().fix().local_minus_utc() / 3600
);
let js = self
.memory_cache
.find(&url, async {
let response = self.client.get(&url).send().await.unwrap();
let expiration = response
.headers()
.get(reqwest::header::EXPIRES)
.and_then(|header| header.to_str().ok())
.and_then(|expiration| DateTime::parse_from_rfc2822(expiration).ok())
.map(|dt_local| DateTime::<Utc>::from(dt_local))
.unwrap_or(Utc::now() + Duration::seconds(3600));
let soluna: SunMoonJs = response.json().await.unwrap();
(expiration, soluna)
})
.await;
SunMoon::from_js(js)
}
}
#[cfg(test)]
mod test {
use super::*;
use serde_json;
const EXAMPLE: &str = "{\"sunRise\":\"7:15\",\"sunTransit\":\"12:30\",\"sunSet\":\"17:45\",\"moonRise\":null,\"moonTransit\":\"7:30\",\"moonUnder\":\"19:54\",\"moonSet\":\"15:02\",\"moonPhase\":\"Waning Crescent\",\"moonIllumination\":0.35889454647387764,\"sunRiseDec\":7.25,\"sunTransitDec\":12.5,\"sunSetDec\":17.75,\"moonRiseDec\":null,\"moonSetDec\":15.033333333333333,\"moonTransitDec\":7.5,\"moonUnderDec\":19.9,\"minor1Start\":null,\"minor1Stop\":null,\"minor2StartDec\":14.533333333333333,\"minor2Start\":\"14:32\",\"minor2StopDec\":15.533333333333333,\"minor2Stop\":\"15:32\",\"major1StartDec\":6.5,\"major1Start\":\"06:30\",\"major1StopDec\":8.5,\"major1Stop\":\"08:30\",\"major2StartDec\":18.9,\"major2Start\":\"18:54\",\"major2StopDec\":20.9,\"major2Stop\":\"20:54\",\"dayRating\":1,\"hourlyRating\":{\"0\":20,\"1\":20,\"2\":0,\"3\":0,\"4\":0,\"5\":0,\"6\":20,\"7\":40,\"8\":40,\"9\":20,\"10\":0,\"11\":0,\"12\":0,\"13\":0,\"14\":0,\"15\":20,\"16\":20,\"17\":20,\"18\":40,\"19\":20,\"20\":20,\"21\":20,\"22\":0,\"23\":0}}";
#[test]
fn it_parses_a_response() {
let sun_moon_js: SunMoonJs = serde_json::from_str(EXAMPLE).unwrap();
let sun_moon = SunMoon::from_js(sun_moon_js);
assert_eq!(
sun_moon,
SunMoon {
sunrise: NaiveTime::from_hms_opt(7, 15, 0).unwrap(),
sunset: NaiveTime::from_hms_opt(17, 45, 0).unwrap(),
moonrise: None,
moonset: Some(NaiveTime::from_hms_opt(15, 02, 0).unwrap()),
moon_phase: LunarPhase::WaningCrescent,
}
);
}
}

View File

@ -87,6 +87,11 @@
buildRustCrateForPkgs = customBuildInfo; buildRustCrateForPkgs = customBuildInfo;
release = true; release = true;
}).rootCrate.build; }).rootCrate.build;
dashboard = (import ./dashboard/Cargo.nix {
inherit pkgs;
release = true;
}).rootCrate.build;
}; };
hydraJobs = { hydraJobs = {

7
geo-types/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "geo-types"
version = "0.1.0"

8
geo-types/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "geo-types"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

37
geo-types/src/lib.rs Normal file
View File

@ -0,0 +1,37 @@
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub struct Latitude(f32);
impl From<f32> for Latitude {
fn from(val: f32) -> Self {
if val > 90.0 || val < -90.0 {
panic!("Latitude is outside of range");
}
Self(val)
}
}
impl fmt::Display for Latitude {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Longitude(f32);
impl From<f32> for Longitude {
fn from(val: f32) -> Self {
if val > 180.0 || val < -180.0 {
panic!("Longitude is outside fo range");
}
Self(val)
}
}
impl fmt::Display for Longitude {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

View File

@ -89,6 +89,12 @@ impl From<u32> for Month {
} }
} }
impl From<u8> for Month {
fn from(val: u8) -> Month {
Month::from(val as u32)
}
}
impl From<Month> for String { impl From<Month> for String {
fn from(val: Month) -> String { fn from(val: Month) -> String {
match val { match val {
@ -152,7 +158,23 @@ impl IFC {
} }
} }
pub fn day_of_week(&self) -> DayOfWeek { pub fn month(&self) -> Month {
match *self {
IFC::Day(Day { month, .. }) => Month::from(month),
IFC::LeapDay(_) => Month::June,
IFC::YearDay(_) => Month::December,
}
}
pub fn day(&self) -> u8 {
match *self {
IFC::Day(Day { day, .. }) => day,
IFC::LeapDay(_) => 29,
IFC::YearDay(_) => 29,
}
}
pub fn weekday(&self) -> DayOfWeek {
match *self { match *self {
IFC::LeapDay(_) => DayOfWeek::LeapDay, IFC::LeapDay(_) => DayOfWeek::LeapDay,
IFC::YearDay(_) => DayOfWeek::YearDay, IFC::YearDay(_) => DayOfWeek::YearDay,
@ -247,37 +269,7 @@ impl From<IFC> for chrono::NaiveDate {
} }
/* /*
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct IFC {
year: u32,
ordinal: u32,
leap_year: bool,
}
impl IFC { impl IFC {
pub fn ymd(year: u32, month: u8, day: u8) -> Self {
let leap_year = is_leap_year(year as i32 - 10000);
let ordinal = if is_leap_year(year as i32 - 10000) {
if month == 6 && day == 29 {
168
} else if month > 6 {
(month as u32 - 1) * 28 + (day as u32)
} else {
(month as u32 - 1) * 28 + (day as u32) - 1
}
} else {
(month as u32 - 1) * 28 + (day as u32) - 1
};
IFC {
year,
ordinal,
leap_year,
}
}
pub fn year_day(year: u32) -> Self { pub fn year_day(year: u32) -> Self {
Self { Self {
year, year,
@ -285,26 +277,6 @@ impl IFC {
} }
} }
pub fn leap_day(year: u32) -> Result<IFC, Error> {
}
pub fn weekday(&self) -> DayOfWeek {
// if self.day.ordinal ==
match self.day() % 7 {
0 => DayOfWeek::Saturday,
1 => DayOfWeek::Sunday,
2 => DayOfWeek::Monday,
3 => DayOfWeek::Tuesday,
4 => DayOfWeek::Wednesday,
5 => DayOfWeek::Thursday,
6 => DayOfWeek::Friday,
_ => panic!("impossible condition"),
}
}
pub fn month_ifc(&self) -> Month {
Month::from(self.month())
}
} }
*/ */
@ -336,12 +308,15 @@ impl From<chrono::NaiveDate> for IFC {
/* /*
impl Datelike for IFC { impl Datelike for IFC {
fn year(&self) -> i32 { fn year(&self) -> i32 {
self.year as i32 // self.year as i32
unimplemented!()
} }
fn month(&self) -> u32 { fn month(&self) -> u32 {
self.month0() + 1 // self.month0() + 1
unimplemented!()
} }
fn month0(&self) -> u32 { fn month0(&self) -> u32 {
/*
if self.leap_year && self.ordinal == 365 { if self.leap_year && self.ordinal == 365 {
12 12
} else if self.leap_year && self.ordinal == 168 { } else if self.leap_year && self.ordinal == 168 {
@ -349,11 +324,15 @@ impl Datelike for IFC {
} else { } else {
self.ordinal / 28 self.ordinal / 28
} }
*/
unimplemented!()
} }
fn day(&self) -> u32 { fn day(&self) -> u32 {
self.day0() + 1 // self.day0() + 1
unimplemented!()
} }
fn day0(&self) -> u32 { fn day0(&self) -> u32 {
/*
if self.leap_year { if self.leap_year {
if self.ordinal == 365 { if self.ordinal == 365 {
28 28
@ -371,12 +350,16 @@ impl Datelike for IFC {
self.ordinal.rem_euclid(28) as u32 self.ordinal.rem_euclid(28) as u32
} }
} }
*/
unimplemented!()
} }
fn ordinal(&self) -> u32 { fn ordinal(&self) -> u32 {
self.ordinal + 1 // self.ordinal + 1
unimplemented!()
} }
fn ordinal0(&self) -> u32 { fn ordinal0(&self) -> u32 {
self.ordinal // self.ordinal
unimplemented!()
} }
fn weekday(&self) -> chrono::Weekday { fn weekday(&self) -> chrono::Weekday {
if self.day0() == 28 { if self.day0() == 28 {
@ -398,37 +381,50 @@ impl Datelike for IFC {
panic!("iso_week is not implemented because chrono does not expose any constructors for IsoWeek!"); panic!("iso_week is not implemented because chrono does not expose any constructors for IsoWeek!");
} }
fn with_year(&self, year: i32) -> Option<IFC> { fn with_year(&self, year: i32) -> Option<IFC> {
/*
Some(IFC { Some(IFC {
year: (year as u32) + 10000, year: (year as u32) + 10000,
ordinal: self.ordinal, ordinal: self.ordinal,
leap_year: is_leap_year(year), leap_year: is_leap_year(year),
}) })
*/
unimplemented!()
} }
fn with_month(&self, month: u32) -> Option<IFC> { fn with_month(&self, month: u32) -> Option<IFC> {
Some(IFC::ymd(self.year, month as u8, self.day() as u8)) // Some(IFC::ymd(self.year, month as u8, self.day() as u8))
unimplemented!()
} }
fn with_month0(&self, month: u32) -> Option<IFC> { fn with_month0(&self, month: u32) -> Option<IFC> {
Some(IFC::ymd(self.year, month as u8 + 1, self.day() as u8)) // Some(IFC::ymd(self.year, month as u8 + 1, self.day() as u8))
unimplemented!()
} }
fn with_day(&self, day: u32) -> Option<IFC> { fn with_day(&self, day: u32) -> Option<IFC> {
Some(IFC::ymd(self.year, self.month() as u8, day as u8)) // Some(IFC::ymd(self.year, self.month() as u8, day as u8))
unimplemented!()
} }
fn with_day0(&self, day: u32) -> Option<IFC> { fn with_day0(&self, day: u32) -> Option<IFC> {
Some(IFC::ymd(self.year, self.month() as u8, day as u8 + 1)) // Some(IFC::ymd(self.year, self.month() as u8, day as u8 + 1))
unimplemented!()
} }
fn with_ordinal(&self, ordinal: u32) -> Option<IFC> { fn with_ordinal(&self, ordinal: u32) -> Option<IFC> {
/*
Some(IFC { Some(IFC {
year: self.year, year: self.year,
ordinal, ordinal,
leap_year: self.leap_year, leap_year: self.leap_year,
}) })
*/
unimplemented!()
} }
fn with_ordinal0(&self, ordinal: u32) -> Option<IFC> { fn with_ordinal0(&self, ordinal: u32) -> Option<IFC> {
/*
Some(IFC { Some(IFC {
year: self.year, year: self.year,
ordinal: ordinal + 1, ordinal: ordinal + 1,
leap_year: self.leap_year, leap_year: self.leap_year,
}) })
*/
unimplemented!()
} }
} }
*/ */
@ -462,43 +458,33 @@ mod tests {
#[test] #[test]
fn it_expresses_day_of_week() { fn it_expresses_day_of_week() {
assert_eq!(IFC::leap_day(12020).unwrap().weekday(), DayOfWeek::LeapDay);
assert_eq!(IFC::year_day(12022).weekday(), DayOfWeek::YearDay);
assert_eq!(IFC::ymd(12022, 1, 1).unwrap().weekday(), DayOfWeek::Sunday);
assert_eq!(IFC::ymd(12022, 1, 2).unwrap().weekday(), DayOfWeek::Monday);
assert_eq!(IFC::ymd(12022, 1, 3).unwrap().weekday(), DayOfWeek::Tuesday);
assert_eq!( assert_eq!(
IFC::leap_day(12020).unwrap().day_of_week(), IFC::ymd(12022, 1, 4).unwrap().weekday(),
DayOfWeek::LeapDay
);
assert_eq!(IFC::year_day(12022).day_of_week(), DayOfWeek::YearDay);
assert_eq!(
IFC::ymd(12022, 1, 1).unwrap().day_of_week(),
DayOfWeek::Sunday
);
assert_eq!(
IFC::ymd(12022, 1, 2).unwrap().day_of_week(),
DayOfWeek::Monday
);
assert_eq!(
IFC::ymd(12022, 1, 3).unwrap().day_of_week(),
DayOfWeek::Tuesday
);
assert_eq!(
IFC::ymd(12022, 1, 4).unwrap().day_of_week(),
DayOfWeek::Wednesday DayOfWeek::Wednesday
); );
assert_eq!( assert_eq!(
IFC::ymd(12022, 1, 5).unwrap().day_of_week(), IFC::ymd(12022, 1, 5).unwrap().weekday(),
DayOfWeek::Thursday DayOfWeek::Thursday
); );
assert_eq!(IFC::ymd(12022, 1, 6).unwrap().weekday(), DayOfWeek::Friday);
assert_eq!( assert_eq!(
IFC::ymd(12022, 1, 6).unwrap().day_of_week(), IFC::ymd(12022, 1, 7).unwrap().weekday(),
DayOfWeek::Friday
);
assert_eq!(
IFC::ymd(12022, 1, 7).unwrap().day_of_week(),
DayOfWeek::Saturday DayOfWeek::Saturday
); );
assert_eq!( assert_eq!(IFC::ymd(12022, 1, 8).unwrap().weekday(), DayOfWeek::Sunday);
IFC::ymd(12022, 1, 8).unwrap().day_of_week(), }
DayOfWeek::Sunday
); #[test]
fn it_expresses_month() {
assert_eq!(IFC::ymd(12022, 1, 4).unwrap().month(), Month::January);
assert_eq!(IFC::ymd(12022, 12, 1).unwrap().month(), Month::November);
assert_eq!(IFC::leap_day(12020).unwrap().month(), Month::June);
assert_eq!(IFC::year_day(12020).month(), Month::December);
} }
#[test] #[test]

678
memory_cache/Cargo.lock generated Normal file
View File

@ -0,0 +1,678 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "futures"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "futures-task"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]]
name = "futures-util"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "iana-time-zone"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "js-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memorycache"
version = "0.1.0"
dependencies = [
"chrono",
"futures",
"serde",
"serde_derive",
"tokio",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a"
[[package]]
name = "serde_derive"
version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "socket2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "2.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "tokio"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

16
memory_cache/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "memorycache"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4" }
futures = { version = "0.3" }
serde_derive = { version = "1" }
serde = { version = "1" }
#[dev-dependencies]
tokio = { version = "1", features = ["full"] }

100
memory_cache/src/lib.rs Normal file
View File

@ -0,0 +1,100 @@
use chrono::{DateTime, Utc};
use futures::Future;
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
#[derive(Clone)]
pub struct MemoryCacheRecord<T> {
pub expiration: DateTime<Utc>,
pub value: T,
}
pub struct MemoryCache<T>(Arc<RwLock<HashMap<String, MemoryCacheRecord<T>>>>);
impl<T: Clone> MemoryCache<T> {
pub fn new() -> MemoryCache<T> {
MemoryCache(Arc::new(RwLock::new(HashMap::new())))
}
pub async fn find(&self, key: &str, f: impl Future<Output = (DateTime<Utc>, T)>) -> T {
let val = {
let cache = self.0.read().unwrap();
cache.get(key).cloned()
};
match val {
Some(ref val) if val.expiration > Utc::now() => val.value.clone(),
_ => {
let response = f.await;
let mut cache = self.0.write().unwrap();
let record = MemoryCacheRecord {
expiration: response.0,
value: response.1.clone(),
};
cache
.entry(key.to_owned())
.and_modify(|rec| *rec = record.clone())
.or_insert(record);
response.1
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Duration;
use std::sync::{Arc, RwLock};
#[derive(Clone, Debug, PartialEq)]
struct Value(i64);
#[tokio::test]
async fn it_runs_the_requestor_when_the_value_does_not_exist() {
let cache = MemoryCache::new();
let value = cache
.find("my_key", async { (Utc::now(), Value(15)) })
.await;
assert_eq!(value, Value(15));
}
#[tokio::test]
async fn it_runs_the_requestor_when_the_value_is_old() {
let run = Arc::new(RwLock::new(false));
let cache = MemoryCache::new();
let _ = cache
.find("my_key", async {
(Utc::now() - Duration::seconds(10), Value(15))
})
.await;
let value = cache
.find("my_key", async {
*run.write().unwrap() = true;
(Utc::now(), Value(16))
})
.await;
assert_eq!(value, Value(16));
assert_eq!(*run.read().unwrap(), true);
}
#[tokio::test]
async fn it_returns_the_cached_value_when_the_value_is_new() {
let run = Arc::new(RwLock::new(false));
let cache = MemoryCache::new();
let _ = cache
.find("my_key", async {
(Utc::now() + Duration::seconds(10), Value(15))
})
.await;
let value = cache
.find("my_key", async {
*run.write().unwrap() = true;
(Utc::now(), Value(16))
})
.await;
assert_eq!(value, Value(15));
assert_eq!(*run.read().unwrap(), false);
}
}