# autoargs
A Rust procedural macro for generating argument structs with default values, allowing for named arguments and partial argument specification. Works with both standalone functions and struct methods.
## Overview
When you annotate a function or method with `#[autoargs]`, the macro:
1. Generates a struct to hold the function's arguments
2. Implements `Default` for that struct, using specified default expressions
3. Creates a macro that lets you call the function with named arguments
## Examples
### Functions
```rust
use autoargs::{autoargs, default};
struct A(String);
struct B(u32);
struct C(bool);
fn foo() -> A { A("default_a".to_string()) }
fn bar() -> B { B(42) }
fn baz() -> C { C(true) }
#[autoargs]
fn draw(
#[default = "foo()"]
a: A,
#[default = "bar()"]
b: B,
#[default = "baz()"]
c: C,
) -> String {
format!("Drawing: a={}, b={}, c={}", a.0, b.0, c.0)
}
// Call with named arguments (unspecified arguments use defaults)
let result = draw!(
a = A("custom_a".to_string()),
b = B(100),
// c is set to its default
);
```
### Methods
For methods, you need to use both `autoargs` on the methods and `impl_autoargs` on the impl block:
```rust
use autoargs::{autoargs, impl_autoargs, default};
struct Canvas {
width: u32,
height: u32,
}
impl Canvas {
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
}
#[impl_autoargs]
impl Canvas {
#[autoargs]
fn draw_rectangle(
&self,
#[default = "0"]
x: u32,
#[default = "0"]
y: u32,
#[default = "100"]
width: u32,
#[default = "50"]
height: u32,
#[default = "\"blue\".to_string()"]
color: String,
) -> String {
format!(
"Drawing a {} rectangle at ({}, {}) with size {}x{} on canvas ({}x{})",
color, x, y, width, height, self.width, self.height
)
}
#[autoargs]
fn resize(
&mut self,
#[default = "800"]
width: u32,
#[default = "600"]
height: u32,
) -> String {
self.width = width;
self.height = height;
format!("Resized canvas to {}x{}", width, height)
}
}
let canvas = Canvas::new(800, 600);
// All defaults
let result1 = draw_rectangle!(&canvas);
// Some custom values
let result2 = draw_rectangle!(&canvas, x = 10, y = 20, color = "red".to_string());
// Create and pass an args struct
let args = DrawRectangleArgs {
x: 30,
y: 40,
width: 200,
height: 100,
color: "green".to_string(),
};
let result3 = draw_rectangle!(&canvas, args);
```
## Generated Code
For functions, the macro generates:
```rust
struct DrawArgs {
pub a: A,
pub b: B,
pub c: C,
}
impl Default for DrawArgs {
fn default() -> Self {
Self {
a: foo(),
b: bar(),
c: baz(),
}
}
}
fn draw(args: DrawArgs) -> String {
let (a, b, c) = (args.a, args.b, args.c);
// original function body
}
macro_rules! draw {
() => {
draw(DrawArgs::default())
};
($($name:ident = $value:expr),* $(,)?) => {
{
let mut args = DrawArgs::default();
$(
args.$name = $value;
)*
draw(args)
}
};
($args:expr) => {
draw($args)
};
}
```
For methods, the `autoargs` attribute generates the args struct and modifies the method itself, while the `impl_autoargs` attribute creates the macro outside the impl block (since Rust doesn't allow macro definitions inside impl blocks):
```rust
// Generated by autoargs
struct DrawRectangleArgs {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
pub color: String,
}
impl Default for DrawRectangleArgs {
fn default() -> Self {
Self {
x: 0,
y: 0,
width: 100,
height: 50,
color: "blue".to_string(),
}
}
}
// Method implementation is modified by autoargs
fn draw_rectangle(&self, args: DrawRectangleArgs) -> String {
let (x, y, width, height, color) = (args.x, args.y, args.width, args.height, args.color);
// original method body
}
// Generated by impl_autoargs
macro_rules! draw_rectangle {
($self:expr) => {
$self.draw_rectangle(DrawRectangleArgs::default())
};
($self:expr, $($name:ident = $value:expr),* $(,)?) => {
{
let mut args = DrawRectangleArgs::default();
$(
args.$name = $value;
)*
$self.draw_rectangle(args)
}
};
($self:expr, $args:expr) => {
$self.draw_rectangle($args)
};
}
```
## Features
- Named arguments with Rust-like syntax
- Default values for arguments via attributes
- Skip any argument to use its default value
- Works with functions and all method types (`&self`, `&mut self`, `self`)
- Generates proper structs and macros with correct visibility
- Proper CamelCase naming convention for generated structs
## Advanced Usage
### Creating Custom Arg Structs
You can create a custom args struct and pass it directly:
```rust
let custom_args = DrawArgs {
a: custom_a,
b: DrawArgs::default().b, // Use default for b
c: custom_c,
};
// Pass the args struct directly
let result = draw!(custom_args);
```
### Using Default Trait
If a parameter doesn't have a `#[default = "..."]` attribute, it will use the type's `Default` implementation:
```rust
#[autoargs]
fn simple(
// Uses String::default()
name: String,
// Uses Option::<i32>::default()
value: Option<i32>,
) { /* ... */ }
```
### Best Practices
1. Always use specific types for your parameters that implement the required traits
2. Provide meaningful default values for each parameter
3. Break complex functions into smaller functions with clear argument sets
4. Use descriptive parameter names
## Implementation Notes
Due to Rust's limitations that prevent macro definitions within impl blocks, a two-part approach is used for methods:
1. `#[autoargs]` attribute on the method: Generates the args struct and rewrites the method to take the args struct
2. `#[impl_autoargs]` attribute on the impl block: Identifies all `#[autoargs]` methods and generates macros for them outside the impl block
3. `#[default = "..."]` attribute uses the `default` attribute from this crate
This approach allows for a clean, ergonomic usage pattern while respecting Rust's constraints.
Important: When using the library, make sure to import all the necessary components:
```rust
// For functions
use autoargs::{autoargs, default};
// For methods in impl blocks
use autoargs::{autoargs, impl_autoargs, default};
```
## Installation
Add to your Cargo.toml:
```toml
[dependencies]
autoargs = "0.1.0"
```
## License
MIT
## Disclaimer
This crate was 100% vibe coded after my friend went on a 15-minute rant about how Rust has no default args and how all the reasons are bikeshedding or ridiculous complaints about how default args are "code smell".