use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceTopics {
pub request: String,
pub reply: String,
}
impl ServiceTopics {
#[must_use]
pub fn from_service(ros_service_name: &str) -> Self {
let base = ros_service_name.trim_start_matches('/');
Self {
request: format!("rq/{base}Request"),
reply: format!("rr/{base}Reply"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceRequestId {
pub client_guid: [u8; 16],
pub sequence_number: i64,
}
#[derive(Debug, Clone, Default)]
pub struct PendingRequests {
pending: BTreeMap<ServiceRequestId, Vec<u8>>,
}
impl PendingRequests {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, id: ServiceRequestId, body: Vec<u8>) -> bool {
if self.pending.contains_key(&id) {
return false;
}
self.pending.insert(id, body);
true
}
pub fn match_reply(&mut self, id: ServiceRequestId) -> Option<Vec<u8>> {
self.pending.remove(&id)
}
#[must_use]
pub fn outstanding(&self) -> usize {
self.pending.len()
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn topic_names_for_simple_service() {
let s = ServiceTopics::from_service("/foo/bar");
assert_eq!(s.request, "rq/foo/barRequest");
assert_eq!(s.reply, "rr/foo/barReply");
}
#[test]
fn topic_names_handle_no_leading_slash() {
let s = ServiceTopics::from_service("foo/bar");
assert_eq!(s.request, "rq/foo/barRequest");
assert_eq!(s.reply, "rr/foo/barReply");
}
#[test]
fn pending_register_and_match_round_trips() {
let mut p = PendingRequests::new();
let id = ServiceRequestId {
client_guid: [0xab; 16],
sequence_number: 7,
};
assert!(p.register(id, b"req".to_vec()));
assert_eq!(p.outstanding(), 1);
let body = p.match_reply(id).expect("match");
assert_eq!(body, b"req");
assert_eq!(p.outstanding(), 0);
}
#[test]
fn duplicate_register_rejected() {
let mut p = PendingRequests::new();
let id = ServiceRequestId {
client_guid: [0; 16],
sequence_number: 1,
};
assert!(p.register(id, b"x".to_vec()));
assert!(!p.register(id, b"y".to_vec()));
}
#[test]
fn unmatched_reply_returns_none() {
let mut p = PendingRequests::new();
let id = ServiceRequestId {
client_guid: [0; 16],
sequence_number: 1,
};
assert!(p.match_reply(id).is_none());
}
}