summary refs log blame commit diff stats
path: root/src/vm/de/unpacker.rs
blob: c7d46addd6dc97d3dcf097e135c8d9606dd852f1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
                                   









                                                       
                               











































                                                                           
                             

























                                                                             
                             

























                                                                            
                             


















































































                                                                                                                                                                 
                             


































































































































































































































































































































                                                                                                                                                                 
// Copyright (C) 2022, 2023 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<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 = UnpackError;

    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 = UnpackError;
    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 = UnpackError;
    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 = UnpackError;
    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),
//                },
//            },
//        ]);
//    }
//}