Skip to main content

camel_processor/
set_header.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use tower::Service;
6
7use camel_api::{CamelError, Exchange, Value};
8
9/// A processor that sets a header on the exchange's input message.
10#[derive(Clone)]
11pub struct SetHeader<P> {
12    inner: P,
13    key: String,
14    value: Value,
15}
16
17impl<P> SetHeader<P> {
18    /// Create a new SetHeader processor that adds the given header.
19    pub fn new(inner: P, key: impl Into<String>, value: impl Into<Value>) -> Self {
20        Self {
21            inner,
22            key: key.into(),
23            value: value.into(),
24        }
25    }
26}
27
28/// A Tower Layer that wraps an inner service with a [`SetHeader`].
29#[derive(Clone)]
30pub struct SetHeaderLayer {
31    key: String,
32    value: Value,
33}
34
35impl SetHeaderLayer {
36    pub fn new(key: impl Into<String>, value: impl Into<Value>) -> Self {
37        Self {
38            key: key.into(),
39            value: value.into(),
40        }
41    }
42}
43
44impl<S> tower::Layer<S> for SetHeaderLayer {
45    type Service = SetHeader<S>;
46
47    fn layer(&self, inner: S) -> Self::Service {
48        SetHeader::new(inner, self.key.clone(), self.value.clone())
49    }
50}
51
52impl<P> Service<Exchange> for SetHeader<P>
53where
54    P: Service<Exchange, Response = Exchange, Error = CamelError> + Clone + Send + 'static,
55    P::Future: Send,
56{
57    type Response = Exchange;
58    type Error = CamelError;
59    type Future = Pin<Box<dyn Future<Output = Result<Exchange, CamelError>> + Send>>;
60
61    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
62        self.inner.poll_ready(cx)
63    }
64
65    fn call(&mut self, mut exchange: Exchange) -> Self::Future {
66        exchange
67            .input
68            .headers
69            .insert(self.key.clone(), self.value.clone());
70        let fut = self.inner.call(exchange);
71        Box::pin(fut)
72    }
73}
74
75/// A processor that sets a header on the exchange's input message,
76/// but ONLY if the header is not already present (if-absent semantics).
77#[derive(Clone)]
78pub struct SetHeaderIfAbsent<P> {
79    inner: P,
80    key: String,
81    value: Value,
82}
83
84impl<P> SetHeaderIfAbsent<P> {
85    /// Create a new SetHeaderIfAbsent processor that adds the given header
86    /// only when it is not already present.
87    pub fn new(inner: P, key: impl Into<String>, value: impl Into<Value>) -> Self {
88        Self {
89            inner,
90            key: key.into(),
91            value: value.into(),
92        }
93    }
94}
95
96impl<P> Service<Exchange> for SetHeaderIfAbsent<P>
97where
98    P: Service<Exchange, Response = Exchange, Error = CamelError> + Clone + Send + 'static,
99    P::Future: Send,
100{
101    type Response = Exchange;
102    type Error = CamelError;
103    type Future = Pin<Box<dyn Future<Output = Result<Exchange, CamelError>> + Send>>;
104
105    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
106        self.inner.poll_ready(cx)
107    }
108
109    fn call(&mut self, mut exchange: Exchange) -> Self::Future {
110        exchange
111            .input
112            .headers
113            .entry(self.key.clone())
114            .or_insert(self.value.clone());
115        let fut = self.inner.call(exchange);
116        Box::pin(fut)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use camel_api::{IdentityProcessor, Message};
124    use tower::ServiceExt;
125
126    #[tokio::test]
127    async fn test_set_header_adds_header() {
128        let exchange = Exchange::new(Message::default());
129
130        let processor = SetHeader::new(IdentityProcessor, "source", Value::String("timer".into()));
131
132        let result = processor.oneshot(exchange).await.unwrap();
133        assert_eq!(
134            result.input.header("source"),
135            Some(&Value::String("timer".into()))
136        );
137    }
138
139    #[tokio::test]
140    async fn test_set_header_overwrites_existing() {
141        let mut exchange = Exchange::new(Message::default());
142        exchange
143            .input
144            .set_header("key", Value::String("old".into()));
145
146        let processor = SetHeader::new(IdentityProcessor, "key", Value::String("new".into()));
147
148        let result = processor.oneshot(exchange).await.unwrap();
149        assert_eq!(
150            result.input.header("key"),
151            Some(&Value::String("new".into()))
152        );
153    }
154
155    #[tokio::test]
156    async fn test_set_header_preserves_body() {
157        let exchange = Exchange::new(Message::new("body content"));
158
159        let processor = SetHeader::new(IdentityProcessor, "header", Value::Bool(true));
160
161        let result = processor.oneshot(exchange).await.unwrap();
162        assert_eq!(result.input.body.as_text(), Some("body content"));
163        assert_eq!(result.input.header("header"), Some(&Value::Bool(true)));
164    }
165
166    #[tokio::test]
167    async fn test_set_header_layer_composes() {
168        use tower::ServiceBuilder;
169
170        let svc = ServiceBuilder::new()
171            .layer(super::SetHeaderLayer::new(
172                "env",
173                Value::String("test".into()),
174            ))
175            .service(IdentityProcessor);
176
177        let exchange = Exchange::new(Message::default());
178        let result = svc.oneshot(exchange).await.unwrap();
179        assert_eq!(
180            result.input.header("env"),
181            Some(&Value::String("test".into()))
182        );
183    }
184
185    // ── SetHeaderIfAbsent ──
186
187    #[tokio::test]
188    async fn test_set_header_if_absent_adds_when_missing() {
189        let exchange = Exchange::new(Message::default());
190
191        let processor =
192            SetHeaderIfAbsent::new(IdentityProcessor, "source", Value::String("timer".into()));
193
194        let result = processor.oneshot(exchange).await.unwrap();
195        assert_eq!(
196            result.input.header("source"),
197            Some(&Value::String("timer".into()))
198        );
199    }
200
201    #[tokio::test]
202    async fn test_set_header_if_absent_preserves_existing() {
203        let mut exchange = Exchange::new(Message::default());
204        exchange
205            .input
206            .set_header("key", Value::String("old".into()));
207
208        let processor =
209            SetHeaderIfAbsent::new(IdentityProcessor, "key", Value::String("new".into()));
210
211        let result = processor.oneshot(exchange).await.unwrap();
212        assert_eq!(
213            result.input.header("key"),
214            Some(&Value::String("old".into()))
215        );
216    }
217
218    #[tokio::test]
219    async fn test_set_header_if_absent_preserves_body() {
220        let exchange = Exchange::new(Message::new("body content"));
221
222        let processor = SetHeaderIfAbsent::new(IdentityProcessor, "header", Value::Bool(true));
223
224        let result = processor.oneshot(exchange).await.unwrap();
225        assert_eq!(result.input.body.as_text(), Some("body content"));
226        assert_eq!(result.input.header("header"), Some(&Value::Bool(true)));
227    }
228}