Skip to content

Commit

Permalink
feat: more key/val hash_map types for field access (#24)
Browse files Browse the repository at this point in the history
Added a few more types that HashMap field access now supports.
Namely key + val can now be different types (before that wasn't properly implemented and the
key type applied to the value as well).

The String type is now supported for keys (not yet for values).
rid::model structs are now supported as value types.

* test: testing hash_map key/val int primitives

* rid-macro: parsing hash_map includes key + val types

- previously the key type was used for both key and value

* rid-macro: handles mixed primitive key/val types for hash_maps

* rid-macro: extracting collection access tokens from vec access method

- needed to make it easier useable for hash maps
- named collection_access_tokens for lack of better idea of what to name
  it for now

* rid-macro: merged render vec access into VecAccess impl module

* rid-macro: rename collection item access tokens

* rid-macro: rid::export for hash_map contains_key

* rid-macro: rid::export to get hash_map item

* rid-macro: rid::export for hash_map len

* rid-macro: generalized render resolved dart ffi arg

- to ease reuse when we specify the arg by name instead via slot

* rid-macro: including proper key arg conversion for hash_map

* rid-macro: hash_map access fns include structs/enums info with rendered export

* rid-macro: pulled collection item conversion out of vec module

* rid-macro: HashMap<String, struct> implementation

* test: adding tests for hash_map with string key
  • Loading branch information
thlorenz authored Sep 5, 2021
1 parent 40f308a commit cced639
Show file tree
Hide file tree
Showing 15 changed files with 620 additions and 306 deletions.
82 changes: 82 additions & 0 deletions rid-macro-impl/src/accesses/collection_item_access_tokens.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;

use crate::{
parse::rust_type::RustType,
render_common::PointerTypeAlias,
render_rust::{render_return_type, RenderedReturnType},
};

use super::AccessKind;

pub struct CollectionItemAccessTokens {
/// The tokens for the type that should be returned by the get item wrapper method
pub item_return_type: TokenStream,

/// The tokens to convert the type of the wrapped method into the one the wrapper returns
pub into_return_type: TokenStream,

/// Type aliases used as a part of the return type
pub type_alias: Option<PointerTypeAlias>,
}

/// Helper function to figure out the return type and into return type tokens for
/// a specific collection type. Used for `Vec` get by index and `HashMap` get by key.
pub fn collection_item_access_tokens(
ptr_ident: Ident,
item_type: &RustType,
access_kind: &AccessKind,
) -> CollectionItemAccessTokens {
// CString and str aren't FFI safe so we send the item content as a *char.
// For consistency we do the same for Strings.
let (item_return_type, into_return_type, type_alias) = if item_type
.is_string_like()
{
let item_return_type = quote! { *const ::std::os::raw::c_char };
let into_return_type = if item_type.is_string() {
quote! {
let s: &String = unsafe {
assert!(!#ptr_ident.is_null());
let #ptr_ident: *mut String = #ptr_ident as *mut String;
#ptr_ident.as_mut().expect("resolve ptr from collection item as_mut failed")
};
let cstring = ::std::ffi::CString::new(s.as_str()).unwrap();
cstring.into_raw()
}
} else if item_type.is_cstring() {
quote! {
let cstring: &::std::ffi::CString = unsafe {
assert!(!#ptr_ident.is_null());
let #ptr_ident: *mut ::std::ffi::CString = #ptr_ident as *mut ::std::ffi::CString;
#ptr_ident.as_mut().expect("resolve_ptr.as_mut failed")
};
cstring.clone().into_raw()
}
} else if item_type.is_str() {
quote! {
let s: &str = unsafe {
assert!(!#ptr_ident.is_null());
let #ptr_ident: *mut str = #ptr_ident as *mut str;
#ptr_ident.as_mut()
.expect("resolve ptr from collection item as_mut failed")
};
let cstring = ::std::ffi::CString::new(s).unwrap();
cstring.into_raw()
}
} else {
panic!("Should have covered all string-like cases")
};
(item_return_type, into_return_type, None)
} else {
let RenderedReturnType {
tokens, type_alias, ..
} = render_return_type(&item_type, access_kind);
(tokens, quote! { #ptr_ident }, type_alias)
};
CollectionItemAccessTokens {
item_return_type,
into_return_type,
type_alias,
}
}
33 changes: 33 additions & 0 deletions rid-macro-impl/src/accesses/collection_item_conversions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::{
attrs::TypeInfoMap,
parse::{dart_type::DartType, rust_type::RustType},
};

pub fn resolved_dart_item_type_string(
item_type: &RustType,
type_infos: &TypeInfoMap,
) -> String {
if item_type.is_struct() {
item_type.rust_ident().to_string()
} else if item_type.is_string_like() {
"String".to_string()
} else {
DartType::from(&item_type, type_infos).render_type(false)
}
}

pub fn map_to_dart_string(
item_type: &RustType,
dart_item_type: &str,
) -> String {
if item_type.is_struct() {
format!(".map((raw) => raw.toDart())")
} else if item_type.is_enum() {
format!(
".map((x) => {enum_type}.values[x])",
enum_type = dart_item_type
)
} else {
"".to_string()
}
}
4 changes: 4 additions & 0 deletions rid-macro-impl/src/accesses/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
mod access;
mod collection_item_access_tokens;
mod collection_item_conversions;
mod hash_map;
mod render_collection_accesses;
mod vec;

pub use access::*;
pub use collection_item_access_tokens::*;
pub use collection_item_conversions::*;
pub use hash_map::*;
pub use render_collection_accesses::*;
pub use vec::*;
5 changes: 4 additions & 1 deletion rid-macro-impl/src/accesses/render_collection_accesses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ pub fn render_collection_accesses(
} else {
"".to_string()
};
let dart_tokens: TokenStream = if dart_config.render && dart_config.tokens {
let dart_tokens: TokenStream = if dart_config.render
&& dart_config.tokens
&& !rendered_dart.is_empty()
{
let dart_tokens: TokenStream = rendered_dart.parse().unwrap();
let fn_include_dart_ident =
format_ident!("__include_dart_for_{}", first_key);
Expand Down
134 changes: 91 additions & 43 deletions rid-macro-impl/src/parse/rust_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,70 +640,87 @@ fn ident_to_kind(
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args,
..
}) => {
// For now assuming one arg
match &args[0] {
GenericArgument::Type(ty) => {
match ident_str.as_str() {
"Vec" => {
}) => match args.len() {
// -----------------
// Single Type Parameter
// -----------------
1 => match &args[0] {
GenericArgument::Type(ty) => match ident_str.as_str() {
"Vec" => {
let inner = resolve_rust_ty(
ty,
type_infos,
RustTypeContext::CollectionItem,
)
.map(|x| Box::new(x));
TypeKind::Composite(Composite::Vec, inner, None)
}
"Option" => {
let inner = resolve_rust_ty(
ty,
type_infos,
RustTypeContext::OptionItem,
)
.map(|x| Box::new(x));
TypeKind::Composite(Composite::Option, inner, None)
}
_ => {
if let Some(type_info) = type_infos.get(&ident_str) {
let inner = resolve_rust_ty(
ty,
type_infos,
RustTypeContext::CollectionItem,
RustTypeContext::CustomItem,
)
.map(|x| Box::new(x));
TypeKind::Composite(Composite::Vec, inner, None)

TypeKind::Composite(
Composite::Custom(
type_info.clone(),
ident_str.clone(),
),
inner,
None,
)
} else {
TypeKind::Unknown
}
"Option" => {
let inner = resolve_rust_ty(
ty,
}
},
_ => TypeKind::Unknown,
},
// -----------------
// Two Type Parameters
// -----------------
2 => match (&args[0], &args[1]) {
(GenericArgument::Type(ty1), GenericArgument::Type(ty2))
=> {
match ident_str.as_str() {
"HashMap" => {
let inner1 = resolve_rust_ty(
ty1,
type_infos,
RustTypeContext::OptionItem,
RustTypeContext::CollectionItem,
)
.map(|x| Box::new(x));
TypeKind::Composite(Composite::Option, inner, None)
}
"HashMap" => {
let inner = resolve_rust_ty(
ty,
let inner2 = resolve_rust_ty(
ty2,
type_infos,
RustTypeContext::CollectionItem,
)
.map(|x| Box::new(x));
// TODO(thlorenz): HashMap needs two inner types
TypeKind::Composite(
Composite::HashMap,
inner.clone(),
inner,
inner1,
inner2,
)
}
_ => {
if let Some(type_info) = type_infos.get(&ident_str)
{
let inner = resolve_rust_ty(
ty,
type_infos,
RustTypeContext::CustomItem,
)
.map(|x| Box::new(x));

TypeKind::Composite(
Composite::Custom(
type_info.clone(),
ident_str.clone(),
),
inner,
None,
)
} else {
TypeKind::Unknown
}
}
_ => todo!("Not yet handling custom angle bracketed types with {} type parameters", args.len()),
}
}
_ => TypeKind::Unknown,
}
}
_ => todo!("Not yet handling angle bracketed types with more {} type parameters", args.len())
},
PathArguments::Parenthesized(args) => {
todo!(
"rust_type::ident_to_kind PathArguments::Parenthesized {:#?}",
Expand All @@ -712,3 +729,34 @@ fn ident_to_kind(
}
}
}
/*
// -----------------
// Two Type Parameters
// -----------------
(GenericArgument::Type(ty1), GenericArgument::Type(ty2))
if len == 2 =>
{
match ident_str.as_str() {
"HashMap" => {
let inner1 = resolve_rust_ty(
ty1,
type_infos,
RustTypeContext::CollectionItem,
)
.map(|x| Box::new(x));
let inner2 = resolve_rust_ty(
ty2,
type_infos,
RustTypeContext::CollectionItem,
)
.map(|x| Box::new(x));
TypeKind::Composite(
Composite::HashMap,
inner1,
inner2,
)
}
_ => todo!("Not yet handling custom angle bracketed types with {} type parameters", len),
}
}
*/
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/// extension Rid_HashMap_ExtOn{hash_map_type} on {pointer_hash_map_type} {
/// int get length => {rid_ffi}.{fn_len_ident}(this);
/// int get length => {rid_ffi}.rid_export_{fn_len_ident}(this);
///
/// bool contains({key_type} key) =>
/// {rid_ffi}.{fn_contains_key_ident}(this, key) != 0;
/// bool contains({resolved_dart_key_type} key) =>
/// {rid_ffi}.rid_export_{fn_contains_key_ident}(this, {key_ffi_arg}) != 0;
///
/// {val_return_type}? get({key_type} key) {
/// final ptr = {rid_ffi}.{fn_get_ident}(this, key);
/// return ptr.address == 0x0 ? null : ptr.value;
/// {resolved_dart_val_type}? get({resolved_dart_key_type} key) {
/// final ptr = {rid_ffi}.rid_export_{fn_get_ident}(this, {key_ffi_arg});
/// return ptr.address == 0x0 ? null : ptr{val_to_dart};
/// }
/// {dart_collection}.HashMap<{key_type}, {val_type}> toDart({bool autoDispose = true}) {
/// {dart_collection}.HashMap<{resolved_dart_key_type}, {resolved_dart_val_type}> toDart({bool autoDispose = true}) {
/// ridStoreLock();
/// final hashMap = new {dart_collection}.HashMap<{key_type}, {val_type}>();
/// final hashMap = new {dart_collection}.HashMap<{resolved_dart_key_type}, {resolved_dart_val_type}>();
///
/// final keys = {rid_ffi}.rid_export_{fn_keys_ident}(this);
/// for (final key in keys.iter()) {
Expand Down
Loading

0 comments on commit cced639

Please sign in to comment.