//! Utilities for (de-)serializing use std::{ collections::BTreeMap, marker::PhantomData, sync::{Arc, LazyLock, Mutex, RwLock}, }; use serde::{ de::{self, Visitor}, ser::SerializeStruct, Deserialize, Serialize, }; use crate::base::source_file::SourceFile; thread_local! { static DEDUPLICATE_SOURCE_FILES: LazyLock> = LazyLock::new(|| RwLock::new(false)); static SERIALIZE_DATA: LazyLock> = LazyLock::new(|| Mutex::new(SerializeData::default())); static DESERIALIZE_DATA: LazyLock>> = LazyLock::new(|| RwLock::new(None)); } /// Wrapper to remove duplicate source file data during (de-)serialization #[expect(clippy::module_name_repetitions)] #[derive(Debug)] pub struct SerdeWrapper(pub T); impl Serialize for SerdeWrapper where T: Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = true); SERIALIZE_DATA.with(|d| d.lock().unwrap().clear()); // hold guard so no other can serialize at the same time in same thread let s = DEDUPLICATE_SOURCE_FILES.with(|d| { let guard = d.read().unwrap(); let mut serialized_data = flexbuffers::FlexbufferSerializer::new(); self.0 .serialize(&mut serialized_data) .map_err(|_| serde::ser::Error::custom("could not buffer serialization"))?; drop(serialized_data); let mut s = serializer.serialize_struct("SerdeWrapper", 3)?; SERIALIZE_DATA.with(|d| { s.serialize_field("source_files", &d.lock().unwrap().id_to_source_file) })?; s.serialize_field("data", &self.0)?; drop(guard); Ok(s) })?; DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = false); s.end() } } impl<'de, T> Deserialize<'de> for SerdeWrapper where T: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "snake_case")] enum Field { Data, SourceFiles, } struct WrapperVisitor(PhantomData); impl<'de, T> Visitor<'de> for WrapperVisitor where T: Deserialize<'de>, { type Value = SerdeWrapper; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("struct SerdeWrapper") } fn visit_seq(self, mut seq: V) -> Result where V: de::SeqAccess<'de>, { let source_files: BTreeMap = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; DESERIALIZE_DATA.with(|d| { *d.write().unwrap() = Some(DeserializeData { id_to_source_file: source_files .into_iter() .map(|(k, v)| (k, Arc::new(v))) .collect(), }); }); let data = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; Ok(SerdeWrapper(data)) } fn visit_map(self, mut map: V) -> Result where V: de::MapAccess<'de>, { let mut source_files: Option> = None; let mut data = None; while let Some(key) = map.next_key()? { match key { Field::Data => { if data.is_some() { return Err(de::Error::duplicate_field("data")); } DESERIALIZE_DATA.with(|d| { *d.write().unwrap() = source_files.as_ref().map(|source_files| DeserializeData { id_to_source_file: source_files .iter() .map(|(&k, v)| (k, Arc::new(v.clone()))) .collect(), }); }); data = Some(map.next_value()?); } Field::SourceFiles => { if source_files.is_some() { return Err(de::Error::duplicate_field("source_files")); } source_files = Some(map.next_value()?); } } } let data = data.ok_or_else(|| de::Error::missing_field("data"))?; Ok(SerdeWrapper(data)) } } DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = true); DESERIALIZE_DATA.with(|d| *d.write().unwrap() = None); let res = deserializer.deserialize_struct( "SerdeWrapper", &["source_files", "data"], WrapperVisitor(PhantomData::), ); DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = false); res } } /// Internally used for Serialization #[derive(Debug, Default)] struct SerializeData { id_counter: u64, ptr_to_id: BTreeMap, id_to_source_file: BTreeMap, } impl SerializeData { fn clear(&mut self) { self.id_counter = 0; self.id_to_source_file.clear(); self.ptr_to_id.clear(); } /// Get id of already stored [`Arc`] or store it and return new id pub fn get_id_of(&mut self, source_file: &Arc) -> u64 { let ptr = Arc::as_ptr(source_file); if let Some(&id) = self.ptr_to_id.get(&(ptr as usize)) { id } else { let id = self.id_counter; self.id_counter += 1; self.ptr_to_id.insert(ptr as usize, id); self.id_to_source_file .insert(id, Arc::unwrap_or_clone(source_file.to_owned())); id } } } #[derive(Debug, Default)] struct DeserializeData { id_to_source_file: BTreeMap>, } pub mod source_file { use std::sync::Arc; use serde::{de, Deserialize, Serialize}; use crate::{base::source_file::SourceFile, serde::DESERIALIZE_DATA}; use super::{DEDUPLICATE_SOURCE_FILES, SERIALIZE_DATA}; pub fn serialize(this: &Arc, serializer: S) -> Result where S: serde::Serializer, { if DEDUPLICATE_SOURCE_FILES.with(|d| *d.read().unwrap()) { SERIALIZE_DATA.with(|d| { let mut data = d.lock().unwrap(); serializer.serialize_u64(data.get_id_of(this)) }) } else { this.as_ref().serialize(serializer) } } pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { if DEDUPLICATE_SOURCE_FILES.with(|d| *d.read().unwrap()) { let id = u64::deserialize(deserializer)?; Ok(DESERIALIZE_DATA.with(|d| { d.read() .unwrap() .as_ref() .ok_or_else(|| de::Error::custom("SourceFiles do not have been loaded yet"))? .id_to_source_file .get(&id) .map(Arc::clone) .ok_or_else(|| serde::de::Error::custom("invalid source_file id")) }))? } else { Ok(Arc::new(SourceFile::deserialize(deserializer)?)) } } } #[cfg(all(test, feature = "shulkerbox"))] mod tests { use std::path::Path; use shulkerbox::virtual_fs::{VFile, VFolder}; use crate::{base::SilentHandler, syntax::syntax_tree::program::ProgramFile}; use super::*; #[test] fn test_serde_wrapper() { let mut vfolder = VFolder::new(); let vfile = VFile::Text(r#"namespace "test";"#.to_string()); vfolder.add_file("main.shu", vfile); let parsed = crate::parse( &SilentHandler::new(), &vfolder, Path::new("main.shu"), "main".to_string(), ) .unwrap(); let wrapper = SerdeWrapper(parsed); let serialized = serde_json::to_string_pretty(&wrapper).unwrap(); let SerdeWrapper(deserialized) = serde_json::from_str::>(&serialized).unwrap(); assert_eq!( Arc::as_ptr( deserialized .namespace() .namespace_keyword() .span .source_file() ), Arc::as_ptr(deserialized.namespace().namespace_name().span.source_file()) ); } }