use crate::backoff::Backoff;
use crate::context::{Context, JournalEntry};
use crate::outcome::{Outcome, TaskError};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::future::Future;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Priority {
High,
#[default]
Normal,
Low,
}
impl Priority {
pub fn as_smallint(self) -> i16 {
match self {
Priority::High => 0,
Priority::Normal => 1,
Priority::Low => 2,
}
}
pub fn from_smallint(value: i16) -> Option<Priority> {
match value {
0 => Some(Priority::High),
1 => Some(Priority::Normal),
2 => Some(Priority::Low),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DedupKey(String);
impl DedupKey {
pub fn new(key: impl Into<String>) -> DedupKey {
DedupKey(key.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub(crate) fn into_string(self) -> String {
self.0
}
}
impl From<String> for DedupKey {
fn from(value: String) -> DedupKey {
DedupKey(value)
}
}
impl From<&str> for DedupKey {
fn from(value: &str) -> DedupKey {
DedupKey(value.to_owned())
}
}
impl From<ulid::Ulid> for DedupKey {
fn from(value: ulid::Ulid) -> DedupKey {
DedupKey(value.to_string())
}
}
impl From<u64> for DedupKey {
fn from(value: u64) -> DedupKey {
DedupKey(value.to_string())
}
}
impl From<i64> for DedupKey {
fn from(value: i64) -> DedupKey {
DedupKey(value.to_string())
}
}
pub struct Pending<T: Task> {
payload: T,
carry: T::Carry,
run_count: u32,
journal: Vec<JournalEntry>,
}
impl<T: Task> Pending<T> {
pub(crate) fn new(
payload: T,
carry: T::Carry,
run_count: u32,
journal: Vec<JournalEntry>,
) -> Pending<T> {
Pending {
payload,
carry,
run_count,
journal,
}
}
pub fn payload(&self) -> &T {
&self.payload
}
pub fn carry(&self) -> &T::Carry {
&self.carry
}
pub fn run_count(&self) -> u32 {
self.run_count
}
pub fn journal(&self) -> &[JournalEntry] {
&self.journal
}
}
pub enum Merge<T: Task> {
Keep,
Replace,
With {
task: T,
carry: T::Carry,
},
Independent,
}
pub trait Task: Serialize + DeserializeOwned + Send + Sync + 'static + Sized {
const KIND: &'static str;
type Carry: Serialize + DeserializeOwned + Default + Send;
fn priority(&self) -> Priority {
Priority::Normal
}
fn dedup_key(&self) -> Option<DedupKey> {
None
}
fn merge(&self, existing: &Pending<Self>) -> Merge<Self> {
let _ = existing;
Merge::Replace
}
fn backoff(&self) -> Option<Backoff> {
None
}
fn lease(&self) -> Option<Duration> {
None
}
}
pub trait Handler<S>: Task {
fn handle(
&self,
ctx: &mut Context<Self::Carry>,
state: &S,
) -> impl Future<Output = Result<Outcome, TaskError>> + Send;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn priority_smallint_round_trips_and_orders() {
for p in [Priority::High, Priority::Normal, Priority::Low] {
assert_eq!(Priority::from_smallint(p.as_smallint()), Some(p));
}
assert!(Priority::High.as_smallint() < Priority::Normal.as_smallint());
assert!(Priority::Normal.as_smallint() < Priority::Low.as_smallint());
assert_eq!(Priority::default(), Priority::Normal);
assert_eq!(Priority::from_smallint(3), None);
}
#[test]
fn dedup_key_from_various_sources() {
assert_eq!(DedupKey::from("abc").as_str(), "abc");
assert_eq!(DedupKey::from(String::from("x")).as_str(), "x");
assert_eq!(DedupKey::from(42u64).as_str(), "42");
let id = ulid::Ulid::nil();
assert_eq!(DedupKey::from(id).as_str(), id.to_string());
}
}