├── .gitignore ├── Cargo.toml ├── LICENSE ├── readme.md ├── rustfmt.toml ├── src └── lib.rs └── tests └── default.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | /.idea 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "overloadable" 3 | version = "0.4.1" 4 | authors = ["OptimisticPeach "] 5 | edition = "2018" 6 | description = "Overloadable functions done easy in rust." 7 | repository = "https://github.com/OptimisticPeach/overloadable.git" 8 | keywords = ["function", "overloadable", "overwrite", "overload"] 9 | license = "MIT" 10 | readme = "readme.md" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | syn = { version = "0.15.39", features = ["full"] } 17 | quote = "0.6.13" 18 | proc-macro2 = { version = "0.4.30", features = ["nightly"] } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 OptimisticPeach 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # overloadable 2 | 3 | Easy to use overloadable functions in rust using nightly features. 4 | 5 | This crate provides you with the capabilities to overload your functions in a similar style to C# or C++, including support for meta attributes, type parameters and constraints, and visibility modifiers. 6 | Please visit the documentation for futher information. 7 | 8 | # Note 9 | This is a **nightly** crate. You _must_ include the following line in your code for this crate to compile: 10 | ```rust 11 | #![feature(unboxed_closures, fn_traits)] 12 | ``` 13 | 14 | ## Example: 15 | 16 | ```rust 17 | #![feature(unboxed_closures, fn_traits)] 18 | use overloadable::overloadable; 19 | 20 | overloadable! { 21 | pub func as 22 | #[inline(always)] 23 | fn(x: usize, y: usize) -> usize { 24 | x * y 25 | }, 26 | fn<'a>(x: &'a usize) -> f32 { 27 | *x as f32 28 | }, 29 | fn<'a, T>(x: &'a [T]) -> &'a T where T: std::fmt::Debug { 30 | println!("Found {:?}", &x[0]); 31 | &x[0] 32 | } 33 | } 34 | 35 | fn foo() { 36 | assert_eq!(func(2, 3), 6); 37 | assert_eq!(func(&32), 32.0); 38 | assert_eq!(func(&[1, 2, 3, 4][..]), &0); 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_layout = "HorizontalVertical" 2 | merge_imports = true 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This crate provides the ability to create overloadable functions in rust through 3 | the use of a macro. 4 | # Syntax: 5 | ```ignore 6 | # #![feature(fn_traits, unboxed_closures, proc_macro_hygiene)] 7 | # struct function_types; 8 | # struct optional_return_type; 9 | # trait constraints {} 10 | # const code_body: () = (); 11 | use overloadable::overloadable; 12 | overloadable!{ 13 | pub function_name as 14 | #[doc = "Some meta attributes."] 15 | fn(function_params: function_types) -> optional_return_type where OptionalTypeArgs: constraints { 16 | code_body 17 | } 18 | } 19 | ``` 20 | # What is produced 21 | Here is an example of the output produced by `overloadable`: 22 | ```ignore 23 | # #![feature(fn_traits, unboxed_closures)] 24 | use overloadable::overloadable; 25 | use std::fmt::Debug; 26 | overloadable!{ 27 | pub my_func as 28 | fn(x: usize, y: &str) -> f32 { 29 | (x * y.len()) as f32 30 | }, 31 | fn() where T: Debug {} 32 | } 33 | //Gives 34 | #[allow(non_camel_case_types)] 35 | pub struct my_func_; 36 | impl Fn<(usize, &str,)> for my_func { 37 | extern "rust-call" fn call(&self, (x, y,): (usize, &str,)) -> f32 { 38 | { 39 | (x * y.len()) as f32 40 | } 41 | } 42 | } 43 | //The rest of the `Fn*` family 44 | impl Fn<()> for my_func where T: Debug { 45 | extern "rust-call" fn call(&self, (): ()) -> () { 46 | {} 47 | } 48 | } 49 | //The rest of the `Fn*` family. 50 | ``` 51 | 52 | Note that you cannot have functions with unused generic parameters due to the 53 | trait-implementing nature of this method. 54 | */ 55 | extern crate proc_macro; 56 | use self::proc_macro::TokenStream; 57 | use proc_macro2::TokenStream as Tok2; 58 | use quote::{quote, quote_spanned, ToTokens}; 59 | use syn::{ 60 | bracketed, 61 | parenthesized, 62 | parse::{Parse, ParseStream, Result}, 63 | parse_macro_input, 64 | punctuated::Punctuated, 65 | spanned::Spanned, 66 | token::{Bracket, Paren}, 67 | Block, 68 | Error, 69 | Generics, 70 | Ident, 71 | Meta, 72 | Pat, 73 | ReturnType, 74 | Token, 75 | Type, 76 | TypeTuple, 77 | Visibility, 78 | WhereClause, 79 | }; 80 | 81 | struct OverloadableGlobal { 82 | vis: Visibility, 83 | name: Ident, 84 | _as_keyword: Token![as], 85 | fns: Punctuated, 86 | } 87 | 88 | impl Parse for OverloadableGlobal { 89 | fn parse(input: ParseStream) -> Result { 90 | Ok(Self { 91 | vis: input.parse()?, 92 | name: input.parse()?, 93 | _as_keyword: input.parse()?, 94 | fns: input.parse_terminated(ParsedFnDef::parse)?, 95 | }) 96 | } 97 | } 98 | 99 | struct OverloadableAssociated { 100 | vis: Visibility, 101 | struct_name: Ident, 102 | _colons: Token![::], 103 | name: Ident, 104 | _as: Token![as], 105 | fns: Punctuated, 106 | } 107 | 108 | impl Parse for OverloadableAssociated { 109 | fn parse(input: ParseStream) -> Result { 110 | Ok(Self { 111 | vis: input.parse()?, 112 | struct_name: input.parse()?, 113 | _colons: input.parse()?, 114 | name: input.parse()?, 115 | _as: input.parse()?, 116 | fns: input.parse_terminated(ParsedFnDef::parse)?, 117 | }) 118 | } 119 | } 120 | 121 | enum ThisDef { 122 | Explicit( 123 | Option, 124 | Token![self], 125 | Token![:], 126 | Type, 127 | Option, 128 | ), 129 | Implicit( 130 | Option, 131 | Option, 132 | Token![self], 133 | Option, 134 | ), 135 | } 136 | 137 | impl Parse for ThisDef { 138 | fn parse(input: ParseStream) -> Result { 139 | if (input.peek2(Token![:]) || input.peek3(Token![:])) 140 | && (input.peek(Token![self]) || input.peek2(Token![self])) 141 | { 142 | //Explicit route 143 | let mutable = if input.peek(Token![mut]) { 144 | Some(input.parse::()?) 145 | } else { 146 | None 147 | }; 148 | let this = input.parse::()?; 149 | Ok(ThisDef::Explicit( 150 | mutable, 151 | this, 152 | input.parse()?, 153 | input.parse()?, 154 | input.parse()?, 155 | )) 156 | } else if input.peek(Token![self]) || input.peek2(Token![self]) || input.peek3(Token![self]) 157 | { 158 | Ok(ThisDef::Implicit( 159 | input.parse()?, 160 | input.parse()?, 161 | input.parse()?, 162 | input.parse()?, 163 | )) 164 | } else { 165 | Err(input.error("Could not find self type!")) 166 | } 167 | } 168 | } 169 | 170 | impl ToTokens for ThisDef { 171 | fn to_tokens(&self, tokens: &mut Tok2) { 172 | match self { 173 | ThisDef::Explicit(mut_def, self_def, colon, ty, comma) => { 174 | mut_def.to_tokens(tokens); 175 | self_def.to_tokens(tokens); 176 | colon.to_tokens(tokens); 177 | ty.to_tokens(tokens); 178 | comma.to_tokens(tokens); 179 | } 180 | ThisDef::Implicit(and, mut_def, self_def, comma) => { 181 | and.to_tokens(tokens); 182 | mut_def.to_tokens(tokens); 183 | self_def.to_tokens(tokens); 184 | comma.to_tokens(tokens); 185 | } 186 | } 187 | } 188 | } 189 | 190 | impl ThisDef { 191 | pub fn is_sized_dependent(this: &Option) -> bool { 192 | if let Some(ThisDef::Implicit(None, ..)) = this { true } 193 | else { false } 194 | } 195 | } 196 | 197 | struct ParsedFnDef { 198 | meta: Vec<(Meta, Bracket)>, 199 | _func: Token![fn], 200 | gen: Option, 201 | paren: Paren, 202 | this: Option, 203 | params: Punctuated<(Pat, Token![:], Type), Token![,]>, 204 | ret: ReturnType, 205 | w_clause: Option, 206 | code: Block, 207 | } 208 | 209 | fn parse_pattern_type_pair(input: ParseStream) -> Result<(Pat, Token![:], Type)> { 210 | Ok((input.parse()?, input.parse()?, input.parse()?)) 211 | } 212 | 213 | impl Parse for ParsedFnDef { 214 | fn parse(input: ParseStream) -> Result { 215 | let mut meta = Vec::new(); 216 | while input.peek(Token![#]) { 217 | input.parse::()?; 218 | let meta_content; 219 | let brackets = bracketed!(meta_content in input); 220 | meta.push((meta_content.parse()?, brackets)) 221 | } 222 | let _func = input.parse::()?; 223 | let gen = if input.peek(Token![<]) { 224 | Some(input.parse::()?) 225 | } else { 226 | None 227 | }; 228 | let params_content; 229 | let paren = parenthesized!(params_content in input); 230 | let this = params_content.parse::().ok(); 231 | let params = params_content.parse_terminated(parse_pattern_type_pair)?; 232 | let ret = input.parse()?; 233 | let w_clause = if input.peek(Token![where]) { 234 | Some(input.parse::()?) 235 | } else { 236 | None 237 | }; 238 | let code = input.parse()?; 239 | Ok(Self { 240 | meta, 241 | _func, 242 | gen, 243 | paren, 244 | this, 245 | params, 246 | ret, 247 | w_clause, 248 | code, 249 | }) 250 | } 251 | } 252 | 253 | fn gen_fn_decls>(fns: T, name: &Ident) -> Result { 254 | let fns: Vec = fns.into_iter().map( 255 | |ParsedFnDef { 256 | gen, 257 | params, 258 | ret, 259 | w_clause, 260 | code, 261 | paren, 262 | meta, 263 | this, 264 | .. 265 | }| { 266 | if this.is_some() { 267 | return Err(Error::new(paren.span, "This declaration cannot contain a `self`-style parameter.")); 268 | } 269 | let ret = match ret { 270 | ReturnType::Type(_, ty) => *ty.clone(), 271 | ReturnType::Default => Type::Tuple(TypeTuple { paren_token: paren, elems: Punctuated::new() }), 272 | }; 273 | let mut param_types = Vec::new(); 274 | let mut param_patterns = Vec::new(); 275 | params.iter().for_each(|(pat, _, ty)| { 276 | param_types.push(ty); 277 | param_patterns.push(pat) 278 | }); 279 | let pty = ¶m_types[..]; 280 | let ppt = ¶m_patterns[..]; 281 | let meta: Vec = meta.iter().map(|(m, b)| quote_spanned!(b.span => #[#m])).collect(); 282 | let meta = &meta[..]; 283 | Ok(quote!( 284 | impl#gen Fn<(#(#pty,)*)> for #name #w_clause { 285 | #(#meta)* 286 | extern "rust-call" fn call(&self, (#(#ppt,)*): (#(#pty,)*)) -> Self::Output { 287 | #code 288 | } 289 | } 290 | impl#gen FnOnce<(#(#pty,)*)> for #name #w_clause { 291 | type Output = #ret; 292 | #(#meta)* 293 | extern "rust-call" fn call_once(self, x: (#(#pty,)*)) -> Self::Output { 294 | self.call(x) 295 | } 296 | } 297 | impl#gen FnMut<(#(#pty,)*)> for #name #w_clause { 298 | #(#meta)* 299 | extern "rust-call" fn call_mut(&mut self, x: (#(#pty,)*)) -> Self::Output { 300 | self.call(x) 301 | } 302 | } 303 | )) 304 | } 305 | ).collect::>>()?; 306 | Ok(quote!( 307 | #(#fns)* 308 | )) 309 | } 310 | 311 | fn gen_trait_fn_decls>( 312 | fns: T, 313 | name: &Ident, 314 | struct_name: &Ident, 315 | vis: &Visibility, 316 | ) -> Result { 317 | let fns: Vec = fns 318 | .into_iter() 319 | .enumerate() 320 | .map( 321 | |( 322 | index, 323 | ParsedFnDef { 324 | gen, 325 | params, 326 | ret, 327 | w_clause, 328 | code, 329 | paren, 330 | meta, 331 | this, 332 | .. 333 | }, 334 | )| { 335 | let ret = match ret { 336 | ReturnType::Type(_, ty) => *ty.clone(), 337 | ReturnType::Default => Type::Tuple(TypeTuple { 338 | paren_token: paren, 339 | elems: Punctuated::new(), 340 | }), 341 | }; 342 | let mut trait_params = Vec::with_capacity(params.len()); 343 | let mut impl_params = Vec::with_capacity(params.len()); 344 | for (index, (lhs, _, rhs)) in params.iter().enumerate() { 345 | let next_ident = Ident::new(&format!("_{}", index), lhs.span()); 346 | trait_params.push(quote!(#next_ident: #rhs)); 347 | impl_params.push(quote!(#lhs: #rhs)); 348 | } 349 | let meta: Vec = meta 350 | .iter() 351 | .map(|(m, b)| quote_spanned!(b.span => #[#m])) 352 | .collect(); 353 | let meta = &meta[..]; 354 | let trait_name = Ident::new( 355 | &format!("{}Trait{}", struct_name, index), 356 | struct_name.span(), 357 | ); 358 | let sized_requirement = if ThisDef::is_sized_dependent(&this) { 359 | quote!(Sized) 360 | } else { quote!() }; 361 | Ok(quote!( 362 | #vis trait #trait_name: #sized_requirement { 363 | fn #name#gen(#this#(#trait_params),*) -> #ret #w_clause; 364 | } 365 | impl #trait_name for #struct_name { 366 | #(#meta)* 367 | fn #name#gen(#this#(#impl_params),*) -> #ret #w_clause { 368 | #code 369 | } 370 | } 371 | )) 372 | }, 373 | ) 374 | .collect::>>()?; 375 | 376 | Ok(quote!( 377 | #(#fns)* 378 | )) 379 | } 380 | 381 | /// 382 | /// Overloadable function macro. Please read the top level documentation for this crate 383 | /// for more information on this. 384 | /// 385 | /// ## Example: 386 | /// ``` 387 | /// # #![feature(fn_traits, unboxed_closures, proc_macro_hygiene)] 388 | /// # use std::fmt::{Debug, Display}; 389 | /// overloadable::overloadable! { 390 | /// my_func as 391 | /// fn(x: usize) -> usize { 392 | /// x * 2 393 | /// }, 394 | /// fn(x: &str) -> usize { 395 | /// x.len() 396 | /// }, 397 | /// fn(x: T, y: T) -> String where T: Display { 398 | /// format!("{:?}, {}", x, y) 399 | /// }, 400 | /// fn((x, y): (T, T)) -> String { 401 | /// my_func(x, y) 402 | /// }, 403 | /// } 404 | /// ``` 405 | /// 406 | #[proc_macro] 407 | pub fn overloadable(input: TokenStream) -> TokenStream { 408 | let OverloadableGlobal { vis, name, fns, .. } = parse_macro_input!(input as OverloadableGlobal); 409 | let name = &name; 410 | let struct_decl = quote_spanned! { name.span() => 411 | #[doc(hidden)] 412 | #[allow(non_camel_case_types)] 413 | #[allow(dead_code)] 414 | #vis struct #name; 415 | }; 416 | let fn_decls = gen_fn_decls(fns, name).unwrap(); 417 | 418 | let expanded = quote! { 419 | #struct_decl 420 | #(#fn_decls)* 421 | }; 422 | TokenStream::from(expanded) 423 | } 424 | 425 | /// 426 | /// Overloadable function macro for members. This allows you to have overloadable methods 427 | /// and associated functions. 428 | /// 429 | /// This has similar syntax to that of `overloadable`, except that it differs in that 430 | /// the function name must be preceded by `StructName::`, for example: 431 | /// 432 | /// ``` 433 | /// # #![feature(fn_traits, unboxed_closures, proc_macro_hygiene)] 434 | /// struct Foo; 435 | /// overloadable::overloadable_member!{ 436 | /// Foo::func_name as 437 | /// fn() {}, 438 | /// fn(self) {}, 439 | /// fn(mut self, x: isize) {}, 440 | /// fn(&self, y: usize) {}, 441 | /// fn(self: Box, z: &str) {} 442 | /// } 443 | /// ``` 444 | /// 445 | /// ** NOTE ** 446 | /// This is internally implemented using custom traits, so to have this functionality 447 | /// carry over, you must use a `use my_mod::*` to import all of the traits defined by 448 | /// this macro. 449 | /// 450 | #[proc_macro] 451 | pub fn overloadable_member(input: TokenStream) -> TokenStream { 452 | let OverloadableAssociated { 453 | vis, 454 | struct_name, 455 | name, 456 | fns, 457 | .. 458 | } = parse_macro_input!(input as OverloadableAssociated); 459 | TokenStream::from(gen_trait_fn_decls(fns, &name, &struct_name, &vis).unwrap()) 460 | } 461 | -------------------------------------------------------------------------------- /tests/default.rs: -------------------------------------------------------------------------------- 1 | #![feature(unboxed_closures, fn_traits)] 2 | use std::fmt::Debug; 3 | overloadable::overloadable! { 4 | pub(crate) func_name as 5 | fn(_: usize) {}, 6 | fn((x, y): (T, usize)) -> String where T: Debug { 7 | format!("{:?}, {:?}", x, y) 8 | }, 9 | #[no_mangle] 10 | fn<'a, 'b: 'a>(a: &mut &'a str, b: &'b str) { 11 | *a = &b[..] 12 | } 13 | } 14 | 15 | #[test] 16 | fn it_works() { 17 | assert_eq!(func_name((1, 2)), "1, 2"); 18 | let a = "abc"; 19 | { 20 | let mut b = "def"; 21 | func_name(&mut b, a); 22 | assert_eq!(b, "abc"); 23 | } 24 | } 25 | 26 | pub struct Foo1; 27 | 28 | overloadable::overloadable_member! { 29 | pub Foo1::func_name as 30 | fn(_: usize) {}, 31 | fn((x, y): (T, usize)) -> String where T: Debug { 32 | format!("{:?}, {:?}", x, y) 33 | }, 34 | #[no_mangle] 35 | fn<'a, 'b: 'a>(self, a: &mut &'a str, b: &'a str) { 36 | *a = &b[..] 37 | }, 38 | fn(&self) -> usize {1} 39 | } 40 | 41 | //Forum example: 42 | #[derive(Clone)] 43 | enum Foo { 44 | A, 45 | B, 46 | } 47 | overloadable::overloadable_member! { 48 | Foo::my_func as 49 | fn(&self) -> &'static str { 50 | match self { 51 | Foo::A => "A", 52 | Foo::B => "B", 53 | } 54 | }, 55 | fn(self, x: usize) -> Vec { 56 | let mut val = Vec::new(); 57 | val.resize_with(x, || self.clone()); 58 | val 59 | }, 60 | fn() -> Box { 61 | Box::new(Foo::A) 62 | }, 63 | fn(self: Box, other: Box) -> usize { 64 | match (&*self, &*other) { 65 | (Foo::A, Foo::A) => 2, 66 | (Foo::B, Foo::A) | (Foo::A, Foo::B) => 1, 67 | _ => 0 68 | } 69 | } 70 | } 71 | --------------------------------------------------------------------------------