diff --git a/Cargo.toml b/Cargo.toml index 748e68e..91a24e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,3 +42,6 @@ strsim = "0.11.1" strum = { version = "0.27.0", features = ["derive"] } thiserror = "2.0.11" tracing = "0.1.41" + +[dev-dependencies] +serde_json = "1.0.138" diff --git a/src/base/source_file.rs b/src/base/source_file.rs index caf7475..e4ab874 100644 --- a/src/base/source_file.rs +++ b/src/base/source_file.rs @@ -146,6 +146,7 @@ impl SourceFile { /// Represents a range of characters in a source file. #[derive(Clone, Getters, CopyGetters)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Span { /// Get the start byte index of the span. #[get_copy = "pub"] @@ -157,6 +158,7 @@ pub struct Span { /// Get the source file that the span is located in. #[get = "pub"] + #[cfg_attr(feature = "serde", serde(with = "crate::serde::source_file"))] source_file: Arc, } diff --git a/src/lib.rs b/src/lib.rs index 0472ae1..463ec98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub mod syntax; pub mod transpile; #[cfg(feature = "serde")] -mod serde; +pub(crate) mod serde; #[cfg(feature = "serde")] #[cfg_attr(feature = "serde", doc(inline))] pub use serde::SerdeWrapper; diff --git a/src/serde.rs b/src/serde.rs index 9130cc0..747a4f1 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -12,7 +12,7 @@ use serde::{ Deserialize, Serialize, }; -use crate::base::source_file::{SourceFile, Span}; +use crate::base::source_file::SourceFile; static DEDUPLICATE_SOURCE_FILES: LazyLock> = LazyLock::new(|| RwLock::new(false)); @@ -23,6 +23,7 @@ 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); @@ -83,7 +84,7 @@ where where V: de::SeqAccess<'de>, { - let source_files: BTreeMap = seq + let source_files: BTreeMap = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; *DESERIALIZE_DATA.write().unwrap() = Some(DeserializeData { @@ -103,7 +104,7 @@ where where V: de::MapAccess<'de>, { - let mut source_files: Option> = None; + let mut source_files: Option> = None; let mut data = None; while let Some(key) = map.next_key()? { @@ -140,7 +141,7 @@ where let res = deserializer.deserialize_struct( "SerdeWrapper", &["source_files", "data"], - WrapperVisitor(PhantomData::::default()), + WrapperVisitor(PhantomData::), ); *DEDUPLICATE_SOURCE_FILES.write().unwrap() = false; @@ -151,9 +152,9 @@ where /// Internally used for Serialization #[derive(Debug, Default)] struct SerializeData { - id_counter: usize, - ptr_to_id: BTreeMap, - id_to_source_file: BTreeMap, + id_counter: u64, + ptr_to_id: BTreeMap, + id_to_source_file: BTreeMap, } impl SerializeData { @@ -164,7 +165,7 @@ impl SerializeData { } /// Get id of already stored [`Arc`] or store it and return new id - pub fn get_id_of(&mut self, source_file: &Arc) -> usize { + 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 @@ -181,150 +182,92 @@ impl SerializeData { } } -impl Serialize for Span { - fn serialize(&self, serializer: S) -> Result +#[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, { - let mut s = serializer.serialize_struct("Span", 3)?; - s.serialize_field("start", &self.start())?; - s.serialize_field("end", &self.end())?; - if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { let mut data = SERIALIZE_DATA.lock().unwrap(); - s.serialize_field("source_file", &data.get_id_of(self.source_file()))?; + serializer.serialize_u64(data.get_id_of(this)) } else { - s.serialize_field("source_file", self.source_file())?; + this.as_ref().serialize(serializer) } - - s.end() } -} -#[derive(Debug, Default)] -struct DeserializeData { - id_to_source_file: BTreeMap>, -} - -impl<'de> Deserialize<'de> for Span { - fn deserialize(deserializer: D) -> Result + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "snake_case")] - enum Field { - Start, - End, - SourceFile, + if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { + let id = u64::deserialize(deserializer)?; + Ok(DESERIALIZE_DATA + .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)?)) } - - struct SpanVisitor; - - impl<'de> Visitor<'de> for SpanVisitor { - type Value = Span; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { - formatter.write_str("struct Span with deduplicated SourceFiles") - } else { - formatter.write_str("struct Span") - } - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let start = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let end = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - let source_file = if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { - DESERIALIZE_DATA - .read() - .unwrap() - .as_ref() - .ok_or_else(|| { - de::Error::custom("SourceFiles do not have been loaded yet") - })? - .id_to_source_file - .get( - &seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(2, &self))?, - ) - .ok_or_else(|| de::Error::custom("invalid source_file id"))? - .clone() - } else { - Arc::new( - seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(2, &self))?, - ) - }; - - Ok(Span::new(source_file, start, end) - .ok_or_else(|| de::Error::custom("Invalid data"))?) - } - - fn visit_map(self, mut map: V) -> Result - where - V: de::MapAccess<'de>, - { - let mut start = None; - let mut end = None; - let mut source_file = None; - - while let Some(key) = map.next_key()? { - match key { - Field::Start => { - if start.is_some() { - return Err(de::Error::duplicate_field("start")); - } - start = Some(map.next_value()?); - } - Field::End => { - if end.is_some() { - return Err(de::Error::duplicate_field("end")); - } - end = Some(map.next_value()?); - } - Field::SourceFile => { - if source_file.is_some() { - return Err(de::Error::duplicate_field("source_file")); - } - source_file = if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { - Some( - DESERIALIZE_DATA - .read() - .unwrap() - .as_ref() - .ok_or_else(|| { - de::Error::custom( - "SourceFiles do not have been loaded yet", - ) - })? - .id_to_source_file - .get(&map.next_value()?) - .ok_or_else(|| de::Error::custom("invalid source_file id"))? - .clone(), - ) - } else { - Some(Arc::new(map.next_value()?)) - }; - } - } - } - let start = start.ok_or_else(|| de::Error::missing_field("start"))?; - let end = end.ok_or_else(|| de::Error::missing_field("end"))?; - let source_file = source_file.ok_or_else(|| de::Error::missing_field("source"))?; - - Ok(Span::new(source_file, start, end) - .ok_or_else(|| de::Error::custom("Invalid data"))?) - } - } - - deserializer.deserialize_struct("Span", &["start", "end", "source_file"], SpanVisitor) + } +} + +#[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()) + ); } }