summary refs log tree commit diff stats
path: root/src/type_tree.rs
blob: e7389d5efc682078a3001491f7e4c35b79fbce89 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (C) 2022 Soni L.
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Type Tree support.
//!
//! Type Trees are a Datafu feature for extracting types from a `serde`-based
//! `Deserialize` in such a way that it can be used with Datafu patterns.
//!
//! They work by matching the `Deserialize` against some data, with the help of
//! `serde_transmute`. Datafu then collects the relevant `Deserialize` calls,
//! and uses them to infer an appropriate type tree for dynamic
//! deserialization.
//!
//! When introspecting the `Deserialize`, all matching parts are extracted, and
//! non-matching parts are ignored. Even if an error occurs, Datafu will gladly
//! infer a type tree for what it could match.
//!
//! For example, given a struct and the corresponding data:
//!
//! ```
//! struct Foo {
//!   bar: i32,
//! }
//!
//! let data = Foo { bar: 0 };
//! ```
//!
//! Building a type tree will first inspect the struct like so:
//!
//! 1. call `deserialize()` on `Foo`.
//! 2. inspect the `deserialize_struct` from `Foo`, storing the name and
//!     fields.
//! 3. give `Foo` the appropriate visitor (from `data`), through
//!     `serde_transmute`.
//! 4. inspect the `deserialize_i32` etc, also storing those.
//!
//! The resulting type tree can then be used in any pattern to effectively
//! match a `Foo`, but more efficiently than with a predicate. Another big
//! difference between predicates and type trees is how predicates are eager,
//! and can consume values that would otherwise be matched by the rest of a
//! pattern, like `IgnoredAny`.
//!
//! Type trees are pretty flexible. Consider the following example:
//!
//! ```
//! struct Foo {
//!   bar: Vec<u32>,
//! }
//! 
//! let data = Foo { bar: vec![1, 2, 3] };
//! ```
//!
//! This will actually produce a type tree which checks that the first 3 items
//! are `u32`! Further, when using different types for the predicate and the
//! data, you can get even more flexiblity. For example, with the following
//! struct and data:
//!
//! ```
//! struct Foo {
//!   bar: Vec<u32>,
//! }
//!
//! let data = ();
//! ```
//!
//! Datafu will actually inspect the `deserialize_struct`, and then the
//! struct visitor will error. But despite the error, it'll still create a type
//! tree for the `deserialize_struct`!

// use serde::Deserializer;

// /// A deserializer which attempts to fill in a type tree.
// struct TypeTreeDeserializer<'tt, D> {
//     inner: D,
//     tt: &'tt mut TypeTreeNode,
// }

/// A Type Tree entry type.
///
/// This represents a type to be deserialized with Serde, with everything that
/// comes with that. It supports the 29 core Serde types, and 2 self-describing
/// ones.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum TypeTreeType {
    /// An open type, which can be anything.
    ///
    /// This represents [`Deserializer::deserialize_any`].
    #[default]
    Any,
    /// A type for a value which will be ignored.
    ///
    /// This represents [`Deserializer::deserialize_ignored_any`].
    IgnoredAny,
    Bool,
}

/// A node of a type tree.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct TypeTreeNode {
    /// The type to be requested for this node.
    pub node_type: TypeTreeType,
    /// The types for when this node is an enum.
    pub enum_nodes: (), // TODO
    /// The types for when this node is a map.
    pub map_nodes: (), // TODO
    /// The types for when this node is a seq.
    pub seq_nodes: (), // TODO
    /// The type for when this node is a some.
    pub some_node: Option<Box<TypeTreeNode>>,
    /// The type for when this node is a newtype struct.
    pub newtype_node: Option<Box<TypeTreeNode>>,
}