fn generate_support_impl(model: &ModelMetadata) -> Result<TokenStream> {
let solution_module = &model.solution.module_ident;
let solution_ident = &model.solution.ident;
let solution_path = quote! { #solution_module::#solution_ident };
let mut descriptor_helpers = Vec::new();
let mut descriptor_attachments = Vec::new();
let mut runtime_helpers = Vec::new();
let mut runtime_attachments = Vec::new();
let mut list_runtime_helpers = Vec::new();
let mut list_runtime_attachments = Vec::new();
let mut list_owner_match_arms = Vec::new();
let mut validation_checks = Vec::new();
let shadow_methods = generate_shadow_methods(model)?;
let scalar_groups_impl = generate_scalar_groups_impl(model);
for collection in model
.solution
.collections
.iter()
.filter(|collection| collection.descriptor_index.is_some())
{
let entity = model
.entities
.get(canonical_type_name(&model.aliases, &collection.type_name))
.expect("entity collection should have been validated");
let descriptor_index = collection.descriptor_index.unwrap();
let entity_field = &collection.field_ident;
let entity_accessor = format_ident!("__solverforge_entity_{}", entity_field);
let entity_type_name = &entity.type_name;
let solution_field_name = &collection.field_name;
validation_checks.push(quote! {
{
let entity_descriptor = descriptor
.entity_descriptors
.get(#descriptor_index)
.expect("planning_model! entity descriptor missing");
assert_eq!(
entity_descriptor.solution_field,
#solution_field_name,
"planning_model! entity descriptor field mismatch",
);
assert_eq!(
entity_descriptor.type_name,
#entity_type_name,
"planning_model! entity descriptor type mismatch",
);
}
});
for variable in &entity.scalar_variables {
let variable_name = &variable.field_name;
validation_checks.push(quote! {
{
let entity_descriptor = descriptor
.entity_descriptors
.get(#descriptor_index)
.expect("planning_model! entity descriptor missing");
let _ = entity_descriptor
.variable_descriptors
.iter()
.find(|variable| {
variable.name == #variable_name
&& variable.usize_getter.is_some()
&& variable.usize_setter.is_some()
})
.expect("planning_model! scalar variable descriptor missing");
}
});
if let Some(path) = &variable.hooks.candidate_values {
let helper = format_ident!(
"__solverforge_descriptor_candidate_values_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_candidate_values_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for scalar candidate values");
#path(solution, entity_index, variable_index)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.candidate_values = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
#path(solution, entity_index, variable_index)
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_candidate_values(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.nearby_value_candidates {
let helper = format_ident!(
"__solverforge_descriptor_nearby_value_candidates_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_nearby_value_candidates_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for nearby value candidates");
#path(solution, entity_index, variable_index)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.nearby_value_candidates = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
#path(solution, entity_index, variable_index)
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_nearby_value_candidates(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.nearby_entity_candidates {
let helper = format_ident!(
"__solverforge_descriptor_nearby_entity_candidates_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_nearby_entity_candidates_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for nearby entity candidates");
#path(solution, entity_index, variable_index)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.nearby_entity_candidates = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
variable_index: usize,
) -> &[usize] {
#path(solution, entity_index, variable_index)
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_nearby_entity_candidates(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.nearby_value_distance_meter {
let helper = format_ident!(
"__solverforge_descriptor_nearby_value_distance_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_nearby_value_distance_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
value: usize,
) -> f64 {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for nearby value distance meter");
let entity = #solution_path::#entity_accessor(solution, entity_index);
#path(solution, entity, value)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.nearby_value_distance_meter = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
_variable_index: usize,
value: usize,
) -> ::core::option::Option<f64> {
let entity = #solution_path::#entity_accessor(solution, entity_index);
::core::option::Option::Some(#path(solution, entity, value))
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_nearby_value_distance_meter(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.nearby_entity_distance_meter {
let helper = format_ident!(
"__solverforge_descriptor_nearby_entity_distance_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_nearby_entity_distance_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
left_entity_index: usize,
right_entity_index: usize,
) -> f64 {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for nearby entity distance meter");
let left = #solution_path::#entity_accessor(solution, left_entity_index);
let right = #solution_path::#entity_accessor(solution, right_entity_index);
#path(solution, left, right)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.nearby_entity_distance_meter = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
left_entity_index: usize,
right_entity_index: usize,
_variable_index: usize,
) -> ::core::option::Option<f64> {
let left = #solution_path::#entity_accessor(solution, left_entity_index);
let right = #solution_path::#entity_accessor(solution, right_entity_index);
::core::option::Option::Some(#path(solution, left, right))
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_nearby_entity_distance_meter(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.construction_entity_order_key {
let helper = format_ident!(
"__solverforge_descriptor_construction_entity_order_key_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_construction_entity_order_key_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
) -> i64 {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for construction entity order key");
let entity = #solution_path::#entity_accessor(solution, entity_index);
#path(solution, entity)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.construction_entity_order_key = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
_variable_index: usize,
) -> ::core::option::Option<i64> {
let entity = #solution_path::#entity_accessor(solution, entity_index);
::core::option::Option::Some(#path(solution, entity))
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_construction_entity_order_key(#runtime_helper);
}
});
}
if let Some(path) = &variable.hooks.construction_value_order_key {
let helper = format_ident!(
"__solverforge_descriptor_construction_value_order_key_{}_{}",
entity_field,
variable.field_name
);
let runtime_helper = format_ident!(
"__solverforge_runtime_construction_value_order_key_{}_{}",
entity_field,
variable.field_name
);
descriptor_helpers.push(quote! {
fn #helper(
solution: &dyn ::std::any::Any,
entity_index: usize,
value: usize,
) -> i64 {
let solution = solution
.downcast_ref::<#solution_path>()
.expect("solution type mismatch for construction value order key");
let entity = #solution_path::#entity_accessor(solution, entity_index);
#path(solution, entity, value)
}
});
descriptor_attachments.push(quote! {
attach_scalar_variable_hook(
descriptor,
#descriptor_index,
#variable_name,
|variable| {
variable.construction_value_order_key = ::core::option::Option::Some(#helper);
},
);
});
runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
entity_index: usize,
_variable_index: usize,
value: usize,
) -> ::core::option::Option<i64> {
let entity = #solution_path::#entity_accessor(solution, entity_index);
::core::option::Option::Some(#path(solution, entity, value))
}
});
runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_construction_value_order_key(#runtime_helper);
}
});
}
}
if let (Some(variable_name), Some(path)) =
(&entity.list_variable_name, &entity.list_element_owner_fn)
{
let runtime_helper = format_ident!(
"__solverforge_runtime_list_element_owner_{}",
entity_field
);
list_runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
element: &usize,
) -> ::core::option::Option<usize> {
#path(solution, *element)
}
});
list_runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_element_owner_fn(::core::option::Option::Some(#runtime_helper));
}
});
list_owner_match_arms.push(quote! {
(#entity_type_name, #variable_name) => #path(solution, *element),
});
}
if let (Some(variable_name), Some(path)) = (
&entity.list_variable_name,
&entity.list_construction_element_order_key,
) {
let runtime_helper = format_ident!(
"__solverforge_runtime_list_construction_element_order_{}",
entity_field
);
list_runtime_helpers.push(quote! {
fn #runtime_helper(
solution: &#solution_path,
element: usize,
) -> i64 {
#path(solution, element)
}
});
list_runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_construction_element_order_key(
::core::option::Option::Some(#runtime_helper),
);
}
});
}
if let (
Some(variable_name),
Some(duration_path),
Some(successors_path),
) = (
&entity.list_variable_name,
&entity.list_precedence_duration_fn,
&entity.list_precedence_successors_fn,
) {
let duration_helper = format_ident!(
"__solverforge_runtime_list_precedence_duration_{}",
entity_field
);
let successors_helper = format_ident!(
"__solverforge_runtime_list_precedence_successors_{}",
entity_field
);
list_runtime_helpers.push(quote! {
fn #duration_helper(
solution: &#solution_path,
element: usize,
) -> usize {
#duration_path(solution, element)
}
fn #successors_helper(
solution: &#solution_path,
element: usize,
out: &mut ::std::vec::Vec<usize>,
) {
#successors_path(solution, element, out)
}
});
list_runtime_attachments.push(quote! {
if slot.descriptor_index == #descriptor_index
&& slot.variable_name == #variable_name
{
slot = slot.with_precedence_hooks(
::core::option::Option::Some(#duration_helper),
::core::option::Option::Some(#successors_helper),
);
}
});
}
}
Ok(quote! {
impl ::solverforge::__internal::PlanningModelSupport for #solution_path {
fn attach_descriptor_hooks(
descriptor: &mut ::solverforge::__internal::SolutionDescriptor,
) {
fn attach_scalar_variable_hook(
descriptor: &mut ::solverforge::__internal::SolutionDescriptor,
descriptor_index: usize,
variable_name: &'static str,
attach: impl FnOnce(&mut ::solverforge::__internal::VariableDescriptor),
) {
let entity_descriptor = descriptor
.entity_descriptors
.get_mut(descriptor_index)
.expect("planning_model! entity descriptor missing for scalar hook");
let variable_descriptor = entity_descriptor
.variable_descriptors
.iter_mut()
.find(|variable| {
variable.name == variable_name
&& variable.usize_getter.is_some()
&& variable.usize_setter.is_some()
})
.expect("planning_model! scalar hook target variable missing");
attach(variable_descriptor);
}
#(#descriptor_helpers)*
#(#descriptor_attachments)*
}
fn attach_runtime_scalar_hooks(
mut slot: ::solverforge::__internal::ScalarVariableSlot<Self>,
) -> ::solverforge::__internal::ScalarVariableSlot<Self> {
#(#runtime_helpers)*
#(#runtime_attachments)*
slot
}
fn attach_runtime_list_hooks<DM, IDM>(
mut slot: ::solverforge::__internal::ListVariableSlot<Self, usize, DM, IDM>,
) -> ::solverforge::__internal::ListVariableSlot<Self, usize, DM, IDM> {
#(#list_runtime_helpers)*
#(#list_runtime_attachments)*
slot
}
fn list_element_owner(
entity_type_name: &'static str,
variable_name: &'static str,
solution: &Self,
element: &usize,
) -> ::core::option::Option<usize> {
match (entity_type_name, variable_name) {
#(#list_owner_match_arms)*
_ => ::core::option::Option::None,
}
}
fn attach_scalar_groups(
scalar_variables: &[::solverforge::__internal::ScalarVariableSlot<Self>],
) -> ::std::vec::Vec<::solverforge::__internal::ScalarGroupBinding<Self>> {
#scalar_groups_impl
}
fn validate_model(descriptor: &::solverforge::__internal::SolutionDescriptor) {
#(#validation_checks)*
}
#shadow_methods
}
})
}