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
use flow_record_common::{FieldType, Object, RecordDescriptor, RecordField};
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::io::Write;

use proc_macro::TokenStream;
use quote::quote;
use syn::Type;

fn to_field_type(ty: &Type) -> FieldType {
    let type_name = quote!(#ty).to_string().replace(' ', "");
    match &type_name[..] {
        "String" => FieldType::String,
        "u8" | "u16" => FieldType::UInt16,
        "u32" => FieldType::UInt32,
        "i64" => FieldType::VarInt,
        "DateTime<Utc>" => FieldType::Datetime,
        "Option<DateTime<Utc>>" => FieldType::Datetime,
        type_id => unimplemented!("no implementation for type '{type_id}' yet"),
    }
}

#[proc_macro_derive(Record)]
pub fn recorddescriptor_derive(input: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();
    let name = &ast.ident;
    let name_as_string = name.to_string();

    let descriptor;
    let hash;

    match ast.data {
        syn::Data::Struct(s) => {
            descriptor = struct_descriptor(&name_as_string, &s);
            hash = calc_descriptor_hash(&name_as_string, &s);
        }

        syn::Data::Enum(_) => panic!("no support for enums yet"),
        syn::Data::Union(_) => panic!("no support for unions yet"),
    }

    let length = descriptor.len();

    let gen = quote!(
        impl Record for #name {
            fn name() -> &'static str {
                #name_as_string
            }
            fn descriptor() -> &'static [u8] {
                static d: [u8; #length] = [ #(#descriptor),* ];
                &d
            }
            fn descriptor_hash() -> u32 {
                #hash
            }
        }
    );
    gen.into()
}

fn calc_descriptor_hash(name: &str, s: &syn::DataStruct) -> u32 {
    let mut hasher = Sha256::new();
    hasher.write_all(name.as_bytes()).unwrap();

    if let Some(fields) = record_fields_from(s) {
        for field in fields {
            hasher.write_all(field.field_name().as_bytes()).unwrap();
            hasher
                .write_all(field.field_type().to_string().as_bytes())
                .unwrap();
        }
    }
    let hash = hasher.finalize();
    u32::from_be_bytes(hash[0..4].try_into().unwrap())
}

fn struct_descriptor(name: &str, s: &syn::DataStruct) -> Vec<u8> {
    if let Some(fields) = record_fields_from(s) {
        let mut buffer: Vec<u8> = Vec::new();
        let mut ser = rmp_serde::Serializer::new(&mut buffer);
        Object::with_descriptor(&RecordDescriptor::new(name.to_owned(), fields))
            .serialize(&mut ser)
            .unwrap();
        buffer
    } else {
        unimplemented!()
    }
}

fn record_fields_from(s: &syn::DataStruct) -> Option<Vec<RecordField>> {
    match &s.fields {
        syn::Fields::Named(n) => {
            let fields: Vec<_> = n
                .named
                .iter()
                .map(|f| {
                    let field_name = f.ident.as_ref().unwrap().to_string();
                    let field_type = to_field_type(&f.ty);
                    RecordField::from((field_name, field_type))
                })
                .collect();
            Some(fields)
        }
        _ => None,
    }
}