Compare commits
	
		
			No commits in common. "f962f5c36b13c36f0380dc63e9bf177a0e2d126d" and "a91b6f82f57150f4b831e4d3bacfde5086657040" have entirely different histories.
		
	
	
		
			f962f5c36b
			...
			a91b6f82f5
		
	
		|  | @ -1 +1,2 @@ | ||||||
| /target | /target | ||||||
|  | /Cargo.lock | ||||||
|  |  | ||||||
|  | @ -11,8 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| 
 | 
 | ||||||
| - Option to deduplicate source files during serialization when using `SerdeWrapper` |  | ||||||
| 
 |  | ||||||
| ### Removed | ### Removed | ||||||
| 
 | 
 | ||||||
| ## [0.1.0] - 2024-10-01 | ## [0.1.0] - 2024-10-01 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -18,7 +18,7 @@ license = "MIT OR Apache-2.0" | ||||||
| default = ["fs_access", "lua", "shulkerbox", "zip"] | default = ["fs_access", "lua", "shulkerbox", "zip"] | ||||||
| fs_access = ["shulkerbox?/fs_access"] | fs_access = ["shulkerbox?/fs_access"] | ||||||
| lua = ["dep:mlua"] | lua = ["dep:mlua"] | ||||||
| serde = ["dep:serde", "dep:flexbuffers", "shulkerbox?/serde"] | serde = ["dep:serde", "shulkerbox?/serde"] | ||||||
| shulkerbox = ["dep:shulkerbox", "dep:chksum-md5"] | shulkerbox = ["dep:shulkerbox", "dep:chksum-md5"] | ||||||
| zip = ["shulkerbox?/zip"] | zip = ["shulkerbox?/zip"] | ||||||
| 
 | 
 | ||||||
|  | @ -30,18 +30,14 @@ chksum-md5 = { version = "0.1.0", optional = true } | ||||||
| colored = "3.0.0" | colored = "3.0.0" | ||||||
| derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] } | derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] } | ||||||
| enum-as-inner = "0.6.0" | enum-as-inner = "0.6.0" | ||||||
| flexbuffers = { version = "25.2.10", optional = true } |  | ||||||
| getset = "0.1.2" | getset = "0.1.2" | ||||||
| itertools = "0.14.0" | itertools = "0.14.0" | ||||||
| mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true } | mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true } | ||||||
| path-absolutize = "3.1.1" | path-absolutize = "3.1.1" | ||||||
| pathdiff = "0.2.3" | pathdiff = "0.2.3" | ||||||
| serde = { version = "1.0.217", features = ["derive"], optional = true } | serde = { version = "1.0.217", features = ["derive", "rc"], optional = true } | ||||||
| shulkerbox = { version = "0.1.0", default-features = false, optional = true } | shulkerbox = { version = "0.1.0", default-features = false, optional = true } | ||||||
| strsim = "0.11.1" | strsim = "0.11.1" | ||||||
| strum = { version = "0.27.0", features = ["derive"] } | strum = { version = "0.27.0", features = ["derive"] } | ||||||
| thiserror = "2.0.11" | thiserror = "2.0.11" | ||||||
| tracing = "0.1.41" | tracing = "0.1.41" | ||||||
| 
 |  | ||||||
| [dev-dependencies] |  | ||||||
| serde_json = "1.0.138" |  | ||||||
|  |  | ||||||
|  | @ -145,8 +145,8 @@ impl SourceFile { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Represents a range of characters in a source file.
 | /// Represents a range of characters in a source file.
 | ||||||
| #[derive(Clone, Getters, CopyGetters)] |  | ||||||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||||||
|  | #[derive(Clone, Getters, CopyGetters)] | ||||||
| pub struct Span { | pub struct Span { | ||||||
|     /// Get the start byte index of the span.
 |     /// Get the start byte index of the span.
 | ||||||
|     #[get_copy = "pub"] |     #[get_copy = "pub"] | ||||||
|  | @ -158,7 +158,6 @@ pub struct Span { | ||||||
| 
 | 
 | ||||||
|     /// Get the source file that the span is located in.
 |     /// Get the source file that the span is located in.
 | ||||||
|     #[get = "pub"] |     #[get = "pub"] | ||||||
|     #[cfg_attr(feature = "serde", serde(with = "crate::serde::source_file"))] |  | ||||||
|     source_file: Arc<SourceFile>, |     source_file: Arc<SourceFile>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,12 +20,6 @@ pub mod lexical; | ||||||
| pub mod syntax; | pub mod syntax; | ||||||
| pub mod transpile; | pub mod transpile; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "serde")] |  | ||||||
| pub(crate) mod serde; |  | ||||||
| #[cfg(feature = "serde")] |  | ||||||
| #[cfg_attr(feature = "serde", doc(inline))] |  | ||||||
| pub use serde::SerdeWrapper; |  | ||||||
| 
 |  | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| 
 | 
 | ||||||
| use base::{source_file::SourceFile, Error, FileProvider, Handler, Result}; | use base::{source_file::SourceFile, Error, FileProvider, Handler, Result}; | ||||||
|  |  | ||||||
							
								
								
									
										273
									
								
								src/serde.rs
								
								
								
								
							
							
						
						
									
										273
									
								
								src/serde.rs
								
								
								
								
							|  | @ -1,273 +0,0 @@ | ||||||
| //! 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; |  | ||||||
| 
 |  | ||||||
| static DEDUPLICATE_SOURCE_FILES: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false)); |  | ||||||
| 
 |  | ||||||
| static SERIALIZE_DATA: LazyLock<Mutex<SerializeData>> = |  | ||||||
|     LazyLock::new(|| Mutex::new(SerializeData::default())); |  | ||||||
| 
 |  | ||||||
| static DESERIALIZE_DATA: LazyLock<RwLock<Option<DeserializeData>>> = |  | ||||||
|     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<T>(pub T); |  | ||||||
| 
 |  | ||||||
| impl<T> Serialize for SerdeWrapper<T> |  | ||||||
| where |  | ||||||
|     T: Serialize, |  | ||||||
| { |  | ||||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|     where |  | ||||||
|         S: serde::Serializer, |  | ||||||
|     { |  | ||||||
|         *DEDUPLICATE_SOURCE_FILES.write().unwrap() = true; |  | ||||||
|         SERIALIZE_DATA.lock().unwrap().clear(); |  | ||||||
|         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)?; |  | ||||||
|         s.serialize_field( |  | ||||||
|             "source_files", |  | ||||||
|             &SERIALIZE_DATA.lock().unwrap().id_to_source_file, |  | ||||||
|         )?; |  | ||||||
|         s.serialize_field("data", &self.0)?; |  | ||||||
|         *DEDUPLICATE_SOURCE_FILES.write().unwrap() = false; |  | ||||||
|         s.end() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'de, T> Deserialize<'de> for SerdeWrapper<T> |  | ||||||
| where |  | ||||||
|     T: Deserialize<'de>, |  | ||||||
| { |  | ||||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |  | ||||||
|     where |  | ||||||
|         D: de::Deserializer<'de>, |  | ||||||
|     { |  | ||||||
|         #[derive(Deserialize)] |  | ||||||
|         #[serde(field_identifier, rename_all = "snake_case")] |  | ||||||
|         enum Field { |  | ||||||
|             Data, |  | ||||||
|             SourceFiles, |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         struct WrapperVisitor<T>(PhantomData<T>); |  | ||||||
| 
 |  | ||||||
|         impl<'de, T> Visitor<'de> for WrapperVisitor<T> |  | ||||||
|         where |  | ||||||
|             T: Deserialize<'de>, |  | ||||||
|         { |  | ||||||
|             type Value = SerdeWrapper<T>; |  | ||||||
| 
 |  | ||||||
|             fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { |  | ||||||
|                 formatter.write_str("struct SerdeWrapper") |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error> |  | ||||||
|             where |  | ||||||
|                 V: de::SeqAccess<'de>, |  | ||||||
|             { |  | ||||||
|                 let source_files: BTreeMap<u64, SourceFile> = seq |  | ||||||
|                     .next_element()? |  | ||||||
|                     .ok_or_else(|| de::Error::invalid_length(0, &self))?; |  | ||||||
|                 *DESERIALIZE_DATA.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<V>(self, mut map: V) -> Result<Self::Value, V::Error> |  | ||||||
|             where |  | ||||||
|                 V: de::MapAccess<'de>, |  | ||||||
|             { |  | ||||||
|                 let mut source_files: Option<BTreeMap<u64, SourceFile>> = 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.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.write().unwrap() = true; |  | ||||||
|         *DESERIALIZE_DATA.write().unwrap() = None; |  | ||||||
|         let res = deserializer.deserialize_struct( |  | ||||||
|             "SerdeWrapper", |  | ||||||
|             &["source_files", "data"], |  | ||||||
|             WrapperVisitor(PhantomData::<T>), |  | ||||||
|         ); |  | ||||||
|         *DEDUPLICATE_SOURCE_FILES.write().unwrap() = false; |  | ||||||
| 
 |  | ||||||
|         res |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Internally used for Serialization
 |  | ||||||
| #[derive(Debug, Default)] |  | ||||||
| struct SerializeData { |  | ||||||
|     id_counter: u64, |  | ||||||
|     ptr_to_id: BTreeMap<usize, u64>, |  | ||||||
|     id_to_source_file: BTreeMap<u64, SourceFile>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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<SourceFile>) -> 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<u64, Arc<SourceFile>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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<S>(this: &Arc<SourceFile>, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|     where |  | ||||||
|         S: serde::Serializer, |  | ||||||
|     { |  | ||||||
|         if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { |  | ||||||
|             let mut data = SERIALIZE_DATA.lock().unwrap(); |  | ||||||
|             serializer.serialize_u64(data.get_id_of(this)) |  | ||||||
|         } else { |  | ||||||
|             this.as_ref().serialize(serializer) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<SourceFile>, D::Error> |  | ||||||
|     where |  | ||||||
|         D: serde::Deserializer<'de>, |  | ||||||
|     { |  | ||||||
|         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)?)) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[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::<SerdeWrapper<ProgramFile>>(&serialized).unwrap(); |  | ||||||
| 
 |  | ||||||
|         assert_eq!( |  | ||||||
|             Arc::as_ptr( |  | ||||||
|                 deserialized |  | ||||||
|                     .namespace() |  | ||||||
|                     .namespace_keyword() |  | ||||||
|                     .span |  | ||||||
|                     .source_file() |  | ||||||
|             ), |  | ||||||
|             Arc::as_ptr(deserialized.namespace().namespace_name().span.source_file()) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue