use std::{
ffi::{CString, NulError},
string::FromUtf8Error,
};
use xplane_sys::XPLMDataTypeID;
use crate::{
data::shared::{SharedData, SharedDataError, SharedDataHandler},
ffi::StringBuffer,
NoSendSync,
};
use self::{
borrowed::{DataRef, FindError},
owned::{CreateError, OwnedData},
};
pub mod borrowed;
pub mod owned;
pub mod shared;
pub enum ReadOnly {}
pub enum ReadWrite {}
pub trait Access {
fn writeable() -> bool;
}
impl Access for ReadOnly {
fn writeable() -> bool {
false
}
}
impl Access for ReadWrite {
fn writeable() -> bool {
true
}
}
pub trait DataRead<T> {
fn get(&self) -> T;
}
pub trait DataReadWrite<T>: DataRead<T> {
fn set(&mut self, value: T);
}
pub trait ArrayRead<T: ArrayType + ?Sized> {
fn get(&self, dest: &mut [T::Element]) -> usize;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn as_vec(&self) -> Vec<T::Element>
where
T::Element: Default + Clone,
{
let mut values = vec![T::Element::default(); self.len()];
self.get(&mut values);
values
}
}
pub trait ArrayReadWrite<T: ArrayType + ?Sized>: ArrayRead<T> {
fn set(&mut self, values: &[T::Element]);
}
pub trait StringRead {
fn get_to_string(&self, out: &mut String) -> Result<(), FromUtf8Error>;
fn get_as_string(&self) -> Result<String, FromUtf8Error>;
}
pub trait StringReadWrite: StringRead {
fn set_as_string(&mut self, value: &str) -> Result<(), NulError>;
}
impl<T> StringRead for T
where
T: ArrayRead<[u8]>,
{
fn get_to_string(&self, out: &mut String) -> Result<(), FromUtf8Error> {
let mut buffer = StringBuffer::new(self.len());
self.get(buffer.as_bytes_mut());
let value_string = buffer.into_string()?;
out.push_str(&value_string);
Ok(())
}
fn get_as_string(&self) -> Result<String, FromUtf8Error> {
let mut buffer = StringBuffer::new(self.len());
self.get(buffer.as_bytes_mut());
buffer.into_string()
}
}
impl<T> StringReadWrite for T
where
T: ArrayReadWrite<[u8]>,
{
fn set_as_string(&mut self, value: &str) -> Result<(), NulError> {
let name_c = CString::new(value)?;
self.set(name_c.as_bytes_with_nul());
Ok(())
}
}
pub trait DataType {
#[doc(hidden)]
type Storage: Sized;
#[doc(hidden)]
fn sim_type() -> XPLMDataTypeID;
#[doc(hidden)]
fn to_storage(&self) -> Self::Storage;
}
pub trait ArrayType: DataType {
type Element;
}
macro_rules! impl_type {
([$native_type:ty] as $sim_type:path) => {
impl DataType for [$native_type] {
type Storage = Vec<$native_type>;
fn sim_type() -> XPLMDataTypeID {
$sim_type
}
fn to_storage(&self) -> Self::Storage {
self.to_vec()
}
}
impl ArrayType for [$native_type] {
type Element = $native_type;
}
};
($native_type:ty as $sim_type:path) => {
impl DataType for $native_type {
type Storage = Self;
fn sim_type() -> XPLMDataTypeID {
$sim_type
}
fn to_storage(&self) -> Self::Storage {
self.clone()
}
}
};
}
impl_type!(bool as XPLMDataTypeID::Int);
impl_type!(u8 as XPLMDataTypeID::Int);
impl_type!(i8 as XPLMDataTypeID::Int);
impl_type!(u16 as XPLMDataTypeID::Int);
impl_type!(i16 as XPLMDataTypeID::Int);
impl_type!(u32 as XPLMDataTypeID::Int);
impl_type!(i32 as XPLMDataTypeID::Int);
impl_type!(f32 as XPLMDataTypeID::Float);
impl_type!(f64 as XPLMDataTypeID::Double);
impl_type!([i32] as XPLMDataTypeID::IntArray);
impl_type!([u32] as XPLMDataTypeID::IntArray);
impl_type!([f32] as XPLMDataTypeID::FloatArray);
impl_type!([u8] as XPLMDataTypeID::Data);
impl_type!([i8] as XPLMDataTypeID::Data);
pub struct DataApi {
pub(crate) _phantom: NoSendSync,
}
impl DataApi {
pub fn find<T: DataType + ?Sized, S: AsRef<str>>(
&mut self,
name: S,
) -> Result<DataRef<T, ReadOnly>, FindError> {
DataRef::find(name)
}
pub fn new_owned<T: DataType + Default + ?Sized, A: Access, S: AsRef<str>>(
&mut self,
name: S,
) -> Result<OwnedData<T, A>, CreateError> {
OwnedData::new_with_value(name, &T::default())
}
pub fn new_owned_with_value<T: DataType + ?Sized, A: Access, S: AsRef<str>>(
&mut self,
name: S,
value: &T,
) -> Result<OwnedData<T, A>, CreateError> {
OwnedData::new_with_value(name, value)
}
pub fn new_shared<S: Into<Vec<u8>>, T: DataType + ?Sized + 'static>(
&mut self,
name: S,
handler: impl SharedDataHandler<T>,
) -> Result<SharedData<T>, SharedDataError> {
SharedData::new(name, handler)
}
}