/
d.vision
/
division_bindgen_tools
Обзор
Документация
Войти
/
d.vision
/
division_bindgen_tools
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/lib.rs
163 строки
4 KB
d.vision
Fixed special case enum name handling
12 июн 2025, 15:00
12 июн 2025, 15:00
b33fafa
Код
Авторство
О чём код?
use convert_case::{Case, Casing}; use proc_macro2::Span; use quote::quote; use syn::{Attribute, Ident, ItemEnum, Meta, Variant}; pub struct EnumOptions { pub src_enum_name: String, pub variant_prefix: String, } #[derive(Debug)] pub struct GenError { pub message: String, } impl std::fmt::Display for GenError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.message) } } impl std::error::Error for GenError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(self) } } impl From<syn::Error> for GenError { fn from(value: syn::Error) -> Self { Self { message: value.to_string(), } } } impl From<std::fmt::Error> for GenError { fn from(value: std::fmt::Error) -> Self { Self { message: value.to_string(), } } } pub type Result<T> = std::result::Result<T, GenError>; pub fn make_wrapper_for_enum_bindings( rust_bindings_content: &str, lib_prefix: &str, ) -> Result<String> { make_wrapper_for_enum_bindings_with_special_cases(rust_bindings_content, lib_prefix, Vec::new()) } pub fn make_wrapper_for_enum_bindings_with_special_cases( rust_bindings_content: &str, lib_prefix: &str, special_cases: Vec<EnumOptions>, ) -> Result<String> { use std::fmt::Write; let parsed_bindings = syn::parse_file(&rust_bindings_content)?; let mut output = String::new(); for i in parsed_bindings.items { let enum_item = match i { syn::Item::Enum(e) => e, _ => continue, }; let repr_attr = enum_item.attrs.iter().find(|a| a.path().is_ident("repr")); let enum_base_type = match repr_attr { Some(Attribute { meta: Meta::List(p), .. }) => Ident::new(&p.tokens.to_string(), Span::call_site()), _ => make_default_repr_type_ident(), }; let src_enum_ident = enum_item.ident.clone(); let src_enum_str = src_enum_ident.to_string(); let full_variant_prefix = if let Some(case) = special_cases .iter() .find(|c| src_enum_str == c.src_enum_name) { case.variant_prefix.clone() } else if src_enum_str.starts_with(lib_prefix) { src_enum_str.to_case(Case::UpperSnake) } else { log_as_warning(&format!("The enum {src_enum_str} parsing has been skipped")); continue; }; log_as_warning(&format!("Parsing the enum {src_enum_str}")); let dst_enum_ident = Ident::new( &src_enum_str.replace(&lib_prefix, ""), src_enum_ident.span(), ); let dst_variants = collect_variants(enum_item, &full_variant_prefix); let dst_variants = if let Some(v) = dst_variants { v } else { log_as_warning(&format!("The enum {src_enum_str} variants can't be parsed")); continue; }; writeln!( output, "{}", quote! { #[derive(Clone, Copy, PartialEq)] pub enum #dst_enum_ident { #(#dst_variants),* } impl Into<#src_enum_ident> for #dst_enum_ident { fn into(self) -> #src_enum_ident { unsafe { mem::transmute(self as #enum_base_type) } } } } )?; } Ok(output) } fn make_default_repr_type_ident() -> Ident { Ident::new("i32", Span::call_site()) } fn collect_variants(enum_item: ItemEnum, full_variant_prefix: &str) -> Option<Vec<Variant>> { let mut variants = Vec::with_capacity(enum_item.variants.len()); for v in enum_item.variants.into_iter() { let variant_str = v.ident.to_string(); if !variant_str.starts_with(&full_variant_prefix) { return None; } let dst_var_name = variant_str .trim_start_matches(&full_variant_prefix) .to_case(Case::Pascal); if dst_var_name.is_empty() { return None; } variants.push(Variant { ident: Ident::new(&dst_var_name, v.ident.span()), ..v }); } Some(variants) } fn log_as_warning(string: &str) { if cfg!(feature = "log_as_warning") { println!("cargo::warning={string}"); } }