Skip to main content

typespec_macros/
lib.rs

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4#![doc = include_str!("../README.md")]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![warn(missing_docs)]
7
8//! This crate contains procedural macros that are used to generate code for the TypeSpec SDK.
9
10use syn::{parse_macro_input, DeriveInput};
11
12extern crate proc_macro;
13
14mod safe_debug;
15
16type Result<T> = ::std::result::Result<T, syn::Error>;
17
18// NOTE: Proc macros must appear in the root of the crate. Just re-exporting them with `pub use` is **not sufficient**.
19// So, all the top-level entry functions for the proc macros will appear here, but they just call inner "impl" functions in the modules.
20
21/// Defines the function signature expected by run_derive_macro
22type DeriveImpl = fn(DeriveInput) -> Result<proc_macro2::TokenStream>;
23
24/// Runs the provided derive macro implementation, automatically generating errors if it returns errors.
25fn run_derive_macro(input: proc_macro::TokenStream, imp: DeriveImpl) -> proc_macro::TokenStream {
26    let ast = parse_macro_input!(input as DeriveInput);
27    match imp(ast) {
28        Ok(tokens) => tokens.into(),
29        Err(e) => e.to_compile_error().into(),
30    }
31}
32
33/// Derive to help prevent leaking personally identifiable information (PII) that deriving [`Debug`](std::fmt::Debug) might otherwise.
34///
35/// `SafeDebug` is not a trait and cannot be implemented, nor should you derive `Debug` explicitly.
36/// Only when you derive `SafeDebug` will types help prevent leaking PII because, by default, only the type name is printed.
37/// Only when you enable the `debug` feature will it derive `Debug` normally.
38///
39/// You can attribute types, fields, and variants with `#[safe(true)]` or `#[safe(false)]` to optionally show or hide members.
40/// The default is that no members are shown. The inner most `#[safe(..)]` attribute determines whether to show or hide a member.
41///
42/// # Examples
43///
44/// ```
45/// # use typespec_macros::SafeDebug;
46/// #[derive(SafeDebug)]
47/// struct Person {
48///     name: String,
49/// }
50///
51/// let person = Person {
52///     name: "Kelly Smith".to_string(),
53/// };
54/// if cfg!(feature = "debug") {
55///     assert_eq!(format!("{person:?}"), r#"Person { name: "Kelly Smith" }"#);
56/// } else {
57///     assert_eq!(format!("{person:?}"), "Person { .. }");
58/// }
59/// ```
60///
61/// Using the `#[safe(..)]` attribute, you can selectively show or hide members.
62/// The default, when not present or inherited, is to always hide members unless the `debug` feature is enabled.
63///
64/// ```
65/// # use typespec_macros::SafeDebug;
66/// use std::ops::Range;
67///
68/// #[derive(SafeDebug)]
69/// struct Employee {
70///     name: String,
71///     #[safe(true)]
72///     position: Position,
73/// }
74///
75/// #[derive(SafeDebug)]
76/// #[safe(true)]
77/// struct Position {
78///     id: i32,
79///     title: String,
80///     #[safe(false)]
81///     salary: Range<i32>,
82/// }
83///
84/// let employee = Employee {
85///     name: "Kelly Smith".to_string(),
86///     position: Position {
87///         id: 12,
88///         title: "Staff Engineer".to_string(),
89///         salary: 200_000..250_000,
90///     },
91/// };
92/// if cfg!(feature = "debug") {
93///     assert_eq!(format!("{employee:?}"), r#"Employee { name: "Kelly Smith", position: Position { id: 12, title: "Staff Engineer", salary: 200000..250000 } }"#);
94/// } else {
95///     assert_eq!(format!("{employee:?}"), r#"Employee { position: Position { id: 12, title: "Staff Engineer", .. }, .. }"#);
96/// }
97/// ```
98#[proc_macro_derive(SafeDebug, attributes(safe))]
99pub fn derive_safe_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
100    run_derive_macro(input, safe_debug::derive_safe_debug_impl)
101}