use std::fmt::{Display, Formatter};
use std::ops::Add;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::__private::TokenStream2;
use crate::ast::{CommaPunctuatedTokens, Depunctuated, SemiPunctuated};
use crate::composer::SourceFermentable;
use crate::lang::LangFermentable;
use crate::lang::objc::ObjCFermentate;
use crate::lang::objc::presentable::ArgPresentation;
use crate::tree::ScopeTree;
#[derive(Clone, Debug)]
pub enum InterfaceImplementation {
Default {
objc_name: TokenStream2,
properties: SemiPunctuated<ArgPresentation>,
},
BindingsDeclaration {
objc_name: TokenStream2,
c_name: TokenStream2,
},
BindingsImplementation {
objc_name: TokenStream2,
c_name: TokenStream2,
to_conversions: CommaPunctuatedTokens,
property_names: CommaPunctuatedTokens
},
ConversionsDeclaration {
objc_name: TokenStream2,
c_name: TokenStream2,
},
ConversionsImplementation {
objc_name: TokenStream2,
c_name: TokenStream2,
from_conversions_statements: TokenStream2,
to_conversions_statements: TokenStream2,
destroy_body: TokenStream2,
},
MacroCall(TokenStream2)
}
impl Display for InterfaceImplementation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
InterfaceImplementation::Default { objc_name, properties } => {
f.write_fmt(format_args!("@interface {objc_name} : NSObject\n"))?;
for property in properties {
f.write_fmt(format_args!("{};\n", property.to_token_stream()))?;
}
f.write_str("@end")
}
InterfaceImplementation::BindingsDeclaration { objc_name, c_name } => {
f.write_fmt(format_args!("@interface {objc_name} (Bindings_{c_name})\n"))?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_ctor:({objc_name}*)obj;\n"))?;
f.write_fmt(format_args!("+ (void)ffi_dtor:(struct {c_name} *)ffi_ref;\n"))?;
f.write_str("@end")
}
InterfaceImplementation::BindingsImplementation { objc_name, c_name, to_conversions, property_names: properties } => {
f.write_fmt(format_args!("@implementation {objc_name} (Bindings_{c_name})\n"))?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_ctor:({objc_name} *)obj {{\n"))?;
for to_conversion in to_conversions {
f.write_fmt(format_args!("\t{};\n", to_conversion))?;
}
f.write_fmt(format_args!("\treturn {c_name}_ctor({});\n", properties.to_token_stream()))?;
f.write_str("}\n")?;
f.write_fmt(format_args!("+ (void)ffi_dtor:(struct {c_name} *)ffi_ref {{\n"))?;
f.write_fmt(format_args!("\t{c_name}_destroy(ffi_ref);\n"))?;
f.write_str("}\n")?;
f.write_str("@end")
}
InterfaceImplementation::ConversionsDeclaration { objc_name, c_name } => {
f.write_fmt(format_args!("@interface {objc_name} (Conversions_{c_name})\n"))?;
f.write_fmt(format_args!("+ ({objc_name} *)ffi_from:(struct {c_name} *)ffi_ref;\n"))?;
f.write_fmt(format_args!("+ ({objc_name} * _Nullable)ffi_from_opt:(struct {c_name} *)ffi_ref;\n"))?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_to:({objc_name} *)obj;\n"))?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_to_opt:({objc_name} * _Nullable)obj;\n"))?;
f.write_fmt(format_args!("+ (void)ffi_destroy:(struct {c_name} *)ffi_ref;\n"))?;
f.write_str("@end")
}
InterfaceImplementation::ConversionsImplementation { objc_name, c_name, from_conversions_statements, to_conversions_statements, destroy_body } => {
f.write_fmt(format_args!("@implementation {objc_name} (Conversions_{c_name})\n"))?;
f.write_fmt(format_args!("+ ({objc_name} *)ffi_from:(struct {c_name} *)ffi_ref {{\n"))?;
if !from_conversions_statements.is_empty() {
f.write_fmt(format_args!("\t{}\n", from_conversions_statements))?;
}
f.write_str("}\n")?;
f.write_fmt(format_args!("+ ({objc_name} * _Nullable)ffi_from_opt:(struct {c_name} *)ffi_ref {{\n"))?;
f.write_str("\treturn ffi_ref ? [self ffi_from:ffi_ref] : nil;\n")?;
f.write_str("}\n")?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_to:({objc_name} *)obj {{\n"))?;
if !to_conversions_statements.is_empty() {
f.write_fmt(format_args!("\t{}\n", to_conversions_statements))?;
}
f.write_str("}\n")?;
f.write_fmt(format_args!("+ (struct {c_name} *)ffi_to_opt:({objc_name} * _Nullable)obj {{\n"))?;
f.write_str("\treturn obj ? [self ffi_to:obj] : nil;\n")?;
f.write_str("}\n")?;
f.write_fmt(format_args!("+ (void)ffi_destroy:(struct {c_name} *)ffi_ref {{\n"))?;
f.write_fmt(format_args!("\t{}\n", destroy_body))?;
f.write_str("}\n")?;
f.write_str("@end")
}
InterfaceImplementation::MacroCall(macro_call) =>
f.write_str(macro_call.to_string().as_str())
}
}
}
impl ToTokens for InterfaceImplementation {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
InterfaceImplementation::Default { objc_name, properties } => {
quote! {
@interface #objc_name: NSObject
#properties;
@end
}
}
InterfaceImplementation::BindingsDeclaration { objc_name, c_name } => {
let interface_name = format_ident!("Bindings_{}", c_name.to_string());
quote! {
@interface #objc_name (#interface_name)
+ (struct #c_name *)ffi_ctor:(#objc_name *)obj;
+ (void)ffi_dtor:(struct #c_name *)ffi_ref;
@end
}
}
InterfaceImplementation::BindingsImplementation { objc_name, c_name, to_conversions, property_names: _ } => {
let ctor_name = format_ident!("{}_ctor", c_name.to_string());
let dtor_name = format_ident!("{}_destroy", c_name.to_string());
let interface_name = format_ident!("Bindings_{}", c_name.to_string());
quote! {
@implementation #objc_name (#interface_name)
+ (struct #c_name *)ffi_ctor:(#objc_name *)obj {
return #ctor_name(#to_conversions);
}
+ (void)ffi_dtor:(struct #c_name *)ffi_ref {
#dtor_name(ffi_ref);
}
@end
}
}
InterfaceImplementation::ConversionsDeclaration { objc_name, c_name } => {
let interface_name = format_ident!("Conversions_{}", c_name.to_string());
quote! {
@interface #objc_name (#interface_name)
+ (#objc_name *)ffi_from:(struct #c_name *)ffi_ref;
+ (#objc_name * _Nullable)ffi_from_opt:(struct #c_name *)ffi_ref;
+ (struct #c_name *)ffi_to:(#objc_name *)obj;
+ (struct #c_name *)ffi_to_opt:(#objc_name * _Nullable)obj;
+ (void)ffi_destroy:(struct #c_name *)ffi_ref;
@end
}
}
InterfaceImplementation::ConversionsImplementation { objc_name, c_name, from_conversions_statements: from_conversions, to_conversions_statements: to_conversions, destroy_body } => {
let interface_name = format_ident!("Conversions_{}", c_name.to_string());
quote! {
@implementation #objc_name (#interface_name)
+ (#objc_name *)ffi_from:(struct #c_name *)ffi_ref {
#from_conversions
}
+ (#objc_name * _Nullable)ffi_from_opt:(struct #c_name *)ffi_ref {
return ffi_ref ? [self ffi_from:ffi_ref] : nil;
}
+ (struct #c_name *)ffi_to:(#objc_name *)obj {
#to_conversions
}
+ (struct #c_name *)ffi_to_opt:(#objc_name * _Nullable)obj {
return obj ? [self ffi_to:obj] : nil;
}
+ (void)ffi_destroy:(struct #c_name *)ffi_ref {
#destroy_body
}
@end
}
},
InterfaceImplementation::MacroCall(macro_call) => {
quote! {
#macro_call
}
}
}.to_tokens(tokens)
}
}
#[derive(Clone, Debug)]
pub enum Fermentate {
Empty,
TokenStream(TokenStream2),
Item {
implementations: Depunctuated<InterfaceImplementation>
},
ScopeTree(ScopeTree),
}
impl LangFermentable for Fermentate {}
impl Default for Fermentate {
fn default() -> Self {
Self::Empty
}
}
fn add_fermented_string(acc: String, i: &InterfaceImplementation) -> String {
acc.add(format!("{i}\n").as_str())
}
impl Display for Fermentate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Empty => {
f.write_str("\n")
},
Self::Item { implementations } => {
f.write_str(implementations.iter().fold(String::new(), add_fermented_string).as_str())
}
Self::TokenStream(token_stream) =>
f.write_str(token_stream.to_string().as_str()),
Self::ScopeTree(tree) =>
f.write_str(<ScopeTree as SourceFermentable<ObjCFermentate>>::ferment(tree).to_token_stream().to_string().as_str()),
}
}
}
impl ToTokens for Fermentate {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Fermentate::Item { implementations } =>
implementations.to_tokens(tokens),
Fermentate::TokenStream(token_stream) =>
token_stream.to_tokens(tokens),
Fermentate::ScopeTree(tree) =>
<ScopeTree as SourceFermentable<ObjCFermentate>>::ferment(tree).to_tokens(tokens),
_ => {}
}
}
}