mavspec_rust_gen 0.6.7

Rust code generation module for MAVSpec.
Documentation
use mavinspect::protocol::Microservices;

// See: https://doc.rust-lang.org/reference/keywords.html
const RUST_RESERVED_KEYWORDS: [&str; 50] = [
    "abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
    "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
    "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
    "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "typeof",
    "virtual", "unsafe", "unsized", "use", "where", "while", "yield",
];
const RUST_RESERVED_IDENTIFIERS: [&str; 1] = ["TryFrom"];

pub const MAX_COMMENT_LENGTH: usize = 80;
pub const NUMERIC_IDENTIFIER_PREFIX: &str = "_";
pub const RUST_KEYWORD_POSTFIX: &str = "_";
pub const EMPTY_IDENT_REPLACEMENT: &str = "_";

pub fn dialect_mod_name(dialect_name: &str) -> String {
    heck::AsSnakeCase(dialect_name).to_string()
}

pub fn dialect_enum_name(dialect_name: &str) -> String {
    heck::AsUpperCamelCase(dialect_name).to_string()
}

pub fn dialect_enum_specta_name(dialect_name: &str) -> String {
    format!("MavLinkDialect{}", dialect_enum_name(dialect_name),)
}

pub fn valid_rust_name(name: &str) -> String {
    if RUST_RESERVED_KEYWORDS.contains(&name) || RUST_RESERVED_IDENTIFIERS.contains(&name) {
        return format!("{name}{}", RUST_KEYWORD_POSTFIX);
    }

    let name = if name.is_empty() {
        EMPTY_IDENT_REPLACEMENT.to_string()
    } else {
        name.into()
    };

    match name.chars().next() {
        Some(ch) if ch.is_numeric() => format!("{}{}", NUMERIC_IDENTIFIER_PREFIX, name),
        None | Some(_) => name,
    }
}

pub fn split_description(value: &str) -> Vec<String> {
    let mut result = "".to_string();
    let mut pos = 0;
    let value = value.replace('\t', " ");

    for ch in value.chars() {
        pos += 1;
        if pos >= MAX_COMMENT_LENGTH && ch == ' ' {
            pos = 0;
            result.push('\n');
        } else {
            result.push(ch);
        }
    }

    result.split('\n').map(|s| s.to_string()).collect()
}

pub fn description_doc_comment_line(line: impl AsRef<str>) -> proc_macro2::TokenStream {
    let indented = line.as_ref().starts_with(" ");
    let line = format!(" {}", line.as_ref());

    if indented {
        quote::quote! { #[cfg_attr(not(doctest), doc = #line)] }
    } else {
        quote::quote! {
            #[doc = #line]
        }
    }
}

pub fn enum_rust_name(enum_name: &str) -> String {
    valid_rust_name(heck::AsUpperCamelCase(enum_name).to_string().as_str())
}

pub fn enum_specta_name(enum_name: &str, dialect_name: &str) -> String {
    enum_rust_name(format!("{}_{}", dialect_name, enum_name).as_str())
}

pub fn enum_mod_name(enum_name: &str) -> String {
    valid_rust_name(heck::AsSnakeCase(enum_name).to_string().as_str())
}

pub fn enum_file_name(message_name: &str) -> String {
    format!("{}.rs", enum_mod_name(message_name))
}

pub fn enum_entry_name(entry_name: &str) -> String {
    valid_rust_name(heck::AsUpperCamelCase(entry_name).to_string().as_str())
}

pub fn enum_bitmask_entry_name(entry_name: &str) -> String {
    valid_rust_name(entry_name)
}

pub fn message_mod_name(message_name: &str) -> String {
    valid_rust_name(heck::AsSnakeCase(message_name).to_string().as_str())
}

pub fn message_file_name(message_name: &str) -> String {
    format!("{}.rs", message_mod_name(message_name))
}

pub fn messages_enum_entry_name(message_name: &str) -> String {
    message_struct_name(message_name)
}

pub fn message_struct_name(message_name: &str) -> String {
    valid_rust_name(heck::AsUpperCamelCase(message_name).to_string().as_str())
}

pub fn message_specta_name(message_name: &str, dialect_name: &str) -> String {
    message_struct_name(format!("{}_{}", dialect_name, message_name).as_str())
}

pub fn rust_var_name(var_name: &str) -> String {
    valid_rust_name(heck::AsSnakeCase(var_name).to_string().as_str())
}

pub fn microservice_mod_name(msrv_name: &str) -> String {
    heck::AsSnakeCase(msrv_name).to_string()
}

pub fn microservice_display_name(msrv_name: &str) -> String {
    heck::AsTitleCase(msrv_name).to_string()
}

pub fn microservice_doc_mention(msrv_name: &str) -> String {
    match Microservices::doc_link(msrv_name) {
        None => format!("`{}`", microservice_display_name(msrv_name)),
        Some(link) => format!("[`{}`]({})", microservice_display_name(msrv_name), link),
    }
}

pub fn microservice_msg_enum_specta_name(msrv_name: &str, dialect_canonical_name: &str) -> String {
    format!(
        "{}{}Msrv",
        dialect_enum_name(dialect_canonical_name),
        dialect_enum_name(msrv_name)
    )
}

pub fn microservice_msg_enum_name(msrv_name: &str) -> String {
    format!("{}Msrv", dialect_enum_name(msrv_name))
}

pub fn microservice_enum_specta_name(
    msrv_name: &str,
    enum_name: &str,
    dialect_name: Option<&str>,
) -> String {
    enum_rust_name(
        match dialect_name {
            None => format!("Msrv_{}_{}", msrv_name, enum_name),
            Some(dialect_name) => format!("{}_{}_{}", dialect_name, msrv_name, enum_name),
        }
        .as_str(),
    )
}

pub fn microservice_msg_specta_name(
    msrv_name: &str,
    msg_name: &str,
    dialect_name: Option<&str>,
) -> String {
    message_struct_name(
        match dialect_name {
            None => format!("Msrv_{}_{}", msrv_name, msg_name),
            Some(dialect_name) => format!("{}_{}_{}", dialect_name, msrv_name, msg_name),
        }
        .as_str(),
    )
}