specta-elm 0.0.1

Yey! now your Rust types in Elm ;)
Documentation
use specta::{
    Types,
    datatype::{Fields, Struct, UnnamedFields},
};

use crate::{
    Error, Exporter,
    module::ModuleImports,
    types::{
        NDT,
        fields::{self, datatype_with_inline_attr},
        primitive::{INDENT, NULL},
    },
};

pub fn render<E: Exporter>(
    s: &mut String,
    imports: &mut ModuleImports,
    exporter: &E,
    types: &Types,
    ndt: &NDT,
    st: &Struct,
    parent_name: Option<&str>,
) -> Result<(), Error> {
    match &st.fields {
        Fields::Unit => s.push_str(NULL),

        Fields::Unnamed(unnamed) => unnamed_fields_datatype(
            s,
            imports,
            exporter,
            types,
            ndt,
            &unnamed,
            parent_name,
            false,
        )?,
        Fields::Named(named) => {
            let fields = named
                .fields
                .iter()
                .filter_map(|(name, field)| field.ty.as_ref().map(|ty| (name, (field, ty))))
                .collect::<Vec<_>>();

            if fields.is_empty() {
                return Ok(());
            }

            let mut unflattened_fields: Vec<String> = Vec::with_capacity(fields.len());
            for (key, (field, ty)) in fields {
                let mut other = String::new();
                let mut field_location = Vec::from(&[ndt.rust_type_path()]);
                field_location.push(key.clone());
                fields::render(
                    &mut other,
                    imports,
                    exporter,
                    types,
                    ndt,
                    key.clone(),
                    (field, ty),
                    parent_name,
                    false,
                    None,
                )?;
                unflattened_fields.push(other);
            }

            // eprintln!("unflattened: {:?}", unflattened_fields);

            let mut unflattened_fields = unflattened_fields.iter().peekable();
            if let Some(first) = unflattened_fields.next() {
                let first_field = format!("\n{INDENT}{{ {first}");
                s.push_str(&first_field);
            } else {
                panic!("no fields on struct")
            }

            while unflattened_fields.peek().is_some() {
                let field = unflattened_fields.next().unwrap();
                let field = format!("\n{INDENT}, {field}");
                s.push_str(&field);
            }

            s.push(' ');
            s.push('}');
        }
    }

    Ok(())
}

fn unnamed_fields_datatype<E: Exporter>(
    s: &mut String,
    imports: &mut ModuleImports,
    exporter: &E,
    types: &Types,
    ndt: &NDT,
    fields: &UnnamedFields,
    parent_name: Option<&str>,
    force_inline: bool,
) -> Result<(), Error> {
    let lonely_tuple_is_really_just_an_alias = fields.fields.len() == 1;
    let fields = fields.fields.clone();
    let mut field_dts = fields.into_iter().filter_map(|f| f.ty).peekable();

    if !lonely_tuple_is_really_just_an_alias {
        s.push_str(&format!("\n{INDENT}( "));
    }

    while let Some(dt) = field_dts.next() {
        datatype_with_inline_attr(
            s,
            imports,
            exporter,
            types,
            &dt,
            ndt,
            parent_name,
            force_inline,
        )?;

        if field_dts.peek().is_some() {
            s.push_str(", ");
        }
    }

    if !lonely_tuple_is_really_just_an_alias {
        s.push_str(" )");
    }

    Ok(())
}