Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #[mul(forward_and_scalar)] #450

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- Added `#[mul(forward_and_scalar)]` to generate both Add-like and Mul-like semantics when deriving Mul-likes.

## 2.0.1 - 2025-02-03

### Added
Expand Down
1 change: 1 addition & 0 deletions impl/doc/mul.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type in question deriving for this is not implemented.
NOTE: In case you don't want this behaviour you can add `#[mul(forward)]` in
addition to `#[derive(Mul)]`. This will instead generate a `Mul` implementation
with the same semantics as `Add`.
You can also add `#[mul(forward_and_scalar)]` to implement both to generate both Mul-like and Add-like semantics.



Expand Down
1 change: 1 addition & 0 deletions impl/doc/mul_assign.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ You can add the `#[mul_assign(forward)]` attribute if you don't want the same
semantics as `Mul`.
This will instead generate a `MulAssign` implementation with the same semantics
as `AddAssign`.
You can add `#[mul_assign(forward_and_scalar)]` to generate both Mul-like and Add-like semantics.



Expand Down
21 changes: 17 additions & 4 deletions impl/src/mul_assign_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
.to_string()
+ "_assign";

let mut state = State::with_attr_params(
let state = State::with_attr_params(
input,
trait_name,
method_name,
AttrParams::struct_(vec!["forward"]),
AttrParams::struct_(vec!["forward", "forward_and_scalar"]),
)?;

if state.default_info.forward {
return Ok(add_assign_like::expand(input, trait_name));
}

if state.default_info.forward_and_scalar {
return Ok(quote! {
{add_assign_like::expand(input, trait_name)}
{expand_mul_assign_like(state)}
});
}

Ok(expand_mul_assign_like(state))
}

fn expand_mul_assign_like(mut state: State<'_>) -> TokenStream {
let scalar_ident = format_ident!("__RhsT");
state.add_trait_path_type_param(quote! { #scalar_ident });
let multi_field_data = state.enabled_fields_data();
Expand Down Expand Up @@ -51,7 +64,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
);
let (impl_generics, _, where_clause) = generics.split_for_impl();

Ok(quote! {
quote! {
#[automatically_derived]
impl #impl_generics #trait_path<#scalar_ident> for #input_type #ty_generics #where_clause {
#[inline]
Expand All @@ -60,5 +73,5 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
#( #exprs; )*
}
}
})
}
}
17 changes: 13 additions & 4 deletions impl/src/mul_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ use std::iter;
use syn::{DeriveInput, Result};

pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
let mut state = State::with_attr_params(
let state = State::with_attr_params(
input,
trait_name,
trait_name.to_lowercase(),
AttrParams::struct_(vec!["forward"]),
AttrParams::struct_(vec!["forward", "forward_and_scalar"]),
)?;
if state.default_info.forward {
return Ok(add_like::expand(input, trait_name));
}
if state.default_info.forward_and_scalar {
return Ok(quote! {
{add_like::expand(input, trait_name)}
{expand_mul_like(state)}
});
}
Ok(expand_mul_like(state))
}

fn expand_mul_like(mut state: State<'_>) -> TokenStream {
let scalar_ident = format_ident!("__RhsT");
state.add_trait_path_type_param(quote! { #scalar_ident });
let multi_field_data = state.enabled_fields_data();
Expand Down Expand Up @@ -47,7 +56,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
);
let body = multi_field_data.initializer(&initializers);
let (impl_generics, _, where_clause) = generics.split_for_impl();
Ok(quote! {
quote! {
#[automatically_derived]
impl #impl_generics #trait_path_with_params for #input_type #ty_generics #where_clause {
type Output = #input_type #ty_generics;
Expand All @@ -58,5 +67,5 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
#body
}
}
})
}
}
20 changes: 20 additions & 0 deletions impl/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ impl<'input> State<'input> {
let defaults = struct_meta_info.into_full(FullMetaInfo {
enabled: default_enabled,
forward: false,
forward_and_scalar: false,
// Default to owned true, except when first attribute has one of owned,
// ref or ref_mut
// - not a single attribute means default true
Expand Down Expand Up @@ -873,6 +874,13 @@ fn get_meta_info(
None,
)?;

if info.forward.is_some() && info.forward_and_scalar.is_some() {
return Err(Error::new(
list.span(),
"Attributes `forward` and `forward_and_scalar` are mutually exclusive",
));
}

Ok(info)
}

Expand Down Expand Up @@ -1016,7 +1024,13 @@ fn parse_punctuated_nested_meta(
match (wrapper_name, attr_name.as_str()) {
(None, "ignore") => info.enabled = Some(false),
(None, "forward") => info.forward = Some(true),
(None, "forward_and_scalar") => {
info.forward_and_scalar = Some(true)
}
(Some("not"), "forward") => info.forward = Some(false),
(Some("not"), "forward_and_scalar") => {
info.forward_and_scalar = Some(false)
}
(None, "owned") => info.owned = Some(true),
(None, "ref") => info.ref_ = Some(true),
(None, "ref_mut") => info.ref_mut = Some(true),
Expand Down Expand Up @@ -1204,6 +1218,7 @@ pub(crate) mod polyfill {
pub struct FullMetaInfo {
pub enabled: bool,
pub forward: bool,
pub forward_and_scalar: bool,
pub owned: bool,
pub ref_: bool,
pub ref_mut: bool,
Expand All @@ -1214,6 +1229,7 @@ pub struct FullMetaInfo {
pub struct MetaInfo {
pub enabled: Option<bool>,
pub forward: Option<bool>,
pub forward_and_scalar: Option<bool>,
pub owned: Option<bool>,
pub ref_: Option<bool>,
pub ref_mut: Option<bool>,
Expand All @@ -1228,6 +1244,9 @@ impl MetaInfo {
FullMetaInfo {
enabled: self.enabled.unwrap_or(defaults.enabled),
forward: self.forward.unwrap_or(defaults.forward),
forward_and_scalar: self
.forward_and_scalar
.unwrap_or(defaults.forward_and_scalar),
owned: self.owned.unwrap_or(defaults.owned),
ref_: self.ref_.unwrap_or(defaults.ref_),
ref_mut: self.ref_mut.unwrap_or(defaults.ref_mut),
Expand Down Expand Up @@ -1727,6 +1746,7 @@ pub(crate) mod attr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
match input.parse::<syn::Path>()? {
p if p.is_ident("forward") => Ok(Self),
p if p.is_ident("forward_and_scalar") => Ok(Self),
p => Err(syn::Error::new(p.span(), "only `forward` allowed here")),
}
}
Expand Down