Move interner to its own module

This commit is contained in:
Joakim Soderlund 2020-05-22 22:55:38 +02:00
parent 2547cf8346
commit 67707830ed
3 changed files with 43 additions and 22 deletions

35
src/archive/interner.rs Normal file
View file

@ -0,0 +1,35 @@
//! interner module.
use std::collections::HashSet;
use std::sync::RwLock;
pub struct Interner<T: 'static>(RwLock<HashSet<&'static T>>);
impl<T> Interner<T>
where
T: Eq + std::hash::Hash,
{
pub fn new() -> Self {
Self(RwLock::new(HashSet::new()))
}
fn get(&self, value: &T) -> Option<&'static T> {
let store = self.0.read().unwrap();
store.get(value).map(|value| *value)
}
fn set(&self, value: T) -> &'static T {
let boxed: Box<T> = Box::new(value);
let leaked: &'static T = Box::leak(boxed);
let mut store = self.0.write().unwrap();
store.insert(leaked);
leaked
}
pub fn leak(&self, value: T) -> &'static T {
self.get(&value).unwrap_or_else(|| self.set(value))
}
}

View file

@ -1,6 +1,7 @@
//! Archive module.
mod fetcher;
mod interner;
mod parser;
mod story;

View file

@ -1,18 +1,15 @@
//! Story meta.
use std::collections::HashSet;
use std::sync::Mutex;
use chrono::prelude::*;
use lazy_static::lazy_static;
use serde::de::Error;
use serde::{Deserialize, Deserializer};
use serde_json::Value;
use lazy_static::lazy_static;
use super::interner::Interner;
lazy_static! {
static ref TAGS: Mutex<HashSet<&'static Tag>> = Mutex::new(HashSet::new());
static ref TAGS: Interner<Tag> = Interner::new();
}
#[derive(Clone, Debug, Deserialize)]
@ -43,7 +40,7 @@ pub struct Story {
pub short_description: String,
pub status: Status,
pub submitted: bool,
#[serde(deserialize_with = "interned_tag")]
#[serde(deserialize_with = "tags_as_static")]
pub tags: Vec<&'static Tag>,
#[serde(deserialize_with = "null_to_text")]
pub title: String,
@ -200,25 +197,13 @@ where
}
}
fn interned_tag<'de, D>(d: D) -> Result<Vec<&'static Tag>, D::Error>
fn tags_as_static<'de, D>(d: D) -> Result<Vec<&'static Tag>, D::Error>
where
D: Deserializer<'de>,
{
let tags = Vec::<Tag>::deserialize(d)?;
let mut store = TAGS.lock().unwrap();
let tags: Vec<Tag> = Vec::deserialize(d)?;
Ok(tags
.into_iter()
.map(|tag| match store.get(&tag) {
Some(tag) => tag,
None => {
let boxed: Box<Tag> = Box::new(tag);
let leaked: &'static Tag = Box::leak(boxed);
store.insert(leaked);
leaked
}
})
.collect())
Ok(tags.into_iter().map(|tag| TAGS.leak(tag)).collect())
}
impl<'de> Deserialize<'de> for Color {