assemble-core 0.2.0

The core crate of the assemble-rs package
Documentation
use crate::__export::TaskId;

use crate::project::error::ProjectResult;
use crate::project::shared::SharedProject;
use crate::task::{
    BuildableTask, FullTask, HasTaskId, ResolveExecutable, TaskHandle, TaskOrdering,
};
use crate::{Project, Task};
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct AnyTaskHandle {
    id: TaskId,
    only_current: bool,
    handle: Arc<Mutex<AnyTaskHandleInner>>,
}

impl Debug for AnyTaskHandle {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "AnyTaskHandle {:?}", self.id)
    }
}

impl HasTaskId for AnyTaskHandle {
    fn task_id(&self) -> TaskId {
        self.id.clone()
    }
}

impl BuildableTask for AnyTaskHandle {
    fn ordering(&self) -> Vec<TaskOrdering> {
        self.with_inner(|inner| inner.buildable().ordering())
    }
}

impl AnyTaskHandle {
    pub fn new<T: Task + Send + Sync + 'static>(provider: TaskHandle<T>) -> Self {
        Self {
            id: provider.task_id(),
            only_current: T::only_in_current(),
            handle: Arc::new(Mutex::new(AnyTaskHandleInner::new(provider))),
        }
    }

    fn with_inner<R, F: FnOnce(&mut AnyTaskHandleInner) -> R>(&self, func: F) -> R {
        let mut guard = self.handle.lock().expect("couldn't get handle");
        (func)(&mut *guard)
    }

    pub fn is<T: Task + Send + Sync + 'static>(&self) -> bool {
        self.with_inner(|handle| handle.is::<T>())
    }

    pub fn as_type<T: Task + Send + Sync + 'static>(&self) -> Option<TaskHandle<T>> {
        if !self.is::<T>() {
            return None;
        }
        self.with_inner(|handle| handle.as_type::<T>())
    }

    fn executable(&mut self, project: &SharedProject) -> ProjectResult<Box<dyn FullTask>> {
        self.with_inner(|p| p.resolvable().get_executable(project))
    }

    pub fn resolve(&mut self, project: &Project) -> ProjectResult<Box<dyn FullTask>> {
        self.executable(&project.as_shared())
    }

    pub fn resolve_shared(&mut self, project: &SharedProject) -> ProjectResult<Box<dyn FullTask>> {
        self.executable(project)
    }

    pub(crate) fn only_current(&self) -> bool {
        self.only_current
    }
}

struct AnyTaskHandleInner {
    task_type: TypeId,
    as_buildable: Box<dyn BuildableTask + Send>,
    as_resolvable: Box<dyn ResolveExecutable + Send>,
    as_any: Box<dyn Any + Send>,
}

impl Debug for AnyTaskHandleInner {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "TaskConnection {{ ... }}")
    }
}

impl AnyTaskHandleInner {
    fn new<T: Task + Send + Sync + 'static>(provider: TaskHandle<T>) -> Self {
        let task_type = TypeId::of::<T>();
        let as_buildable: Box<dyn BuildableTask + Send> = Box::new(provider.clone());
        let as_resolvable: Box<dyn ResolveExecutable + Send> = Box::new(provider.clone());
        let as_any: Box<dyn Any + Send> = Box::new(provider);
        Self {
            task_type,
            as_buildable,
            as_resolvable,
            as_any,
        }
    }

    fn is<T: Task + Send + Sync + 'static>(&self) -> bool {
        self.task_type == TypeId::of::<T>()
    }

    fn as_type<T: Task + Send + Sync + 'static>(&self) -> Option<TaskHandle<T>> {
        if !self.is::<T>() {
            return None;
        }
        self.as_any.downcast_ref::<TaskHandle<T>>().cloned()
    }

    fn buildable(&self) -> &dyn BuildableTask {
        self.as_buildable.as_ref()
    }

    fn resolvable(&mut self) -> &mut dyn ResolveExecutable {
        self.as_resolvable.as_mut()
    }

    fn executable(&mut self, project: &SharedProject) -> ProjectResult<Box<dyn FullTask>> {
        self.resolvable().get_executable(project)
    }
}

assert_impl_all!(AnyTaskHandleInner: Send);