diff --git a/Cargo.lock b/Cargo.lock index a690f91..4dfc310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,7 @@ version = "0.1.0" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index b270d5a..03b94a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,9 @@ features = ["serde"] [dependencies.hex] version = "*" +[dependencies.lazy_static] +version = "*" + [dependencies.rayon] version = "*" diff --git a/src/archive/story.rs b/src/archive/story.rs index 7cf3bcd..092a738 100644 --- a/src/archive/story.rs +++ b/src/archive/story.rs @@ -1,10 +1,20 @@ //! Story meta. +use std::collections::HashSet; +use std::sync::Mutex; + use chrono::prelude::*; + use serde::de::Error; use serde::{Deserialize, Deserializer}; use serde_json::Value; +use lazy_static::lazy_static; + +lazy_static! { + static ref TAGS: Mutex> = Mutex::new(HashSet::new()); +} + #[derive(Clone, Debug, Deserialize)] pub struct Story { pub archive: Archive, @@ -33,7 +43,8 @@ pub struct Story { pub short_description: String, pub status: Status, pub submitted: bool, - pub tags: Vec, + #[serde(deserialize_with = "interned_tag")] + pub tags: Vec<&'static Tag>, #[serde(deserialize_with = "null_to_text")] pub title: String, pub total_num_views: i32, @@ -143,7 +154,7 @@ pub enum Status { Visible, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize)] pub struct Tag { pub id: i64, pub name: String, @@ -189,6 +200,27 @@ where } } +fn interned_tag<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let tags = Vec::::deserialize(d)?; + let mut store = TAGS.lock().unwrap(); + + Ok(tags + .into_iter() + .map(|tag| match store.get(&tag) { + Some(tag) => tag, + None => { + let boxed: Box = Box::new(tag); + let leaked: &'static Tag = Box::leak(boxed); + store.insert(leaked); + leaked + } + }) + .collect()) +} + impl<'de> Deserialize<'de> for Color { fn deserialize(d: D) -> Result where