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