//! Bookkeeping to make sure only one long-running operation is being executed
//! at a time.

pub(crate) type Cause = String;

/// A single-item queue that allows callers to request an operation to
/// be performed later.
///
/// ```ignore
/// let queue = OpQueue::default();
///
/// // Request work to be done.
/// queue.request_op("user pushed a button", ());
///
/// // In a later iteration of the server loop, we start the work.
/// if let Some((_cause, ())) = queue.should_start_op() {
///     dbg!("Some slow operation here");
/// }
///
/// // In an even later iteration of the server loop, we can see that the work
/// // was completed.
/// if !queue.op_in_progress() {
///     dbg!("Work has been done!");
/// }
/// ```
#[derive(Debug)]
pub(crate) struct OpQueue<Args = (), Output = ()> {
    op_requested: Option<(Cause, Args)>,
    op_in_progress: bool,
    last_op_result: Option<Output>,
}

impl<Args, Output> Default for OpQueue<Args, Output> {
    fn default() -> Self {
        Self { op_requested: None, op_in_progress: false, last_op_result: None }
    }
}

impl<Args: std::fmt::Debug, Output> OpQueue<Args, Output> {
    /// Request an operation to start.
    pub(crate) fn request_op(&mut self, reason: Cause, args: Args) {
        self.op_requested = Some((reason, args));
    }

    /// If there was an operation requested, mark this queue as
    /// started and return the request arguments.
    pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> {
        if self.op_in_progress {
            return None;
        }
        self.op_in_progress = self.op_requested.is_some();
        self.op_requested.take()
    }

    /// Mark an operation as completed.
    pub(crate) fn op_completed(&mut self, result: Output) {
        assert!(self.op_in_progress);
        self.op_in_progress = false;
        self.last_op_result = Some(result);
    }

    /// Get the result of the last operation.
    pub(crate) fn last_op_result(&self) -> Option<&Output> {
        self.last_op_result.as_ref()
    }

    // Is there an operation that has started, but hasn't yet finished?
    pub(crate) fn op_in_progress(&self) -> bool {
        self.op_in_progress
    }

    // Has an operation been requested?
    pub(crate) fn op_requested(&self) -> bool {
        self.op_requested.is_some()
    }
}
