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
#![deny(clippy::all)]
#![warn(clippy::pedantic)]

mod fpg;

use casper_utils::design_sources::fpg::read_fpg_file;
use fpg::{
    generate_constructors,
    generate_field_names,
    generate_struct_fields,
    FpgFpga,
};
use proc_macro::TokenStream;
use quote::quote;
use std::path::PathBuf;
use syn::{
    parse_macro_input,
    DeriveInput,
};

#[proc_macro_derive(CasperSerde)]
/// Derived on a [`PackedStruct`] to shim in our serde methods on packed structs
pub fn derive_casper_serde(tokens: TokenStream) -> TokenStream {
    let input = parse_macro_input!(tokens as DeriveInput);
    let block_name = input.ident;
    let generated = quote! {
        impl Serialize for #block_name {
            type Chunk = <Self as PackedStruct>::ByteArray;

            fn serialize(&self) -> Self::Chunk {
                self.pack().expect("Packing failed, this shouldn't happen")
            }
        }

        impl Deserialize for #block_name {
            type Chunk = <Self as PackedStruct>::ByteArray;
            type Error = PackingError;

            fn deserialize(chunk: Self::Chunk) -> Result<Self, Self::Error> {
                Self::unpack(&chunk)
            }
        }
    };
    TokenStream::from(generated)
}

#[proc_macro_attribute]
/// Implement the Address trait on this struct, allowing for automatic addressing when reading and
/// writing
/// # Panics
/// Panics on bad address literals
#[allow(clippy::manual_flatten)]
#[allow(clippy::manual_let_else)]
pub fn address(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attr = match syn::parse::<syn::Lit>(attr).expect("Error parsing attribute") {
        syn::Lit::Int(v) => v,
        _ => panic!("The address must be a literal integer (hopefully a u8)"),
    };
    let num = attr;
    // Get the struct name this address is for
    let item = parse_macro_input!(item as DeriveInput);
    let ident = item.clone().ident;

    let generated = quote! {
        impl Address for #ident {
            fn addr() -> u16 {
                #num as u16
            }
        }
        #item
    };
    TokenStream::from(generated)
}

#[proc_macro]
/// Generates a fully-typed and specified FPGA instance using the object definitions from a given
/// fpg file.
#[allow(clippy::missing_panics_doc)]
pub fn fpga_from_fpg(tokens: TokenStream) -> TokenStream {
    let FpgFpga { name, filename } = parse_macro_input!(tokens as FpgFpga);
    let filename = filename.value();

    let fpg = read_fpg_file(PathBuf::from(filename)).expect("Couldn't read FPG file");

    let struct_fields = generate_struct_fields(&fpg.devices);
    let field_names = generate_field_names(&fpg.devices);
    let constructors = generate_constructors(&fpg.devices);

    // For every device in the fpg file, create a typed entry in the struct
    let generated = quote! {
        #[derive(Debug)]
        pub struct #name<T> {
            pub transport: std::sync::Arc<std::sync::Mutex<T>>,
            #(#struct_fields),*
        }

        impl<T> #name<T>
        where
            T: casperfpga::transport::Transport
        {
            pub fn new(transport: T) -> Result<Self, casperfpga::yellow_blocks::Error> {
                // Create the Arc Mutex for the transport
                let tarc = std::sync::Arc::new(std::sync::Mutex::new(transport));
                // And create the weak to pass to the yellow blocks
                let tweak = std::sync::Arc::downgrade(&tarc);
                // For every fpg device, run its `from_fpg` method
                #(#constructors)*
                // We probably want to actualy enforce that we program the FPGA at some point
                Ok(Self {transport: tarc, #(#field_names,)*})
            }
        }
    };
    TokenStream::from(generated)
}