Skip to content

Commit

Permalink
fix(FFI): do not use a panic as the mechanism to detect end of iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Jan 19, 2024
1 parent e9f7c06 commit 367aa25
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 67 deletions.
12 changes: 7 additions & 5 deletions rust/pact_ffi/src/lib.rs
Expand Up @@ -222,11 +222,13 @@ ffi_fn! {
let iter = as_mut!(iter);
let mismatches = as_ref!(iter.mismatches);
let index = iter.next();
let mismatch = mismatches
.0
.get(index)
.ok_or(anyhow::anyhow!("iter past the end of mismatches"))?;
mismatch as *const Mismatch
match mismatches.0.get(index) {
Some(mismatch) => mismatch as *const Mismatch,
None => {
trace!("iter past the end of mismatches");
std::ptr::null()
}
}
} {
std::ptr::null()
}
Expand Down
11 changes: 8 additions & 3 deletions rust/pact_ffi/src/models/expressions.rs
Expand Up @@ -14,7 +14,7 @@ use pact_models::matchingrules::expressions::{
};
use pact_models::matchingrules::MatchingRule;
use pact_models::time_utils::validate_datetime;
use tracing::{debug, error};
use tracing::{debug, error, trace};

use crate::{as_mut, as_ref, ffi_fn, safe_str};
use crate::error::set_error_msg;
Expand Down Expand Up @@ -383,8 +383,13 @@ ffi_fn! {
/// This function will return a NULL pointer if passed a NULL pointer or if an error occurs.
fn pactffi_matching_rule_iter_next(iter: *mut MatchingRuleIterator) -> *const MatchingRuleResult {
let iter = as_mut!(iter);
let result = iter.next().ok_or(anyhow::anyhow!("iter past the end of messages"))?;
result as *const MatchingRuleResult
match iter.next() {
Some(result) => result as *const MatchingRuleResult,
None => {
trace!("iter past the end of matching rules");
std::ptr::null()
}
}
} {
std::ptr::null()
}
Expand Down
15 changes: 11 additions & 4 deletions rust/pact_ffi/src/models/generators.rs
Expand Up @@ -16,7 +16,7 @@ use pact_models::path_exp::DocPath;
use pact_models::v4::http_parts::{HttpRequest, HttpResponse};
use pact_models::v4::message_parts::MessageContents;
use serde_json::Value;
use tracing::{error, warn};
use tracing::{error, warn, trace};

use crate::{as_mut, as_ref, ffi_fn};
use crate::util::{ptr, string};
Expand Down Expand Up @@ -266,9 +266,16 @@ ffi_fn! {
fn pactffi_generators_iter_next(iter: *mut GeneratorCategoryIterator) -> *const GeneratorKeyValuePair {
let iter = as_mut!(iter);

let (path, generator) = iter.next().ok_or(anyhow::anyhow!("iter past the end of the generators"))?;
let pair = GeneratorKeyValuePair::new(&path.to_string(), generator)?;
ptr::raw_to(pair)
match iter.next() {
Some((path, generator)) => {
let pair = GeneratorKeyValuePair::new(&path.to_string(), generator)?;
ptr::raw_to(pair)
}
None => {
trace!("iter past the end of the generators");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down
43 changes: 31 additions & 12 deletions rust/pact_ffi/src/models/iterators.rs
@@ -1,6 +1,9 @@
//! FFI wrapper code for iterating over Pact interactions

use std::panic::RefUnwindSafe;

use tracing::trace;

use pact_models::message::Message;
use pact_models::message_pact::MessagePact;
use pact_models::v4::pact::V4Pact;
Expand Down Expand Up @@ -62,9 +65,13 @@ ffi_fn! {
/// This function will return a NULL pointer if passed a NULL pointer or if an error occurs.
fn pactffi_pact_message_iter_next(iter: *mut PactMessageIterator) -> *mut Message {
let iter = as_mut!(iter);
let message = iter.next()
.ok_or(anyhow::anyhow!("iter past the end of messages"))?;
message as *mut Message
match iter.next() {
Some(message) => message as *mut Message,
None => {
trace!("iter past the end of messages");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down Expand Up @@ -116,9 +123,13 @@ ffi_fn! {
/// This function will return a NULL pointer if passed a NULL pointer or if an error occurs.
fn pactffi_pact_sync_message_iter_next(iter: *mut PactSyncMessageIterator) -> *mut SynchronousMessage {
let iter = as_mut!(iter);
let message = iter.next()
.ok_or(anyhow::anyhow!("iter past the end of messages"))?;
message as *mut SynchronousMessage
match iter.next() {
Some(message) => message as *mut SynchronousMessage,
None => {
trace!("iter past the end of messages");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down Expand Up @@ -177,9 +188,13 @@ ffi_fn! {
/// This function will return a NULL pointer if passed a NULL pointer or if an error occurs.
fn pactffi_pact_sync_http_iter_next(iter: *mut PactSyncHttpIterator) -> *mut SynchronousHttp {
let iter = as_mut!(iter);
let interaction = iter.next()
.ok_or(anyhow::anyhow!("iter past the end of the list"))?;
interaction as *mut SynchronousHttp
match iter.next() {
Some(interaction) => interaction as *mut SynchronousHttp,
None => {
trace!("iter past the end of the list");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down Expand Up @@ -237,9 +252,13 @@ ffi_fn! {
/// This function will return a NULL pointer if passed a NULL pointer or if an error occurs.
fn pactffi_pact_interaction_iter_next(iter: *mut PactInteractionIterator) -> *const PactInteraction {
let iter = as_mut!(iter);
let interaction = iter.next()
.ok_or(anyhow::anyhow!("iter past the end of messages"))?;
interaction as *const PactInteraction
match iter.next() {
Some(interaction) => interaction as *const PactInteraction,
None => {
trace!("iter past the end of messages");
std::ptr::null()
}
}
} {
std::ptr::null()
}
Expand Down
18 changes: 13 additions & 5 deletions rust/pact_ffi/src/models/matching_rules.rs
@@ -1,13 +1,14 @@
//! FFI functions to deal with matching rules

use itertools::Itertools;
use pact_models::matchingrules::{Category, MatchingRule};
use libc::c_char;
use pact_models::matchingrules::{Category, MatchingRule};
use pact_models::path_exp::DocPath;
use pact_models::v4::http_parts::{HttpRequest, HttpResponse};
use pact_models::v4::message_parts::MessageContents;
use tracing::trace;

use crate::{ffi_fn, as_ref, as_mut};
use crate::{as_mut, as_ref, ffi_fn};
use crate::util::{ptr, string};
use crate::util::ptr::{drop_raw, raw_to};

Expand Down Expand Up @@ -180,9 +181,16 @@ ffi_fn! {
fn pactffi_matching_rules_iter_next(iter: *mut MatchingRuleCategoryIterator) -> *const MatchingRuleKeyValuePair {
let iter = as_mut!(iter);

let (path, rule) = iter.next().ok_or(anyhow::anyhow!("iter past the end of the matching rules"))?;
let pair = MatchingRuleKeyValuePair::new(&path.to_string(), rule)?;
ptr::raw_to(pair)
match iter.next() {
Some((path, rule)) => {
let pair = MatchingRuleKeyValuePair::new(&path.to_string(), rule)?;
ptr::raw_to(pair)
}
None => {
trace!("iter past the end of the matching rules");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down
34 changes: 22 additions & 12 deletions rust/pact_ffi/src/models/message.rs
Expand Up @@ -15,6 +15,7 @@ use itertools::Itertools;
use libc::{c_char, c_int, c_uchar, c_uint, EXIT_FAILURE, EXIT_SUCCESS, size_t};
use serde_json::from_str as from_json_str;
use serde_json::Value as JsonValue;
use tracing::trace;

use pact_models::bodies::OptionalBody;
use pact_models::content_types::{ContentType, ContentTypeHint};
Expand Down Expand Up @@ -419,11 +420,13 @@ ffi_fn! {
let message_ptr = unsafe { guard.as_mut() };
match message_ptr {
Some(message) => {
let provider_state = message
.provider_states_mut()
.get_mut(index)
.ok_or(anyhow::anyhow!("iter past the end of provider states"))?;
provider_state as *mut ProviderState
match message.provider_states_mut().get_mut(index) {
Some(provider_state) => provider_state as *mut ProviderState,
None => {
trace!("iter past the end of provider states");
std::ptr::null_mut()
}
}
}
None => std::ptr::null_mut()
}
Expand Down Expand Up @@ -555,14 +558,21 @@ ffi_fn! {
&contents.metadata
}
};
let key = iter.next().ok_or(anyhow::anyhow!("iter past the end of metadata"))?;

let (key, value) = metadata
.get_key_value(key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let pair = MessageMetadataPair::new(key, json_to_string(&value).as_str())?;
ptr::raw_to(pair)
match iter.next() {
Some(key) => {
let (key, value) = metadata
.get_key_value(key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let pair = MessageMetadataPair::new(key, json_to_string(&value).as_str())?;
ptr::raw_to(pair)
}
None => {
trace!("iter past the end of metadata");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down
47 changes: 28 additions & 19 deletions rust/pact_ffi/src/models/message_pact.rs
Expand Up @@ -4,6 +4,7 @@ use std::iter::{self, Iterator};

use anyhow::{anyhow, Context};
use libc::c_char;
use tracing::trace;

use pact_models::{Consumer, Provider};
use pact_models::message::Message;
Expand Down Expand Up @@ -137,11 +138,13 @@ ffi_fn! {
let iter = as_mut!(iter);
let message_pact = as_mut!(iter.message_pact);
let index = iter.next();
let message = message_pact
.messages
.get_mut(index)
.ok_or(anyhow::anyhow!("iter past the end of messages"))?;
message as *mut Message
match message_pact.messages.get_mut(index) {
Some(message) => message as *mut Message,
None => {
trace!("iter past the end of messages");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down Expand Up @@ -243,20 +246,26 @@ ffi_fn! {
fn pactffi_message_pact_metadata_iter_next(iter: *mut MessagePactMetadataIterator) -> *mut MessagePactMetadataTriple {
let iter = as_mut!(iter);
let message_pact = as_ref!(iter.message_pact);
let (outer_key, inner_key) = iter.next().ok_or(anyhow::anyhow!("iter past the end of metadata"))?;

let (outer_key, sub_tree) = message_pact
.metadata
.get_key_value(outer_key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let (inner_key, value) = sub_tree
.get_key_value(inner_key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let triple = MessagePactMetadataTriple::new(outer_key, inner_key, value)?;

ptr::raw_to(triple)
match iter.next() {
Some((outer_key, inner_key)) => {
let (outer_key, sub_tree) = message_pact
.metadata
.get_key_value(outer_key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let (inner_key, value) = sub_tree
.get_key_value(inner_key)
.ok_or(anyhow::anyhow!("iter provided invalid metadata key"))?;

let triple = MessagePactMetadataTriple::new(outer_key, inner_key, value)?;

ptr::raw_to(triple)
}
None => {
trace!("iter past the end of metadata");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down
22 changes: 15 additions & 7 deletions rust/pact_ffi/src/models/provider_state.rs
Expand Up @@ -3,6 +3,7 @@
use anyhow::anyhow;
use libc::c_char;
use serde_json::Value as JsonValue;
use tracing::trace;

use pact_models::provider_states::ProviderState;

Expand Down Expand Up @@ -82,13 +83,20 @@ ffi_fn! {
) -> *mut ProviderStateParamPair {
let iter = as_mut!(iter);
let provider_state = as_ref!(iter.provider_state);
let key = iter.next().ok_or(anyhow!("iter past the end of params"))?;
let (key, value) = provider_state
.params
.get_key_value(key)
.ok_or(anyhow!("iter provided invalid param key"))?;
let pair = ProviderStateParamPair::new(key, value)?;
ptr::raw_to(pair)
match iter.next() {
Some(key) => {
let (key, value) = provider_state
.params
.get_key_value(key)
.ok_or(anyhow!("iter provided invalid param key"))?;
let pair = ProviderStateParamPair::new(key, value)?;
ptr::raw_to(pair)
}
None => {
trace!("iter past the end of params");
std::ptr::null_mut()
}
}
} {
std::ptr::null_mut()
}
Expand Down

0 comments on commit 367aa25

Please sign in to comment.