pdk_classy/
plugin.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! A wrapper to bundle the logic that will be part of the policy.
6
7use std::marker::PhantomData;
8
9use crate::proxy_wasm::traits::RootContext;
10
11use crate::{
12    entrypoint::Entrypoint,
13    event::{Event, RequestHeaders, ResponseHeaders},
14    middleware::{EventHandler, EventHandlerPush, EventHandlerStack},
15};
16
17#[derive(Default)]
18/// A collection of event handlers and an entrypoint that will transform into the root context.
19pub struct Plugin<E = (), T = ()> {
20    event_handlers: EventHandlerStack,
21    entrypoint: E,
22    _types: PhantomData<T>,
23}
24
25impl EventHandlerPush<RequestHeaders> for Plugin {
26    fn push<H>(&mut self, handler: H)
27    where
28        H: EventHandler<RequestHeaders> + 'static,
29    {
30        self.event_handlers.push(handler)
31    }
32}
33
34impl EventHandlerPush<ResponseHeaders> for Plugin {
35    fn push<H>(&mut self, handler: H)
36    where
37        H: EventHandler<ResponseHeaders> + 'static,
38    {
39        self.event_handlers.push(handler)
40    }
41}
42
43impl Plugin {
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    pub fn event_handler<H, S>(mut self, handler: H) -> Self
49    where
50        S: Event,
51        H: EventHandler<S> + 'static,
52        Self: EventHandlerPush<S>,
53    {
54        self.push(handler);
55        self
56    }
57
58    pub fn entrypoint<C, T, E>(self, entrypoint: E) -> Plugin<E, (C, T)>
59    where
60        E: Entrypoint<C, T>,
61    {
62        Plugin {
63            event_handlers: self.event_handlers,
64            entrypoint,
65            _types: PhantomData,
66        }
67    }
68}
69
70impl<E, C, T> Plugin<E, (C, T)>
71where
72    E: Entrypoint<C, T>,
73{
74    pub fn create_root_context(self, context_id: u32) -> Box<dyn RootContext> {
75        self.entrypoint
76            .create_root_context(self.event_handlers, context_id)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use crate::{
83        bootstrap::Launcher,
84        client::HttpClient,
85        event::{EventData, Exchange, RequestHeaders, ResponseHeaders},
86        Configuration, Plugin,
87    };
88
89    #[test]
90    fn test_configure() {
91        Plugin::new()
92            .entrypoint(|_: Launcher| async {})
93            .create_root_context(1);
94    }
95
96    #[test]
97    fn test_filter() {
98        Plugin::new()
99            .entrypoint(|_: Exchange<RequestHeaders>| async {})
100            .create_root_context(1);
101    }
102
103    #[test]
104    fn test_configure_with_dependencies() {
105        Plugin::new()
106            .entrypoint(|_: Launcher, _: Configuration| async {})
107            .create_root_context(1);
108    }
109
110    #[test]
111    fn test_filter_with_dependencies() {
112        Plugin::new()
113            .entrypoint(|_: Launcher, _: HttpClient| async {})
114            .create_root_context(1);
115    }
116
117    #[test]
118    fn test_configure_with_event_handlers() {
119        Plugin::new()
120            .event_handler(|_: &EventData<RequestHeaders>| Ok(()))
121            .event_handler(|_: &EventData<ResponseHeaders>| Ok(()))
122            .entrypoint(|_: Launcher| async {})
123            .create_root_context(1);
124    }
125}