Skip to content

Commit

Permalink
reorganize util functionalities into modules
Browse files Browse the repository at this point in the history
  • Loading branch information
shouya committed Oct 29, 2024
1 parent d3ce750 commit d306376
Showing 1 changed file with 112 additions and 103 deletions.
215 changes: 112 additions & 103 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
mod html;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use url::Url;

pub use self::html::{convert_relative_url, fragment_root_node_id, html_body};
Expand Down Expand Up @@ -69,131 +67,142 @@ mod app_base {

pub use self::app_base::app_base_from_env;

#[derive(
JsonSchema, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash,
)]
#[serde(untagged)]
pub enum SingleOrVec<T> {
/// A single entry
Single(T),
/// A list of entries
Vec(Vec<T>),
}

impl<T> Default for SingleOrVec<T> {
fn default() -> Self {
Self::empty()
mod single_or_vec {
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(
JsonSchema, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash,
)]
#[serde(untagged)]
pub enum SingleOrVec<T> {
/// A single entry
Single(T),
/// A list of entries
Vec(Vec<T>),
}
}

pub enum SingleOrVecIter<'a, T> {
Single(std::iter::Once<&'a T>),
Vec(std::slice::Iter<'a, T>),
}
impl<T> Default for SingleOrVec<T> {
fn default() -> Self {
Self::empty()
}
}

impl<T> SingleOrVec<T> {
pub fn empty() -> Self {
Self::Vec(Vec::new())
pub enum SingleOrVecIter<'a, T> {
Single(std::iter::Once<&'a T>),
Vec(std::slice::Iter<'a, T>),
}

pub fn into_vec(self) -> Vec<T> {
match self {
Self::Single(s) => vec![s],
Self::Vec(v) => v,
impl<T> SingleOrVec<T> {
pub fn empty() -> Self {
Self::Vec(Vec::new())
}

pub fn into_vec(self) -> Vec<T> {
match self {
Self::Single(s) => vec![s],
Self::Vec(v) => v,
}
}
}
}

impl<'a, T> IntoIterator for &'a SingleOrVec<T> {
type Item = &'a T;
type IntoIter = SingleOrVecIter<'a, T>;
impl<'a, T> IntoIterator for &'a SingleOrVec<T> {
type Item = &'a T;
type IntoIter = SingleOrVecIter<'a, T>;

fn into_iter(self) -> Self::IntoIter {
match self {
SingleOrVec::Single(s) => SingleOrVecIter::Single(std::iter::once(s)),
SingleOrVec::Vec(v) => SingleOrVecIter::Vec(v.iter()),
fn into_iter(self) -> Self::IntoIter {
match self {
SingleOrVec::Single(s) => SingleOrVecIter::Single(std::iter::once(s)),
SingleOrVec::Vec(v) => SingleOrVecIter::Vec(v.iter()),
}
}
}
}

impl<'a, T> Iterator for SingleOrVecIter<'a, T> {
type Item = &'a T;
impl<'a, T> Iterator for SingleOrVecIter<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Single(s) => s.next(),
Self::Vec(v) => v.next(),
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Single(s) => s.next(),
Self::Vec(v) => v.next(),
}
}
}
}

use std::{
hash::Hash,
num::NonZeroUsize,
sync::{
atomic::{AtomicUsize, Ordering},
RwLock,
},
time::{Duration, Instant},
};

use lru::LruCache;

pub struct Timed<T> {
value: T,
created: Instant,
}
pub use self::single_or_vec::SingleOrVec;

mod cache {
use std::{
hash::Hash,
num::NonZeroUsize,
sync::{
atomic::{AtomicUsize, Ordering},
RwLock,
},
time::{Duration, Instant},
};

pub struct TimedLruCache<K: Hash + Eq, V: Clone> {
map: RwLock<LruCache<K, Timed<V>>>,
misses: AtomicUsize,
hits: AtomicUsize,
timeout: Duration,
}
use lru::LruCache;

impl<K: Hash + Eq, V: Clone> TimedLruCache<K, V> {
pub fn new(max_entries: usize, timeout: Duration) -> Self {
let max_entries = max_entries.try_into().unwrap_or(NonZeroUsize::MIN);
Self {
map: RwLock::new(LruCache::new(max_entries)),
timeout,
misses: AtomicUsize::new(0),
hits: AtomicUsize::new(0),
}
struct Timed<T> {
value: T,
created: Instant,
}

pub fn get_cached(&self, key: &K) -> Option<V> {
let mut map = self.map.write().ok()?;
let Some(entry) = map.get(key) else {
self.misses.fetch_add(1, Ordering::Relaxed);
return None;
};

if entry.created.elapsed() > self.timeout {
self.misses.fetch_add(1, Ordering::Relaxed);
map.pop(key);
return None;
pub struct TimedLruCache<K: Hash + Eq, V: Clone> {
map: RwLock<LruCache<K, Timed<V>>>,
misses: AtomicUsize,
hits: AtomicUsize,
timeout: Duration,
}

impl<K: Hash + Eq, V: Clone> TimedLruCache<K, V> {
pub fn new(max_entries: usize, timeout: Duration) -> Self {
let max_entries = max_entries.try_into().unwrap_or(NonZeroUsize::MIN);
Self {
map: RwLock::new(LruCache::new(max_entries)),
timeout,
misses: AtomicUsize::new(0),
hits: AtomicUsize::new(0),
}
}

self.hits.fetch_add(1, Ordering::Relaxed);
Some(entry.value.clone())
}
pub fn get_cached(&self, key: &K) -> Option<V> {
let mut map = self.map.write().ok()?;
let Some(entry) = map.get(key) else {
self.misses.fetch_add(1, Ordering::Relaxed);
return None;
};

if entry.created.elapsed() > self.timeout {
self.misses.fetch_add(1, Ordering::Relaxed);
map.pop(key);
return None;
}

self.hits.fetch_add(1, Ordering::Relaxed);
Some(entry.value.clone())
}

pub fn insert(&self, key: K, value: V) -> Option<()> {
let timed = Timed {
value,
created: Instant::now(),
};
self.map.write().ok()?.push(key, timed);
Some(())
}
pub fn insert(&self, key: K, value: V) -> Option<()> {
let timed = Timed {
value,
created: Instant::now(),
};
self.map.write().ok()?.push(key, timed);
Some(())
}

// hit, miss
#[allow(unused)]
pub fn stats(&self) -> (usize, usize) {
(
self.hits.load(Ordering::Relaxed),
self.misses.load(Ordering::Relaxed),
)
// hit, miss
#[allow(unused)]
pub fn stats(&self) -> (usize, usize) {
(
self.hits.load(Ordering::Relaxed),
self.misses.load(Ordering::Relaxed),
)
}
}
}

pub use self::cache::TimedLruCache;

0 comments on commit d306376

Please sign in to comment.