diff --git a/crates/indicator_derive/src/operator/args.rs b/crates/indicator_derive/src/operator/args.rs index c11a95a..1f0dcfe 100644 --- a/crates/indicator_derive/src/operator/args.rs +++ b/crates/indicator_derive/src/operator/args.rs @@ -10,6 +10,7 @@ pub(crate) struct OperatorArgs { pub(crate) generate_out: Option, } +#[derive(Clone, Copy)] pub(crate) enum GenerateOut { Out, WithData, diff --git a/crates/indicator_derive/src/operator/extractor.rs b/crates/indicator_derive/src/operator/extractor.rs index 6a8a636..92441d9 100644 --- a/crates/indicator_derive/src/operator/extractor.rs +++ b/crates/indicator_derive/src/operator/extractor.rs @@ -1,5 +1,5 @@ use convert_case::{Case, Casing}; -use proc_macro2::Ident; +use proc_macro2::{Ident, TokenStream}; use syn::{ parse::{Parse, ParseStream}, parse_quote, @@ -99,8 +99,56 @@ type Optional = bool; pub(super) enum Extractor { Plain(Box), - Borrow(FnArg, Optional), - AsRef(FnArg, Optional), + Borrow(Ident, FnArg, Optional), + AsRef(Ident, FnArg, Optional), +} + +impl Extractor { + pub(super) fn expand(&self) -> TokenStream { + let indicator = indicator(); + match self { + Self::Plain(ty) => { + quote! { + { + let __a: #ty = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); + __a + } + } + } + Self::Borrow(name, arg, false) => { + quote! { + { + let #arg = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); + core::borrow::Borrow::borrow(#name) + } + } + } + Self::Borrow(name, arg, true) => { + quote! { + { + let #arg = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); + #name.map(core::borrow::Borrow::borrow) + } + } + } + Self::AsRef(name, arg, false) => { + quote! { + { + let #arg = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); + core::convert::AsRef::as_ref(#name) + } + } + } + Self::AsRef(name, arg, true) => { + quote! { + { + let #arg = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); + #name.map(core::convert::AsRef::as_ref) + } + } + } + } + } } struct ExtractorWithGenerics { @@ -124,10 +172,10 @@ impl ExtractorWithGenerics { if way.is_borrow() { generics .push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty>))?); - Extractor::Borrow(pat, false) + Extractor::Borrow(name, pat, false) } else { generics.push(syn::parse2(quote!(#ty: AsRef<#target_ty>))?); - Extractor::AsRef(pat, false) + Extractor::AsRef(name, pat, false) } } Attr::Env(way, false) => { @@ -138,12 +186,12 @@ impl ExtractorWithGenerics { let pat = parse_quote!(#indicator::context::Env(#name): #indicator::context::Env<&#ty>); if way.is_borrow() { generics.push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static))?); - Extractor::Borrow(pat, false) + Extractor::Borrow(name, pat, false) } else { generics.push(syn::parse2( quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static), )?); - Extractor::AsRef(pat, false) + Extractor::AsRef(name, pat, false) } } Attr::Env(way, true) => { @@ -155,12 +203,12 @@ impl ExtractorWithGenerics { let pat = parse_quote!(#indicator::context::Env(#name): #indicator::context::Env>); if way.is_borrow() { generics.push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static))?); - Extractor::Borrow(pat, true) + Extractor::Borrow(name, pat, true) } else { generics.push(syn::parse2( quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static), )?); - Extractor::AsRef(pat, true) + Extractor::AsRef(name, pat, true) } } Attr::Data(way, false) => { @@ -171,12 +219,12 @@ impl ExtractorWithGenerics { let pat = parse_quote!(#indicator::context::Data(#name): #indicator::context::Data<&#ty>); if way.is_borrow() { generics.push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static))?); - Extractor::Borrow(pat, false) + Extractor::Borrow(name, pat, false) } else { generics.push(syn::parse2( quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static), )?); - Extractor::AsRef(pat, false) + Extractor::AsRef(name, pat, false) } } Attr::Data(way, true) => { @@ -188,12 +236,12 @@ impl ExtractorWithGenerics { let pat = parse_quote!(#indicator::context::Data(#name): #indicator::context::Data>); if way.is_borrow() { generics.push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static))?); - Extractor::Borrow(pat, true) + Extractor::Borrow(name, pat, true) } else { generics.push(syn::parse2( quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static), )?); - Extractor::AsRef(pat, true) + Extractor::AsRef(name, pat, true) } } Attr::Prev(way) => { @@ -205,12 +253,12 @@ impl ExtractorWithGenerics { let pat = parse_quote!(#indicator::context::Prev(#name): #indicator::context::Prev<&#ty>); if way.is_borrow() { generics.push(syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static))?); - Extractor::Borrow(pat, true) + Extractor::Borrow(name, pat, true) } else { generics.push(syn::parse2( quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static), )?); - Extractor::AsRef(pat, true) + Extractor::AsRef(name, pat, true) } } } diff --git a/crates/indicator_derive/src/operator/mod.rs b/crates/indicator_derive/src/operator/mod.rs index a225f28..168a185 100644 --- a/crates/indicator_derive/src/operator/mod.rs +++ b/crates/indicator_derive/src/operator/mod.rs @@ -1,24 +1,12 @@ -use convert_case::{Case, Casing}; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::quote; -use syn::{ - punctuated::Punctuated, FnArg, GenericParam, Ident, ItemFn, Lifetime, LifetimeParam, Meta, - PatType, ReturnType, Token, Type, Visibility, -}; +use proc_macro2::TokenStream as TokenStream2; -use self::{ - args::{GenerateOut, OperatorArgs}, - operator_fn::OperatorFn, -}; +use self::{args::OperatorArgs, operator_fn::OperatorFn}; use super::indicator; /// Arguments for generating operator. mod args; -/// Expand the signature. -mod signature; - /// Operator Fn. mod operator_fn; @@ -32,197 +20,6 @@ pub(super) fn generate_operator( input: TokenStream, ) -> syn::Result { let args = syn::parse::(args)?; - let op_fn = OperatorFn::parse_with(input.clone().into(), &args)?; - let mut next = syn::parse::(input)?; - - let unattributed = signature::expand(&mut next, &args)?; - let next_fn = generate_next_fn(&unattributed)?; - - // Documentations. - let docs = next - .attrs - .iter() - .filter(|attr| attr.path().is_ident("doc")) - .cloned() - .collect::>(); - - // Generate struct name. - let fn_name = &next.sig.ident; - let name = Ident::new( - &format!("{}Op", fn_name.to_string().to_case(Case::Pascal)), - Span::call_site(), - ); - - // Handle generics. - let struct_def = generate_struct_def(&next.vis, &next.sig.generics, &name, &docs)?; - let (orig_impl_generics, type_generics, where_clause) = next.sig.generics.split_for_impl(); - - // Add lifetime to generics. - let mut generics = next.sig.generics.clone(); - generics - .params - .push(GenericParam::Lifetime(LifetimeParam::new(Lifetime::new( - "'value", - Span::call_site(), - )))); - let (impl_generics, _, _) = generics.split_for_impl(); - - // Handle extractors. - let mut extractors = Punctuated::<_, Token![,]>::new(); - for arg in next.sig.inputs.iter() { - let FnArg::Typed(arg) = arg else { - return Err(syn::Error::new_spanned(arg, "expected typed argument")); - }; - extractors.push(parse_extractor(arg)?); - } - - // Handle output. - let output = match &next.sig.output { - ReturnType::Default => quote!(()), - ReturnType::Type(_, ty) => quote!(#ty), - }; - - let indicator = indicator(); - let vis = &next.vis; - let input_type = &args.input_type; - let unattributed_type_generics = if unattributed.sig.generics.params.is_empty() { - quote!() - } else { - let (_, type_generics, _) = unattributed.sig.generics.split_for_impl(); - quote!(::#type_generics) - }; - let return_stmt = match args.generate_out { - Some(GenerateOut::Out) => { - quote! { - __next #unattributed_type_generics (#extractors).into() - } - } - Some(GenerateOut::Data) => { - quote! { - __next #unattributed_type_generics (#extractors).map(Into::into) - } - } - Some(GenerateOut::WithData) => { - quote! { - { - let (__out, __data) = __next #unattributed_type_generics (#extractors); - (__out.into(), __data.map(Into::into)) - } - } - } - None => { - quote! { - __next #unattributed_type_generics (#extractors) - } - } - }; - - // Expand. - Ok(quote! { - #struct_def - - impl #impl_generics #indicator::context::RefOperator<'value, #input_type> for #name #type_generics #where_clause { - type Output = #output; - - fn next(&mut self, __input: #indicator::context::ValueRef<'value, #input_type>) -> Self::Output { - #next_fn - #return_stmt - } - } - - #(#docs)* - #vis fn #fn_name #orig_impl_generics() -> #name #type_generics #where_clause { - #name::default() - } - }) -} - -fn generate_next_fn(next: &ItemFn) -> syn::Result { - let mut next = next.clone(); - next.vis = Visibility::Inherited; - next.sig.ident = Ident::new("__next", next.sig.ident.span()); - Ok(quote! { - #next - }) -} - -fn parse_extractor(arg: &PatType) -> syn::Result { - let indicator = indicator(); - let PatType { ty, attrs, pat, .. } = arg; - let expanded = if let Some(attr) = attrs.first() { - let Meta::List(attr) = &attr.meta else { - unreachable!("must be meta list"); - }; - let kind = attr.path.get_ident().unwrap(); - let name: Ident = syn::parse2(attr.tokens.clone()).unwrap(); - let rt = match kind.to_string().as_str() { - "borrow" => quote!(core::borrow::Borrow::borrow(#name)), - "as_ref" => quote!(core::convert::AsRef::as_ref(#name)), - _ => unreachable!(), - }; - quote! { - { - let #pat: #ty = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); - #rt - } - } - } else { - quote! { - { - let __a: #ty = #indicator::context::extractor::FromValueRef::from_value_ref(&__input); - __a - } - } - }; + let expanded = OperatorFn::parse_with(input.clone().into(), &args)?.expand(); Ok(expanded) } - -fn generate_struct_def( - vis: &Visibility, - generics: &syn::Generics, - name: &syn::Ident, - docs: &[syn::Attribute], -) -> syn::Result { - if generics.params.is_empty() { - return Ok(quote! { - #[derive(Default)] - #[allow(non_camel_case_types)] - #vis struct #name; - }); - } - let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); - let phantom_data_type = generate_phantom_data_type(generics); - Ok(quote! { - #(#docs)* - #[allow(non_camel_case_types)] - #vis struct #name #impl_generics (core::marker::PhantomData<#phantom_data_type> ) #where_clause; - - impl #impl_generics Default for #name #type_generics #where_clause { - fn default() -> Self { - Self(core::marker::PhantomData) - } - } - }) -} - -fn generate_phantom_data_type(generics: &syn::Generics) -> Type { - let params: Vec<_> = generics - .params - .iter() - .filter_map(|param| { - if let syn::GenericParam::Type(type_param) = param { - let ident = &type_param.ident; - Some(quote! { #ident }) - } else { - None - } - }) - .collect(); - - // Generate `fn() -> (generics)` type - let phantom_data_type = quote! { fn() -> (#(#params),*) }; - - // Parse as `fn() -> (generics)` type - let phantom_data_type: Type = syn::parse2(phantom_data_type).unwrap(); - phantom_data_type -} diff --git a/crates/indicator_derive/src/operator/operator_fn.rs b/crates/indicator_derive/src/operator/operator_fn.rs index bd2027b..3bbed54 100644 --- a/crates/indicator_derive/src/operator/operator_fn.rs +++ b/crates/indicator_derive/src/operator/operator_fn.rs @@ -1,10 +1,12 @@ +use convert_case::{Case, Casing}; use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; use syn::{ - Attribute, GenericParam, Generics, ItemFn, Result, ReturnType, Type, TypeTuple, Visibility, + punctuated::Punctuated, Attribute, GenericParam, Generics, ItemFn, Result, ReturnType, Token, + Type, TypeTuple, Visibility, }; -use crate::operator::args::GenerateOut; +use crate::{indicator, operator::args::GenerateOut}; use super::{ args::OperatorArgs, @@ -20,6 +22,8 @@ pub(super) struct OperatorFn { pub(super) generics: Generics, pub(super) output_ty: Type, pub(super) next_fn: ItemFn, + input_ty: Type, + out: Option, } impl OperatorFn { @@ -68,8 +72,135 @@ impl OperatorFn { output_ty, next_fn: original, extractors, + input_ty: options.input_type.clone(), + out: options.generate_out, }) } + + pub(super) fn expand(&self) -> TokenStream { + let indicator = indicator(); + let vis = &self.vis; + let docs = &self.docs; + let struct_def = self.expand_struct(); + let name = self.struct_name(); + let fn_name = &self.name; + let (orig_impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let input_ty = &self.input_ty; + let output_ty = &self.output_ty; + let next_fn = &self.next_fn; + + let gen = self.generics_with_lifetime(); + let impl_generics = gen.split_for_impl().0; + + let return_stmt = self.expand_stmt(); + quote! { + #struct_def + + impl #impl_generics #indicator::context::RefOperator<'value, #input_ty> for #name #type_generics #where_clause { + type Output = #output_ty; + + fn next(&mut self, __input: #indicator::context::ValueRef<'value, #input_ty>) -> Self::Output { + #next_fn + #return_stmt + } + } + + #(#docs)* + #vis fn #fn_name #orig_impl_generics() -> #name #type_generics #where_clause { + #name::default() + } + } + } + + fn struct_name(&self) -> Ident { + Ident::new( + &format!("{}Op", self.name.to_string().to_case(Case::Pascal)), + self.name.span(), + ) + } + + fn generics_with_lifetime(&self) -> Generics { + let mut generics = self.generics.clone(); + generics.params.push(syn::parse_quote!('value)); + generics + } + + fn expand_struct(&self) -> TokenStream { + let vis = &self.vis; + let name = self.struct_name(); + let generics = &self.generics; + let docs = &self.docs; + + if generics.params.is_empty() { + return quote! { + #[derive(Default)] + #[allow(non_camel_case_types)] + #vis struct #name; + }; + } + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + let phantom_data_type = generate_phantom_data_type(generics); + quote! { + #(#docs)* + #[allow(non_camel_case_types)] + #vis struct #name #impl_generics (core::marker::PhantomData<#phantom_data_type> ) #where_clause; + + impl #impl_generics Default for #name #type_generics #where_clause { + fn default() -> Self { + Self(core::marker::PhantomData) + } + } + } + } + + fn expand_call_name(&self) -> TokenStream { + let call_generics = if self.next_fn.sig.generics.params.is_empty() { + quote!() + } else { + let (_, type_generics, _) = self.next_fn.sig.generics.split_for_impl(); + quote!(::#type_generics) + }; + quote! { + __next #call_generics + } + } + + fn expand_extractors(&self) -> Punctuated { + self.extractors + .iter() + .map(|extractor| extractor.expand()) + .collect() + } + + fn expand_stmt(&self) -> TokenStream { + let call_name = self.expand_call_name(); + let extractors = self.expand_extractors(); + match self.out { + Some(GenerateOut::Out) => { + quote! { + #call_name (#extractors).into() + } + } + Some(GenerateOut::Data) => { + quote! { + #call_name (#extractors).map(Into::into) + } + } + Some(GenerateOut::WithData) => { + quote! { + { + let (__out, __data) = #call_name (#extractors); + (__out.into(), __data.map(Into::into)) + } + } + } + None => { + quote! { + #call_name (#extractors) + } + } + } + } } struct OutputTy { @@ -126,3 +257,25 @@ impl OutputTy { Ok(Self { generics, ty }) } } + +fn generate_phantom_data_type(generics: &syn::Generics) -> Type { + let params: Vec<_> = generics + .params + .iter() + .filter_map(|param| { + if let syn::GenericParam::Type(type_param) = param { + let ident = &type_param.ident; + Some(quote! { #ident }) + } else { + None + } + }) + .collect(); + + // Generate `fn() -> (generics)` type + let phantom_data_type = quote! { fn() -> (#(#params),*) }; + + // Parse as `fn() -> (generics)` type + let phantom_data_type: Type = syn::parse2(phantom_data_type).unwrap(); + phantom_data_type +} diff --git a/crates/indicator_derive/src/operator/signature.rs b/crates/indicator_derive/src/operator/signature.rs deleted file mode 100644 index 257d6ae..0000000 --- a/crates/indicator_derive/src/operator/signature.rs +++ /dev/null @@ -1,337 +0,0 @@ -use convert_case::{Case, Casing}; -use proc_macro2::{Ident, Span}; -use quote::quote; -use syn::{ - FnArg, ItemFn, Meta, Pat, Result, ReturnType, Signature, Type, TypeReference, TypeTuple, -}; - -use super::args::{GenerateOut, OperatorArgs}; - -pub(super) fn expand(input: &mut ItemFn, args: &OperatorArgs) -> Result { - let mut unattributed = input.clone(); - remove_input_attributes(&mut unattributed)?; - expand_generics(&mut input.sig, args)?; - expand_inputs(&mut input.sig, args)?; - Ok(unattributed) -} - -fn remove_input_attributes(item_fn: &mut ItemFn) -> Result<()> { - for arg in item_fn.sig.inputs.iter_mut() { - let FnArg::Typed(arg) = arg else { - return Err(syn::Error::new_spanned(arg, "expected typed argument")); - }; - arg.attrs.clear(); - } - Ok(()) -} - -fn expand_generics(sig: &mut Signature, args: &OperatorArgs) -> Result<()> { - match args.generate_out { - Some(GenerateOut::Out) => { - let ty = get_return_type(sig); - sig.generics - .params - .push(syn::parse2(quote!(OutTy: From<#ty>))?); - sig.output = syn::parse2(quote!(-> OutTy))?; - } - Some(GenerateOut::Data) => { - let ty = get_return_type(sig); - let data_ty = get_type_inside_option(&ty)?; - sig.generics - .params - .push(syn::parse2(quote!(DataTy: From<#data_ty>))?); - sig.output = syn::parse2(quote!(-> Option))?; - } - Some(GenerateOut::WithData) => { - let ty = get_return_type(sig); - let Type::Tuple(tuple) = ty else { - return Err(syn::Error::new_spanned( - ty, - "the return type must be of the form `(_, Option<_>)`", - )); - }; - if tuple.elems.len() != 2 { - return Err(syn::Error::new_spanned( - tuple, - "the return type must be of the form `(_, Option<_>)`", - )); - } - let out_ty = &tuple.elems[0]; - sig.generics - .params - .push(syn::parse2(quote!(OutTy: From<#out_ty>))?); - let data_ty = get_type_inside_option(&tuple.elems[1])?; - sig.generics - .params - .push(syn::parse2(quote!(DataTy: From<#data_ty>))?); - sig.output = syn::parse2(quote!(-> (OutTy, Option)))?; - } - _ => {} - } - Ok(()) -} - -fn expand_inputs(sig: &mut Signature, args: &OperatorArgs) -> Result<()> { - let mut generics = vec![]; - let mut ctx = Ctx::default(); - for arg in sig.inputs.iter_mut() { - if let Some(generic) = expand_input(&mut ctx, arg, &args.input_type)? { - generics.push(generic); - } - } - if !generics.is_empty() { - sig.generics.params.extend(generics); - } - Ok(()) -} - -#[derive(Default)] -struct Ctx { - input: usize, - env: usize, - data: usize, -} - -enum Way { - Borrow, - AsRef, -} - -impl Way { - fn is_borrow(&self) -> bool { - matches!(self, Way::Borrow) - } -} - -enum ArgKind { - Input(Way), - Env(Way), - Data(Way), - Prev(Way), -} - -impl<'a> TryFrom<&'a Meta> for ArgKind { - type Error = syn::Error; - - fn try_from(value: &'a Meta) -> std::result::Result { - match value { - Meta::Path(path) => { - let ident = path.get_ident().ok_or_else(|| { - syn::Error::new_spanned( - path, - "unsupported attribute, expected `input`, `env` or `data`", - ) - })?; - match ident.to_string().as_str() { - "input" => Ok(ArgKind::Input(Way::Borrow)), - "env" => Ok(ArgKind::Env(Way::Borrow)), - "data" => Ok(ArgKind::Data(Way::Borrow)), - "prev" => Ok(ArgKind::Prev(Way::Borrow)), - _ => Err(syn::Error::new_spanned( - path, - "unsupported attribute, expected `input`, `env` or `data`", - )), - } - } - Meta::List(list) => { - let way: Ident = syn::parse2(list.tokens.clone())?; - let way = match way.to_string().as_str() { - "borrow" => Way::Borrow, - "as_ref" => Way::AsRef, - _ => { - return Err(syn::Error::new_spanned( - way, - "unsupported value, expected `borrow` or `as_ref`", - )) - } - }; - let ident = list.path.get_ident().ok_or_else(|| { - syn::Error::new_spanned( - list, - "unsupported attribute, expected `input`, `env` or `data`", - ) - })?; - match ident.to_string().as_str() { - "input" => Ok(ArgKind::Input(way)), - "env" => Ok(ArgKind::Env(way)), - "data" => Ok(ArgKind::Data(way)), - "prev" => Ok(ArgKind::Prev(way)), - _ => Err(syn::Error::new_spanned( - ident, - "unsupported attribute, expected `input`, `env` or `data`", - )), - } - } - _ => Err(syn::Error::new_spanned( - value, - "unsupported attribute, expected `input`, `env` or `data`", - )), - } - } -} - -fn expand_input( - ctx: &mut Ctx, - fn_arg: &mut FnArg, - input_ty: &Type, -) -> Result> { - let indicator = super::indicator(); - - let FnArg::Typed(arg) = fn_arg else { - return Err(syn::Error::new_spanned(fn_arg, "expected typed argument")); - }; - let attr = arg.attrs.pop(); - if !arg.attrs.is_empty() { - return Err(syn::Error::new_spanned( - arg, - "expected at most one attribute", - )); - } - let Some(attr) = attr else { - return Ok(None); - }; - let Type::Reference(TypeReference { - elem: target_ty, - lifetime: None, - mutability: None, - .. - }) = &*arg.ty - else { - return Err(syn::Error::new_spanned( - arg, - "expected reference type without lifetime and mutability, e.g. `&T`", - )); - }; - let kind: ArgKind = ArgKind::try_from(&attr.meta)?; - let name = get_variable_name(&arg.pat).map(|n| n.to_string()); - let generic = match kind { - ArgKind::Input(way) => { - let name = Ident::new( - &name.unwrap_or_else(|| format!("input{}", ctx.input)), - Span::call_site(), - ); - ctx.input += 1; - let pat = quote!(#indicator::context::In(#name)); - let ty = input_ty.clone(); - let (generic, attr) = if way.is_borrow() { - ( - syn::parse2(quote!(#ty: core::borrow::Borrow<#target_ty>))?, - quote!(#[borrow(#name)]), - ) - } else { - ( - syn::parse2(quote!(#ty: AsRef<#target_ty>))?, - quote!(#[as_ref(#name)]), - ) - }; - *fn_arg = syn::parse2(quote!(#attr #pat: #indicator::context::In<&#ty>))?; - generic - } - ArgKind::Env(way) => { - let name_string = name.unwrap_or_else(|| format!("env{}", ctx.env)); - let name = Ident::new(&name_string, Span::call_site()); - ctx.env += 1; - let pat = quote!(#indicator::context::Env(#name)); - let ty = Ident::new(&name_string.to_case(Case::Pascal), Span::call_site()); - let (generic, attr) = if way.is_borrow() { - ( - syn::parse2( - quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static), - )?, - quote!(#[borrow(#name)]), - ) - } else { - ( - syn::parse2(quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static))?, - quote!(#[as_ref(#name)]), - ) - }; - *fn_arg = syn::parse2(quote!(#attr #pat: #indicator::context::Env<&#ty>))?; - generic - } - ArgKind::Data(way) => { - let name_string = name.unwrap_or_else(|| format!("data{}", ctx.env)); - let name = Ident::new(&name_string, Span::call_site()); - ctx.data += 1; - let pat = quote!(#indicator::context::Data(#name)); - let ty = Ident::new(&name_string.to_case(Case::Pascal), Span::call_site()); - let (generic, attr) = if way.is_borrow() { - ( - syn::parse2( - quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static), - )?, - quote!(#[borrow(#name)]), - ) - } else { - ( - syn::parse2(quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static))?, - quote!(#[as_ref(#name)]), - ) - }; - *fn_arg = syn::parse2(quote!(#attr #pat: #indicator::context::Data<&#ty>))?; - generic - } - ArgKind::Prev(way) => { - let name_string = name.unwrap_or_else(|| format!("prev{}", ctx.env)); - let name = Ident::new(&name_string, Span::call_site()); - ctx.data += 1; - let pat = quote!(#indicator::context::Prev(#name)); - let ty = Ident::new(&name_string.to_case(Case::Pascal), Span::call_site()); - let (generic, attr) = if way.is_borrow() { - ( - syn::parse2( - quote!(#ty: core::borrow::Borrow<#target_ty> + Send + Sync + 'static), - )?, - quote!(#[borrow(#name)]), - ) - } else { - ( - syn::parse2(quote!(#ty: AsRef<#target_ty> + Send + Sync + 'static))?, - quote!(#[as_ref(#name)]), - ) - }; - *fn_arg = syn::parse2(quote!(#attr #pat: #indicator::context::Prev<&#ty>))?; - generic - } - }; - Ok(Some(generic)) -} - -fn get_variable_name(pat: &Pat) -> Option<&Ident> { - match pat { - Pat::Ident(pat) => Some(&pat.ident), - _ => None, - } -} - -fn get_return_type(sig: &Signature) -> Type { - match &sig.output { - ReturnType::Default => Type::Tuple(TypeTuple { - paren_token: Default::default(), - elems: Default::default(), - }), - ReturnType::Type(_, ty) => (**ty).clone(), - } -} - -fn get_type_inside_option(ty: &Type) -> Result<&Type> { - let path = match ty { - Type::Path(path) => path, - _ => return Err(syn::Error::new_spanned(ty, "expected `Option<_>`")), - }; - let segment = path.path.segments.last().unwrap(); - if segment.ident != "Option" { - return Err(syn::Error::new_spanned(ty, "expected `Option<_>`")); - } - let args = &segment.arguments; - let args = match args { - syn::PathArguments::AngleBracketed(args) => args, - _ => return Err(syn::Error::new_spanned(ty, "expected `Option<_>`")), - }; - let arg = args.args.first().unwrap(); - let arg = match arg { - syn::GenericArgument::Type(ty) => ty, - _ => return Err(syn::Error::new_spanned(ty, "expected `Option<_>`")), - }; - Ok(arg) -} diff --git a/examples/context/context.rs b/examples/context/context.rs index c86418c..e155f4e 100644 --- a/examples/context/context.rs +++ b/examples/context/context.rs @@ -20,16 +20,10 @@ where #[derive(Clone)] struct Ma(T); -impl core::borrow::Borrow for Ma { - fn borrow(&self) -> &T { - &self.0 - } -} - /// An operator that does the following: /// `x => (x + prev(x)) / 2` -#[operator(input = I)] -fn ma(#[env] x: &T, Prev(prev): Prev<&Ma>) -> Ma +#[operator(input = T)] +fn ma(Env(x): Env<&T>, Prev(prev): Prev<&Ma>) -> Ma where T: Num + Clone, T: Send + Sync + 'static, @@ -40,7 +34,7 @@ where } fn main() -> anyhow::Result<()> { - let op = output_with(ma::>) + let op = output_with(ma) .inspect(|value| { println!("input: {}", value.value()); if let Some(AddTwo(x)) = value.context().env().get::>() { diff --git a/examples/context/data.rs b/examples/context/data.rs index 876def6..cd01554 100644 --- a/examples/context/data.rs +++ b/examples/context/data.rs @@ -2,20 +2,14 @@ use indicator::{prelude::*, IndicatorIteratorExt}; struct Count(usize); -impl From for Count { - fn from(value: usize) -> Self { - Self(value) - } -} - /// Odds counter. -#[operator(input = i32, generate_data)] -fn odds_counter(In(value): In<&i32>, Data(count): Data>) -> Option { +#[operator(input = i32)] +fn odds_counter(In(value): In<&i32>, Data(count): Data>) -> Option { let count = count.map(|c| c.0).unwrap_or(0); if *value % 2 == 1 { - Some(count + 1) + Some(Count(count + 1)) } else if count == 0 { - Some(0) + Some(Count(0)) } else { None } @@ -57,7 +51,7 @@ fn main() -> anyhow::Result<()> { }) .from_context::<&str>() // Asserting that the context has a `&str` data. .provide("This is my data!") - .insert_data(odds_counter::) + .insert_data(odds_counter) .insert_with_data(even_signal::) .cache(1) .finish();