diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2023-04-08 18:52:00 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2023-04-08 18:52:00 -0300 |
commit | d849f5e301fa47cfd87df1e7f1ad0346ddf387f1 (patch) | |
tree | a2480d05b753d94a6a8afee9832a902edf02d266 /src/vm/de/unpacker.rs | |
parent | 6dd30531ac62f6a3a564b7341d43f6cd71b90794 (diff) |
Initial success
Diffstat (limited to 'src/vm/de/unpacker.rs')
-rw-r--r-- | src/vm/de/unpacker.rs | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/src/vm/de/unpacker.rs b/src/vm/de/unpacker.rs new file mode 100644 index 0000000..8b16aa3 --- /dev/null +++ b/src/vm/de/unpacker.rs @@ -0,0 +1,518 @@ +// Copyright (C) 2022 Soni L. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Unpacker-related parts of the VM. + +use std::collections::HashMap; +use std::collections::hash_map::IntoIter as HMIntoIter; + +use serde::de::IntoDeserializer; +use serde::de::value::StrDeserializer; + +use crate::errors::QueryError; +use crate::vm::Pack; + +/// A `Deserializer` for Datafu output. +/// +/// This converts from Datafu's internal representation (a "pack") into the +/// desired output type. +pub struct Unpacker<'pat, 'de> { + packs: Vec<Pack<'pat, 'de>>, + call_limit: usize, +} + +/// Wrapper for an `&mut Unpacker<'pat, 'de>` with additional field +/// unpacking logic. +struct UnpackerFields<'a, 'pat, 'de> { + unpacker: &'a mut Unpacker<'pat, 'de>, + fields: HMIntoIter<&'static str, Option<crate::vm::SerdeObject<'de>>>, + value: Option<crate::vm::SerdeObject<'de>>, +} + +impl<'pat, 'de> Unpacker<'pat, 'de> { + /// Creates an Unpacker for unpacking a given Pack. + pub fn new(pack: Pack<'pat, 'de>, call_limit: usize) -> Self { + // invariant: no empty packs + let packs = if pack.subpacks.is_empty() { + vec![] + } else { + vec![pack] + }; + Self { + packs, call_limit, + } + } + + /// Takes the next subpack from the last pack in the pack stack. + fn take_next_pack(&mut self) -> Option<Pack<'pat, 'de>> { + self.packs.last_mut().and_then(|x| { + x.subpacks.front_mut() + }).map(|&mut (_, ref mut pack)| { + std::mem::take(pack) + }).filter(|pack| !pack.subpacks.is_empty()) + } +} + +impl<'pat, 'de> serde::de::SeqAccess<'de> for Unpacker<'pat, 'de> { + type Error = QueryError; + + fn next_element_seed<T: serde::de::DeserializeSeed<'de>>( + &mut self, + seed: T, + ) -> Result<Option<T::Value>, Self::Error> { + if self.packs.is_empty() { + return Ok(None) + } + seed.deserialize(self).map(Some) + } +} + +impl Drop for UnpackerFields<'_, '_, '_> { + fn drop(&mut self) { + let unpacker = &mut *self.unpacker; + while let Some(mut pack) = unpacker.packs.pop() { + pack.subpacks.pop_front(); + if !pack.subpacks.is_empty() { + unpacker.packs.push(pack); + break; + } + } + } +} + +impl<'pat, 'de> serde::de::MapAccess<'de> for UnpackerFields<'_, 'pat, 'de> { + type Error = QueryError; + fn next_key_seed<T: serde::de::DeserializeSeed<'de>>( + &mut self, + seed: T, + ) -> Result<Option<T::Value>, Self::Error> { + while let Some((key, value)) = self.fields.next() { + if value.is_some() { + self.value = value; + return seed.deserialize(StrDeserializer::new(key)).map(Some) + } + } + Ok(None) + } + + fn next_value_seed<T: serde::de::DeserializeSeed<'de>>( + &mut self, + seed: T, + ) -> Result<T::Value, Self::Error> { + if let Some(value) = self.value.take() { + seed.deserialize(value.into_deserializer()) + } else { + panic!("broken visitor") + } + } +} + +impl<'pat, 'de> serde::Deserializer<'de> for &mut Unpacker<'pat, 'de> { + type Error = QueryError; + fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_bool<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i8<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i16<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u8<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u16<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_f32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_f64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_char<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_str<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_string<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_bytes<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_byte_buf<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_option<V: serde::de::Visitor<'de>>( + self, + visitor: V, + ) -> Result<V::Value, Self::Error> { + todo!() + } + fn deserialize_unit<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_unit_struct<V>(self, _: &'static str, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_newtype_struct<V>(self, _: &'static str, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!(); } + fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_tuple_struct<V>(self, _: &'static str, _: usize, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_map<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_struct<V>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: serde::de::Visitor<'de>, + { + let _ = name; + let mut field_map = fields.iter().copied().map(|n| { + (n, None::<crate::vm::SerdeObject<'de>>) + }).collect::<HashMap<&'static str, Option<_>>>(); + // unroll packs + while let Some(pack) = self.take_next_pack() { + self.packs.push(pack); + } + // roll them back up + 'roll: while let Some(pack) = self.packs.pop() { + let (ref entries, _) = pack.subpacks[0]; + for key in entries.keys().copied() { + if field_map.contains_key(key) { + self.packs.push(pack); + break 'roll; + } + } + match self.packs.last_mut().map(|x| &mut x.subpacks[0]) { + Some((_, subpack)) => { + *subpack = pack; + }, + None => todo!(), + } + } + for pack in self.packs.iter() { + let (ref entries, _) = pack.subpacks[0]; + for (key, value) in entries.iter() { + if let Some(entry) = field_map.get_mut(*key) { + *entry = Some(value.clone()); + } + } + } + visitor.visit_map(UnpackerFields { + unpacker: self, + fields: field_map.into_iter(), + value: None, + }) + } + fn deserialize_enum<V>(self, _: &'static str, _: &'static [&'static str], _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_identifier<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_ignored_any<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } +} + +impl<'pat, 'de> serde::Deserializer<'de> for Unpacker<'pat, 'de> { + type Error = QueryError; + fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_bool<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i8<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i16<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_i64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u8<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u16<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_u64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_f32<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_f64<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_char<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_str<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_string<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_bytes<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_byte_buf<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_option<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_unit<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_unit_struct<V>(self, _: &'static str, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_newtype_struct<V>(self, _: &'static str, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: serde::de::Visitor<'de>, + { + visitor.visit_seq(self) + } + fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_tuple_struct<V>(self, _: &'static str, _: usize, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_map<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_struct<V>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: serde::de::Visitor<'de>, + { + todo!() + } + fn deserialize_enum<V>(self, _: &'static str, _: &'static [&'static str], _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_identifier<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } + fn deserialize_ignored_any<V>(self, _: V) -> Result<V::Value, Self::Error> where V: serde::de::Visitor<'de> { todo!() } +} + +//#[cfg(test)] +//mod tests { +// use serde::Deserialize; +// use crate::vm::MAX_CALLS; +// +// /// Mock struct for repo options. +// #[derive(Deserialize)] +// #[derive(Debug, PartialEq, Eq)] +// struct Options { +// active: bool, +// federate: Option<bool>, +// pinned: Option<bool>, +// } +// +// /// Mock struct for a project branch. +// #[derive(Deserialize)] +// #[derive(Debug, PartialEq, Eq)] +// struct ProjectBranch { +// /// The project commit. +// commit: String, +// /// The URL to the repo. +// url: String, +// /// The relevant branch. +// branch: String, +// /// Branch options. +// options: Options, +// } +// +// fn get_real_data() -> crate::vm::Pack<'static, 'static> { +// use indexmap::indexmap; +// use crate::vm::Pack; +// use crate::vm::SerdeObject; +// use crate::vm::SerdeObject::*; +// +// fn mkstr<'a>(s: &'a str) -> SerdeObject<'a> { +// SerdeObject::Str(s.into()) +// } +// +// Pack { +// subpacks: vec![ +// indexmap!{ +// "commit" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "url" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "branch" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "active" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "HEAD", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "https://github.com/ganarchy/GAnarchy", +// ), +// ), +// }, +// indexmap!{ +// "url" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "branch" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "pinned" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// "active" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "HEAD", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "https://soniex2.autistic.space/git-repos/ganarchy.git", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "385e734a52e13949a7a5c71827f6de920dbfea43", +// ), +// ), +// }, +// indexmap!{ +// "commit" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "url" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "branch" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "pinned" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// "active" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "HEAD", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "https://soniex2.autistic.space/git-repos/mmorfc.git", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "a8fb5087f79eafe312db270082c052c427b208c2", +// ), +// ), +// }, +// indexmap!{ +// "commit" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "url" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "branch" => ( +// Pack { +// subpacks: vec![ +// indexmap!{ +// "pinned" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// "active" => ( +// Pack { +// subpacks: vec![].into(), +// }, +// Bool( +// true, +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "HEAD", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "https://soniex2.autistic.space/git-repos/chewstuff.git", +// ), +// ), +// }, +// ].into(), +// }, +// mkstr( +// "2d0b363fe3179087de59d9ef4a2d14af21d89071", +// ), +// ), +// }, +// ].into(), +// } +// } +// +// #[test] +// fn to_vec_of_struct() { +// let der = super::Unpacker::new(get_real_data(), MAX_CALLS); +// let res: Vec<ProjectBranch> = Deserialize::deserialize(der).unwrap(); +// assert_eq!(res, [ +// ProjectBranch { +// commit: "385e734a52e13949a7a5c71827f6de920dbfea43".into(), +// url: "https://github.com/ganarchy/GAnarchy".into(), +// branch: "HEAD".into(), +// options: Options { +// active: true, +// federate: None, +// pinned: None, +// }, +// }, +// ProjectBranch { +// commit: "385e734a52e13949a7a5c71827f6de920dbfea43".into(), +// url: "https://soniex2.autistic.space/git-repos/ganarchy.git".into(), +// branch: "HEAD".into(), +// options: Options { +// active: true, +// federate: None, +// pinned: Some(true), +// }, +// }, +// ProjectBranch { +// commit: "a8fb5087f79eafe312db270082c052c427b208c2".into(), +// url: "https://soniex2.autistic.space/git-repos/mmorfc.git".into(), +// branch: "HEAD".into(), +// options: Options { +// active: true, +// federate: None, +// pinned: Some(true), +// }, +// }, +// ProjectBranch { +// commit: "2d0b363fe3179087de59d9ef4a2d14af21d89071".into(), +// url: "https://soniex2.autistic.space/git-repos/chewstuff.git".into(), +// branch: "HEAD".into(), +// options: Options { +// active: true, +// federate: None, +// pinned: Some(true), +// }, +// }, +// ]); +// } +//} |