Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ pub use ustatus::{UCode, UStatus};

mod utransport;
pub use utransport::{
ComparableListener, LocalUriProvider, StaticUriProvider, UListener, UTransport,
verify_filter_criteria, ComparableListener, LocalUriProvider, StaticUriProvider, UListener,
UTransport,
};
#[cfg(feature = "test-util")]
pub use utransport::{MockLocalUriProvider, MockTransport, MockUListener};
Expand Down
61 changes: 61 additions & 0 deletions src/utransport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,47 @@ use async_trait::async_trait;

use crate::{UCode, UMessage, UStatus, UUri};

/// Verifies that given UUris can be used as source and sink filter UUris
/// for registering listeners.
///
/// This function is helpful for implementing [`UTransport`] in accordance with the
/// uProtocol Transport Layer specification.
///
/// # Errors
///
/// Returns a [`UStatus`] with a [`UCode::INVALID_ARGUMENT`] and a corresponding detail
/// message, if any of the given UUris cannot be used as filter criteria.
///
pub fn verify_filter_criteria(
source_filter: &UUri,
sink_filter: Option<&UUri>,
) -> Result<(), UStatus> {
if let Some(sink_filter_uuri) = sink_filter {
if sink_filter_uuri.is_notification_destination()
&& source_filter.is_notification_destination()
{
return Err(UStatus::fail_with_code(
UCode::INVALID_ARGUMENT,
"source and sink filters must not both have resource ID 0",
));
}
if sink_filter_uuri.is_rpc_method()
&& !source_filter.has_wildcard_resource_id()
&& !source_filter.is_notification_destination()
{
return Err(UStatus::fail_with_code(
UCode::INVALID_ARGUMENT,
"source filter must either have the wildcard resource ID or resource ID 0, if sink filter matches RPC method resource ID"));
}
} else if !source_filter.has_wildcard_resource_id() && !source_filter.is_event() {
return Err(UStatus::fail_with_code(
UCode::INVALID_ARGUMENT,
"source filter must either have the wildcard resource ID or a resource ID from topic range, if sink filter is empty"));
}
// everything else might match valid messages
Ok(())
}

/// A factory for URIs representing this uEntity's resources.
///
/// Implementations may use arbitrary mechanisms to determine the information that
Expand Down Expand Up @@ -394,6 +435,7 @@ mod tests {
use std::{
hash::{DefaultHasher, Hash, Hasher},
ops::Deref,
str::FromStr,
sync::Arc,
};

Expand Down Expand Up @@ -545,4 +587,23 @@ mod tests {
let debug_output = format!("{comp_listener:?}");
assert!(!debug_output.is_empty());
}

#[test_case::test_case(
"//vehicle1/AA/1/0",
Some("//vehicle2/BB/1/0");
"source and sink both having resource ID 0")]
#[test_case::test_case(
"//vehicle1/AA/1/CC",
Some("//vehicle2/BB/1/1A");
"sink is RPC but source has invalid resource ID")]
#[test_case::test_case(
"//vehicle1/AA/1/CC",
None;
"sink is empty but source has non-topic resource ID")]
fn test_verify_filter_criteria_fails_for(source: &str, sink: Option<&str>) {
let source_filter = UUri::from_str(source).expect("invalid source URI");
let sink_filter = sink.map(|s| UUri::from_str(s).expect("invalid sink URI"));
assert!(verify_filter_criteria(&source_filter, sink_filter.as_ref())
.is_err_and(|err| matches!(err.get_code(), UCode::INVALID_ARGUMENT)));
}
}
Loading