1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
//! Common data shared among input/output/services and utilities related to it.
use std::collections::HashMap;
/// Common data shared among input/output/services.
/// This is the language `service-io` talk.
/// Each input/output/service understand this structure.
///
/// # Example
/// ```rust
/// use service_io::message::Message;
///
/// let request = Message::default()
/// .user("user_01")
/// .service_name("my_service")
/// .args(["arg0", "arg1", "arg2", "this is arg3"])
/// .body("body of the message")
/// .attach([
/// ("file1.txt", b"content data".to_vec()),
/// ("file2.txt", b"1234".to_vec())
/// ]);
/// ```
///
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Message {
/// The user this message is related to.
/// If the message is in the input side, this user means the originator of the message.
/// If the message is in the output side, this user means the recipient of the message.
pub user: String,
/// The service name this message going to/come from.
/// The value of this field should match to any name used for register services.
///
/// See also: [`Engine::add_service()`]
///
/// [`Engine::add_service()`]: crate::engine::Engine::add_service()
pub service_name: String,
/// Arguments of the message.
/// Each service implementation will understand these values in their own way.
pub args: Vec<String>,
/// Main body of the message.
/// Each service implementation will understand this value in their own way.
pub body: String,
/// Attached content of the message.
/// Each service implementation will understand these values in their own way.
pub attached_data: HashMap<String, Vec<u8>>,
}
impl Message {
/// Sugar to perform a response of a received message.
/// Creates an empty message with same [`Message::user`]
/// and [`Message::service_name`] as the passed message.
///
/// # Example
/// ```rust
/// use service_io::message::Message;
///
/// let request = Message::default()
/// .user("user_01")
/// .service_name("my_service")
/// .body("1234");
///
/// let response = Message::response(&request);
///
/// // Name and service_name are copied
/// assert_eq!(request.user, response.user);
/// assert_eq!(request.service_name, response.service_name);
///
/// // But other fields as body are not copied.
/// assert_ne!(request.body, response.body);
/// ```
pub fn response(message: &Message) -> Message {
Message {
user: message.user.clone(),
service_name: message.service_name.clone(),
..Default::default()
}
}
/// Set a user for the message
pub fn user(mut self, user: impl Into<String>) -> Self {
self.user = user.into();
self
}
/// Set a service name for the message
pub fn service_name(mut self, service_name: impl Into<String>) -> Self {
self.service_name = service_name.into();
self
}
/// Set args for the message
pub fn args<S: Into<String>>(mut self, args: impl IntoIterator<Item = S>) -> Self {
self.args = args.into_iter().map(|s| s.into()).collect();
self
}
/// Set a body for the message
pub fn body(mut self, body: impl Into<String>) -> Self {
self.body = body.into();
self
}
/// Set attached data for the message
pub fn attach<S: Into<String>>(
mut self,
attached: impl IntoIterator<Item = (S, Vec<u8>)>,
) -> Self {
self.attached_data = attached
.into_iter()
.map(|(name, data)| (name.into(), data))
.collect();
self
}
}
/// Utilities related to the `Message`
pub mod util {
use super::Message;
/// Modify the [`Message::service_name`] value to make the first letter lowercase.
///
/// This utility can be used in [`Engine::map_input()`] to send always
/// a first letter lowercase version of the service_name to the engine to make the correct
/// service match.
///
/// This is useful because some users could specify a first capital letter without realizing
/// (usually in email clients where the first letter is uppercase by default).
/// If the service_name is registered with lowercase, their message will not match.
///
/// [`Engine::map_input()`]: crate::engine::Engine::map_input()
pub fn service_name_first_char_to_lowercase(mut message: Message) -> Message {
let mut chars = message.service_name.chars();
message.service_name = match chars.next() {
Some(first_letter) => first_letter.to_lowercase().collect::<String>() + chars.as_str(),
None => String::new(),
};
message
}
}