factorio-frontend 0.1.1

Rust frontend that lowers Factorio mod sources into factorio-ir
Documentation
use syn::{Attribute, Expr, ExprLit, FnArg, Lit, Meta, Signature, Visibility};

use super::types::{
    lower_binding_pattern, receiver_source_string, return_type_string, type_source_string,
};

const fn visibility_prefix(visibility: &Visibility) -> &'static str {
    if matches!(visibility, Visibility::Public(_)) {
        "pub "
    } else {
        ""
    }
}

pub fn extract_doc_comments(attrs: &[Attribute]) -> Option<String> {
    let docs = attrs
        .iter()
        .filter_map(|attr| {
            if !attr.path().is_ident("doc") {
                return None;
            }

            let Meta::NameValue(meta) = &attr.meta else {
                return None;
            };

            let Expr::Lit(ExprLit {
                lit: Lit::Str(value),
                ..
            }) = &meta.value
            else {
                return None;
            };

            Some(value.value().trim().to_string())
        })
        .collect::<Vec<_>>();

    if docs.is_empty() {
        None
    } else {
        Some(docs.join("\n"))
    }
}

fn format_fn_arg_for_comment(arg: &FnArg) -> String {
    match arg {
        FnArg::Receiver(receiver) => receiver_source_string(receiver),
        FnArg::Typed(pat_type) => {
            let name = lower_binding_pattern(&pat_type.pat).unwrap_or_else(|_| "_".to_string());
            format!("{}: {}", name, type_source_string(&pat_type.ty))
        }
    }
}

pub fn function_header_comment(visibility: &Visibility, signature: &Signature) -> String {
    let params = signature
        .inputs
        .iter()
        .map(format_fn_arg_for_comment)
        .collect::<Vec<_>>()
        .join(", ");
    let return_suffix = return_type_string(signature)
        .map_or_else(String::new, |return_type| format!(" -> {return_type}"));

    format!(
        "{}fn {}({}){}",
        visibility_prefix(visibility),
        signature.ident,
        params,
        return_suffix
    )
}

pub fn struct_header_comment(
    visibility: &Visibility,
    name: &str,
    fields: &[factorio_ir::structure::StructField],
) -> String {
    if fields.is_empty() {
        return format!("{}struct {name}", visibility_prefix(visibility));
    }

    let field_list = fields
        .iter()
        .map(|field| {
            field
                .source_type
                .as_ref()
                .map_or_else(|| field.name.clone(), |ty| format!("{}: {ty}", field.name))
        })
        .collect::<Vec<_>>()
        .join(", ");

    format!(
        "{}struct {name} {{ {field_list} }}",
        visibility_prefix(visibility)
    )
}