summary refs log tree commit diff stats
path: root/src/vm/de/unpacker.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/de/unpacker.rs')
-rw-r--r--src/vm/de/unpacker.rs518
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),
+//                },
+//            },
+//        ]);
+//    }
+//}