use std::ops::RangeInclusive;
use std::{ptr, slice};
use crate::_macro::concat_byte_slices;
use crate::{ComponentOutputArgs, FromComponentOutputArgs};
pub mod mock;
pub struct ComponentInputs {
pub ctx: *mut std::ffi::c_void,
pub free_ctx: fn(ctx: *mut std::ffi::c_void),
pub text: fn(ctx: *mut std::ffi::c_void, input_id: TextInputId) -> String,
}
impl Drop for ComponentInputs {
fn drop(&mut self) {
(self.free_ctx)(self.ctx)
}
}
impl FromComponentOutputArgs for ComponentInputs {
fn from_args(args: ComponentOutputArgs) -> Self {
Self::new(args.inputs_ptr)
}
}
pub struct ComponentInput {
pub input_id: u16,
pub ty: ValueType,
pub name: &'static str,
pub description: &'static str,
}
pub enum ValueType {
Text {
length: RangeInclusive<u32>,
},
}
impl ValueType {
#[doc(hidden)]
pub const fn encode_text_bytes(&self) -> [u8; 10] {
let encoding_version = 0;
let text_header = 12;
match self {
ValueType::Text { length } => concat_byte_slices::<10>(&[
&[encoding_version, text_header],
&length.start().to_le_bytes(),
&length.end().to_le_bytes(),
]),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TextInputId(u16);
impl TextInputId {
#[doc(hidden)]
pub const fn _private_new(id: u16) -> Self {
Self(id)
}
}
impl ComponentInputs {
pub fn new(inputs_ptr: *const u8) -> Self {
Self {
ctx: inputs_ptr as *mut std::ffi::c_void,
free_ctx: |_inputs_ptr| {},
text: |ctx, _input_id| {
let inputs_ptr: *const u8 = ctx.cast();
let text_len_ptr = unsafe { inputs_ptr.add(3) };
let text_len = {
let text_len_ptr: *const u32 = text_len_ptr.cast();
unsafe { ptr::read_unaligned(text_len_ptr) }
};
let text_ptr = unsafe { text_len_ptr.add(4) };
let text_bytes = unsafe { slice::from_raw_parts(text_ptr, text_len as usize) };
let text_bytes = unsafe { str::from_utf8_unchecked(text_bytes) };
text_bytes.to_owned()
},
}
}
pub fn get_text(&self, text_id: TextInputId) -> String {
(self.text)(self.ctx, text_id)
}
}
#[macro_export]
macro_rules! define_text_input {
($definition:path) => {
{
#[cfg_attr(not(target_arch = "wasm32"), expect(unused, reason = "Rust does not consider this as used for some reason."))]
const BYTE_COUNT: usize =
1
+ 2
+ 10
+ 1 + $definition.name.as_bytes().len()
+ 2 + $definition.description.as_bytes().len()
;
#[cfg_attr(target_arch = "wasm32", used)]
#[cfg_attr(target_arch = "wasm32", unsafe(link_section = "__afia__$input_descriptors"))]
static _AFIA_INPUT_DESCRIPTOR: [u8; BYTE_COUNT] =
afia_component::_macro::concat_byte_slices(&[
&[0],
&$definition.input_id.to_le_bytes(),
&$definition.ty.encode_text_bytes(),
&[$definition.name.len() as u8],
$definition.name.as_bytes(),
&($definition.description.len() as u16).to_le_bytes(),
$definition.description.as_bytes(),
]);
const _ASSERT_IS_COMPONENT_INPUT: $crate::input::ComponentInput = $definition;
const _ASSERT_INPUT_TYPE_IS_TEXT: () = {
assert!(matches!($definition.ty, $crate::input::ValueType::Text { .. }));
};
$crate::input::TextInputId::_private_new($definition.input_id)
}
};
}