Skip to main content

camel_api/
message.rs

1use std::collections::HashMap;
2
3use crate::body::Body;
4use crate::value::Value;
5
6/// A message flowing through the Camel framework.
7#[derive(Debug, Clone)]
8pub struct Message {
9    /// Message headers (metadata).
10    // TODO(API-003): Replace HashMap with a proper CaseInsensitiveMap for full HTTP compliance.
11    pub headers: HashMap<String, Value>,
12    /// Message body (payload).
13    pub body: Body,
14}
15
16impl Default for Message {
17    fn default() -> Self {
18        Self {
19            headers: HashMap::new(),
20            body: Body::Empty,
21        }
22    }
23}
24
25impl Message {
26    /// Create a new message with the given body.
27    pub fn new(body: impl Into<Body>) -> Self {
28        Self {
29            headers: HashMap::new(),
30            body: body.into(),
31        }
32    }
33
34    /// Get a header value by key.
35    pub fn header(&self, key: &str) -> Option<&Value> {
36        self.headers.get(key)
37    }
38
39    /// Get a header value, matching case-insensitively.
40    pub fn header_ic(&self, key: &str) -> Option<&Value> {
41        self.headers
42            .iter()
43            .find(|(k, _)| k.eq_ignore_ascii_case(key))
44            .map(|(_, v)| v)
45    }
46
47    /// Set a header value.
48    pub fn set_header(&mut self, key: impl Into<String>, value: impl Into<Value>) {
49        self.headers.insert(key.into(), value.into());
50    }
51
52    /// Set a header value with a normalized lowercase key.
53    pub fn set_header_normalized(&mut self, key: impl Into<String>, value: impl Into<Value>) {
54        self.headers
55            .insert(key.into().to_ascii_lowercase(), value.into());
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_message_default() {
65        let msg = Message::default();
66        assert!(msg.body.is_empty());
67        assert!(msg.headers.is_empty());
68    }
69
70    #[test]
71    fn test_message_new_with_body() {
72        let msg = Message::new("hello");
73        assert_eq!(msg.body.as_text(), Some("hello"));
74    }
75
76    #[test]
77    fn test_message_headers() {
78        let mut msg = Message::default();
79        msg.set_header("key", Value::String("value".into()));
80        assert_eq!(msg.header("key"), Some(&Value::String("value".into())));
81        assert_eq!(msg.header("missing"), None);
82    }
83
84    #[test]
85    fn test_header_ic_case_insensitive() {
86        let mut msg = Message::default();
87        msg.set_header("Content-Type", Value::String("application/json".into()));
88        assert_eq!(
89            msg.header_ic("content-type"),
90            Some(&Value::String("application/json".into()))
91        );
92        assert_eq!(
93            msg.header_ic("CONTENT-TYPE"),
94            Some(&Value::String("application/json".into()))
95        );
96    }
97
98    #[test]
99    fn test_set_header_normalized_lowercases_key() {
100        let mut msg = Message::default();
101        msg.set_header_normalized("X-Request-Id", Value::String("abc-123".into()));
102
103        assert!(msg.headers.contains_key("x-request-id"));
104        assert_eq!(
105            msg.header("x-request-id"),
106            Some(&Value::String("abc-123".into()))
107        );
108    }
109}