pdk-classy 1.9.1-alpha.2

PDK Classy
Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use crate::reactor::http::HttpReactor;
use crate::{
    event::{
        Event, EventKind, Exchange, ExchangeComplete, RequestBody, RequestHeaders, RequestTrailers,
        ResponseBody, ResponseHeaders, ResponseTrailers, Start,
    },
    BoxFuture,
};

pub trait ExchangeEvent: Event {
    fn into_dynamic_exchange(exchange: Exchange<Self>) -> DynamicExchange;

    fn get(dynamic_exchange: &DynamicExchange) -> Option<&Exchange<Self>>;

    fn insert(dynamic_exchange: &mut DynamicExchange, exchange: Exchange<Self>) -> &Exchange<Self>;

    fn wait_for(
        dynamic_exchange: &mut DynamicExchange,
        buffering: bool,
    ) -> BoxFuture<Option<Exchange<Self>>>;
}

macro_rules! impl_exchange_event {
    () => {};

    ($event:ident $(,$before:ident)*) => {
        impl ExchangeEvent for $event {
            fn into_dynamic_exchange(exchange: Exchange<Self>) -> DynamicExchange {
                DynamicExchange::$event(exchange)
            }

            fn get(dynamic_exchange: &DynamicExchange) -> Option<&Exchange<Self>> {
                if let DynamicExchange::$event(e) = dynamic_exchange {
                    Some(e)
                } else {
                    None
                }
            }

            fn insert(dynamic_exchange: &mut DynamicExchange, exchange: Exchange<Self>) -> &Exchange<Self> {
                *dynamic_exchange = DynamicExchange::$event(exchange);
                if let DynamicExchange::$event(e) = dynamic_exchange {
                    e
                } else {
                    unreachable!()
                }
            }

            #[allow(unused)]
            fn wait_for(dynamic_exchange: &mut DynamicExchange, buffering: bool) -> BoxFuture<Option<Exchange<Self>>> {
                Box::pin(async move {
                    let mut current = DynamicExchange::Taken;
                    std::mem::swap(dynamic_exchange, &mut current);
                    match current {
                        $(
                            DynamicExchange::$before(e) => return Some(e.wait_for_event_buffering::<Self>(buffering).await),
                        )*
                        DynamicExchange::$event(e) => return Some(e),
                        _ => {}
                    }
                    std::mem::swap(dynamic_exchange, &mut current);
                    None
                })
            }
        }

        impl_exchange_event!($($before),*);
    };
}

macro_rules! exchange_event {
    ([] $($reversed:tt)*) => {
        impl_exchange_event!($($reversed),*); // base case
    };

    ([$first:tt $($rest:tt)*] $($reversed:tt)*) => {
        exchange_event!([$($rest)*] $first $($reversed)*);  // recursion
    };
}

macro_rules! send_response {
    (request, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
        $exchange.send_response($status_code, $headers, $body);
        true
    }};

    (response_headers, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
        $exchange.send_response($status_code, $headers, $body);
        true
    }};

    ($dir:ident, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
        *($this) = DynamicExchange::$event($exchange);
        false
    }};
}

macro_rules! dynamic_exchange_impl {
    ($($dir:ident $event:ident)*) => {
        pub enum DynamicExchange {
            Taken,
            Consumed,
            $($event(Exchange<$event>),)*
        }

        impl DynamicExchange {
            pub fn current_event(&self) -> Option<EventKind> {
                match self {
                    Self::Taken => unreachable!(),
                    Self::Consumed => None,
                    $(
                        Self::$event(_) => Some($event::kind()),
                    )*
                }
            }

            pub fn get_reactor(&self) -> Option<&HttpReactor> {
                match self {
                    Self::Taken => unreachable!(),
                    Self::Consumed => None,
                    $(
                        Self::$event(e) => Some(&e.reactor),
                    )*
                }
            }

            pub fn send_response(
                &mut self,
                status_code: u32,
                headers: Vec<(&str, &str)>,
                body: Option<&[u8]>
            ) -> bool {
                let mut current = DynamicExchange::Consumed;
                std::mem::swap(self, &mut current);
                match current {
                    Self::Taken => unreachable!(),
                    Self::Consumed => false,
                    $(
                        Self::$event(exchange) =>
                            send_response!(
                                $dir,
                                $event,
                                self,
                                exchange,
                                status_code,
                                headers,
                                body
                            ),
                    )*
                }
            }
        }

        exchange_event!([$($event)*]);
    };
}

dynamic_exchange_impl! {
    start Start
    request RequestHeaders
    request RequestBody
    request RequestTrailers
    response_headers ResponseHeaders
    response ResponseBody
    response ResponseTrailers
    done ExchangeComplete
}

impl DynamicExchange {
    pub fn new<E: ExchangeEvent>(exchange: Exchange<E>) -> Self {
        E::into_dynamic_exchange(exchange)
    }

    pub fn get<E: ExchangeEvent>(&self) -> Option<&Exchange<E>> {
        E::get(self)
    }

    pub async fn wait_for_event<'a, E: ExchangeEvent + 'a>(
        &'a mut self,
    ) -> Option<&'a Exchange<E>> {
        self.wait_for_event_buffering(true).await
    }

    pub async fn wait_for_event_buffering<'a, E: ExchangeEvent + 'a>(
        &'a mut self,
        buffering: bool,
    ) -> Option<&'a Exchange<E>> {
        E::wait_for(self, buffering)
            .await
            .map(move |e| E::insert(self, e))
    }
}