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}