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
#![allow(unused)]
mod shared;
use proc_macro::TokenStream;
use quote::quote;
use shared::{get_segment_from_type, tmp};
use syn::{parse_macro_input, Field, Fields, FieldsNamed, ItemStruct, PathSegment};
/// Реализация трейта [`Default`] с указанием значений по умолчанию для каждого поля структуры.
///
/// Макрос поддерживает работу с именованными и неименнованными структурами.
///
/// Чтобы указать дефолтное значение поля необходимо использовать атрибут `default_field` и следующий синтаксис с ним:
/// ```
/// # use std_reset_macros::Default;
/// # #[derive(Debug, Default, PartialEq)]
/// # struct Wrap(
/// #[default_field("10_i32")]
/// # i32
/// # );
/// ```
/// выражение, которое будет подставляться в поле как его дефолтное значение указывается внутри скобок и описан как строковый литерал.
///
/// P.s. Выражение необходимо записывать в виде строки, потому что`rust` требует
/// указывать в атрибутах только литерал. Поэтому под капотом
/// [`Default`] преобразует [cтроковый лиетрал](https://doc.rust-lang.org/reference/tokens.html?highlight=literal#string-literals) в выржаение.
///
/// Например для того, чтобы указать дефолтное значение для поля с типом `&str` необходимо написать следующее:
/// ```
/// # use std_reset_macros::Default;
/// # #[derive(Debug, Default, PartialEq)]
/// # struct Wrap(
/// #[default_field("\"crab\"")]
/// # &'static str
/// # );
/// ```
///
/// # Примеры
/// - Структура с _именнованными_ полями:
///
/// ```
/// use std_reset_macros::Default;
///
/// #[derive(Debug, Default, PartialEq)]
/// struct User {
/// #[default_field("String::from(\"Ferris\")")]
/// name: String,
/// #[default_field("String::from(\"123FerF\")")]
/// password: String,
/// #[default_field("8_9999_999_999")]
/// number: u128,
/// email: Option<String>,
/// #[default_field("Some(32)")]
/// age: Option<u32>,
/// }
///
/// assert_eq!(
/// User::default(),
/// User {
/// name: "Ferris".to_string(),
/// password: "123FerF".to_string(),
/// number: 8_9999_999_999,
/// email: None,
/// age: Some(32),
/// }
/// );
/// ```
/// - Структура с _неименнованными_ полями:
/// ```
/// # use std_reset_macros::Default;
/// #
/// #[derive(Debug, Default, PartialEq)]
/// struct User(
/// #[default_field("String::from(\"Ferris\")")] String,
/// #[default_field("String::from(\"123FerF\")")] String,
/// #[default_field("8_9999_999_999")] u128,
/// Option<String>,
/// #[default_field("Some(32)")] Option<u32>,
/// );
///
/// assert_eq!(
/// User::default(),
/// User(
/// "Ferris".to_string(),
/// "123FerF".to_string(),
/// 8_9999_999_999,
/// None,
/// Some(32),
/// )
/// );
/// ```
#[proc_macro_derive(Default, attributes(default_field))]
pub fn default_macro_derive(input: TokenStream) -> TokenStream {
let ItemStruct { ident, fields, .. } = parse_macro_input!(input);
let get_default_value = |field: &Field| {
let Field { ty, .. } = field;
field
.attrs
.iter()
.find_map(|attr| {
if attr.path.is_ident("default_field") {
attr.parse_args::<syn::LitStr>()
.map(|default_value| {
default_value
.value()
.parse::<proc_macro2::TokenStream>()
.expect("Failed to parse tokens")
})
.ok()
} else {
None
}
})
.unwrap_or_else(|| {
quote! { <#ty as std::default::Default>::default() }
})
};
let body = match fields {
Fields::Named(_) => {
let field_defaults = fields.iter().map(|field| {
let Field { ident, .. } = field;
let default_value = get_default_value(field);
quote! {
#ident: #default_value
}
});
quote! {
{
#(#field_defaults),*
}
}
}
Fields::Unnamed(_) => {
let field_defaults = fields.iter().map(|field| get_default_value(field));
quote! {
(#(#field_defaults),*)
}
}
_ => panic!("Struct must have named or unnamed fields"),
};
quote! {
impl std::default::Default for #ident {
fn default() -> Self {
Self #body
}
}
}
.into()
}