use std::sync::Arc;
use super::admission::AdmissionPolicy;
use crate::TaskSpec;
#[derive(Clone)]
#[must_use]
pub struct ControllerSpec {
admission: AdmissionPolicy,
task_spec: TaskSpec,
slot: Option<Arc<str>>,
}
impl std::fmt::Debug for ControllerSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ControllerSpec")
.field("admission", &self.admission)
.field("task_spec", &self.task_spec)
.field("slot", &self.slot_name())
.finish()
}
}
impl ControllerSpec {
pub fn new(admission: AdmissionPolicy, task_spec: TaskSpec) -> Self {
Self {
admission,
task_spec,
slot: None,
}
}
#[must_use]
pub fn admission(&self) -> AdmissionPolicy {
self.admission
}
pub fn with_admission(mut self, admission: AdmissionPolicy) -> Self {
self.admission = admission;
self
}
#[must_use = "use the returned task specification"]
pub fn task_spec(&self) -> &TaskSpec {
&self.task_spec
}
pub fn with_task_spec(mut self, task_spec: TaskSpec) -> Self {
self.task_spec = task_spec;
self
}
pub fn into_task_spec(self) -> TaskSpec {
self.task_spec
}
pub fn with_slot(mut self, slot: impl Into<Arc<str>>) -> Self {
self.slot = Some(slot.into());
self
}
pub fn without_slot(mut self) -> Self {
self.slot = None;
self
}
#[must_use]
pub fn slot_name(&self) -> &str {
self.slot
.as_deref()
.unwrap_or_else(|| self.task_spec.name())
}
#[must_use]
pub fn slot_override(&self) -> Option<&str> {
self.slot.as_deref()
}
pub fn queue(task_spec: TaskSpec) -> Self {
Self::new(AdmissionPolicy::Queue, task_spec)
}
pub fn replace(task_spec: TaskSpec) -> Self {
Self::new(AdmissionPolicy::Replace, task_spec)
}
pub fn drop_if_running(task_spec: TaskSpec) -> Self {
Self::new(AdmissionPolicy::DropIfRunning, task_spec)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TaskContext;
use crate::{BackoffPolicy, RestartPolicy, TaskFn, TaskRef};
fn make_spec(name: &str) -> TaskSpec {
let task: TaskRef = TaskFn::arc(name, |_ctx: TaskContext| async { Ok(()) });
TaskSpec::new(task, RestartPolicy::Never, BackoffPolicy::default(), None)
}
#[test]
fn convenience_constructors_set_correct_policy() {
for (spec, expected) in [
(
ControllerSpec::queue(make_spec("queue")),
AdmissionPolicy::Queue,
),
(
ControllerSpec::replace(make_spec("replace")),
AdmissionPolicy::Replace,
),
(
ControllerSpec::drop_if_running(make_spec("drop")),
AdmissionPolicy::DropIfRunning,
),
] {
assert_eq!(spec.admission(), expected);
}
}
#[test]
fn slot_name_falls_back_to_task_name() {
let cs = ControllerSpec::queue(make_spec("my-slot"));
assert_eq!(cs.slot_name(), "my-slot");
}
#[test]
fn slot_name_uses_explicit_slot() {
let cs = ControllerSpec::queue(make_spec("runner-web-7")).with_slot("web");
assert_eq!(cs.slot_name(), "web");
assert_eq!(cs.slot_override(), Some("web"));
assert_eq!(cs.task_spec().name(), "runner-web-7");
}
}