solverforge-core 0.8.2

Core types and traits for SolverForge constraint solver
Documentation
/* Entity reference types for dynamic entity access.

These types enable the solver to work with entities at runtime without
knowing their concrete types at compile time.
*/

use std::any::Any;
use std::fmt::Debug;

/* A reference to a planning entity with its index in the solution.

This struct provides a way to identify and access entities during solving
without needing to know the concrete entity type.
*/
#[derive(Debug, Clone)]
pub struct EntityRef {
    // Index of this entity in its collection.
    pub index: usize,
    // Name of the entity type.
    pub type_name: &'static str,
    // Name of the collection field in the solution.
    pub collection_field: &'static str,
}

impl EntityRef {
    pub fn new(index: usize, type_name: &'static str, collection_field: &'static str) -> Self {
        Self {
            index,
            type_name,
            collection_field,
        }
    }
}

/// Trait for extracting entities from a planning solution.
///
/// This trait is implemented by closures/functions that can extract
/// entity references from a solution of a specific type.
pub trait EntityExtractor: Send + Sync {
    fn count(&self, solution: &dyn Any) -> Option<usize>;

    fn get<'a>(&self, solution: &'a dyn Any, index: usize) -> Option<&'a dyn Any>;

    fn get_mut<'a>(&self, solution: &'a mut dyn Any, index: usize) -> Option<&'a mut dyn Any>;

    // Returns an iterator over entity references.
    fn entity_refs(&self, solution: &dyn Any) -> Vec<EntityRef>;

    // Clone this extractor.
    fn clone_box(&self) -> Box<dyn EntityExtractor>;

    /* Clones an entity as a boxed value for insertion into the constraint session.

    This is used for incremental scoring where entities need to be inserted
    into the BAVET session as owned, type-erased values.
    */
    fn clone_entity_boxed(
        &self,
        solution: &dyn Any,
        index: usize,
    ) -> Option<Box<dyn Any + Send + Sync>>;

    fn entity_type_id(&self) -> std::any::TypeId;
}

impl Clone for Box<dyn EntityExtractor> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}

/// A concrete entity extractor for a specific solution and entity type.
///
/// # Type Parameters
/// * `S` - The solution type
/// * `E` - The entity type
pub struct EntityCollectionExtractor<S, E> {
    // Name of the entity type.
    type_name: &'static str,
    // Name of the collection field in the solution.
    collection_field: &'static str,
    // Function to get the entity collection from a solution.
    get_collection: fn(&S) -> &Vec<E>,
    // Function to get the mutable entity collection from a solution.
    get_collection_mut: fn(&mut S) -> &mut Vec<E>,
}

impl<S, E> EntityCollectionExtractor<S, E>
where
    S: 'static,
    E: 'static,
{
    pub fn new(
        type_name: &'static str,
        collection_field: &'static str,
        get_collection: fn(&S) -> &Vec<E>,
        get_collection_mut: fn(&mut S) -> &mut Vec<E>,
    ) -> Self {
        Self {
            type_name,
            collection_field,
            get_collection,
            get_collection_mut,
        }
    }
}

impl<S, E> EntityExtractor for EntityCollectionExtractor<S, E>
where
    S: Send + Sync + 'static,
    E: Clone + Send + Sync + 'static,
{
    fn count(&self, solution: &dyn Any) -> Option<usize> {
        let solution = solution.downcast_ref::<S>()?;
        Some((self.get_collection)(solution).len())
    }

    fn get<'a>(&self, solution: &'a dyn Any, index: usize) -> Option<&'a dyn Any> {
        let solution = solution.downcast_ref::<S>()?;
        let collection = (self.get_collection)(solution);
        collection.get(index).map(|e| e as &dyn Any)
    }

    fn get_mut<'a>(&self, solution: &'a mut dyn Any, index: usize) -> Option<&'a mut dyn Any> {
        let solution = solution.downcast_mut::<S>()?;
        let collection = (self.get_collection_mut)(solution);
        collection.get_mut(index).map(|e| e as &mut dyn Any)
    }

    fn entity_refs(&self, solution: &dyn Any) -> Vec<EntityRef> {
        let Some(solution) = solution.downcast_ref::<S>() else {
            return Vec::new();
        };
        let collection = (self.get_collection)(solution);
        (0..collection.len())
            .map(|i| EntityRef::new(i, self.type_name, self.collection_field))
            .collect()
    }

    fn clone_box(&self) -> Box<dyn EntityExtractor> {
        Box::new(Self {
            type_name: self.type_name,
            collection_field: self.collection_field,
            get_collection: self.get_collection,
            get_collection_mut: self.get_collection_mut,
        })
    }

    fn clone_entity_boxed(
        &self,
        solution: &dyn Any,
        index: usize,
    ) -> Option<Box<dyn Any + Send + Sync>> {
        let solution = solution.downcast_ref::<S>()?;
        let collection = (self.get_collection)(solution);
        let entity = collection.get(index)?;
        Some(Box::new(entity.clone()) as Box<dyn Any + Send + Sync>)
    }

    fn entity_type_id(&self) -> std::any::TypeId {
        std::any::TypeId::of::<E>()
    }
}

impl<S, E> Debug for EntityCollectionExtractor<S, E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("EntityCollectionExtractor")
            .field("type_name", &self.type_name)
            .field("collection_field", &self.collection_field)
            .finish()
    }
}