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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
use proc_macro::TokenStream;
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::{parse_macro_input, ItemConst};

use anchor_codegen::{
    enumeration::Enumeration,
    generate::GenerateConfig,
    output::Output,
    reply::Reply,
    static_string::{Shutdown, StaticString},
};

/// Sends a message to the remote end
///
/// This macro allows sending data to the remote end in a type safe way. The general syntax is:
/// ```
/// klipper_reply!(reply_name[, arg: type = value]);
/// ```
///
/// For example, the `uptime` command could be sent as:
/// ```
/// klippy_reply!(uptime, high: u32 = var_high, clock: u32 = var_clock);
/// ```
///
/// The `= value` part can be left out. In this case, a variable with the same name as the argument
/// from the local scope will be used. E.g. the following is valid:
/// ```
/// klipper_reply!(data, clock: u32 = clock.into(), data: u32);
/// ```
#[proc_macro_error]
#[proc_macro]
pub fn klipper_reply(item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as Reply);
    let sender = input.sender_fn_name();
    let args = input
        .args
        .iter()
        .map(|arg| match &arg.value {
            Some(value) => quote! { #value },
            None => {
                let name = &arg.name;
                quote! { #name }
            }
        })
        .collect::<Vec<_>>();

    TokenStream::from(quote! {
        crate::_anchor_config::message_handlers::#sender(#(#args),*)
    })
}

/// Sends a `printf`-style message to the remote end
///
/// Dynamic messages can be sent to the remote end using this command. The main use case is for
/// debugging. E.g.
/// ```
/// klipper_output!("This the %uth test! %*s?", 10, "A test string");
/// ```
///
/// A `printf`-style syntax is used, to match the syntax from Klipper. The format string is parsed
/// at compile time, including type checking. The supplied argument type must match the format
/// string.
///
/// The format strings available, and their matching types, are:
///
/// | Format string | Rust type |
/// |---------------|-----------|
/// | `%u`          | `u32`     |
/// | `%i`          | `i32`     |
/// | `%hu`         | `u16`     |
/// | `%hi`         | `i16`     |
/// | `%c`          | `u8`      |
/// | `%.*s`        | `&[u8]`   |
/// | `%*s`         | `&str`    |
#[proc_macro_error]
#[proc_macro]
pub fn klipper_output(item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as Output);
    let sender = input.sender_fn_name();
    let args = input
        .args
        .iter()
        .map(|arg| match &arg.value {
            Some(value) => quote! { #value },
            None => unreachable!(),
        })
        .collect::<Vec<_>>();

    TokenStream::from(quote! {
        crate::_anchor_config::message_handlers::#sender(#(#args),*)
    })
}

/// Generate compile-time configuration
///
/// This macro generates the protocol dictionary, message handlers, encoders, and dispatcher
/// needed. The actual code is generated by the `anchor_codegen` build script, and the macro
/// ensures that the generated code is included correctly.
///
/// This must be called exactly once at the root of your crate, typically in `main.rs`.
///
/// The syntax is as follows:
/// ```
/// klipper_config_generate!(options);
/// ```
///
/// The following options are supported:
///
///   * `transport = path: type`  
///     This is the `TransportOutput` that will be used when sending messages to the remote end.
///     The user crate must provide this, and it must be a global `const`. Any `klipper_reply!`
///     calls will call the `output` method. Both a path and type must be provided, both must be
///     fully expanded. E.g.:  
///     `transport = crate::usb::TRANSPORT_OUTPUT: crate::usb::BufferTransportOutput`  
///
///   * `context = type`  
///     An optional context can be passed to all `klipper_command` functions. The lifetime `'ctx`
///     is available, and allows the context to capture the lifetime when the generated dispatcher
///     is called, and pass this along to the handler functions. If no context type is given, the
///     default is the empty tuple `()`.
///
/// An example invocation could be:
/// ```
/// klipper_config_generate!(
///     transport = crate::usb::TRANSPORT_OUTPUT: crate::usb::BufferTransportOutput,
///     context = &'ctx mut crate::State,
/// );
/// ```
///
/// This generates a module called `_anchor_config`, and exports a `KLIPPER_TRANSPORT` symbol from
/// it.
#[proc_macro_error]
#[proc_macro]
pub fn klipper_config_generate(item: TokenStream) -> TokenStream {
    let cfg = parse_macro_input!(item as GenerateConfig);
    if let Err(e) = cfg.validate() {
        abort!("Invalid klipper config: {}", e);
    }
    let target = std::env::var("OUT_DIR").unwrap() + "/_anchor_config.rs";
    TokenStream::from(quote! {
        #[path = #target]
        mod _anchor_config;
        pub(crate) use _anchor_config::TRANSPORT as KLIPPER_TRANSPORT;
    })
}

/// Adds a static string to the generated protocol dictionary
///
/// This directly adds a static string to the `static_string_id` dictionary. The returned value
/// will be the ID of the added string.
///
/// This compile to a constant value.
#[proc_macro_error]
#[proc_macro]
pub fn klipper_static_string(item: TokenStream) -> TokenStream {
    let compile_name = parse_macro_input!(item as StaticString).compile_name();
    TokenStream::from(quote! {
        crate::_anchor_config::static_strings::#compile_name
    })
}

/// Sends a `shutdown` message to the remote end
///
/// ```
/// klipper_shutdown!("Some error", cur_clock());
/// ```
///
/// When called, immediately sends a static string and clock to the remote end. It is up to the
/// user code to perform any further shutdown-related handling.
#[proc_macro_error]
#[proc_macro]
pub fn klipper_shutdown(item: TokenStream) -> TokenStream {
    let info = parse_macro_input!(item as Shutdown);
    let compile_name = info.msg.compile_name();
    let clock = info.clock;
    TokenStream::from(quote! {
        crate::_anchor_config::message_handlers::send_reply_shutdown(
            #clock,
            crate::_anchor_config::static_strings::#compile_name
        );
    })
}

/// Creates a Klipper compatible enumeration
///
/// The general syntax can be seen by the following example:
/// ```
/// klipper_enumeration! {
///     #[derive(Debug)]
///     #[klipper_enumeration(name = "pin", rename_all = "UPPERCASE")]
///     enum Pins {
///         Range(PA, 0, 15),
///         Range(PB, 0, 15),
///         AdcTemperature,
///     }
/// }
/// ```
///
/// The item must always be an `enum`.
///
/// Variants can either be directly specified, or a range can be requested. For ranges, the format
/// is:
/// ```
/// Range(Prefix, start, count)
/// ```
/// This will generate `count` items named `Prefix{start+i}`.
///
/// Variants can be enabled or disabled using standard `#[cfg(feature...)]` feature flags.
///
/// The top item and the enumerations can accept parameters. These can be given using the
/// `#[klipper_enumeration(opts)]` attribute. For the top level item, `opts` can take the following
/// forms:
///
///   * `name = "a_name"`: An override name of the enumeration seen in the dictionary
///   * `rename_all = "UPPERCASE|lowercase|snake_case"`: a default renaming option for all variants
///
/// For each variant entry, the following options are available:
///
///   * `rename`: An override name of this variant in the dictionary. For range variants, all
///   values are renamed.
///
/// All values will be automatically mapped to IDs. Implementations of `TryFrom<u{8, 16, 32, 64,
/// size>` are generated automatically, along with  implementations of `From<Self> for u{8, 16, 32,
/// 64, usize}`. The number of bits in these generated functions is determining by the number of
/// variants. E.g. if the enum has more than 255 variants, the `u8` functions are not generated.
#[proc_macro_error]
#[proc_macro]
pub fn klipper_enumeration(item: TokenStream) -> TokenStream {
    let enumeration = parse_macro_input!(item as Enumeration);
    TokenStream::from(enumeration.to_token_stream())
}

/// Creates protocol command handler
///
/// This attribute is put on functions that should be exposed to the remote end. Only free standing
/// functions are supported.
///
/// An example usage:
/// ```
/// #[klipper_command]
/// fn finalize_config(context: &mut State, crc: u32) {
///   ...
/// }
/// ```
///
/// Argumenst are automatically converted to protocol compatible types and from wire format back to
/// Rust values.
///
/// A context argument may optionally be included. If included, this argument **must** be called
/// `context` or `ctx` and **must** be the first argument. It must have a type matching the one
/// given as the `context` parameter to the `klipper_config_generate` macro.
///
/// The following types are supported: `u8`, `i16`, `u16`, `i32`, `u32`, `bool`, `&[u8]`.
///
/// While Anchor places no restrictions on the number of arguments, be aware that individual
/// messages in the protocol are limited to 64 bytes of length. For larger sized data, one must
/// split the data across multiple messages.
#[proc_macro_error]
#[proc_macro_attribute]
pub fn klipper_command(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

/// Expose a constant
///
/// Rust constants can be exposed to the remote end by marking them as `#[klipper_constant]`. The
/// exposed constant must be either a string or an integer number.
///
/// ```
/// #[klipper_constant]
/// const CLOCK_FREQ: u32 = 100_000_000;
///
/// #[klipper_constant]
/// const MCU: &str = "beacon";
/// ```
#[proc_macro_error]
#[proc_macro_attribute]
pub fn klipper_constant(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as ItemConst);
    TokenStream::from(quote! {
        #[allow(dead_code)]
        #input
    })
}