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, solver_toml_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    let solver_toml_attr =
59        solver_toml_path.map(|p| quote! { #[solverforge_solver_toml_path = #p] });
60
61    let expanded = quote! {
62        #[derive(Clone, Debug, #serde_derives ::solverforge::PlanningSolutionImpl)]
63        #constraints_attr
64        #config_attr
65        #solver_toml_attr
66        #(#attrs)*
67        #vis struct #name #generics #fields
68    };
69    expanded.into()
70}
71
72#[proc_macro_attribute]
73pub fn problem_fact(attr: TokenStream, item: TokenStream) -> TokenStream {
74    let has_serde = has_serde_flag(attr);
75    let input = parse_macro_input!(item as ItemStruct);
76    let name = &input.ident;
77    let vis = &input.vis;
78    let generics = &input.generics;
79    let attrs: Vec<_> = input.attrs.iter().collect();
80    let fields = &input.fields;
81
82    let serde_derives = if has_serde {
83        quote! { ::serde::Serialize, ::serde::Deserialize, }
84    } else {
85        quote! {}
86    };
87
88    let expanded = quote! {
89        #[derive(Clone, Debug, PartialEq, Eq, #serde_derives ::solverforge::ProblemFactImpl)]
90        #(#attrs)*
91        #vis struct #name #generics #fields
92    };
93    expanded.into()
94}
95
96#[proc_macro_derive(
97    PlanningEntityImpl,
98    attributes(
99        planning_id,
100        planning_variable,
101        planning_list_variable,
102        planning_pin,
103        inverse_relation_shadow_variable,
104        previous_element_shadow_variable,
105        next_element_shadow_variable,
106        cascading_update_shadow_variable
107    )
108)]
109pub fn derive_planning_entity(input: TokenStream) -> TokenStream {
110    let input = parse_macro_input!(input as DeriveInput);
111    planning_entity::expand_derive(input)
112        .unwrap_or_else(|e| e.to_compile_error())
113        .into()
114}
115
116#[proc_macro_derive(
117    PlanningSolutionImpl,
118    attributes(
119        planning_entity_collection,
120        planning_list_element_collection,
121        problem_fact_collection,
122        planning_score,
123        value_range_provider,
124        shadow_variable_updates,
125        solverforge_constraints_path,
126        solverforge_config_path,
127        solverforge_solver_toml_path
128    )
129)]
130pub fn derive_planning_solution(input: TokenStream) -> TokenStream {
131    let input = parse_macro_input!(input as DeriveInput);
132    planning_solution::expand_derive(input)
133        .unwrap_or_else(|e| e.to_compile_error())
134        .into()
135}
136
137#[proc_macro_derive(ProblemFactImpl, attributes(planning_id))]
138pub fn derive_problem_fact(input: TokenStream) -> TokenStream {
139    let input = parse_macro_input!(input as DeriveInput);
140    problem_fact::expand_derive(input)
141        .unwrap_or_else(|e| e.to_compile_error())
142        .into()
143}