use std::ffi::{CStr, c_char};
use std::ptr;
use aivdm::AisMessage;
pub struct AivdmMessage(AisMessage);
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AivdmError {
Ok = 0,
NullInput = 1,
InvalidUtf8 = 2,
Nmea = 3,
IncompleteFragment = 4,
Fragment = 5,
Bit = 6,
Message = 7,
}
impl From<aivdm::AisError> for AivdmError {
fn from(e: aivdm::AisError) -> Self {
match e {
aivdm::AisError::Nmea(_) => Self::Nmea,
aivdm::AisError::IncompleteFragment => Self::IncompleteFragment,
aivdm::AisError::Fragment(_) => Self::Fragment,
aivdm::AisError::Bit(_) => Self::Bit,
_ => Self::Message,
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_decode_line(
line: *const c_char,
out_error: *mut AivdmError,
) -> *mut AivdmMessage {
let set_error = |code: AivdmError| {
if !out_error.is_null() {
unsafe {
out_error.write(code);
}
}
};
if line.is_null() {
set_error(AivdmError::NullInput);
return ptr::null_mut();
}
let Ok(line_str) = unsafe { CStr::from_ptr(line) }.to_str() else {
set_error(AivdmError::InvalidUtf8);
return ptr::null_mut();
};
match aivdm::decode_line(line_str) {
Ok(msg) => {
set_error(AivdmError::Ok);
Box::into_raw(Box::new(AivdmMessage(msg)))
}
Err(e) => {
set_error(AivdmError::from(e));
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_free(msg: *mut AivdmMessage) {
if !msg.is_null() {
drop(unsafe { Box::from_raw(msg) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_type(msg: *const AivdmMessage) -> u8 {
match unsafe { msg.as_ref() } {
Some(msg) => msg.0.message_type(),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_repeat_indicator(msg: *const AivdmMessage) -> u8 {
match unsafe { msg.as_ref() } {
Some(msg) => msg.0.repeat_indicator(),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_mmsi(msg: *const AivdmMessage) -> u32 {
match unsafe { msg.as_ref() } {
Some(msg) => msg.0.mmsi().raw(),
None => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_position(
msg: *const AivdmMessage,
out_lat: *mut f64,
out_lon: *mut f64,
) -> bool {
let Some(msg) = (unsafe { msg.as_ref() }) else {
return false;
};
let Some(position) = msg.0.position() else {
return false;
};
unsafe {
out_lat.write(position.latitude);
out_lon.write(position.longitude);
}
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_sog_knots(
msg: *const AivdmMessage,
out_sog: *mut f64,
) -> bool {
let Some(msg) = (unsafe { msg.as_ref() }) else {
return false;
};
let Some(sog) = msg.0.sog_knots() else {
return false;
};
unsafe {
out_sog.write(sog);
}
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_cog_degrees(
msg: *const AivdmMessage,
out_cog: *mut f64,
) -> bool {
let Some(msg) = (unsafe { msg.as_ref() }) else {
return false;
};
let Some(cog) = msg.0.cog_degrees() else {
return false;
};
unsafe {
out_cog.write(cog);
}
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_heading_degrees(
msg: *const AivdmMessage,
out_heading: *mut u16,
) -> bool {
let Some(msg) = (unsafe { msg.as_ref() }) else {
return false;
};
let Some(heading) = msg.0.heading_degrees() else {
return false;
};
unsafe {
out_heading.write(heading);
}
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aivdm_message_navigation_status(
msg: *const AivdmMessage,
out_status: *mut u8,
) -> bool {
let Some(msg) = (unsafe { msg.as_ref() }) else {
return false;
};
let Some(status) = msg.0.navigation_status() else {
return false;
};
let status = status.to_raw();
unsafe {
out_status.write(status);
}
true
}
#[unsafe(no_mangle)]
pub extern "C" fn aivdm_version() -> *const c_char {
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
VERSION.as_ptr().cast::<c_char>()
}