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