Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/cgp-core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use cgp_error::{
};
pub use cgp_field::{
Char, Cons, Either, Field, FieldGetter, FromFields, HasField, HasFieldMut, HasFields,
HasFieldsRef, Index, MutFieldGetter, Nil, ToFields, ToFieldsRef, UseField, Void,
HasFieldsRef, Index, MRef, MutFieldGetter, Nil, ToFields, ToFieldsRef, UseField, Void,
};
pub use cgp_macro::{
cgp_auto_getter, cgp_component, cgp_context, cgp_getter, cgp_new_provider, cgp_preset,
Expand Down
2 changes: 2 additions & 0 deletions crates/cgp-field/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod char;
mod field;
mod index;
mod mref;
mod product;
mod sum;

pub use char::*;
pub use field::*;
pub use index::*;
pub use mref::*;
pub use product::*;
pub use sum::*;
47 changes: 47 additions & 0 deletions crates/cgp-field/src/types/mref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use core::ops::Deref;

pub enum MRef<'a, T> {
Ref(&'a T),
Owned(T),
}

impl<T> Deref for MRef<'_, T> {
type Target = T;

fn deref(&self) -> &T {
match self {
Self::Ref(value) => value,
Self::Owned(value) => value,
}
}
}

impl<T> AsRef<T> for MRef<'_, T> {
fn as_ref(&self) -> &T {
self.deref()
}
}

impl<T> From<T> for MRef<'_, T> {
fn from(value: T) -> Self {
Self::Owned(value)
}
}

impl<'a, T> From<&'a T> for MRef<'a, T> {
fn from(value: &'a T) -> Self {
Self::Ref(value)
}
}

impl<T> MRef<'_, T>
where
T: Clone,
{
pub fn get_or_clone(self) -> T {
match self {
Self::Ref(value) => value.clone(),
Self::Owned(value) => value,
}
}
}
12 changes: 9 additions & 3 deletions crates/cgp-macro-lib/src/derive_getter/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse2, TypeParamBound};

use crate::derive_getter::GetterField;
use crate::derive_getter::{FieldMode, GetterField};

pub fn derive_getter_constraint(
spec: &GetterField,
Expand All @@ -11,8 +11,14 @@ pub fn derive_getter_constraint(
let provider_type = &spec.field_type;

let constraint = if spec.field_mut.is_none() {
quote! {
HasField< #field_symbol, Value = #provider_type >
if let FieldMode::Slice = spec.field_mode {
quote! {
HasField< #field_symbol, Value: AsRef< [ #provider_type ] > + 'static >
}
} else {
quote! {
HasField< #field_symbol, Value = #provider_type >
}
}
} else {
quote! {
Expand Down
2 changes: 2 additions & 0 deletions crates/cgp-macro-lib/src/derive_getter/getter_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct GetterField {
pub enum FieldMode {
Reference,
OptionRef,
MRef,
Str,
Clone,
Slice,
}
18 changes: 15 additions & 3 deletions crates/cgp-macro-lib/src/derive_getter/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ pub fn derive_getter_method(
}
}
}
FieldMode::MRef => {
quote! {
MRef::Ref( #call_expr )
}
}
FieldMode::Str => {
if spec.field_mut.is_none() {
quote! {
Expand All @@ -95,9 +100,16 @@ pub fn derive_getter_method(
}
}
}
FieldMode::Clone => quote! {
#call_expr .clone()
},
FieldMode::Clone => {
quote! {
#call_expr .clone()
}
}
FieldMode::Slice => {
quote! {
#call_expr .as_ref()
}
}
};

let return_type = &spec.return_type;
Expand Down
28 changes: 26 additions & 2 deletions crates/cgp-macro-lib/src/derive_getter/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,12 @@ fn parse_field_type(return_type: &Type, field_mut: &Option<Mut>) -> syn::Result<
let field_type: Type = parse_quote! { String };

Ok((field_type, FieldMode::Str))
} else if let (Type::Slice(slice), None) = (type_ref.elem.as_ref(), field_mut) {
let field_type = slice.elem.as_ref().clone();

Ok((field_type, FieldMode::Slice))
} else {
let field_type: Type = type_ref.elem.as_ref().clone();
let field_type = type_ref.elem.as_ref().clone();

Ok((field_type, FieldMode::Reference))
}
Expand All @@ -218,6 +222,8 @@ fn parse_field_type(return_type: &Type, field_mut: &Option<Mut>) -> syn::Result<
parse2(quote! { Option< #field_type > })?,
FieldMode::OptionRef,
))
} else if let (Some(field_type), None) = (try_parse_mref(type_path), field_mut) {
Ok((field_type.clone(), FieldMode::MRef))
} else {
Ok((return_type.clone(), FieldMode::Clone))
}
Expand Down Expand Up @@ -263,11 +269,29 @@ fn try_parse_option_ref(type_path: &TypePath) -> Option<&Type> {

if segment.ident == "Option" {
if let PathArguments::AngleBracketed(args) = &segment.arguments {
if let Some(GenericArgument::Type(Type::Reference(type_ref))) = args.args.first() {
let [arg] = Vec::from_iter(args.args.iter()).try_into().ok()?;

if let GenericArgument::Type(Type::Reference(type_ref)) = arg {
return Some(type_ref.elem.as_ref());
}
}
}

None
}

fn try_parse_mref(type_path: &TypePath) -> Option<&Type> {
let segment = parse_single_segment_type_path(type_path).ok()?;

if segment.ident == "MRef" {
if let PathArguments::AngleBracketed(args) = &segment.arguments {
let [arg1, arg2] = Vec::from_iter(args.args.iter()).try_into().ok()?;

if let (GenericArgument::Lifetime(_), GenericArgument::Type(ty)) = (arg1, arg2) {
return Some(ty);
}
}
}

None
}
12 changes: 9 additions & 3 deletions crates/cgp-macro-lib/src/derive_getter/with_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{parse2, Generics, Ident, ItemImpl, ItemTrait};

use crate::derive_getter::getter_field::GetterField;
use crate::derive_getter::{derive_getter_method, ContextArg};
use crate::derive_getter::{derive_getter_method, ContextArg, FieldMode};
use crate::parse::ComponentSpec;

pub fn derive_with_provider_impl(
Expand All @@ -24,8 +24,14 @@ pub fn derive_with_provider_impl(
let component_type = quote! { #component_name < #component_params > };

let provider_constraint = if field.field_mut.is_none() {
quote! {
FieldGetter< #context_type, #component_type , Value = #provider_type >
if let FieldMode::Slice = field.field_mode {
quote! {
FieldGetter< #context_type, #component_type, Value: AsRef< [ #provider_type ] > + 'static >
}
} else {
quote! {
FieldGetter< #context_type, #component_type , Value = #provider_type >
}
}
} else {
quote! {
Expand Down
66 changes: 66 additions & 0 deletions crates/cgp-tests/src/tests/getter/abstract_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use cgp::prelude::*;

#[test]
pub fn test_abstract_type_getter() {
#[cgp_type]
pub trait HasNameType {
type Name;
}

#[cgp_getter {
provider: NameGetter,
}]
pub trait HasName: HasNameType {
fn name(&self) -> &Self::Name;
}

#[cgp_context(AppComponents)]
#[derive(HasField)]
pub struct App {
pub name: String,
}

delegate_components! {
AppComponents {
NameTypeProviderComponent: UseType<String>,
NameGetterComponent: UseField<symbol!("name")>,
}
}

let context = App {
name: "Alice".to_owned(),
};

assert_eq!(context.name(), "Alice");
}

#[test]
pub fn test_abstract_type_auto_getter() {
#[cgp_type]
pub trait HasNameType {
type Name;
}

#[cgp_auto_getter]
pub trait HasName: HasNameType {
fn name(&self) -> &Self::Name;
}

#[cgp_context(AppComponents)]
#[derive(HasField)]
pub struct App {
pub name: String,
}

delegate_components! {
AppComponents {
NameTypeProviderComponent: UseType<String>,
}
}

let context = App {
name: "Alice".to_owned(),
};

assert_eq!(context.name(), "Alice");
}
66 changes: 66 additions & 0 deletions crates/cgp-tests/src/tests/getter/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use cgp::prelude::*;

#[test]
pub fn test_clone_getter() {
#[cgp_type]
pub trait HasNameType {
type Name;
}

#[cgp_getter {
provider: NameGetter,
}]
pub trait HasName: HasNameType<Name: Clone> {
fn name(&self) -> Self::Name;
}

#[cgp_context(AppComponents)]
#[derive(HasField)]
pub struct App {
pub name: String,
}

delegate_components! {
AppComponents {
NameTypeProviderComponent: UseType<String>,
NameGetterComponent: UseField<symbol!("name")>,
}
}

let context = App {
name: "Alice".to_owned(),
};

assert_eq!(context.name(), "Alice");
}

#[test]
pub fn test_clone_auto_getter() {
#[cgp_type]
pub trait HasNameType {
type Name;
}

#[cgp_auto_getter]
pub trait HasName: HasNameType<Name: Clone> {
fn name(&self) -> Self::Name;
}

#[cgp_context(AppComponents)]
#[derive(HasField)]
pub struct App {
pub name: String,
}

delegate_components! {
AppComponents {
NameTypeProviderComponent: UseType<String>,
}
}

let context = App {
name: "Alice".to_owned(),
};

assert_eq!(context.name(), "Alice");
}
6 changes: 6 additions & 0 deletions crates/cgp-tests/src/tests/getter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod abstract_type;
pub mod clone;
pub mod mref;
pub mod option;
pub mod slice;
pub mod string;
Loading
Loading