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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Represents the state of providers in a message.

use anyhow::anyhow;
use libc::c_char;
use serde_json::Value as JsonValue;

use pact_models::provider_states::ProviderState;

use crate::{as_mut, as_ref, ffi_fn};
use crate::util::*;

ffi_fn! {
    /// Get the name of the provider state as a string, which needs to be deleted with `string_delete`.
    ///
    /// # Safety
    ///
    /// This function is safe.
    ///
    /// # Error Handling
    ///
    /// If the provider_state param is NULL, this returns NULL.
    fn provider_state_get_name(provider_state: *const ProviderState) -> *const c_char {
        let provider_state = as_ref!(provider_state);
        let name = string::to_c(&provider_state.name)?;
        name as *const c_char
    } {
        ptr::null_to::<c_char>()
    }
}

ffi_fn! {
    /// Get an iterator over the params of a provider state.
    ///
    /// # Safety
    ///
    /// This iterator carries a pointer to the provider state, and must
    /// not outlive the provider state.
    ///
    /// The provider state params also must not be modified during iteration. If it is,
    /// the old iterator must be deleted and a new iterator created.
    ///
    /// # Errors
    ///
    /// On failure, this function will return a NULL pointer.
    ///
    /// This function may fail if any of the Rust strings contain
    /// embedded null ('\0') bytes.
    fn provider_state_get_param_iter(
        provider_state: *mut ProviderState
    ) -> *mut ProviderStateParamIterator {
        let provider_state = as_mut!(provider_state);

        let iter = ProviderStateParamIterator {
            keys:  provider_state.params.keys().cloned().collect(),
            current: 0,
            provider_state: provider_state as *const ProviderState,
        };

        ptr::raw_to(iter)
    } {
        ptr::null_mut_to::<ProviderStateParamIterator>()
    }
}

ffi_fn! {
    /// Get the next key and value out of the iterator, if possible
    ///
    /// Returns a pointer to a heap allocated array of 2 elements, the pointer to the
    /// key string on the heap, and the pointer to the value string on the heap.
    ///
    /// # Safety
    ///
    /// The underlying data must not be modified during iteration.
    ///
    /// The user needs to free both the contained strings and the array.
    ///
    /// # Error Handling
    ///
    /// Returns NULL if there's no further elements or the iterator is NULL.
    fn provider_state_param_iter_next(
        iter: *mut ProviderStateParamIterator
    ) -> *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)
    } {
        ptr::null_mut_to::<ProviderStateParamPair>()
    }
}

ffi_fn! {
    /// Free the provider state when you're done using it.
    fn provider_state_delete(provider_state: *mut ProviderState) {
        ptr::drop_raw(provider_state);
    }
}

ffi_fn! {
    /// Free the provider state param iterator when you're done using it.
    fn provider_state_param_iter_delete(iter: *mut ProviderStateParamIterator) {
        ptr::drop_raw(iter);
    }
}

ffi_fn! {
    /// Free a pair of key and value returned from `provider_state_param_iter_next`.
    fn provider_state_param_pair_delete(pair: *mut ProviderStateParamPair) {
        ptr::drop_raw(pair);
    }
}

/// An iterator that enables FFI iteration over provider state params by putting all the keys on the heap
/// and tracking which one we're currently at.
///
/// This assumes no mutation of the underlying provider state happens while the iterator is live.
#[derive(Debug)]
pub struct ProviderStateParamIterator {
    /// The provider state param keys
    keys: Vec<String>,
    /// The current key
    current: usize,
    /// Pointer to the provider state.
    provider_state: *const ProviderState,
}

impl ProviderStateParamIterator {
    fn next(&mut self) -> Option<&String> {
        let idx = self.current;
        self.current += 1;
        self.keys.get(idx)
    }
}

/// A single key-value pair exported to the C-side.
#[derive(Debug)]
#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct ProviderStateParamPair {
    /// The key of the `ProviderState` parameter.
    key: *const c_char,
    /// The value of the `ProviderState` parameter.
    value: *const c_char,
}

impl ProviderStateParamPair {
    fn new(
        key: &str,
        value: &JsonValue,
    ) -> anyhow::Result<ProviderStateParamPair> {
        let value = value.to_string();

        Ok(ProviderStateParamPair {
            key: string::to_c(key)? as *const c_char,
            value: string::to_c(&value)? as *const c_char,
        })
    }
}