// 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::UnpackError; 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>, 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>>, value: Option>, } 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> { 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 = UnpackError; fn next_element_seed>( &mut self, seed: T, ) -> Result, 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 = UnpackError; fn next_key_seed>( &mut self, seed: T, ) -> Result, 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>( &mut self, seed: T, ) -> Result { 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 = UnpackError; fn deserialize_any(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_bool(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i8(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i16(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u8(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u16(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_f32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_f64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_char(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_str(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_string(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_bytes(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_byte_buf(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_option>( self, visitor: V, ) -> Result { todo!() } fn deserialize_unit(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_unit_struct(self, _: &'static str, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_newtype_struct(self, _: &'static str, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_seq(self, visitor: V) -> Result where V: serde::de::Visitor<'de> { todo!(); } fn deserialize_tuple(self, _: usize, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_tuple_struct(self, _: &'static str, _: usize, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_map(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_struct( self, name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: serde::de::Visitor<'de>, { let _ = name; let mut field_map = fields.iter().copied().map(|n| { (n, None::>) }).collect::>>(); // 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(self, _: &'static str, _: &'static [&'static str], _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_identifier(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_ignored_any(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } } impl<'pat, 'de> serde::Deserializer<'de> for Unpacker<'pat, 'de> { type Error = UnpackError; fn deserialize_any(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_bool(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i8(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i16(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_i64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u8(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u16(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_u64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_f32(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_f64(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_char(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_str(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_string(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_bytes(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_byte_buf(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_option(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_unit(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_unit_struct(self, _: &'static str, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_newtype_struct(self, _: &'static str, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_seq(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_seq(self) } fn deserialize_tuple(self, _: usize, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_tuple_struct(self, _: &'static str, _: usize, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_map(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_struct( self, name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: serde::de::Visitor<'de>, { todo!() } fn deserialize_enum(self, _: &'static str, _: &'static [&'static str], _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_identifier(self, _: V) -> Result where V: serde::de::Visitor<'de> { todo!() } fn deserialize_ignored_any(self, _: V) -> Result 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, // pinned: Option, // } // // /// 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 = 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), // }, // }, // ]); // } //}