solverforge_derive/lib.rs
1//! Derive macros for SolverForge domain types.
2//!
3//! This crate provides derive macros for implementing `PlanningEntity` and
4//! `PlanningSolution` traits from `solverforge-core`.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use solverforge_derive::{PlanningEntity, PlanningSolution};
10//!
11//! #[derive(PlanningEntity, Clone)]
12//! pub struct Lesson {
13//! #[planning_id]
14//! pub id: String,
15//! pub subject: String,
16//! #[planning_variable(value_range_provider = "rooms")]
17//! pub room: Option<Room>,
18//! }
19//!
20//! #[derive(PlanningSolution, Clone)]
21//! #[constraint_provider = "define_constraints"]
22//! pub struct Timetable {
23//! #[problem_fact_collection]
24//! #[value_range_provider(id = "rooms")]
25//! pub rooms: Vec<Room>,
26//! #[planning_entity_collection]
27//! pub lessons: Vec<Lesson>,
28//! #[planning_score]
29//! pub score: Option<HardSoftScore>,
30//! }
31//! ```
32
33use proc_macro::TokenStream;
34
35mod domain_struct;
36mod entity;
37mod solution;
38
39/// Derive macro for implementing the `PlanningEntity` trait.
40///
41/// # Attributes
42///
43/// ## Field Attributes
44///
45/// - `#[planning_id]` - Marks the field as the unique identifier for this entity.
46/// Required for every planning entity.
47///
48/// - `#[planning_variable(value_range_provider = "...")]` - Marks the field as a
49/// planning variable. The solver will assign values from the specified value
50/// range provider.
51///
52/// - `#[planning_variable(value_range_provider = "...", allows_unassigned = true)]` -
53/// Allows the variable to remain unassigned (null/None).
54///
55/// # Example
56///
57/// ```ignore
58/// #[derive(PlanningEntity, Clone)]
59/// pub struct Lesson {
60/// #[planning_id]
61/// pub id: String,
62///
63/// pub subject: String,
64/// pub teacher: String,
65///
66/// #[planning_variable(value_range_provider = "timeslots")]
67/// pub timeslot: Option<Timeslot>,
68///
69/// #[planning_variable(value_range_provider = "rooms", allows_unassigned = true)]
70/// pub room: Option<Room>,
71/// }
72/// ```
73#[proc_macro_derive(
74 PlanningEntity,
75 attributes(
76 planning_id,
77 planning_variable,
78 planning_list_variable,
79 inverse_relation_shadow,
80 index_shadow,
81 next_element_shadow,
82 previous_element_shadow,
83 anchor_shadow,
84 cascading_update_shadow
85 )
86)]
87pub fn derive_planning_entity(input: TokenStream) -> TokenStream {
88 entity::derive_planning_entity_impl(input)
89}
90
91/// Derive macro for implementing the `PlanningSolution` trait.
92///
93/// # Attributes
94///
95/// ## Struct Attributes
96///
97/// - `#[constraint_provider = "function_name"]` - Specifies the function that
98/// provides constraints for this solution. The function must have signature
99/// `fn(ConstraintFactory) -> Vec<Constraint>`.
100///
101/// ## Field Attributes
102///
103/// - `#[problem_fact_collection]` - Marks a collection field as containing
104/// immutable problem facts.
105///
106/// - `#[problem_fact]` - Marks a single field as a problem fact.
107///
108/// - `#[planning_entity_collection]` - Marks a collection field as containing
109/// planning entities that will be modified during solving.
110///
111/// - `#[planning_entity]` - Marks a single field as a planning entity.
112///
113/// - `#[value_range_provider(id = "...")]` - Marks a field as providing values
114/// for planning variables with the matching `value_range_provider` reference.
115///
116/// - `#[planning_score]` - Marks the field that will hold the solution's score.
117///
118/// # Example
119///
120/// ```ignore
121/// #[derive(PlanningSolution, Clone)]
122/// #[constraint_provider = "define_constraints"]
123/// pub struct Timetable {
124/// #[problem_fact_collection]
125/// #[value_range_provider(id = "timeslots")]
126/// pub timeslots: Vec<Timeslot>,
127///
128/// #[problem_fact_collection]
129/// #[value_range_provider(id = "rooms")]
130/// pub rooms: Vec<Room>,
131///
132/// #[planning_entity_collection]
133/// pub lessons: Vec<Lesson>,
134///
135/// #[planning_score]
136/// pub score: Option<HardSoftScore>,
137/// }
138/// ```
139#[proc_macro_derive(
140 PlanningSolution,
141 attributes(
142 constraint_provider,
143 domain_structs,
144 problem_fact_collection,
145 problem_fact,
146 planning_entity_collection,
147 planning_entity,
148 value_range_provider,
149 planning_score
150 )
151)]
152pub fn derive_planning_solution(input: TokenStream) -> TokenStream {
153 solution::derive_planning_solution_impl(input)
154}
155
156/// Derive macro for implementing the `DomainStruct` trait.
157///
158/// Use this for nested structs (like `Location`) that are not planning entities
159/// but need their fields accessible in WASM memory for constraint evaluation
160/// and shadow variable computation.
161///
162/// # Example
163///
164/// ```ignore
165/// #[derive(DomainStruct, Clone)]
166/// struct Location {
167/// latitude: f64,
168/// longitude: f64,
169/// }
170/// ```
171///
172/// The struct's fields will be registered in the domain model, enabling
173/// expressions to access them via nested field access:
174///
175/// ```ignore
176/// Expression::FieldAccess {
177/// object: Box::new(visit_location_expr),
178/// class_name: "Location".to_string(),
179/// field_name: "latitude".to_string(),
180/// field_type: WasmFieldType::F64,
181/// }
182/// ```
183#[proc_macro_derive(DomainStruct)]
184pub fn derive_domain_struct(input: TokenStream) -> TokenStream {
185 domain_struct::derive_domain_struct_impl(input)
186}