dustr 0.1.2

Generate dart code based on a rust ffishim
Documentation
use ::heck::{MixedCase,SnakeCase};
use ::itertools::Itertools;
use crate::helpers::*;
use crate::types::Behavior;

impl ::std::fmt::Display for crate::Struct {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f,
            "class {name} extends Struct {{\n\
                \t{declarations}\n\
                \n\
                \t{getters}\n\
                \n\
                \t{new_method}\n\
                \t{free_method}\n\
            }}\n\
            {new_func}\n\
            {free_func}\n\
            ",
            name = self.name,

            declarations = self.declarations.iter().join("\n\t"),
            getters = self.getters.iter().join("\n\t"),

            new_method = tostring_if_some(&self.new_method),
            free_method = tostring_if_some(&self.free_method),

            new_func = tostring_if_some(&self.new_func),
            free_func = tostring_if_some(&self.free_func),
        )
    }
}

impl crate::Struct {
    pub fn from_data(data: &::ffishim::Data) -> ::anyhow::Result<Self> {
        if data.opaque {
            Ok(Self{
                name: data.ident.to_string(),

                declarations: vec![],
                getters: vec![],

                new_func: None,
                new_method: None,
                free_func: Some(crate::Function::free_function(&data.ident)?),
                free_method: Some(crate::FreeMethod::from_func_name(
                    format!("free{}", data.ident),
                    Some(data.ident.to_string()),
                )),
            })
        } else {
            Self::from_fields(&data.ident, unwrap_fields(data))
        }
    }

    pub fn from_variant(
        enum_name: &::syn::Ident,
        variant: &::ffishim::Variant,
    ) -> ::anyhow::Result<Self> {
        let mut s = Self::from_fields(&concat_idents(enum_name, &variant.ident), &variant.fields)?;

        s.declarations.insert(0, crate::FieldDeclaration::enum_tag());
        s.getters.insert(0, crate::Getter::enum_tag(enum_name));
        s.getters.insert(1, crate::Getter::enum_repr(enum_name));

        s.free_func = None;
        s.free_method = Some(crate::FreeMethod::from_func_name(
            format!("free{}", enum_name),
            Some(enum_name.to_string()),
        ));

        Ok(s)
    }

    pub fn from_fields(
        name: &::syn::Ident,
        fields: &::darling::ast::Fields<::ffishim::Field>,
    ) -> ::anyhow::Result<Self> {
        let declarations = crate::FieldDeclaration::from_fields(fields);
        let getters = crate::Getter::from_fields(fields);

        let mut new_func = None;
        let mut new_method = None;
        if fields.iter().all(|f| !f.opaque) {
            new_func = Some(generate_new_func(&name, fields)?);
            new_method = Some(crate::NewMethod::new(
                name.to_string(),
                new_func.as_ref().expect("new_func was just initialized").name.clone(),
                fields,
            ));
        }

        let free_func = crate::Function::free_function(&name)?;
        let free_method = crate::FreeMethod::from_func_name(free_func.name.clone(), None);

        Ok(Self{
            name: name.to_string(),
            declarations,
            getters,

            new_func,
            new_method,
            free_func: Some(free_func),
            free_method: Some(free_method),
        })
    }
}

fn annotation(sty: &::syn::Type) -> Option<String> {
    if crate::types::BehaviorScalars.is(sty) {
        Some(format!("@{}()", crate::types::BehaviorScalars.shim(sty)))
    } else if crate::types::BehaviorDuration.is(sty) {
        Some(format!("@{}()", crate::types::BehaviorDuration.shim(sty)))
    } else if crate::types::BehaviorBool.is(sty) {
        Some(format!("@{}()", crate::types::BehaviorBool.shim(sty)))
    } else {
        None
    }
}

pub fn generate_new_func(
    name: &::syn::Ident,
    fields: &::darling::ast::Fields<::ffishim::Field>,
) -> ::anyhow::Result<crate::Function> {
    let shim_name = format!("new_{}", name.to_string().to_snake_case());

    Ok(crate::Function{
        lib_name: "dylib".to_owned(),

        name: shim_name.to_mixed_case(),
        field_types: fields.iter().map(|f| {
            crate::types::switch(&f.ty).ffi(&f.ty)
        }).collect(),
        ret_type: format!("Pointer<{}>", name),

        shim_name,
        shim_field_types: fields.iter().map(|f| crate::types::switch(&f.ty).shim(&f.ty)).collect(),
        shim_ret_type: format!("Pointer<{}>", name),
    })
}

fn unwrap_fields(d: &::ffishim::Data) -> &::darling::ast::Fields<::ffishim::Field> {
    match &d.data {
        ::darling::ast::Data::Struct(fields) => fields,
        ::darling::ast::Data::Enum(_) => panic!("cannot build struct out of enum"),
    }
}

impl ::std::fmt::Display for crate::NewMethod {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f,
            "factory {struct_name}.build({args}) => {call_expr};",
            struct_name = self.struct_name,
            args = self.args.iter().map(|(t, n)| format!("{} {}", t, n)).join(", "),
            call_expr = self.call_expr,
        )
    }
}
impl crate::NewMethod {
    fn new(
        struct_name: String,
        new_func: String,
        fields: &::darling::ast::Fields<::ffishim::Field>,
    ) -> Self {
        Self{
            struct_name,
            args: fields.iter().enumerate().map(|(idx, f)| (
                crate::types::switch(&f.ty).native(&f.ty),
                name_of_field(idx as u32, &f.ident),
            )).collect(),
            call_expr: format!(
                "{}({}).ref",
                new_func,
                fields.iter().enumerate().map(|(idx, f)| {
                    let name = name_of_field(idx as u32, &f.ident);
                    crate::types::switch(&f.ty).native_to_ffi(&f.ty, name)
                }).join(", "),
            ),
        }
    }
}

impl ::std::fmt::Display for crate::FreeMethod {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        if let Some(cast_to) = self.cast_to.as_ref() {
            write!(f,
               "void free() {{ {name}(Pointer<{cast_to}>.fromAddress(addressOf.address)); }}",
               name = self.func_name,
               cast_to = cast_to,
            )
        } else {
            write!(f, "void free() {{ {name}(addressOf); }}", name = self.func_name)
        }
    }
}
impl crate::FreeMethod {
    pub fn from_func_name(func_name: String, cast_to: Option<String>) -> Self {
        Self{func_name, cast_to}
    }
}

impl ::std::fmt::Display for crate::FieldDeclaration {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f,
            "{annotation} {ffi_type} {name};",
            annotation = tostring_if_some(&self.annotation),
            ffi_type = self.ffi_type,
            name = self.name,
        )
    }
}
impl crate::FieldDeclaration {
    pub fn enum_tag() -> Self {
        Self{
            annotation: Some("@Uint16()".to_owned()),
            ffi_type: "int".to_owned(),
            name: "_tag".to_owned(),
        }
    }

    fn from_fields(fields: &::darling::ast::Fields<::ffishim::Field>) -> Vec<Self> {
        fields.iter().enumerate().map(|(idx, f)| {
            let name = format!("_{}", name_of_field(idx as u32, &f.ident));
            if f.opaque {
                Self{annotation: None, ffi_type: "Pointer<Void>".to_owned(), name}
            } else {
                Self{
                    annotation: annotation(&f.ty),
                    ffi_type: crate::types::switch(&f.ty).ffi(&f.ty),
                    name,
                }
            }
        }).collect()
    }
}

impl ::std::fmt::Display for crate::Getter {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f,
            "{native_type} get {name} => {expr};",
            native_type = self.native_type,
            name = self.name,
            expr = self.expr,
        )
    }
}
impl crate::Getter {
    fn from_fields(fields: &::darling::ast::Fields<::ffishim::Field>) -> Vec<Self> {
        fields.iter().enumerate().filter_map(|(idx, f)| {
            if f.opaque {
                None
            } else {
                let behavior = crate::types::switch(&f.ty);
                let getter_name = name_of_field(idx as u32, &f.ident);
                let field_name = format!("_{}", getter_name);
                Some(Self{
                    native_type: behavior.native(&f.ty),
                    name: getter_name,
                    expr: behavior.ffi_to_native(&f.ty, field_name),
                })
            }
        }).collect()
    }

    pub fn enum_tag(enum_name: &::syn::Ident) -> Self {
        Self{
            native_type: format!("{}Tag", enum_name),
            name: "tag".to_owned(),
            expr: format!("{}Tag.values[_tag]", enum_name),
        }
    }

    pub fn enum_variant(enum_ident: &::syn::Ident, variant_ident: &::syn::Ident) -> Self {
        let fullname = concat_idents(&enum_ident, &variant_ident);
        let expr = format!("Pointer<{}>.fromAddress(addressOf.address).ref", fullname);
        Self{
            native_type: fullname.to_string(),
            name: variant_ident.to_string(),
            expr,
        }
    }

    fn enum_repr(enum_ident: &::syn::Ident) -> Self {
        Self{
            native_type: enum_ident.to_string(),
            name: "repr".to_owned(),
            expr: format!("Pointer<{}>.fromAddress(addressOf.address).ref", enum_ident),
        }
    }
}