Skip to content

Commit

Permalink
feat(EnumHint): add EnumHintEntry to store entry info
Browse files Browse the repository at this point in the history
  • Loading branch information
Bogay committed Oct 14, 2023
1 parent 825ad68 commit 98e0f4f
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 67 deletions.
70 changes: 46 additions & 24 deletions gdnative-core/src/export/property/hint.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Strongly typed property hints.

use std::fmt::{self, Write};
use std::fmt::{self, Display, Write};
use std::ops::RangeInclusive;

use crate::core_types::GodotString;
Expand Down Expand Up @@ -116,52 +116,71 @@ where
/// ```
#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct EnumHint {
values: Vec<(String, Option<i64>)>,
entries: Vec<EnumHintEntry>,
}

impl EnumHint {
#[inline]
pub fn new(values: Vec<String>) -> Self {
let values = values.into_iter().map(|v| (v, None)).collect();
EnumHint { values }
pub fn new(keys: Vec<String>) -> Self {
let entries = keys.into_iter().map(EnumHintEntry::new).collect();
EnumHint { entries }
}

#[inline]
pub fn with_numbers(values: Vec<(String, i64)>) -> Self {
let values = values
.into_iter()
.map(|(key, val)| (key, Some(val)))
.collect();
EnumHint { values }
pub fn with_entries(entries: Vec<EnumHintEntry>) -> Self {
EnumHint { entries }
}

/// Formats the hint as a Godot hint string.
fn to_godot_hint_string(&self) -> GodotString {
let mut s = String::new();

let mut iter = self.values.iter();
let write_item = |s: &mut String, item: &(String, Option<i64>)| match item {
(key, Some(val)) => {
write!(s, "{key}:{val}")
}
(key, None) => {
write!(s, "{key}")
}
};
let mut iter = self.entries.iter();

if let Some(first) = iter.next() {
write_item(&mut s, first).unwrap();
write!(s, "{first}").unwrap();
}

for rest in iter {
write!(s, ",").unwrap();
write_item(&mut s, rest).unwrap();
write!(s, ",{rest}").unwrap();
}

s.into()
}
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct EnumHintEntry {
key: String,
value: Option<i64>,
}

impl EnumHintEntry {
#[inline]
pub fn new(key: String) -> Self {
Self { key, value: None }
}

#[inline]
pub fn with_value(key: String, value: i64) -> Self {
Self {
key,
value: Some(value),
}
}
}

impl Display for EnumHintEntry {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.key)?;
if let Some(value) = self.value {
write!(f, ":{}", value)?;
}
Ok(())
}
}

/// Possible hints for integers.
#[derive(Clone, Debug)]
#[non_exhaustive]
Expand Down Expand Up @@ -495,6 +514,9 @@ godot_test!(test_enum_hint_without_mapping {
});

godot_test!(test_enum_hint_with_mapping {
let hint = EnumHint::with_numbers(vec![("Foo".into(), 42), ("Bar".into(), 67)]);
let hint = EnumHint::with_entries(vec![
EnumHintEntry::with_value("Foo".to_string(), 42),
EnumHintEntry::with_value("Bar".to_string(), 67),
]);
assert_eq!(hint.to_godot_hint_string().to_string(), "Foo:42,Bar:67".to_string(),);
});
25 changes: 14 additions & 11 deletions gdnative-derive/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::crate_gdnative_core;
use proc_macro2::{Span, TokenStream as TokenStream2};
use syn::spanned::Spanned;
use syn::{DeriveInput, Fields};
Expand Down Expand Up @@ -35,30 +36,32 @@ fn impl_export(enum_ty: &syn::Ident, data: &syn::DataEnum) -> syn::Result<TokenS
return Err(err);
}

let gdnative_core = crate_gdnative_core();
let mappings = data
.variants
.iter()
.map(|variant| {
let key = &variant.ident;
let val = quote! { #enum_ty::#key as i64 };
quote! { (stringify!(#key).to_string(), #val) }
quote! { #gdnative_core::export::hint::EnumHintEntry::with_value(stringify!(#key).to_string(), #val) }
})
.collect::<Vec<_>>();

let impl_block = quote! {
impl ::gdnative::export::Export for #enum_ty {
type Hint = ::gdnative::export::hint::IntHint<i64>;
#[inline]
fn export_info(hint: Option<Self::Hint>) -> ::gdnative::export::ExportInfo {
if let Some(hint) = hint {
return hint.export_info();
} else {
const _: () = {
pub enum NoHint {}

impl #gdnative_core::export::Export for #enum_ty {
type Hint = NoHint;

#[inline]
fn export_info(_hint: Option<Self::Hint>) -> #gdnative_core::export::ExportInfo {
let mappings = vec![ #(#mappings),* ];
let enum_hint = ::gdnative::export::hint::EnumHint::with_numbers(mappings);
return ::gdnative::export::hint::IntHint::<i64>::Enum(enum_hint).export_info();
let enum_hint = #gdnative_core::export::hint::EnumHint::with_entries(mappings);
return #gdnative_core::export::hint::IntHint::<i64>::Enum(enum_hint).export_info();
}
}
}
};
};

Ok(impl_block)
Expand Down
2 changes: 0 additions & 2 deletions test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ mod test_as_arg;
mod test_async;
mod test_constructor;
mod test_derive;
mod test_export_enum;
mod test_free_ub;
mod test_generic_class;
mod test_indexed_props;
Expand Down Expand Up @@ -53,7 +52,6 @@ pub extern "C" fn run_tests(
status &= test_vararray_return::run_tests();
status &= test_variant_call_args::run_tests();
status &= test_variant_ops::run_tests();
status &= test_export_enum::run_tests();

Variant::new(status).leak()
}
Expand Down
30 changes: 0 additions & 30 deletions test/src/test_export_enum.rs

This file was deleted.

0 comments on commit 98e0f4f

Please sign in to comment.