File: remote_types.rs

package info (click to toggle)
rust-rkyv 0.8.15-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,512 kB
  • sloc: makefile: 2
file content (147 lines) | stat: -rw-r--r-- 4,454 bytes parent folder | download | duplicates (2)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use rancor::Failure;
use rkyv::{with::With, Archive, Deserialize, Serialize};

// Assume this is a remote module or crate that you cannot modify.
mod remote {
    // Notably, this type does not implement the rkyv traits
    #[derive(Debug, PartialEq)]
    pub struct Foo {
        pub ch: char,
        pub bytes: [u8; 4],
        pub _uninteresting: u32,
        // ... and even has private fields
        bar: Bar<i32>,
    }

    #[derive(Debug, PartialEq)]
    pub struct Bar<T>(pub T);

    impl Foo {
        // A constructor which is necessary for deserialization because there
        // are private fields.
        pub fn new(
            ch: char,
            bytes: [u8; 4],
            _uninteresting: u32,
            bar: Bar<i32>,
        ) -> Self {
            Self {
                ch,
                bytes,
                _uninteresting,
                bar,
            }
        }

        // The getter for a private field.
        pub fn bar(&self) -> &Bar<i32> {
            &self.bar
        }
    }
}

// Let's create a local type that will serve as `with`-wrapper for `Foo`.
// Fields must have the same name and type but it's not required to define all
// fields.
#[derive(Archive, Serialize, Deserialize)]
#[rkyv(remote = remote::Foo)] // <-
#[rkyv(archived = ArchivedFoo)]
// ^ not necessary but we might as well replace the default name
// `ArchivedFooDef` with `ArchivedFoo`.
struct FooDef {
    // The field's type implements `Archive` and we don't want to apply any
    // conversion for the archived type so we don't need to specify
    // `#[rkyv(with = ..)]`.
    ch: char,
    // The field is private in the remote type so we need to specify a getter
    // to access it. Also, its type doesn't implement `Archive` so we need
    // to specify a `with`-wrapper too.
    #[rkyv(getter = remote::Foo::bar, with = BarDef)]
    bar: remote::Bar<i32>,
    // The remote `bytes` field is public but we can still customize our local
    // field when using a getter.
    #[rkyv(getter = get_first_byte)]
    first_byte: u8,
}

fn get_first_byte(foo: &remote::Foo) -> u8 {
    foo.bytes[0]
}

// Deriving `Deserialize` with `remote = ..` requires a `From` implementation.
impl From<FooDef> for remote::Foo {
    fn from(value: FooDef) -> Self {
        remote::Foo::new(value.ch, [value.first_byte, 2, 3, 4], 567, value.bar)
    }
}

#[derive(Archive, Serialize, Deserialize)]
#[rkyv(remote = remote::Bar<i32>)]
struct BarDef(i32);

impl From<BarDef> for remote::Bar<i32> {
    fn from(BarDef(value): BarDef) -> Self {
        remote::Bar(value)
    }
}

fn main() -> Result<(), Failure> {
    let foo = remote::Foo::new('!', [1, 2, 3, 4], 567, remote::Bar(89));

    // To make use of all the utility functions for serialization, accessing,
    // and deserialization, we can use the `With` type.

    let bytes = rkyv::to_bytes(With::<remote::Foo, FooDef>::cast(&foo))?;
    let archived: &ArchivedFoo = rkyv::access(&bytes)?;
    let deserialized: remote::Foo =
        rkyv::deserialize(With::<ArchivedFoo, FooDef>::cast(archived))?;

    assert_eq!(foo, deserialized);

    // ... or better yet, incorporate the remote type in our own types!

    #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
    struct Baz {
        #[rkyv(with = FooDef)]
        foo: remote::Foo,
    }

    let baz = Baz { foo };

    let bytes = rkyv::to_bytes(&baz)?;
    let archived: &ArchivedBaz = rkyv::access(&bytes)?;
    let deserialized: Baz = rkyv::deserialize(archived)?;

    assert_eq!(baz, deserialized);

    Ok(())
}

#[allow(unused)]
mod another_remote {
    // Another remote type, this time an enum.
    #[non_exhaustive] // <- notice this inconvenience too
    pub enum Qux {
        Unit,
        Tuple(i32),
        Struct { value: bool },
    }
}

#[allow(unused)]
// Enums work similarly
#[derive(Archive, Serialize)]
#[rkyv(remote = another_remote::Qux)]
enum QuxDef {
    // Variants must have the same name and type, e.g. a remote *tuple*
    // variant requires a local *tuple* variant.
    Unit,
    // Same as for actual structs - fields of struct variants may be omitted.
    Struct {},
    // If `Serialize` should be derived and either the remote enum is
    // `#[non_exhaustive]` or any of its variants were omitted (notice the
    // `Tuple(i32)` variant is missing), then the last variant *must* be a
    // unit variant with the `#[rkyv(other)]` attribute.
    #[rkyv(other)]
    Unknown,
}