Skip to main content

diffsol_c/
c_api_utils.rs

1use crate::error_c::set_last_error;
2
3pub const DIFFSOL_OK: i32 = 0;
4pub const DIFFSOL_ERR: i32 = -1;
5pub const DIFFSOL_BAD_ARG: i32 = -2;
6
7pub trait CMapTo<Out> {
8    fn c_map_to(self) -> Out;
9}
10
11pub trait CMapFrom<In> {
12    fn c_map_from(value: In) -> Self;
13}
14
15macro_rules! impl_c_map_identity {
16    ($t:ty) => {
17        impl CMapTo<$t> for $t {
18            #[inline]
19            fn c_map_to(self) -> $t {
20                self
21            }
22        }
23
24        impl CMapFrom<$t> for $t {
25            #[inline]
26            fn c_map_from(value: $t) -> Self {
27                value
28            }
29        }
30    };
31}
32
33impl_c_map_identity!(i32);
34impl_c_map_identity!(usize);
35impl_c_map_identity!(u64);
36impl_c_map_identity!(i64);
37impl_c_map_identity!(f64);
38impl_c_map_identity!(f32);
39impl_c_map_identity!(bool);
40
41impl CMapTo<i32> for bool {
42    #[inline]
43    fn c_map_to(self) -> i32 {
44        if self {
45            1
46        } else {
47            0
48        }
49    }
50}
51
52impl CMapFrom<i32> for bool {
53    #[inline]
54    fn c_map_from(value: i32) -> Self {
55        value != 0
56    }
57}
58
59#[inline]
60pub fn map_get<In, Out>(value: In) -> Out
61where
62    In: CMapTo<Out>,
63{
64    value.c_map_to()
65}
66
67#[inline]
68pub fn map_set<In, Out>(value: In) -> Out
69where
70    Out: CMapFrom<In>,
71{
72    Out::c_map_from(value)
73}
74
75#[inline]
76pub fn invalid_arg_at(msg: &str, file: &'static str, line: u32) -> i32 {
77    set_last_error(msg, file, line);
78    DIFFSOL_BAD_ARG
79}
80
81#[inline]
82pub fn error_at(msg: &str, file: &'static str, line: u32) -> i32 {
83    set_last_error(msg, file, line);
84    DIFFSOL_ERR
85}
86
87#[inline]
88pub fn null_err_at<T>(ptr: *const T, msg: &str, file: &'static str, line: u32) -> bool {
89    if ptr.is_null() {
90        set_last_error(msg, file, line);
91        true
92    } else {
93        false
94    }
95}
96
97#[inline]
98pub fn valid_f64_ptr(ptr: *const f64, len: usize) -> bool {
99    len == 0 || !ptr.is_null()
100}
101
102#[macro_export]
103macro_rules! c_invalid_arg {
104    ($msg:expr) => {
105        $crate::c_api_utils::invalid_arg_at($msg, file!(), line!())
106    };
107}
108
109#[macro_export]
110macro_rules! c_error {
111    ($msg:expr) => {
112        $crate::c_api_utils::error_at($msg, file!(), line!())
113    };
114}
115
116#[macro_export]
117macro_rules! c_null_err {
118    ($ptr:expr, $msg:expr) => {
119        $crate::c_api_utils::null_err_at($ptr, $msg, file!(), line!())
120    };
121}
122
123#[macro_export]
124macro_rules! c_getter_simple {
125    ($prefix:ident, $opt_ty:ty, $out_ty:ty, $field:ident) => {
126        ::paste::paste! {
127            #[doc = "Get a solver option value."]
128            #[doc = ""]
129            #[doc = "# Safety"]
130            #[doc = "`options` must be a valid pointer created by this library. `out_value` must"]
131            #[doc = "be a valid, writable pointer for a single output value."]
132            #[unsafe(no_mangle)]
133            pub unsafe extern "C" fn [<$prefix _get_ $field>](options: *const $opt_ty, out_value: *mut $out_ty) -> i32 {
134                if options.is_null() || out_value.is_null() {
135                    return $crate::c_invalid_arg!(concat!(
136                        "invalid arguments to ",
137                        stringify!([<$prefix _get_ $field>])
138                    ));
139                }
140                let options = unsafe { &*options };
141                match options.[<get_ $field>]() {
142                    Ok(value) => {
143                        let mapped: $out_ty = $crate::c_api_utils::map_get::<_, $out_ty>(value);
144                        unsafe {
145                            *out_value = mapped;
146                        }
147                        $crate::c_api_utils::DIFFSOL_OK
148                    }
149                    Err(err) => $crate::c_error!(&format!("{}", err)),
150                }
151            }
152        }
153    };
154}
155
156#[macro_export]
157macro_rules! c_setter_simple {
158    ($prefix:ident, $opt_ty:ty, $in_ty:ty, $field:ident) => {
159        ::paste::paste! {
160            #[doc = "Set a solver option value."]
161            #[doc = ""]
162            #[doc = "# Safety"]
163            #[doc = "`options` must be a valid mutable pointer created by this library."]
164            #[unsafe(no_mangle)]
165            pub unsafe extern "C" fn [<$prefix _set_ $field>](options: *mut $opt_ty, value: $in_ty) -> i32 {
166                if options.is_null() {
167                    return $crate::c_invalid_arg!(concat!(
168                        "invalid arguments to ",
169                        stringify!([<$prefix _set_ $field>])
170                    ));
171                }
172                let options = unsafe { &mut *options };
173                let mapped = $crate::c_api_utils::map_set::<$in_ty, _>(value);
174                match options.[<set_ $field>](mapped) {
175                    Ok(()) => $crate::c_api_utils::DIFFSOL_OK,
176                    Err(err) => $crate::c_error!(&format!("{}", err)),
177                }
178            }
179        }
180    };
181}