use clang::*; use inflector::Inflector; use crate::build_utils::{ config::HandlerConfigs, handle_function_prototype::MethodFlavor, process_children, HandlerMap, }; use super::RecordFlavor; pub fn handle_spi_record( entity: &Entity, handlers: &HandlerMap, configs: &mut HandlerConfigs, full_rust_struct_name: &str, ) -> Vec { let mut lines: Vec = Vec::new(); let vtable_struct_name = format!("{full_rust_struct_name}VTable"); let full_trait_name = format!("{full_rust_struct_name}Trait"); lines.push(format!("\n/* Generated by handle_spi_trait */")); lines.extend(handle_spi_trait( entity, handlers, configs, &full_rust_struct_name, &full_trait_name, )); lines.push(format!("\n/* Generated by handle_spi_vtable */")); lines.extend(handle_spi_vtable( entity, handlers, configs, &full_rust_struct_name, &vtable_struct_name, )); lines.push(format!("\n/* Generated by handle_spi_output_enum */")); lines.extend(handle_spi_output_enum( entity, handlers, configs, &full_rust_struct_name, )); lines.push(format!( "\n/* Generated by handle_spi_output_enum_struct */" )); lines.extend(handle_spi_output_enum_struct(entity, handlers, configs)); lines.push(format!("\n/* Generated by handle_spi_static_table */")); lines.extend(handle_spi_static_table( entity, handlers, configs, &full_rust_struct_name, )); lines.push(format!("\n/* Generated by handle_spi_c_fn */")); lines.extend(handle_spi_c_fn(entity, handlers, configs)); lines.push(format!("\n/* Generated by handle_spi_fat */")); lines.push(handle_spi_fat( entity, handlers, configs, &full_rust_struct_name, &vtable_struct_name, &full_trait_name, )); lines.push(format!("\n/* Generated by handle_spi_stream_code */")); lines.push(handle_spi_stream_code( full_rust_struct_name, &format!("{full_rust_struct_name}Output"), )); lines.push(format!("\n/* Generated by handle_spi_fn */")); lines.extend(handle_spi_fn( entity, handlers, configs, &full_trait_name, &full_rust_struct_name, )); lines } pub fn handle_spi_trait( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_rust_struct_name: &str, full_trait_name: &str, ) -> Vec { let mut lines = Vec::new(); lines.push(format!("\npub trait {full_trait_name}<'a> {{\n")); lines.extend(process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::SpiTrait, ..configs.clone() }, )); lines.push("}\n".to_string()); lines } pub fn handle_spi_vtable( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_rust_struct_name: &str, vtable_struct_name: &str, ) -> Vec { let mut lines = Vec::new(); lines.push(format!( r#" #[repr(C)] #[derive(Debug)] pub struct {vtable_struct_name} {{ "# )); lines.extend(process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::VTableStruct, ..configs.clone() }, )); lines.push("}\n".to_string()); lines } pub fn handle_spi_output_enum( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_rust_struct_name: &str, ) -> Vec { let mut lines = Vec::new(); let full_spi_output_enum_name = format!("{full_rust_struct_name}Output<'a>"); lines.push(format!( r#" #[derive(Clone)] pub enum {full_spi_output_enum_name} {{ "# )); lines.extend(process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::OutputEnum, ..configs.clone() }, )); lines.push("}\n".to_string()); lines } pub fn handle_spi_output_enum_struct( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, ) -> Vec { process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::OutputEnumStruct, ..configs.clone() }, ) } pub fn handle_spi_static_table( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_rust_struct_name: &str, ) -> Vec { let mut lines = Vec::new(); let full_static_vtable_var_name = Inflector::to_snake_case(full_rust_struct_name).to_uppercase() + "_VTABLE"; let vtable_struct_name = format!("{full_rust_struct_name}VTable"); lines.push(format!( r#" pub static {full_static_vtable_var_name}: {vtable_struct_name} = {vtable_struct_name} {{ "# )); lines.extend(process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::StaticTable, ..configs.clone() }, )); lines.push("};\n".to_string()); lines } pub fn handle_spi_c_fn( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, ) -> Vec { process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::CFn, record_flavor: RecordFlavor::SPI, ..configs.clone() }, ) } pub fn handle_spi_fat( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_rust_struct_name: &str, vtable_struct_name: &str, full_trait_name: &str, ) -> String { format!( r#" #[repr(C)] pub struct {full_rust_struct_name}Fat<'a> {{ vtable: *const {vtable_struct_name}, pub md_spi_ptr: *mut dyn {full_trait_name}<'a>, }} "# ) } pub fn handle_spi_stream_code(full_spi_name: &str, full_spi_output_enum_name: &str) -> String { format!( r#" use futures::stream::Stream; use std::{{ pin::Pin, sync::{{Arc, Mutex}}, task::Waker, }}; struct {full_spi_name}Inner<'a> {{ buf: std::collections::VecDeque<{full_spi_output_enum_name}<'a>>, waker: Option, }} impl<'a> {full_spi_name}Inner<'a> {{ fn push(&mut self, msg: {full_spi_output_enum_name}<'a>) {{ self.buf.push_back(msg); if let Some(ref waker) = &self.waker {{ waker.clone().wake() }} }} }} pub struct {full_spi_name}Stream<'a: 'static> {{ inner: Arc>>, }} impl<'a> Stream for {full_spi_name}Stream<'a> {{ type Item = {full_spi_output_enum_name}<'a>; fn poll_next( self: Pin<&mut Self>, cx: &mut futures::task::Context<'_>, ) -> futures::task::Poll> {{ use futures::task::Poll; let mut inner = self.inner.lock().unwrap(); if let Some(i) = inner.buf.pop_front() {{ Poll::Ready(Some(i)) }} else {{ inner.waker = Some(cx.waker().clone()); Poll::Pending }} }} fn size_hint(&self) -> (usize, Option) {{ (0, None) }} }} pub fn create_spi() -> (Box<{full_spi_name}Stream<'static>>, *mut {full_spi_name}Stream<'static>) {{ let i = {full_spi_name}Inner {{ buf: std::collections::VecDeque::new(), waker: None, }}; let xspi = {full_spi_name}Stream {{ inner: Arc::new(Mutex::new(i)), }}; let myspi = Box::new(xspi); let pp = Box::into_raw(myspi); let pp2 = pp.clone(); (unsafe {{ Box::from_raw(pp2) }}, pp) }} "#, ) } pub fn handle_spi_fn( entity: &Entity, handlers: &HandlerMap, configs: &HandlerConfigs, full_trait_name: &str, full_rust_struct_name: &str, ) -> Vec { let mut lines = Vec::new(); lines.push(format!( "\nimpl<'a> {full_trait_name}<'a> for {full_rust_struct_name}Stream<'a> {{\n", )); lines.extend(process_children( entity, handlers, &mut HandlerConfigs { method_flavor: MethodFlavor::SpiFn, ..configs.clone() }, )); lines.push("}\n".to_string()); lines }