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
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,12 @@ harness = false # allows Cucumber to print output instead of libtest
# this prevents cargo from complaining about code blocks
# excluded from tarpaulin coverage checks
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin_include)'] }

[lints.clippy]
# see https://corrode.dev/blog/defensive-programming/#clippy-lints-for-defensive-programming
indexing_slicing = "deny"
fallible_impl_from = "deny"
wildcard_enum_match_arm = "deny"
unneeded_field_pattern = "deny"
fn_params_excessive_bools = "deny"
must_use_candidate = "allow"
8 changes: 6 additions & 2 deletions src/cloudevents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,12 @@ impl TryFrom<UMessage> for CloudEvent {
})?;
event.set_text_data(data);
}
_ => {
event.set_binary_data(payload.into());
UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED
| UPayloadFormat::UPAYLOAD_FORMAT_RAW
| UPayloadFormat::UPAYLOAD_FORMAT_SHM
| UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP
| UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP_TLV => {
event.set_binary_data(payload.to_vec());
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/communication/udiscovery_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ mod tests {
assert!(udiscovery_client
.find_services(service_pattern_uri.clone(), false)
.await
.is_ok_and(|result| result.len() == 1 && service_pattern_uri.matches(&result[0])));
.is_ok_and(
|result| result.len() == 1 && service_pattern_uri.matches(result.first().unwrap())
));
}

#[tokio::test]
Expand Down Expand Up @@ -227,6 +229,7 @@ mod tests {
.get_service_topics(topic_pattern_uri.clone(), false)
.await
.is_ok_and(|result| result.len() == 1
&& topic_pattern_uri.matches(result[0].topic.as_ref().unwrap())));
&& topic_pattern_uri
.matches(result.first().and_then(|r| r.topic.as_ref()).unwrap())));
}
}
74 changes: 62 additions & 12 deletions src/symphony.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,25 +174,49 @@ impl<T: DeploymentTarget> RequestHandler for GetOperation<T> {
serde_json::to_string_pretty(&request_data).expect("failed to serialize Value")
);
}
let deployment_spec: DeploymentSpec =
serde_json::from_value(request_data["deployment"].clone()).map_err(|err| {
let deployment_spec: DeploymentSpec = request_data
.get("deployment")
.ok_or_else(|| {
debug!(
source = source_uri,
"request does not contain DeploymentSpec: {err}"
"request does not contain DeploymentSpec"
);
ServiceInvocationError::InvalidArgument(
"request does not contain DeploymentSpec".to_string(),
)
})
.and_then(|deployment| {
serde_json::from_value(deployment.clone()).map_err(|err| {
debug!(
source = source_uri,
"request contains invalid DeploymentSpec: {err}"
);
ServiceInvocationError::InvalidArgument(
"request contains invalid DeploymentSpec".to_string(),
)
})
})?;
let component_specs: Vec<ComponentSpec> =
serde_json::from_value(request_data["components"].clone()).map_err(|err| {
let component_specs: Vec<ComponentSpec> = request_data
.get("components")
.ok_or_else(|| {
debug!(
source = source_uri,
"request does not contain ComponentSpec array: {err}"
"request does not contain ComponentSpec array"
);
ServiceInvocationError::InvalidArgument(
"request does not contain ComponentSpec array".to_string(),
)
})
.and_then(|components| {
serde_json::from_value(components.clone()).map_err(|err| {
debug!(
source = source_uri,
"request contains invalid ComponentSpec array: {err}"
);
ServiceInvocationError::InvalidArgument(
"request contains invalid ComponentSpec array".to_string(),
)
})
})?;

let result = self
Expand Down Expand Up @@ -249,28 +273,54 @@ impl<T: DeploymentTarget> RequestHandler for ApplyOperation<T> {
trace!("payload: {}", json);
}

let deployment_spec: DeploymentSpec =
serde_json::from_value(request_data["deployment"].clone()).map_err(|err| {
let deployment_spec: DeploymentSpec = request_data
.get("deployment")
.ok_or_else(|| {
debug!(
source = source_uri,
method = sink_uri,
"request does not contain DeploymentSpec: {err}"
"request does not contain DeploymentSpec"
);
ServiceInvocationError::InvalidArgument(
"request does not contain DeploymentSpec".to_string(),
)
})
.and_then(|deployment| {
serde_json::from_value(deployment.clone()).map_err(|err| {
debug!(
source = source_uri,
method = sink_uri,
"request contains invalid DeploymentSpec: {err}"
);
ServiceInvocationError::InvalidArgument(
"request contains invalid DeploymentSpec".to_string(),
)
})
})?;

let affected_components: Vec<ComponentSpec> =
serde_json::from_value(request_data["components"].clone()).map_err(|err| {
let affected_components: Vec<ComponentSpec> = request_data
.get("components")
.ok_or_else(|| {
debug!(
source = source_uri,
method = sink_uri,
"request does not contain ComponentSpec array: {err}"
"request does not contain ComponentSpec array"
);
ServiceInvocationError::InvalidArgument(
"request does not contain ComponentSpec array".to_string(),
)
})
.and_then(|components| {
serde_json::from_value(components.clone()).map_err(|err| {
debug!(
source = source_uri,
method = sink_uri,
"request contains invalid ComponentSpec array: {err}"
);
ServiceInvocationError::InvalidArgument(
"request contains invalid ComponentSpec array".to_string(),
)
})
})?;

let result = match resource_id {
Expand Down
4 changes: 3 additions & 1 deletion src/uattributes/uattributesvalidator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ impl UAttributesValidators {
UMessageType::UMESSAGE_TYPE_REQUEST => Box::new(RequestValidator),
UMessageType::UMESSAGE_TYPE_RESPONSE => Box::new(ResponseValidator),
UMessageType::UMESSAGE_TYPE_NOTIFICATION => Box::new(NotificationValidator),
_ => Box::new(PublishValidator),
UMessageType::UMESSAGE_TYPE_UNSPECIFIED | UMessageType::UMESSAGE_TYPE_PUBLISH => {
Box::new(PublishValidator)
}
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/umessage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,13 @@ pub(crate) fn deserialize_protobuf_bytes<T: MessageFull + Default>(
Err(e) => Err(UMessageError::DataSerializationError(e)),
})
}
_ => {
UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED
| UPayloadFormat::UPAYLOAD_FORMAT_JSON
| UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP
| UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP_TLV
| UPayloadFormat::UPAYLOAD_FORMAT_RAW
| UPayloadFormat::UPAYLOAD_FORMAT_TEXT
| UPayloadFormat::UPAYLOAD_FORMAT_SHM => {
let detail_msg = payload_format.to_media_type().map_or_else(
|| format!("Unknown payload format: {}", payload_format.value()),
|mt| format!("Invalid/unsupported payload format: {mt}"),
Expand Down
74 changes: 37 additions & 37 deletions src/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,45 +170,45 @@ impl FromStr for UUri {
.map_or(Ok(String::default()), Self::verify_parsed_authority)?;

let path_segments = parsed_uri.path().segments();
if path_segments.len() != 3 {
return Err(UUriError::serialization_error(
match path_segments {
[entity, version, resource] => {
if entity.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty entity ID",
));
}
let ue_id = u32::from_str_radix(entity, 16).map_err(|e| {
UUriError::serialization_error(format!("Cannot parse entity ID: {e}"))
})?;
if version.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty entity version",
));
}
let ue_version_major = u8::from_str_radix(version, 16).map_err(|e| {
UUriError::serialization_error(format!("Cannot parse entity version: {e}"))
})?;
if resource.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty resource ID",
));
}
let resource_id = u16::from_str_radix(resource, 16).map_err(|e| {
UUriError::serialization_error(format!("Cannot parse resource ID: {e}"))
})?;

Ok(UUri {
authority_name,
ue_id,
ue_version_major: ue_version_major as u32,
resource_id: resource_id as u32,
..Default::default()
})
}
_ => Err(UUriError::serialization_error(
"uProtocol URI must contain entity ID, entity version and resource ID",
));
)),
}
let entity = path_segments[0].as_str();
if entity.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty entity ID",
));
}
let ue_id = u32::from_str_radix(entity, 16)
.map_err(|e| UUriError::serialization_error(format!("Cannot parse entity ID: {e}")))?;
let version = path_segments[1].as_str();
if version.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty entity version",
));
}
let ue_version_major = u8::from_str_radix(version, 16).map_err(|e| {
UUriError::serialization_error(format!("Cannot parse entity version: {e}"))
})?;
let resource = path_segments[2].as_str();
if resource.is_empty() {
return Err(UUriError::serialization_error(
"URI must contain non-empty resource ID",
));
}
let resource_id = u16::from_str_radix(resource, 16).map_err(|e| {
UUriError::serialization_error(format!("Cannot parse resource ID: {e}"))
})?;

Ok(UUri {
authority_name,
ue_id,
ue_version_major: ue_version_major as u32,
resource_id: resource_id as u32,
..Default::default()
})
}
}

Expand Down