solverforge_macros/
lib.rs1use proc_macro::TokenStream;
4use quote::quote;
5use syn::parse::Parser;
6use syn::{parse_macro_input, Attribute, DeriveInput, Expr, ItemStruct, Lit, Meta};
7
8mod planning_entity;
9mod planning_solution;
10mod problem_fact;
11
12#[proc_macro_attribute]
13pub fn planning_entity(_attr: TokenStream, item: TokenStream) -> TokenStream {
14 let input = parse_macro_input!(item as ItemStruct);
15 let name = &input.ident;
16 let vis = &input.vis;
17 let generics = &input.generics;
18 let attrs: Vec<_> = input.attrs.iter().collect();
19 let fields = &input.fields;
20
21 let expanded = quote! {
22 #[derive(Clone, Debug, PartialEq, Eq, Hash, ::solverforge::PlanningEntityImpl)]
23 #(#attrs)*
24 #vis struct #name #generics #fields
25 };
26 expanded.into()
27}
28
29#[proc_macro_attribute]
30pub fn planning_solution(_attr: TokenStream, item: TokenStream) -> TokenStream {
31 let input = parse_macro_input!(item as ItemStruct);
32 let name = &input.ident;
33 let vis = &input.vis;
34 let generics = &input.generics;
35 let attrs: Vec<_> = input.attrs.iter().collect();
36 let fields = &input.fields;
37
38 let expanded = quote! {
39 #[derive(Clone, Debug, ::solverforge::PlanningSolutionImpl)]
40 #(#attrs)*
41 #vis struct #name #generics #fields
42 };
43 expanded.into()
44}
45
46#[proc_macro_attribute]
47pub fn problem_fact(_attr: TokenStream, item: TokenStream) -> TokenStream {
48 let input = parse_macro_input!(item as ItemStruct);
49 let name = &input.ident;
50 let vis = &input.vis;
51 let generics = &input.generics;
52 let attrs: Vec<_> = input.attrs.iter().collect();
53 let fields = &input.fields;
54
55 let expanded = quote! {
56 #[derive(Clone, Debug, PartialEq, Eq, ::solverforge::ProblemFactImpl)]
57 #(#attrs)*
58 #vis struct #name #generics #fields
59 };
60 expanded.into()
61}
62
63#[proc_macro_derive(
64 PlanningEntityImpl,
65 attributes(planning_id, planning_variable, planning_list_variable, planning_pin)
66)]
67pub fn derive_planning_entity(input: TokenStream) -> TokenStream {
68 let input = parse_macro_input!(input as DeriveInput);
69 planning_entity::expand_derive(input)
70 .unwrap_or_else(|e| e.to_compile_error())
71 .into()
72}
73
74#[proc_macro_derive(
75 PlanningSolutionImpl,
76 attributes(
77 planning_entity_collection,
78 problem_fact_collection,
79 planning_score,
80 value_range_provider
81 )
82)]
83pub fn derive_planning_solution(input: TokenStream) -> TokenStream {
84 let input = parse_macro_input!(input as DeriveInput);
85 planning_solution::expand_derive(input)
86 .unwrap_or_else(|e| e.to_compile_error())
87 .into()
88}
89
90#[proc_macro_derive(ProblemFactImpl, attributes(planning_id))]
91pub fn derive_problem_fact(input: TokenStream) -> TokenStream {
92 let input = parse_macro_input!(input as DeriveInput);
93 problem_fact::expand_derive(input)
94 .unwrap_or_else(|e| e.to_compile_error())
95 .into()
96}
97
98fn has_attribute(attrs: &[Attribute], name: &str) -> bool {
99 attrs.iter().any(|attr| attr.path().is_ident(name))
100}
101
102fn get_attribute<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> {
103 attrs.iter().find(|attr| attr.path().is_ident(name))
104}
105
106fn parse_attribute_bool(attr: &Attribute, key: &str) -> Option<bool> {
107 if let Meta::List(meta_list) = &attr.meta {
108 let parser = syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated;
109 if let Ok(nested) = parser.parse2(meta_list.tokens.clone()) {
110 for meta in nested {
111 if let Meta::NameValue(nv) = meta {
112 if nv.path.is_ident(key) {
113 if let Expr::Lit(expr_lit) = &nv.value {
114 if let Lit::Bool(lit_bool) = &expr_lit.lit {
115 return Some(lit_bool.value());
116 }
117 }
118 }
119 }
120 }
121 }
122 }
123 None
124}
125
126fn parse_attribute_string(attr: &Attribute, key: &str) -> Option<String> {
127 if let Meta::List(meta_list) = &attr.meta {
128 let parser = syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated;
129 if let Ok(nested) = parser.parse2(meta_list.tokens.clone()) {
130 for meta in nested {
131 if let Meta::NameValue(nv) = meta {
132 if nv.path.is_ident(key) {
133 if let Expr::Lit(expr_lit) = &nv.value {
134 if let Lit::Str(lit_str) = &expr_lit.lit {
135 return Some(lit_str.value());
136 }
137 }
138 }
139 }
140 }
141 }
142 }
143 None
144}