questdb_confstr_ffi/
lib.rs1#![doc = include_str!("../README.md")]
26#![allow(clippy::missing_safety_doc)]
27
28use questdb_confstr::{parse_conf_str, ConfStr};
29use std::collections::hash_map;
30use std::os::raw::c_char;
31use std::ptr;
32use std::slice;
33
34#[repr(C)]
35pub struct questdb_conf_str {
36 inner: ConfStr,
37}
38
39#[repr(C)]
40pub struct questdb_conf_str_parse_err {
41 msg: *const c_char,
42 msg_len: usize,
43 pos: usize,
44}
45
46fn new_err(msg: String, pos: usize) -> *mut questdb_conf_str_parse_err {
47 let msg_len = msg.len();
48 let msg = Box::into_raw(msg.into_boxed_str()) as *const c_char;
49 Box::into_raw(Box::new(questdb_conf_str_parse_err { msg, msg_len, pos }))
50}
51
52#[no_mangle]
53pub unsafe extern "C" fn questdb_conf_str_parse_err_free(err: *mut questdb_conf_str_parse_err) {
54 if !err.is_null() {
55 let err = Box::from_raw(err);
56 drop(Box::from_raw(err.msg as *mut c_char));
57 drop(err);
58 }
59}
60
61#[no_mangle]
62pub unsafe extern "C" fn questdb_conf_str_parse(
63 str: *const c_char,
64 len: usize,
65 err_out: *mut *mut questdb_conf_str_parse_err,
66) -> *mut questdb_conf_str {
67 let input = slice::from_raw_parts(str as *const u8, len);
68 let input_str = match std::str::from_utf8(input) {
69 Ok(s) => s,
70 Err(utf8err) => {
71 let first_bad_byte = utf8err.valid_up_to();
72 *err_out = new_err(
73 format!("invalid UTF-8 sequence at position {}", first_bad_byte),
74 first_bad_byte,
75 );
76 return ptr::null_mut();
77 }
78 };
79
80 match parse_conf_str(input_str) {
81 Ok(conf_str) => Box::into_raw(Box::new(questdb_conf_str { inner: conf_str })),
82 Err(err) => {
83 *err_out = new_err(err.to_string(), err.position());
84 ptr::null_mut()
85 }
86 }
87}
88
89#[no_mangle]
90pub unsafe extern "C" fn questdb_conf_str_service(
91 conf_str: *const questdb_conf_str,
92 len_out: *mut usize,
93) -> *const c_char {
94 if conf_str.is_null() {
95 return ptr::null();
96 }
97
98 let conf_str = &(*conf_str).inner;
99 let service = conf_str.service();
100 *len_out = service.len();
101 service.as_ptr() as *const c_char
102}
103
104#[no_mangle]
105pub unsafe extern "C" fn questdb_conf_str_get(
106 conf_str: *const questdb_conf_str,
107 key: *const c_char,
108 key_len: usize,
109 val_len_out: *mut usize,
110) -> *const c_char {
111 if conf_str.is_null() || key.is_null() {
112 return ptr::null();
113 }
114
115 let conf_str = &(*conf_str).inner;
116 let key = slice::from_raw_parts(key as *const u8, key_len);
117 let key_str = match std::str::from_utf8(key) {
118 Ok(s) => s,
119 Err(_) => return ptr::null(),
120 };
121
122 match conf_str.get(key_str) {
123 Some(val) => {
124 let val_str = val.as_ptr() as *const c_char;
125 *val_len_out = val.len();
126 val_str
127 }
128 None => ptr::null(),
129 }
130}
131
132#[repr(C)]
133pub struct questdb_conf_str_iter {
134 inner: hash_map::Iter<'static, String, String>,
135}
136
137#[no_mangle]
138pub unsafe extern "C" fn questdb_conf_str_iter_pairs(
139 conf_str: *const questdb_conf_str,
140) -> *mut questdb_conf_str_iter {
141 if conf_str.is_null() {
142 return ptr::null_mut();
143 }
144 let conf_str = &(*conf_str).inner;
145 let iter = questdb_conf_str_iter {
146 inner: conf_str.params().iter(),
147 };
148 Box::into_raw(Box::new(iter))
149}
150
151#[no_mangle]
152pub unsafe extern "C" fn questdb_conf_str_iter_next(
153 iter: *mut questdb_conf_str_iter,
154 key_out: *mut *const c_char,
155 key_len_out: *mut usize,
156 val_out: *mut *const c_char,
157 val_len_out: *mut usize,
158) -> bool {
159 let iter = &mut *iter;
160 match iter.inner.next() {
161 Some((key, val)) => {
162 let key_str = key.as_ptr() as *const c_char;
163 let val_str = val.as_ptr() as *const c_char;
164 unsafe {
165 *key_out = key_str;
166 *key_len_out = key.len();
167 *val_out = val_str;
168 *val_len_out = val.len();
169 }
170 true
171 }
172 None => false,
173 }
174}
175
176#[no_mangle]
177pub unsafe extern "C" fn questdb_conf_str_iter_free(iter: *mut questdb_conf_str_iter) {
178 if !iter.is_null() {
179 drop(Box::from_raw(iter));
180 }
181}
182
183#[no_mangle]
184pub unsafe extern "C" fn questdb_conf_str_free(conf_str: *mut questdb_conf_str) {
185 if !conf_str.is_null() {
186 drop(Box::from_raw(conf_str));
187 }
188}