postcard-bindgen-core 0.7.1

A crate to generate bindings for the postcard binary format for other languages than Rust - Core Crate
Documentation
use std::{
    borrow::Cow,
    path::{Path, PathBuf},
};

use container::BindingTypeGenerateable;
use convert_case::{Case, Casing};
use genco::{quote, quote_in, tokens::quoted};

use crate::{
    code_gen::{python::ImportRegistry, utils::TokensIterExt},
    registry::{BindingType, Container, ContainerCollection, Module},
};

use super::{ExportFile, Tokens};

pub mod container;
pub mod types;

pub fn gen_typings(
    containers: &ContainerCollection,
    generate_package_name: String,
) -> Vec<ExportFile> {
    let mut files = Vec::new();

    let (containers, mods) = containers.containers_per_module();

    generate_typings_for_mod(
        "",
        containers.into_iter(),
        mods.into_iter(),
        &mut files,
        generate_package_name,
    );

    files
}

fn generate_typings_for_mod<'a>(
    package_path: impl AsRef<Path>,
    containers: impl Iterator<Item = Container> + Clone,
    mods: impl Iterator<Item = Module<'a>> + Clone,
    files: &mut Vec<ExportFile>,
    generate_package_name: String,
) {
    let container_exports = containers.clone().map(|f| {
        let mut l = vec![Cow::from(f.name)];
        if let BindingType::Enum(e) = f.r#type {
            l.extend(
                e.variants
                    .iter()
                    .map(|v| format!("{}_{}", f.name, v.name).into()),
            );
        }
        l
    });
    let mod_exports = mods.clone().map(|f| f.name().to_owned());

    let all_exports = container_exports
        .clone()
        .flatten()
        .chain(mod_exports.clone().map(|m| m.into()));

    let mut tokens = Tokens::new();

    let all_exports_tokens: Tokens = all_exports.map(|e| quote!($(quoted(e)))).join_with_comma();

    quote_in!(tokens=> __all__ = [
        $all_exports_tokens
    ]);

    tokens.line();

    for container_export in container_exports {
        let export_items = container_export.join(", ");
        quote_in!(tokens=> from ._$(container_export.first().unwrap().to_case(convert_case::Case::Snake)) import $export_items);
        tokens.push();
    }

    tokens.line();

    for mod_export in mod_exports {
        quote_in!(tokens=> from . import $mod_export);
        tokens.push();
    }

    let path = PathBuf::new().join("types").join(package_path);

    files.push(ExportFile {
        content_type: path.join("__init__").to_string_lossy().into_owned(),
        content: tokens,
    });

    for container in containers {
        let mut import_registry = ImportRegistry::new(generate_package_name.clone());
        let types = container
            .r#type
            .gen_typings_body((&container).into(), &mut import_registry);

        files.push(ExportFile {
            content_type: path
                .join(format!("_{}", container.name.to_case(Case::Snake)))
                .to_string_lossy()
                .into_owned(),
            content: quote! {
                $import_registry

                $types
            },
        });
    }

    for r#mod in mods {
        let (containers, mods) = r#mod.entries();

        generate_typings_for_mod(
            PathBuf::new().join(r#mod.path()).join(r#mod.name()),
            containers.into_iter(),
            mods.into_iter(),
            files,
            generate_package_name.clone(),
        );
    }
}

pub fn gen_basic_typings() -> Tokens {
    let types = [
        "u8",
        "i8",
        "u16",
        "i16",
        "u32",
        "i32",
        "u64",
        "i64",
        "u128",
        "i128",
        "NonZeroU8",
        "NonZeroI8",
        "NonZeroU16",
        "NonZeroI16",
        "NonZeroU32",
        "NonZeroI32",
        "NonZeroU64",
        "NonZeroI64",
        "NonZeroU128",
        "NonZeroI128",
    ];

    types
        .into_iter()
        .map(|t| quote!($(t) = int))
        .join_with_line_breaks()
}