Hi,
I am working on creating an FFI binding for the INDIGO Astronomy lib in C. Bindgen happily creates all of the 1243 constants used for property and item names into constant byte string literals. Here are the first few lines:
pub const CONNECTION_PROPERTY_NAME: &[u8; 11] = b"CONNECTION\0";
pub const CONNECTION_CONNECTED_ITEM_NAME: &[u8; 10] = b"CONNECTED\0";
pub const CONNECTION_DISCONNECTED_ITEM_NAME: &[u8; 13] = b"DISCONNECTED\0";
pub const INFO_PROPERTY_NAME: &[u8; 5] = b"INFO\0";
...
Currently I am using a simple function that converts these byte literals into a String
:
fn const_to_string(name: &[u8]) -> String {
// if we are calling with a faulty argument it is a bug that warrants the ensuing panic...
let name = CStr::from_bytes_with_nul(name).unwrap();
name.to_string_lossy().into_owned()
}
... but ideally I would like to create an PropertyName
and PropertyItemName
enums for my wrapper that:
- Provides an
&str
reference backed by the byte string literal. - Maps between the value of the FFI struct fields to the corresponding enum variant.
- Maps the enum variant to the backing byte string literal
Something along these lines:
pub enum PropertyName {
Connection,
Info,
...
}
pub enum PropertyItemName {
ConnectionConnected,
ConnectionDisconnected,
...
}
impl PropertyName {
pub fn name() -> &str { ... }
pub fn bytes() -> &[u8] { ... }
}
impl Into<&str> for PropertyName { ... }
impl<const N: usize> Into<&[c_char; N]> for PropertyName { ... }
impl<const N: usize> From<&[c_char; N]> for PropertyName { ... }
// Display, Debug, Eq, PartialEq, Copy, and Clone trait implementations
// Same traits implementations for PropertyItemName
...
Given the large number of constants, it would be desirable if this can be achieved automatically based on the name convention that ends each constant with either _PROPERTY_NAME
or _ITEM_NAME
...
I realise this is a tall order and I suspect that I would have to resort to writing macros and/or somehow extend/tweak bindgen
.
Some of the enum related crates (e.g. enum-assoc, or enum_from_functions) seem to be usable for easing the pain but there still seems to be quite a bit of boilerplate to write. This Stack Overflow thread has some useful pointers, but I could still benefit from some advice in my specific case.
This is my first Rust project. I am still learning the language and ecosystem, so please bear with me if I miss something evident.
Appreciate any feedback/help on how to best approach or solve this issue!