emit_macros/
lib.rs

1/*!
2Implementation details for `emit!` macros.
3
4This crate is not intended to be consumed directly.
5*/
6
7/*
8# Organization
9
10This crate contains the proc-macros that are exported in the `emit` crate. It expands to code that uses the `emit::__private` API, in particular the `emit::macro_hooks` module.
11
12# Hooks
13
14Code is transformed through _hooks_. A hook is a well-known method call, like `a.__private_emit_capture_as_default()`. The behavior of the hook is defined in `emit::macro_hooks`. Attribute macros look for these hooks and replace them to change behavior. For example, `#[emit::as_debug]` looks for any `__private_emit_capture_as_*` method and replaces it with `__private_emit_capture_as_debug`.
15
16# Testing
17
18Tests for this project mostly live in the top-level `test/ui` crate.
19*/
20
21#![deny(missing_docs)]
22#![doc(html_logo_url = "https://raw.githubusercontent.com/emit-rs/emit/main/asset/logo.svg")]
23
24extern crate proc_macro;
25
26#[macro_use]
27extern crate quote;
28
29#[macro_use]
30extern crate syn;
31
32use std::collections::HashMap;
33
34use proc_macro2::TokenStream;
35
36mod args;
37mod build;
38mod capture;
39mod dbg;
40mod emit;
41mod fmt;
42mod format;
43mod hook;
44mod key;
45mod optional;
46mod props;
47mod span;
48mod template;
49mod util;
50
51use util::ResultToTokens;
52
53/**
54The set of hooks defined as a map.
55
56Hooks are regular attribute macros, but will be eagerly applied when expanding other macros to avoid the nightly-only feature that allows attribute macros on expressions. The `hook::eval_hooks` function will do this expansion.
57*/
58fn hooks() -> HashMap<&'static str, fn(TokenStream, TokenStream) -> syn::Result<TokenStream>> {
59    let mut map = HashMap::new();
60
61    map.insert(
62        "fmt",
63        (|args: TokenStream, expr: TokenStream| {
64            fmt::rename_hook_tokens(fmt::RenameHookTokens { args, expr })
65        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
66    );
67
68    map.insert(
69        "key",
70        (|args: TokenStream, expr: TokenStream| {
71            key::rename_hook_tokens(key::RenameHookTokens { args, expr })
72        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
73    );
74
75    map.insert(
76        "optional",
77        (|args: TokenStream, expr: TokenStream| {
78            optional::rename_hook_tokens(optional::RenameHookTokens { args, expr })
79        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
80    );
81
82    map.insert(
83        "as_value",
84        (|args: TokenStream, expr: TokenStream| {
85            capture_as(
86                "as_value",
87                args,
88                expr,
89                quote!(__private_capture_as_value),
90                quote!(__private_capture_anon_as_value),
91            )
92        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
93    );
94
95    map.insert(
96        "as_debug",
97        (|args: TokenStream, expr: TokenStream| {
98            capture_as(
99                "as_debug",
100                args,
101                expr,
102                quote!(__private_capture_as_debug),
103                quote!(__private_capture_anon_as_debug),
104            )
105        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
106    );
107
108    map.insert(
109        "as_display",
110        (|args: TokenStream, expr: TokenStream| {
111            capture_as(
112                "as_display",
113                args,
114                expr,
115                quote!(__private_capture_as_display),
116                quote!(__private_capture_anon_as_display),
117            )
118        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
119    );
120
121    map.insert(
122        "as_sval",
123        (|args: TokenStream, expr: TokenStream| {
124            #[cfg(feature = "sval")]
125            {
126                capture_as(
127                    "as_sval",
128                    args,
129                    expr,
130                    quote!(__private_capture_as_sval),
131                    quote!(__private_capture_anon_as_sval),
132                )
133            }
134            #[cfg(not(feature = "sval"))]
135            {
136                use syn::spanned::Spanned;
137
138                let _ = args;
139
140                Err(syn::Error::new(expr.span(), "capturing with `sval` is only possible when the `sval` Cargo feature is enabled"))
141            }
142        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>
143    );
144
145    map.insert(
146        "as_serde",
147        (|args: TokenStream, expr: TokenStream| {
148            #[cfg(feature = "serde")]
149            {
150                capture_as(
151                    "as_serde",
152                    args,
153                    expr,
154                    quote!(__private_capture_as_serde),
155                    quote!(__private_capture_anon_as_serde),
156                )
157            }
158            #[cfg(not(feature = "serde"))]
159            {
160                use syn::spanned::Spanned;
161
162                let _ = args;
163
164                Err(syn::Error::new(expr.span(), "capturing with `serde` is only possible when the `serde` Cargo feature is enabled"))
165            }
166        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>
167    );
168
169    map.insert(
170        "as_error",
171        (|args: TokenStream, expr: TokenStream| {
172            #[cfg(feature = "std")]
173            {
174                capture_as(
175                    "as_error",
176                    args,
177                    expr,
178                    quote!(__private_capture_as_error),
179                    quote!(__private_capture_as_error),
180                )
181            }
182            #[cfg(not(feature = "std"))]
183            {
184                use syn::spanned::Spanned;
185
186                let _ = args;
187
188                Err(syn::Error::new(
189                    expr.span(),
190                    "capturing errors is only possible when the `std` Cargo feature is enabled",
191                ))
192            }
193        }) as fn(TokenStream, TokenStream) -> syn::Result<TokenStream>,
194    );
195
196    map
197}
198
199#[doc = "Format a template."]
200#[doc = ""]
201#[doc = include_str!("./doc_fmt.md")]
202#[proc_macro]
203pub fn format(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
204    format::expand_tokens(format::ExpandTokens {
205        input: TokenStream::from(item),
206    })
207    .unwrap_or_compile_error()
208}
209
210#[doc = "Construct an event that can be emitted manually."]
211#[doc = ""]
212#[doc = include_str!("./doc_evt.md")]
213#[proc_macro]
214pub fn evt(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
215    build::expand_evt_tokens(build::ExpandEvtTokens {
216        level: None,
217        input: item.into(),
218    })
219    .unwrap_or_compile_error()
220}
221
222#[doc = "Construct an event at the debug level that can be emitted manually."]
223#[doc = ""]
224#[doc = include_str!("./doc_evt.md")]
225#[proc_macro]
226pub fn debug_evt(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
227    build::expand_evt_tokens(build::ExpandEvtTokens {
228        level: Some(quote!(emit::Level::Debug)),
229        input: item.into(),
230    })
231    .unwrap_or_compile_error()
232}
233
234#[doc = "Construct an event at the info level that can be emitted manually."]
235#[doc = ""]
236#[doc = include_str!("./doc_evt.md")]
237#[proc_macro]
238pub fn info_evt(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
239    build::expand_evt_tokens(build::ExpandEvtTokens {
240        level: Some(quote!(emit::Level::Info)),
241        input: item.into(),
242    })
243    .unwrap_or_compile_error()
244}
245
246#[doc = "Construct an event at the warn level that can be emitted manually."]
247#[doc = ""]
248#[doc = include_str!("./doc_evt.md")]
249#[proc_macro]
250pub fn warn_evt(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
251    build::expand_evt_tokens(build::ExpandEvtTokens {
252        level: Some(quote!(emit::Level::Warn)),
253        input: item.into(),
254    })
255    .unwrap_or_compile_error()
256}
257
258#[doc = "Construct an event at the error level that can be emitted manually."]
259#[doc = ""]
260#[doc = include_str!("./doc_evt.md")]
261#[proc_macro]
262pub fn error_evt(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
263    build::expand_evt_tokens(build::ExpandEvtTokens {
264        level: Some(quote!(emit::Level::Error)),
265        input: item.into(),
266    })
267    .unwrap_or_compile_error()
268}
269
270#[doc = "Trace the execution of a function."]
271#[doc = ""]
272#[doc = include_str!("./doc_span.md")]
273#[proc_macro_attribute]
274pub fn span(
275    args: proc_macro::TokenStream,
276    item: proc_macro::TokenStream,
277) -> proc_macro::TokenStream {
278    span::expand_tokens(span::ExpandTokens {
279        level: None,
280        input: TokenStream::from(args),
281        item: TokenStream::from(item),
282    })
283    .unwrap_or_compile_error()
284}
285
286#[doc = "Trace the execution of a function at the debug level."]
287#[doc = ""]
288#[doc = include_str!("./doc_span.md")]
289#[proc_macro_attribute]
290pub fn debug_span(
291    args: proc_macro::TokenStream,
292    item: proc_macro::TokenStream,
293) -> proc_macro::TokenStream {
294    span::expand_tokens(span::ExpandTokens {
295        level: Some(quote!(emit::Level::Debug)),
296        input: TokenStream::from(args),
297        item: TokenStream::from(item),
298    })
299    .unwrap_or_compile_error()
300}
301
302#[doc = "Trace the execution of a function at the info level."]
303#[doc = ""]
304#[doc = include_str!("./doc_span.md")]
305#[proc_macro_attribute]
306pub fn info_span(
307    args: proc_macro::TokenStream,
308    item: proc_macro::TokenStream,
309) -> proc_macro::TokenStream {
310    span::expand_tokens(span::ExpandTokens {
311        level: Some(quote!(emit::Level::Info)),
312        input: TokenStream::from(args),
313        item: TokenStream::from(item),
314    })
315    .unwrap_or_compile_error()
316}
317
318#[doc = "Trace the execution of a function at the warn level."]
319#[doc = ""]
320#[doc = include_str!("./doc_span.md")]
321#[proc_macro_attribute]
322pub fn warn_span(
323    args: proc_macro::TokenStream,
324    item: proc_macro::TokenStream,
325) -> proc_macro::TokenStream {
326    span::expand_tokens(span::ExpandTokens {
327        level: Some(quote!(emit::Level::Warn)),
328        input: TokenStream::from(args),
329        item: TokenStream::from(item),
330    })
331    .unwrap_or_compile_error()
332}
333
334#[doc = "Trace the execution of a function at the error level."]
335#[doc = ""]
336#[doc = include_str!("./doc_span.md")]
337#[proc_macro_attribute]
338pub fn error_span(
339    args: proc_macro::TokenStream,
340    item: proc_macro::TokenStream,
341) -> proc_macro::TokenStream {
342    span::expand_tokens(span::ExpandTokens {
343        level: Some(quote!(emit::Level::Error)),
344        input: TokenStream::from(args),
345        item: TokenStream::from(item),
346    })
347    .unwrap_or_compile_error()
348}
349
350#[doc = "Create a span that can be started and completed manually."]
351#[doc = ""]
352#[doc = include_str!("./doc_new_span.md")]
353#[proc_macro]
354pub fn new_span(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
355    span::expand_new_tokens(span::ExpandNewTokens {
356        level: None,
357        input: TokenStream::from(item),
358    })
359    .unwrap_or_compile_error()
360}
361
362#[doc = "Create a span at the debug level that can be started and completed manually."]
363#[doc = ""]
364#[doc = include_str!("./doc_new_span.md")]
365#[proc_macro]
366pub fn new_debug_span(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
367    span::expand_new_tokens(span::ExpandNewTokens {
368        level: Some(quote!(emit::Level::Debug)),
369        input: TokenStream::from(item),
370    })
371    .unwrap_or_compile_error()
372}
373
374#[doc = "Create a span at the info level that can be started and completed manually."]
375#[doc = ""]
376#[doc = include_str!("./doc_new_span.md")]
377#[proc_macro]
378pub fn new_info_span(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
379    span::expand_new_tokens(span::ExpandNewTokens {
380        level: Some(quote!(emit::Level::Info)),
381        input: TokenStream::from(item),
382    })
383    .unwrap_or_compile_error()
384}
385
386#[doc = "Create a span at the warn level that can be started and completed manually."]
387#[doc = ""]
388#[doc = include_str!("./doc_new_span.md")]
389#[proc_macro]
390pub fn new_warn_span(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
391    span::expand_new_tokens(span::ExpandNewTokens {
392        level: Some(quote!(emit::Level::Warn)),
393        input: TokenStream::from(item),
394    })
395    .unwrap_or_compile_error()
396}
397
398#[doc = "Create a span at the error level that can be started and completed manually."]
399#[doc = ""]
400#[doc = include_str!("./doc_new_span.md")]
401#[proc_macro]
402pub fn new_error_span(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
403    span::expand_new_tokens(span::ExpandNewTokens {
404        level: Some(quote!(emit::Level::Error)),
405        input: TokenStream::from(item),
406    })
407    .unwrap_or_compile_error()
408}
409
410#[doc = "Construct a template."]
411#[doc = ""]
412#[doc = include_str!("./doc_tpl.md")]
413#[proc_macro]
414pub fn tpl(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
415    build::expand_tpl_tokens(build::ExpandTplTokens {
416        input: TokenStream::from(item),
417    })
418    .unwrap_or_compile_error()
419}
420
421#[doc = "Emit an event."]
422#[doc = ""]
423#[doc = include_str!("./doc_emit.md")]
424#[proc_macro]
425pub fn emit(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
426    emit::expand_tokens(emit::ExpandTokens {
427        level: None,
428        input: TokenStream::from(item),
429    })
430    .unwrap_or_compile_error()
431}
432
433#[doc = "Emit an event at the debug level."]
434#[doc = ""]
435#[doc = include_str!("./doc_emit.md")]
436#[proc_macro]
437pub fn debug(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
438    emit::expand_tokens(emit::ExpandTokens {
439        level: Some(quote!(emit::Level::Debug)),
440        input: TokenStream::from(item),
441    })
442    .unwrap_or_compile_error()
443}
444
445#[doc = "Emit an event at the info level."]
446#[doc = ""]
447#[doc = include_str!("./doc_emit.md")]
448#[proc_macro]
449pub fn info(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
450    emit::expand_tokens(emit::ExpandTokens {
451        level: Some(quote!(emit::Level::Info)),
452        input: TokenStream::from(item),
453    })
454    .unwrap_or_compile_error()
455}
456
457#[doc = "Emit an event at the warn level."]
458#[doc = ""]
459#[doc = include_str!("./doc_emit.md")]
460#[proc_macro]
461pub fn warn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
462    emit::expand_tokens(emit::ExpandTokens {
463        level: Some(quote!(emit::Level::Warn)),
464        input: TokenStream::from(item),
465    })
466    .unwrap_or_compile_error()
467}
468
469#[doc = "Emit an event at the error level."]
470#[doc = ""]
471#[doc = include_str!("./doc_emit.md")]
472#[proc_macro]
473pub fn error(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
474    emit::expand_tokens(emit::ExpandTokens {
475        level: Some(quote!(emit::Level::Error)),
476        input: TokenStream::from(item),
477    })
478    .unwrap_or_compile_error()
479}
480
481#[doc = "Emit a temporary event as a quick-and-dirty debugging aid."]
482#[doc = ""]
483#[doc = include_str!("./doc_dbg.md")]
484#[proc_macro]
485pub fn dbg(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
486    dbg::expand_tokens(dbg::ExpandTokens {
487        input: TokenStream::from(item),
488    })
489    .unwrap_or_compile_error()
490}
491
492/**
493Construct a path.
494
495# Syntax
496
497```text
498path
499```
500
501where
502
503- `path`: A string literal containing a valid `emit` path.
504*/
505#[proc_macro]
506pub fn path(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
507    build::expand_path_tokens(build::ExpandPathTokens {
508        input: TokenStream::from(item),
509    })
510    .unwrap_or_compile_error()
511}
512
513/**
514Construct a set of properties.
515
516# Syntax
517
518```text
519(property),*
520```
521
522where
523
524- `property`: A Rust field-value for a property. The identifier of the field-value is the key of the property.
525*/
526#[proc_macro]
527pub fn props(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
528    build::expand_props_tokens(build::ExpandPropsTokens {
529        input: TokenStream::from(item),
530    })
531    .unwrap_or_compile_error()
532}
533
534/**
535Specify Rust format flags to use when rendering a property in a template.
536
537# Syntax
538
539```text
540fmt_string
541```
542
543where
544
545- `fmt_string`: A string literal with the format flags, like `":?"`. See the [`std::fmt`](https://doc.rust-lang.org/std/fmt/index.html) docs for details on available flags.
546
547# Applicable to
548
549This attribute can be applied to properties that appear in a template.
550*/
551#[proc_macro_attribute]
552pub fn fmt(
553    args: proc_macro::TokenStream,
554    item: proc_macro::TokenStream,
555) -> proc_macro::TokenStream {
556    (hook::get("fmt").unwrap())(TokenStream::from(args), TokenStream::from(item))
557        .unwrap_or_compile_error()
558}
559
560/**
561Specify the key for a property.
562
563# Syntax
564
565```text
566key
567```
568
569where
570
571- `key`: A string literal with the key to use. The key doesn't need to be a valid Rust identifier.
572
573# Applicable to
574
575This attribute can be applied to properties.
576*/
577#[proc_macro_attribute]
578pub fn key(
579    args: proc_macro::TokenStream,
580    item: proc_macro::TokenStream,
581) -> proc_macro::TokenStream {
582    (hook::get("key").unwrap())(TokenStream::from(args), TokenStream::from(item))
583        .unwrap_or_compile_error()
584}
585
586/**
587Specify that a property value of `None` should not be captured, instead of being captured as `null`.
588
589# Syntax
590
591This macro doesn't accept any arguments.
592
593# Applicable to
594
595This attribute can be applied to properties where the type is `Option<&T>`.
596*/
597#[proc_macro_attribute]
598pub fn optional(
599    args: proc_macro::TokenStream,
600    item: proc_macro::TokenStream,
601) -> proc_macro::TokenStream {
602    (hook::get("optional").unwrap())(TokenStream::from(args), TokenStream::from(item))
603        .unwrap_or_compile_error()
604}
605
606/**
607Capture a property using its `ToValue` implementation.
608
609# Syntax
610
611This macro doesn't accept any arguments.
612
613# Applicable to
614
615This attribute can be applied to properties.
616*/
617#[proc_macro_attribute]
618pub fn as_value(
619    args: proc_macro::TokenStream,
620    item: proc_macro::TokenStream,
621) -> proc_macro::TokenStream {
622    (hook::get("as_value").unwrap())(TokenStream::from(args), TokenStream::from(item))
623        .unwrap_or_compile_error()
624}
625
626/**
627Capture a property using its `Debug` implementation.
628
629# Syntax
630
631This macro doesn't accept any arguments.
632
633# Applicable to
634
635This attribute can be applied to properties.
636*/
637#[proc_macro_attribute]
638pub fn as_debug(
639    args: proc_macro::TokenStream,
640    item: proc_macro::TokenStream,
641) -> proc_macro::TokenStream {
642    (hook::get("as_debug").unwrap())(TokenStream::from(args), TokenStream::from(item))
643        .unwrap_or_compile_error()
644}
645
646/**
647Capture a property using its `Display` implementation.
648
649# Syntax
650
651This macro doesn't accept any arguments.
652
653# Applicable to
654
655This attribute can be applied to properties.
656*/
657#[proc_macro_attribute]
658pub fn as_display(
659    args: proc_macro::TokenStream,
660    item: proc_macro::TokenStream,
661) -> proc_macro::TokenStream {
662    (hook::get("as_display").unwrap())(TokenStream::from(args), TokenStream::from(item))
663        .unwrap_or_compile_error()
664}
665
666/**
667Capture a property using its `sval::Value` implementation.
668
669# Syntax
670
671This macro doesn't accept any arguments.
672
673# Applicable to
674
675This attribute can be applied to properties.
676*/
677#[proc_macro_attribute]
678pub fn as_sval(
679    args: proc_macro::TokenStream,
680    item: proc_macro::TokenStream,
681) -> proc_macro::TokenStream {
682    (hook::get("as_sval").unwrap())(TokenStream::from(args), TokenStream::from(item))
683        .unwrap_or_compile_error()
684}
685
686/**
687Capture a property using its `serde::Serialize` implementation.
688
689# Syntax
690
691This macro doesn't accept any arguments.
692
693# Applicable to
694
695This attribute can be applied to properties.
696*/
697#[proc_macro_attribute]
698pub fn as_serde(
699    args: proc_macro::TokenStream,
700    item: proc_macro::TokenStream,
701) -> proc_macro::TokenStream {
702    (hook::get("as_serde").unwrap())(TokenStream::from(args), TokenStream::from(item))
703        .unwrap_or_compile_error()
704}
705
706/**
707Capture a property using its `Error` implementation.
708
709# Syntax
710
711This macro doesn't accept any arguments.
712
713# Applicable to
714
715This attribute can be applied to properties.
716*/
717#[proc_macro_attribute]
718pub fn as_error(
719    args: proc_macro::TokenStream,
720    item: proc_macro::TokenStream,
721) -> proc_macro::TokenStream {
722    (hook::get("as_error").unwrap())(TokenStream::from(args), TokenStream::from(item))
723        .unwrap_or_compile_error()
724}
725
726/*
727fn base_emit(level: Option<TokenStream>, item: TokenStream) -> proc_macro::TokenStream {
728    emit::expand_tokens(emit::ExpandTokens { level, input: item }).unwrap_or_compile_error()
729}
730
731fn base_span(
732    level: Option<TokenStream>,
733    input: TokenStream,
734    item: TokenStream,
735) -> proc_macro::TokenStream {
736    if filter::matches_build_filter() {
737        span::expand_tokens(span::ExpandTokens { level, input, item }).unwrap_or_compile_error()
738    } else {
739        item.into()
740    }
741}
742*/
743
744fn capture_as(
745    name: &'static str,
746    args: TokenStream,
747    expr: TokenStream,
748    as_fn: TokenStream,
749    as_anon_fn: TokenStream,
750) -> syn::Result<TokenStream> {
751    capture::rename_hook_tokens(capture::RenameHookTokens {
752        name,
753        args,
754        expr,
755        to: |args: &capture::Args| {
756            if args.inspect {
757                as_fn.clone()
758            } else {
759                as_anon_fn.clone()
760            }
761        },
762    })
763}