Skip to content

Commit

Permalink
Merge pull request #94 from greyblake/infer-type-for-mut-callback
Browse files Browse the repository at this point in the history
Infer type for mut callback
  • Loading branch information
greyblake authored Oct 12, 2023
2 parents 7d8e8db + ee4a1da commit 80b069f
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 13 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,4 +448,4 @@ Thank you.

## License

MIT © [Sergey Potapov](https://www.greyblake.com)
MIT © [Serhii Potapov](https://www.greyblake.com)
2 changes: 1 addition & 1 deletion dummy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use nutype::nutype;

#[nutype(
derive(Debug, PartialEq, Deref, AsRef),
sanitize(with = |mut guests: Vec<String>| { guests.sort(); guests }),
sanitize(with = |mut guests| { guests.sort(); guests }),
validate(predicate = |guests| !guests.is_empty() ),
)]
pub struct GuestList(Vec<String>);
Expand Down
2 changes: 1 addition & 1 deletion nutype/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@
//!
//! #[nutype(
//! derive(Debug, PartialEq, Deref, AsRef),
//! sanitize(with = |mut guests: Vec<String>| { guests.sort(); guests }),
//! sanitize(with = |mut guests| { guests.sort(); guests }),
//! validate(predicate = |guests| !guests.is_empty() ),
//! )]
//! pub struct GuestList(Vec<String>);
Expand Down
47 changes: 39 additions & 8 deletions nutype_macros/src/common/gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,51 @@ use syn::Visibility;
/// |s| s.trim().to_lowercase()
/// Output:
/// |s: String| s.trim().to_lowercase()
///
/// or
///
/// Input:
/// |mut s| s.trim().to_lowercase()
/// Output:
/// |mut s: String| s.trim().to_lowercase()
// TODO: consider using syn instead messing with TokenStream directly
pub fn type_custom_closure(
closure_or_func_path: &TokenStream,
inner_type: impl ToTokens,
) -> TokenStream {
let inner_type_tokens = quote!(#inner_type);
let mut ts: Vec<TokenTree> = closure_or_func_path.clone().into_iter().collect();

// Check if the tokens match `|s|` pattern
// If so, inject the type, e.g. `|s: String|`
if ts.len() >= 3 && is_pipe(&ts[0]) && is_ident(&ts[1]) && is_pipe(&ts[2]) {
let colon = TokenTree::Punct(Punct::new(':', Spacing::Alone));
ts.insert(2, colon);
for (index, tok) in inner_type_tokens.into_iter().enumerate() {
let pos = index + 3;
ts.insert(pos, tok);
}
// If the tokens match `|s|` pattern
// then inject the type, e.g. `|s: String|`
insert_type_at_position(&mut ts, inner_type_tokens, 2);
} else if ts.len() >= 4
&& is_pipe(&ts[0])
&& is_mut(&ts[1])
&& is_ident(&ts[2])
&& is_pipe(&ts[3])
{
// If the tokens match `|mut s|` pattern,
// then inject the type, e.g. `|mut s: String|`
insert_type_at_position(&mut ts, inner_type_tokens, 3);
}

ts.into_iter().collect()
}

fn insert_type_at_position(ts: &mut Vec<TokenTree>, inner_type: TokenStream, pos: usize) {
// Insert `:`
let colon = TokenTree::Punct(Punct::new(':', Spacing::Alone));
ts.insert(pos, colon);

// Insert tokens of the type at position `pos +1` (basically after `:`)
for (index, tok) in inner_type.into_iter().enumerate() {
let pos = pos + 1 + index;
ts.insert(pos, tok);
}
}

fn is_pipe(token: &TokenTree) -> bool {
match token {
TokenTree::Punct(ref punct) => punct.as_char() == '|',
Expand All @@ -60,6 +84,13 @@ fn is_ident(token: &TokenTree) -> bool {
matches!(token, TokenTree::Ident(_))
}

fn is_mut(token: &TokenTree) -> bool {
match token {
TokenTree::Ident(ident) => ident == "mut",
_ => false,
}
}

pub fn gen_module_name_for_type(type_name: &TypeName) -> ModuleName {
let ident = format_ident!("__nutype_private_{type_name}__");
ModuleName::new(ident)
Expand Down

0 comments on commit 80b069f

Please sign in to comment.