cognis_core/wrappers/
fallback.rs1use std::marker::PhantomData;
4
5use async_trait::async_trait;
6
7use crate::runnable::{Runnable, RunnableConfig};
8use crate::Result;
9
10pub struct Fallback<P, F, I, O> {
14 primary: P,
15 fallback: F,
16 _phantom: PhantomData<fn(I) -> O>,
17}
18
19impl<P, F, I, O> Fallback<P, F, I, O>
20where
21 P: Runnable<I, O>,
22 F: Runnable<I, O>,
23 I: Clone + Send + 'static,
24 O: Send + 'static,
25{
26 pub fn new(primary: P, fallback: F) -> Self {
28 Self {
29 primary,
30 fallback,
31 _phantom: PhantomData,
32 }
33 }
34}
35
36#[async_trait]
37impl<P, F, I, O> Runnable<I, O> for Fallback<P, F, I, O>
38where
39 P: Runnable<I, O>,
40 F: Runnable<I, O>,
41 I: Clone + Send + 'static,
42 O: Send + 'static,
43{
44 async fn invoke(&self, input: I, config: RunnableConfig) -> Result<O> {
45 match self.primary.invoke(input.clone(), config.clone()).await {
46 Ok(v) => Ok(v),
47 Err(_) => self.fallback.invoke(input, config).await,
48 }
49 }
50 fn name(&self) -> &str {
51 "Fallback"
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::CognisError;
59
60 struct Err1;
61 struct Ok99;
62
63 #[async_trait]
64 impl Runnable<u32, u32> for Err1 {
65 async fn invoke(&self, _: u32, _: RunnableConfig) -> Result<u32> {
66 Err(CognisError::Internal("nope".into()))
67 }
68 }
69
70 #[async_trait]
71 impl Runnable<u32, u32> for Ok99 {
72 async fn invoke(&self, _: u32, _: RunnableConfig) -> Result<u32> {
73 Ok(99)
74 }
75 }
76
77 #[tokio::test]
78 async fn falls_through_on_error() {
79 let f = Fallback::new(Err1, Ok99);
80 assert_eq!(f.invoke(0, RunnableConfig::default()).await.unwrap(), 99);
81 }
82
83 #[tokio::test]
84 async fn primary_wins_on_success() {
85 let f = Fallback::new(Ok99, Err1);
86 assert_eq!(f.invoke(0, RunnableConfig::default()).await.unwrap(), 99);
87 }
88}