solverforge-macros 0.12.0

Derive macros for SolverForge constraint solver
Documentation
macro_rules! __solverforge_list_setup {
    ($fields:ident, $entity_collections:ident, $source_len_arms:ident, $source_element_arms:ident, $owner_helpers:ident) => {
    let $entity_collections: Vec<_> = $fields
        .iter()
        .filter(|field| has_attribute(&field.attrs, "planning_entity_collection"))
        .enumerate()
        .filter_map(|(descriptor_index, field)| {
            let field_ident = field.ident.as_ref()?;
            let entity_type = extract_collection_inner_type(&field.ty)?;
            Some((descriptor_index, field_ident, entity_type))
        })
        .collect();

    if $entity_collections.is_empty() {
        return TokenStream::new();
    }

    let $source_len_arms: Vec<_> = $fields
        .iter()
        .filter(|field| {
            has_attribute(&field.attrs, "problem_fact_collection")
                || has_attribute(&field.attrs, "planning_entity_collection")
                || has_attribute(&field.attrs, "planning_list_element_collection")
        })
        .filter_map(|field| {
            let field_ident = field.ident.as_ref()?;
            let field_name = field_ident.to_string();
            Some(quote! { ::core::option::Option::Some(#field_name) => s.#field_ident.len(), })
        })
        .collect();

    let $source_element_arms: Vec<_> = $fields
        .iter()
        .filter(|field| {
            has_attribute(&field.attrs, "problem_fact_collection")
                || has_attribute(&field.attrs, "planning_entity_collection")
                || has_attribute(&field.attrs, "planning_list_element_collection")
        })
        .filter_map(|field| {
            let field_ident = field.ident.as_ref()?;
            let field_name = field_ident.to_string();
            let value_expr = if has_attribute(&field.attrs, "planning_list_element_collection") {
                quote! { s.#field_ident[idx] }
            } else {
                quote! { idx }
            };
            Some(quote! { ::core::option::Option::Some(#field_name) => { #value_expr } })
        })
        .collect();

    let $owner_helpers: Vec<_> = $entity_collections
        .iter()
        .map(|(_, field_ident, entity_type)| {
            let field_name = field_ident.to_string();
            let list_trait =
                quote! { <#entity_type as ::solverforge::__internal::ListVariableEntity<Self>> };
            let list_len_ident = format_ident!("__solverforge_list_len_{}", field_name);
            let list_remove_ident = format_ident!("__solverforge_list_remove_{}", field_name);
            let list_insert_ident = format_ident!("__solverforge_list_insert_{}", field_name);
            let list_get_ident = format_ident!("__solverforge_list_get_{}", field_name);
            let list_set_ident = format_ident!("__solverforge_list_set_{}", field_name);
            let list_reverse_ident = format_ident!("__solverforge_list_reverse_{}", field_name);
            let sublist_remove_ident =
                format_ident!("__solverforge_sublist_remove_{}", field_name);
            let sublist_insert_ident =
                format_ident!("__solverforge_sublist_insert_{}", field_name);
            let ruin_remove_ident = format_ident!("__solverforge_ruin_remove_{}", field_name);
            let ruin_insert_ident = format_ident!("__solverforge_ruin_insert_{}", field_name);
            let list_remove_for_construction_ident =
                format_ident!("__solverforge_list_remove_for_construction_{}", field_name);
            let index_to_element_ident =
                format_ident!("__solverforge_index_to_element_{}", field_name);
            let element_count_ident = format_ident!("__solverforge_element_count_{}", field_name);
            let assigned_elements_ident =
                format_ident!("__solverforge_assigned_elements_{}", field_name);
            let n_entities_ident = format_ident!("__solverforge_n_entities_{}", field_name);
            let assign_element_ident =
                format_ident!("__solverforge_assign_element_{}", field_name);

            quote! {
                #[inline]
                fn #list_len_ident(s: &Self, entity_idx: usize) -> usize {
                    s.#field_ident
                        .get(entity_idx)
                        .map_or(0, |entity| #list_trait::list_field(entity).len())
                }

                #[inline]
                fn #list_remove_ident(
                    s: &mut Self,
                    entity_idx: usize,
                    pos: usize,
                ) -> ::core::option::Option<usize> {
                    s.#field_ident
                        .get_mut(entity_idx)
                        .map(|entity| #list_trait::list_field_mut(entity).remove(pos))
                }

                #[inline]
                fn #list_insert_ident(s: &mut Self, entity_idx: usize, pos: usize, val: usize) {
                    if let Some(entity) = s.#field_ident.get_mut(entity_idx) {
                        #list_trait::list_field_mut(entity).insert(pos, val);
                    }
                }

                #[inline]
                fn #list_get_ident(
                    s: &Self,
                    entity_idx: usize,
                    pos: usize,
                ) -> ::core::option::Option<usize> {
                    s.#field_ident
                        .get(entity_idx)
                        .and_then(|entity| #list_trait::list_field(entity).get(pos).copied())
                }

                #[inline]
                fn #list_set_ident(s: &mut Self, entity_idx: usize, pos: usize, val: usize) {
                    if let Some(entity) = s.#field_ident.get_mut(entity_idx) {
                        let list = #list_trait::list_field_mut(entity);
                        if pos < list.len() {
                            list[pos] = val;
                        }
                    }
                }

                #[inline]
                fn #list_reverse_ident(
                    s: &mut Self,
                    entity_idx: usize,
                    start: usize,
                    end: usize,
                ) {
                    if let Some(entity) = s.#field_ident.get_mut(entity_idx) {
                        #list_trait::list_field_mut(entity)[start..end].reverse();
                    }
                }

                #[inline]
                fn #sublist_remove_ident(
                    s: &mut Self,
                    entity_idx: usize,
                    start: usize,
                    end: usize,
                ) -> Vec<usize> {
                    s.#field_ident
                        .get_mut(entity_idx)
                        .map(|entity| #list_trait::list_field_mut(entity).drain(start..end).collect())
                        .unwrap_or_default()
                }

                #[inline]
                fn #sublist_insert_ident(
                    s: &mut Self,
                    entity_idx: usize,
                    pos: usize,
                    items: Vec<usize>,
                ) {
                    if let Some(entity) = s.#field_ident.get_mut(entity_idx) {
                        let list = #list_trait::list_field_mut(entity);
                        for (offset, item) in items.into_iter().enumerate() {
                            list.insert(pos + offset, item);
                        }
                    }
                }

                #[inline]
                fn #ruin_remove_ident(s: &mut Self, entity_idx: usize, pos: usize) -> usize {
                    #list_trait::list_field_mut(&mut s.#field_ident[entity_idx]).remove(pos)
                }

                #[inline]
                fn #ruin_insert_ident(s: &mut Self, entity_idx: usize, pos: usize, val: usize) {
                    #list_trait::list_field_mut(&mut s.#field_ident[entity_idx]).insert(pos, val);
                }

                #[inline]
                fn #list_remove_for_construction_ident(
                    s: &mut Self,
                    entity_idx: usize,
                    pos: usize,
                ) -> usize {
                    #list_trait::list_field_mut(&mut s.#field_ident[entity_idx]).remove(pos)
                }

                #[inline]
                fn #index_to_element_ident(s: &Self, idx: usize) -> usize {
                    let element_count = Self::#element_count_ident(s);
                    if idx >= element_count {
                        panic!(
                            "list element index {} is out of bounds for {} elements",
                            idx,
                            element_count
                        );
                    }

                    match #list_trait::LIST_ELEMENT_SOURCE {
                        #(#$source_element_arms)*
                        ::core::option::Option::Some(source) => {
                            panic!(
                                "list source field `{}` was not found on the planning solution",
                                source
                            );
                        }
                        ::core::option::Option::None => idx,
                    }
                }

                #[inline]
                fn #element_count_ident(s: &Self) -> usize {
                    match #list_trait::LIST_ELEMENT_SOURCE {
                        #(#$source_len_arms)*
                        ::core::option::Option::Some(source) => {
                            panic!(
                                "list source field `{}` was not found on the planning solution",
                                source
                            );
                        }
                        ::core::option::Option::None => 0,
                    }
                }

                #[inline]
                fn #assigned_elements_ident(s: &Self) -> Vec<usize> {
                    s.#field_ident
                        .iter()
                        .flat_map(|entity| #list_trait::list_field(entity).iter().copied())
                        .collect()
                }

                #[inline]
                fn #n_entities_ident(s: &Self) -> usize {
                    s.#field_ident.len()
                }

                #[inline]
                fn #assign_element_ident(s: &mut Self, entity_idx: usize, elem: usize) {
                    if let Some(entity) = s.#field_ident.get_mut(entity_idx) {
                        #list_trait::list_field_mut(entity).push(elem);
                    }
                }
            }
        })
        .collect();

    };
}