alef 0.115.6

Opinionated polyglot binding generator for Rust libraries
Documentation
use super::super::conversions::primitive_name;
use crate::core::ir::{CoreWrapper, FieldDef, PrimitiveType, TypeRef};

struct FieldExpression {
    value: String,
    fallible: bool,
}

impl FieldExpression {
    fn plain(value: String) -> Self {
        Self { value, fallible: false }
    }

    fn finish(self) -> String {
        if self.fallible {
            format!("{}?", self.value)
        } else {
            self.value
        }
    }
}

pub(super) fn field_from_expr(field: &FieldDef, variable: &str) -> String {
    prepare(field, variable).finish()
}

fn wrapped(field: &FieldDef, ty: TypeRef, variable: &str) -> FieldExpression {
    prepare(
        &FieldDef {
            ty,
            optional: false,
            ..field.clone()
        },
        variable,
    )
}

fn optional(field: &FieldDef, inner: TypeRef, variable: &str) -> FieldExpression {
    let expression = wrapped(field, inner, "v");
    let value = if expression.value == "v.clone()" {
        format!("{variable}.clone()")
    } else if expression.fallible {
        format!("{variable}.as_ref().map(|v| {}).transpose()", expression.value)
    } else {
        format!("{variable}.as_ref().map(|v| {})", expression.value)
    };
    FieldExpression {
        value,
        fallible: expression.fallible,
    }
}

fn sequence(field: &FieldDef, inner: &TypeRef, variable: &str) -> FieldExpression {
    let expression = wrapped(field, inner.clone(), "x");
    let collect = if expression.fallible {
        "collect::<Result<_, String>>()"
    } else {
        "collect()"
    };
    FieldExpression {
        value: format!("{variable}.iter().map(|x| {}).{collect}", expression.value),
        fallible: expression.fallible,
    }
}

fn prepare(field: &FieldDef, variable: &str) -> FieldExpression {
    if field.optional && !matches!(field.ty, TypeRef::Optional(_)) {
        return optional(field, field.ty.clone(), variable);
    }
    match &field.ty {
        TypeRef::Optional(inner) => optional(field, *inner.clone(), variable),
        TypeRef::Vec(inner) => sequence(field, inner, variable),
        TypeRef::String if field.core_wrapper == CoreWrapper::StaticStr => FieldExpression {
            value: format!("lookup_static_str({variable})"),
            fallible: true,
        },
        _ => FieldExpression::plain(scalar(field, variable)),
    }
}

fn scalar(field: &FieldDef, variable: &str) -> String {
    match &field.ty {
        TypeRef::Primitive(PrimitiveType::I64 | PrimitiveType::F64 | PrimitiveType::Bool) => format!("*{variable}"),
        TypeRef::Primitive(primitive) => format!("*{variable} as {}", primitive_name(primitive)),
        TypeRef::Duration => format!("std::time::Duration::from_millis(*{variable} as u64)"),
        TypeRef::String if field.core_wrapper == CoreWrapper::Cow => {
            format!("std::borrow::Cow::Owned({variable}.clone())")
        }
        TypeRef::Char => format!("{variable}.chars().next().unwrap_or('\\0')"),
        _ => format!("{variable}.clone()"),
    }
}