mahler-core 0.21.3

An automated job orchestration library that builds and executes dynamic workflows
Documentation
use super::context::Context;
use super::description::Description;
use super::handler::Handler;
use super::Task;
use std::cmp::Ordering;

#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Clone)]
/// The operation a Job is applicable to
pub enum Operation {
    /// Tells the `Worker` the job is not to be automatically selected for any operation
    ///
    /// `None` jobs can still be used as part of compound tasks
    None,
    /// Tells the `Worker` the job is assignable to any operation
    Any,
    /// Tells the `Worker` the job is assignable to a [remove](https://datatracker.ietf.org/doc/html/rfc6902#section-4.2) operation
    Delete,
    /// Tells the `Worker` the job is assignable to an [add](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1) operation
    Create,
    /// Tells the `Worker` the job is assignable to a [replace](https://datatracker.ietf.org/doc/html/rfc6902#section-4.3) operation
    Update,
}

/// Encodes a generic repeatable system operation
///
/// A Job is the generic type used by the [Worker](`crate::worker::Worker`) to create the specific
/// tasks that form a Workflow generated by the planning step.
///
/// A `Job` is created from a [`Handler`](`super::Handler`). It is assigned to an [`Operation`] and
/// may define a specific priority. During planning, the operation is used to determine the
/// applicability of a job to a state change, and the priority it is used (amongst other factors) to
/// decide what jobs will be tried first.
#[derive(Debug, Clone)]
pub struct Job {
    operation: Operation,
    task: Task,
    priority: u8,
}

impl Job {
    pub(crate) fn new(task: Task) -> Self {
        Job {
            operation: Operation::Update,
            task,
            // all tasks have the lowest priority
            priority: 0,
        }
    }

    /// Get the unique identifier for the job
    ///
    /// The id is determined from the [`Handler`] type name
    pub fn id(&self) -> &str {
        self.task.id()
    }

    /// Get the operation assigned to the job
    pub fn operation(&self) -> &Operation {
        &self.operation
    }

    // Get the job priority
    pub fn priority(&self) -> u8 {
        self.priority
    }

    /// Set job priority.
    ///
    /// This defines search priority when looking for jobs
    /// the higher the value, the higher the priority
    pub fn with_priority(mut self, priority: u8) -> Self {
        self.priority = priority;
        self
    }

    /// Set the job operation
    ///
    /// This is for internal use only. Users can set the operation by using the constructor
    /// functions [`create`], [`update`], [`delete`], etc.
    fn with_operation(mut self, operation: Operation) -> Self {
        self.operation = operation;
        self
    }

    /// Set a human readable description for the Job
    ///
    /// A [description](`Description`) is any function that accepts zero or more context
    /// extractors and returns a String. A context extractor is a type that implements
    /// [FromContext](`super::FromContext`)
    ///
    /// The description will be used to describe the tasks used by the crate
    /// [observability features](`crate#observability`) and for human readable logs
    ///
    /// ```rust
    /// use mahler::task::{Job, Description, update};
    /// use mahler::extract::Args;
    ///
    /// fn foo() {}
    ///
    /// let job = update(foo).with_description(|Args(foo): Args<String>| format!("a job for {foo}!"));
    /// ```
    pub fn with_description<D, T>(mut self, description: D) -> Self
    where
        D: Description<T>,
    {
        self.task = self.task.with_description(description);
        self
    }

    /// Create a new task from the Job and the given Context
    pub(crate) fn new_task(&self, context: Context) -> Task {
        self.task.clone().with_context(context)
    }
}

macro_rules! define_job {
    ($func_name:ident, $operation:expr) => {
        #[doc = concat!("Create a new `Job` for the [`", stringify!($operation), "`] operation.")]
        pub fn $func_name<H, T, O, I>(handler: H) -> Job
        where
            H: Handler<T, O, I>,
            I: 'static,
        {
            Job::new(handler.into_task()).with_operation($operation)
        }
    };
}

define_job!(create, Operation::Create);
define_job!(update, Operation::Update);
define_job!(delete, Operation::Delete);
define_job!(any, Operation::Any);
define_job!(none, Operation::None);

impl PartialEq for Job {
    fn eq(&self, other: &Self) -> bool {
        self.task.id() == other.task.id()
            && self.operation == other.operation
            && self.priority == other.priority
    }
}
impl Eq for Job {}

impl PartialOrd for Job {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Job {
    fn cmp(&self, other: &Self) -> Ordering {
        self.task.id().cmp(other.task.id())
    }
}