1
use bincode::Options;
2
use chrono::{DateTime, Utc};
3
use cli_table::{Cell, Table};
4
use fake::faker::filesystem::en::FilePath;
5
use fake::faker::internet::en::Username;
6
use fake::faker::lorem::en::Sentence;
7
use fake::Fake;
8
use rand::{thread_rng, Rng};
9
use serde::{Deserialize, Serialize};
10
use thousands::Separable;
11

            
12
1
fn main() -> anyhow::Result<()> {
13
1
    // Generate a bunch of logs
14
1
    let logs = LogArchive::generate(&mut thread_rng(), 10_000);
15

            
16
    // Encode without a persistent session
17
1
    let pot_bytes = pot::to_vec(&logs)?;
18
1
    let bincode_bytes = bincode::serialize(&logs)?;
19
1
    let bincode_varint_bytes = bincode::DefaultOptions::default()
20
1
        .with_varint_encoding()
21
1
        .serialize(&logs)?;
22
1
    let mut cbor_bytes = Vec::new();
23
1
    ciborium::ser::into_writer(&logs, &mut cbor_bytes)?;
24
1
    let msgpack_bytes = rmp_serde::to_vec_named(&logs)?;
25
1
    let msgpack_compact_bytes = rmp_serde::to_vec(&logs)?;
26

            
27
1
    cli_table::print_stdout(
28
1
        vec![
29
1
            vec![
30
1
                "pot".cell(),
31
1
                pot_bytes.len().separate_with_commas().cell(),
32
1
                "yes".cell(),
33
1
            ],
34
1
            vec![
35
1
                "cbor".cell(),
36
1
                cbor_bytes.len().separate_with_commas().cell(),
37
1
                "yes".cell(),
38
1
            ],
39
1
            vec![
40
1
                "msgpack(named)".cell(),
41
1
                msgpack_bytes.len().separate_with_commas().cell(),
42
1
                "yes".cell(),
43
1
            ],
44
1
            vec![
45
1
                "msgpack".cell(),
46
1
                msgpack_compact_bytes.len().separate_with_commas().cell(),
47
1
                "no".cell(),
48
1
            ],
49
1
            vec![
50
1
                "bincode(varint)".cell(),
51
1
                bincode_varint_bytes.len().separate_with_commas().cell(),
52
1
                "no".cell(),
53
1
            ],
54
1
            vec![
55
1
                "bincode".cell(),
56
1
                bincode_bytes.len().separate_with_commas().cell(),
57
1
                "no".cell(),
58
1
            ],
59
1
        ]
60
1
        .table()
61
1
        .title(vec!["Format", "Bytes", "Self-Describing"]),
62
1
    )?;
63

            
64
    // With Pot, you can also use a persistent encoding session to save more
65
    // bandwidth, as long as you guarantee payloads are serialized and
66
    // deserialized in a consistent order.
67
    //
68
    // In this situation, the payloads across a network are generally smaller,
69
    // so let's show the benefits by just encoding a single log entry.
70
1
    let mut sender_state = pot::ser::SymbolMap::default();
71
1
    let mut receiver_state = pot::de::SymbolMap::default();
72
1
    let mut payload_buffer = sender_state.serialize_to_vec(&logs.entries[0])?;
73
1
    let first_transmission_length = payload_buffer.len();
74
    {
75
1
        assert_eq!(
76
1
            &receiver_state.deserialize_slice::<Log>(&payload_buffer)?,
77
1
            &logs.entries[0]
78
        );
79
    }
80
1
    payload_buffer.clear();
81
1
    sender_state.serialize_to(&mut payload_buffer, &logs.entries[0])?;
82
1
    let subsequent_transmission_length = payload_buffer.len();
83
1
    assert_eq!(
84
1
        &receiver_state.deserialize_slice::<Log>(&payload_buffer)?,
85
1
        &logs.entries[0]
86
    );
87

            
88
1
    println!(
89
1
        "Using a persistent encoding session, the first payload was {first_transmission_length} bytes long.",
90
1

            
91
1
    );
92
1
    println!(
93
1
        "The same payload sent a second time was {subsequent_transmission_length} bytes long.",
94
1
    );
95
1

            
96
1
    Ok(())
97
1
}
98

            
99
880003
#[derive(Serialize, Deserialize, Debug, PartialEq)]
100
pub struct Log {
101
    pub level: Level,
102
    pub user_id: String,
103
    pub timestamp: DateTime<Utc>,
104
    pub request: String,
105
    pub message: Option<String>,
106
    pub code: u16,
107
    pub size: u64,
108
}
109

            
110
880003
#[derive(Serialize, Deserialize, Debug, PartialEq)]
111
pub enum Level {
112
    Trace,
113
    Debug,
114
    Info,
115
    Warn,
116
    Error,
117
}
118

            
119
8008
#[derive(Serialize, Deserialize, Debug, PartialEq)]
120
pub struct LogArchive {
121
    entries: Vec<Log>,
122
}
123

            
124
impl LogArchive {
125
1001
    fn generate<R: Rng>(rand: &mut R, count: usize) -> Self {
126
1001
        let mut entries = Vec::new();
127
110000
        for _ in 0..count {
128
110000
            entries.push(Log::generate(rand));
129
110000
        }
130
1001
        Self { entries }
131
1001
    }
132
}
133

            
134
impl Log {
135
110001
    fn generate<R: Rng>(rand: &mut R) -> Self {
136
110001
        Self {
137
110001
            user_id: Username().fake_with_rng(rand),
138
110001
            timestamp: Utc::now(),
139
110001
            code: rand.gen(),
140
110001
            size: rand.gen(),
141
110001
            level: Level::generate(rand),
142
110001
            request: FilePath().fake_with_rng(rand),
143
110001
            message: if rand.gen() {
144
55167
                Some(Sentence(3..100).fake_with_rng(rand))
145
            } else {
146
54834
                None
147
            },
148
        }
149
110001
    }
150
}
151

            
152
impl Level {
153
110001
    fn generate<R: Rng>(rand: &mut R) -> Self {
154
110001
        match rand.gen_range(0_u8..=4u8) {
155
22152
            0 => Level::Trace,
156
22051
            1 => Level::Debug,
157
22079
            2 => Level::Info,
158
22021
            3 => Level::Warn,
159
21698
            4 => Level::Error,
160
            _ => unreachable!(),
161
        }
162
110001
    }
163
}
164

            
165
1
#[test]
166
1
fn runs() {
167
1
    main().unwrap();
168
1
}
169

            
170
1
#[test]
171
1
fn one_log() {
172
1
    let log = Log::generate(&mut thread_rng());
173
1
    let bytes = pot::to_vec(&log).unwrap();
174
1
    let result = pot::from_slice(&bytes).unwrap();
175
1
    assert_eq!(log, result);
176
1
}
177

            
178
1
#[test]
179
1
fn average_sizes() {
180
1
    let mut bincode_sizes = Vec::new();
181
1
    let mut bincode_varint_sizes = Vec::new();
182
1
    let mut cbor_sizes = Vec::new();
183
1
    let mut pot_sizes = Vec::new();
184
1
    let mut msgpack_sizes = Vec::new();
185
1
    let mut msgpack_compact_sizes = Vec::new();
186
1

            
187
1
    const ITERATIONS: usize = 1_000;
188
1
    println!("Generating {} LogArchives with 100 entries.", ITERATIONS);
189
1001
    for _ in 0..ITERATIONS {
190
1000
        let log = LogArchive::generate(&mut thread_rng(), 100);
191
1000
        bincode_sizes.push(bincode::serialize(&log).unwrap().len());
192
1000
        bincode_varint_sizes.push(
193
1000
            bincode::DefaultOptions::default()
194
1000
                .with_varint_encoding()
195
1000
                .serialize(&log)
196
1000
                .unwrap()
197
1000
                .len(),
198
1000
        );
199
1000
        let mut cbor_bytes = Vec::new();
200
1000
        ciborium::ser::into_writer(&log, &mut cbor_bytes).unwrap();
201
1000
        cbor_sizes.push(cbor_bytes.len());
202
1000
        pot_sizes.push(pot::to_vec(&log).unwrap().len());
203
1000
        msgpack_sizes.push(rmp_serde::to_vec_named(&log).unwrap().len());
204
1000
        msgpack_compact_sizes.push(rmp_serde::to_vec(&log).unwrap().len());
205
1000
    }
206

            
207
1
    let bincode_average = bincode_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
208
1
    let bincode_varint_average =
209
1
        bincode_varint_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
210
1
    let cbor_average = cbor_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
211
1
    let pot_average = pot_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
212
1
    let msgpack_average = msgpack_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
213
1
    let msgpack_compact_average =
214
1
        msgpack_compact_sizes.iter().copied().sum::<usize>() as f64 / ITERATIONS as f64;
215
1

            
216
1
    cli_table::print_stdout(
217
1
        vec![
218
1
            vec!["pot".cell(), pot_average.separate_with_commas().cell()],
219
1
            vec![
220
1
                "bincode(varint)".cell(),
221
1
                bincode_varint_average.separate_with_commas().cell(),
222
1
            ],
223
1
            vec![
224
1
                "bincode".cell(),
225
1
                bincode_average.separate_with_commas().cell(),
226
1
            ],
227
1
            vec!["cbor".cell(), cbor_average.separate_with_commas().cell()],
228
1
            vec![
229
1
                "msgpack".cell(),
230
1
                msgpack_average.separate_with_commas().cell(),
231
1
            ],
232
1
            vec![
233
1
                "msgpack(compact)".cell(),
234
1
                msgpack_compact_average.separate_with_commas().cell(),
235
1
            ],
236
1
        ]
237
1
        .table()
238
1
        .title(vec!["Format", "Avg. Bytes"]),
239
1
    )
240
1
    .unwrap();
241
1
}