pdk_classy/hl/
context.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5use std::{
6    cell::{Cell, RefCell},
7    rc::Rc,
8};
9
10use crate::{
11    context,
12    extract::{
13        context::FilterContext, extractability, AlreadyExtracted, Exclusive, FromContext,
14        FromContextOnce,
15    },
16    BoxFuture,
17};
18
19use super::{
20    dynamic_exchange::DynamicExchange, entity::EntityState, request::InvalidRequestState,
21    response::InvalidResponseState, IntoBodyState, RequestBodyState, RequestData,
22    RequestHeadersState, RequestState, ResponseBodyState, ResponseHeadersState, ResponseState,
23};
24
25use super::{IntoBodyStreamState, RequestBodyStreamState, ResponseBodyStreamState};
26
27pub struct RequestContext {
28    parent: Rc<FilterContext>,
29    exchange: Rc<RefCell<DynamicExchange>>,
30}
31
32impl RequestContext {
33    pub(super) fn new(parent: Rc<FilterContext>, exchange: Rc<RefCell<DynamicExchange>>) -> Self {
34        Self { parent, exchange }
35    }
36
37    fn parent(&self) -> &FilterContext {
38        &self.parent
39    }
40}
41
42context!(RequestContext => FilterContext {RequestContext::parent});
43
44impl FromContextOnce<RequestContext> for RequestState {
45    type Error = InvalidRequestState;
46
47    type Future<'c> = BoxFuture<'c, Result<Self, Self::Error>>;
48
49    fn from_context_once(context: Exclusive<RequestContext>) -> Self::Future<'_> {
50        Box::pin(RequestState::new(context.exchange.clone()))
51    }
52}
53
54impl FromContextOnce<RequestContext> for RequestHeadersState {
55    type Error = InvalidRequestState;
56
57    type Future<'c> = BoxFuture<'c, Result<Self, Self::Error>>;
58
59    fn from_context_once(context: Exclusive<RequestContext>) -> Self::Future<'_> {
60        Box::pin(async {
61            Ok(RequestState::from_context_once(context)
62                .await?
63                .into_headers_state()
64                .await)
65        })
66    }
67}
68
69impl FromContextOnce<RequestContext> for RequestBodyState {
70    type Error = InvalidRequestState;
71
72    type Future<'c> = BoxFuture<'c, Result<Self, Self::Error>>;
73
74    fn from_context_once(context: Exclusive<RequestContext>) -> Self::Future<'_> {
75        Box::pin(async {
76            Ok(RequestState::from_context_once(context)
77                .await?
78                .into_body_state()
79                .await)
80        })
81    }
82}
83
84impl FromContextOnce<RequestContext> for RequestBodyStreamState {
85    type Error = InvalidRequestState;
86
87    type Future<'c> = BoxFuture<'c, Result<Self, Self::Error>>;
88
89    fn from_context_once(context: Exclusive<RequestContext>) -> Self::Future<'_> {
90        Box::pin(async {
91            Ok(RequestState::from_context_once(context)
92                .await?
93                .into_body_stream_state()
94                .await)
95        })
96    }
97}
98
99pub struct ResponseContext<D> {
100    parent: Rc<FilterContext>,
101    exchange: Rc<RefCell<DynamicExchange>>,
102    request_data: Cell<Option<RequestData<D>>>,
103}
104
105impl<D> ResponseContext<D> {
106    pub(super) fn new(
107        parent: Rc<FilterContext>,
108        exchange: Rc<RefCell<DynamicExchange>>,
109        request_data: RequestData<D>,
110    ) -> Self {
111        Self {
112            parent,
113            exchange,
114            request_data: Cell::new(Some(request_data)),
115        }
116    }
117
118    fn parent(&self) -> &FilterContext {
119        &self.parent
120    }
121}
122
123context!(<D> ResponseContext<D> => FilterContext {ResponseContext::parent});
124
125impl<D> FromContextOnce<ResponseContext<D>> for ResponseState {
126    type Error = InvalidResponseState;
127
128    type Future<'c>
129        = BoxFuture<'c, Result<Self, Self::Error>>
130    where
131        D: 'c;
132
133    fn from_context_once(context: Exclusive<ResponseContext<D>>) -> Self::Future<'_> {
134        Box::pin(ResponseState::new(context.exchange.clone()))
135    }
136}
137
138impl<D> FromContextOnce<ResponseContext<D>> for ResponseHeadersState {
139    type Error = InvalidResponseState;
140
141    type Future<'c>
142        = BoxFuture<'c, Result<Self, Self::Error>>
143    where
144        D: 'c;
145
146    fn from_context_once(context: Exclusive<ResponseContext<D>>) -> Self::Future<'_> {
147        Box::pin(async {
148            Ok(ResponseState::from_context_once(context)
149                .await?
150                .into_headers_state()
151                .await)
152        })
153    }
154}
155
156impl<D> FromContextOnce<ResponseContext<D>> for ResponseBodyState {
157    type Error = InvalidResponseState;
158
159    type Future<'c>
160        = BoxFuture<'c, Result<Self, Self::Error>>
161    where
162        D: 'c;
163
164    fn from_context_once(context: Exclusive<ResponseContext<D>>) -> Self::Future<'_> {
165        Box::pin(async {
166            Ok(ResponseState::from_context_once(context)
167                .await?
168                .into_body_state()
169                .await)
170        })
171    }
172}
173
174impl<D> FromContextOnce<ResponseContext<D>> for ResponseBodyStreamState {
175    type Error = InvalidResponseState;
176
177    type Future<'c>
178        = BoxFuture<'c, Result<Self, Self::Error>>
179    where
180        D: 'c;
181
182    fn from_context_once(context: Exclusive<ResponseContext<D>>) -> Self::Future<'_> {
183        Box::pin(async {
184            Ok(ResponseState::from_context_once(context)
185                .await?
186                .into_body_stream_state()
187                .await)
188        })
189    }
190}
191
192impl<D> FromContext<ResponseContext<D>, extractability::Transitive> for RequestData<D>
193where
194    D: 'static,
195{
196    type Error = AlreadyExtracted<RequestData<D>>;
197
198    fn from_context(context: &ResponseContext<D>) -> Result<Self, Self::Error> {
199        context
200            .request_data
201            .take()
202            .ok_or_else(AlreadyExtracted::default)
203    }
204}