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