hyperactor_config_macros/
lib.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9//! Procedural macros for hyperactor_config.
10
11use proc_macro::TokenStream;
12use quote::quote;
13use syn::DeriveInput;
14use syn::parse_macro_input;
15
16/// Derive the [`hyperactor_config::attrs::AttrValue`] trait for a struct or enum.
17///
18/// This macro generates an implementation that uses the type's `ToString` and `FromStr`
19/// implementations for the `display` and `parse` methods respectively.
20///
21/// The type must already implement the required super-traits:
22/// `Named + Sized + Serialize + DeserializeOwned + Send + Sync + Clone + 'static`
23/// as well as `ToString` and `FromStr`.
24///
25/// # Example
26///
27/// ```ignore
28/// #[derive(AttrValue, Named, Serialize, Deserialize, Clone)]
29/// struct MyCustomType {
30///     value: String,
31/// }
32///
33/// impl std::fmt::Display for MyCustomType {
34///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35///         write!(f, "{}", self.value)
36///     }
37/// }
38///
39/// impl std::str::FromStr for MyCustomType {
40///     type Err = std::io::Error;
41///
42///     fn from_str(s: &str) -> Result<Self, Self::Err> {
43///         Ok(MyCustomType {
44///             value: s.to_string(),
45///         })
46///     }
47/// }
48/// ```
49#[proc_macro_derive(AttrValue)]
50pub fn derive_attr_value(input: TokenStream) -> TokenStream {
51    let input = parse_macro_input!(input as DeriveInput);
52    let name = &input.ident;
53
54    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
55
56    TokenStream::from(quote! {
57        impl #impl_generics hyperactor_config::attrs::AttrValue for #name #ty_generics #where_clause {
58            fn display(&self) -> String {
59                self.to_string()
60            }
61
62            fn parse(value: &str) -> Result<Self, anyhow::Error> {
63                value.parse().map_err(|e| anyhow::anyhow!("failed to parse {}: {}", stringify!(#name), e))
64            }
65        }
66    })
67}