Skip to main content

solverforge_macros/
lib.rs

1// Macros for SolverForge domain models.
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, DeriveInput, ItemStruct};
6
7mod attr_parse;
8mod list_registry;
9mod planning_entity;
10mod planning_solution;
11mod problem_fact;
12
13use attr_parse::{has_serde_flag, parse_solution_flags};
14
15#[proc_macro_attribute]
16pub fn planning_entity(attr: TokenStream, item: TokenStream) -> TokenStream {
17    let has_serde = has_serde_flag(attr);
18    let input = parse_macro_input!(item as ItemStruct);
19    let name = &input.ident;
20    let vis = &input.vis;
21    let generics = &input.generics;
22    let attrs: Vec<_> = input.attrs.iter().collect();
23    let fields = &input.fields;
24
25    let serde_derives = if has_serde {
26        quote! { ::serde::Serialize, ::serde::Deserialize, }
27    } else {
28        quote! {}
29    };
30
31    let expanded = quote! {
32        #[derive(Clone, Debug, PartialEq, Eq, Hash, #serde_derives ::solverforge::PlanningEntityImpl)]
33        #(#attrs)*
34        #vis struct #name #generics #fields
35    };
36    expanded.into()
37}
38
39#[proc_macro_attribute]
40pub fn planning_solution(attr: TokenStream, item: TokenStream) -> TokenStream {
41    let (has_serde, constraints_path, config_path) = parse_solution_flags(attr);
42    let input = parse_macro_input!(item as ItemStruct);
43    let name = &input.ident;
44    let vis = &input.vis;
45    let generics = &input.generics;
46    let attrs: Vec<_> = input.attrs.iter().collect();
47    let fields = &input.fields;
48
49    let serde_derives = if has_serde {
50        quote! { ::serde::Serialize, ::serde::Deserialize, }
51    } else {
52        quote! {}
53    };
54
55    let constraints_attr =
56        constraints_path.map(|p| quote! { #[solverforge_constraints_path = #p] });
57    let config_attr = config_path.map(|p| quote! { #[solverforge_config_path = #p] });
58
59    let expanded = quote! {
60        #[derive(Clone, Debug, #serde_derives ::solverforge::PlanningSolutionImpl)]
61        #constraints_attr
62        #config_attr
63        #(#attrs)*
64        #vis struct #name #generics #fields
65    };
66    expanded.into()
67}
68
69#[proc_macro_attribute]
70pub fn problem_fact(attr: TokenStream, item: TokenStream) -> TokenStream {
71    let has_serde = has_serde_flag(attr);
72    let input = parse_macro_input!(item as ItemStruct);
73    let name = &input.ident;
74    let vis = &input.vis;
75    let generics = &input.generics;
76    let attrs: Vec<_> = input.attrs.iter().collect();
77    let fields = &input.fields;
78
79    let serde_derives = if has_serde {
80        quote! { ::serde::Serialize, ::serde::Deserialize, }
81    } else {
82        quote! {}
83    };
84
85    let expanded = quote! {
86        #[derive(Clone, Debug, PartialEq, Eq, #serde_derives ::solverforge::ProblemFactImpl)]
87        #(#attrs)*
88        #vis struct #name #generics #fields
89    };
90    expanded.into()
91}
92
93#[proc_macro_derive(
94    PlanningEntityImpl,
95    attributes(
96        planning_id,
97        planning_variable,
98        planning_list_variable,
99        planning_pin,
100        inverse_relation_shadow_variable,
101        previous_element_shadow_variable,
102        next_element_shadow_variable,
103        cascading_update_shadow_variable
104    )
105)]
106pub fn derive_planning_entity(input: TokenStream) -> TokenStream {
107    let input = parse_macro_input!(input as DeriveInput);
108    planning_entity::expand_derive(input)
109        .unwrap_or_else(|e| e.to_compile_error())
110        .into()
111}
112
113#[proc_macro_derive(
114    PlanningSolutionImpl,
115    attributes(
116        planning_entity_collection,
117        planning_list_element_collection,
118        problem_fact_collection,
119        planning_score,
120        value_range_provider,
121        shadow_variable_updates,
122        solverforge_constraints_path,
123        solverforge_config_path
124    )
125)]
126pub fn derive_planning_solution(input: TokenStream) -> TokenStream {
127    let input = parse_macro_input!(input as DeriveInput);
128    planning_solution::expand_derive(input)
129        .unwrap_or_else(|e| e.to_compile_error())
130        .into()
131}
132
133#[proc_macro_derive(ProblemFactImpl, attributes(planning_id))]
134pub fn derive_problem_fact(input: TokenStream) -> TokenStream {
135    let input = parse_macro_input!(input as DeriveInput);
136    problem_fact::expand_derive(input)
137        .unwrap_or_else(|e| e.to_compile_error())
138        .into()
139}