objs 0.1.0

Lightweight macros for creating typed, or untyped, named, or anonymous objects in Rust.
Documentation
# objs

[![Crates.io](https://img.shields.io/crates/v/objs.svg)](https://crates.io/crates/objs)
[![Docs.rs](https://docs.rs/objs/badge.svg)](https://docs.rs/objs)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](#license)
[![License](https://img.shields.io/badge/license-APACHE-2.svg)](#license)

**`objs`** is a small, zero-dependency Rust macro library for creating **named** and **unnamed (anonymous) objects** on the fly — without hand-writing a `struct` declaration every time you need a quick, ad-hoc bundle of fields.

It's useful for test fixtures, one-off "record" values, lightweight configuration objects, and anywhere you'd reach for an anonymous struct or object literal in other languages, but want to stay in idiomatic, zero-cost Rust.

```rust
use objs::obj;

let point = obj! {
    x: i32 = 10,
    y: i32 = 20,
};

assert_eq!(point.x, 10);
assert_eq!(point.y, 20);
```

Under the hood, every macro in this crate expands to a completely ordinary Rust `struct`. There's no runtime overhead, no dynamic typing, and no hidden allocations — you get a real, statically-typed value that the compiler treats exactly like any struct you'd write by hand.

---

## Table of contents

- [Installation](#installation)
- [Overview](#overview)
- [`obj!` — anonymous, explicitly-typed objects](#obj--anonymous-explicitly-typed-objects)
- [`infer_obj!` — anonymous, type-inferred objects](#infer_obj--anonymous-type-inferred-objects)
- [`let_obj!` — named objects bound to a variable](#let_obj--named-objects-bound-to-a-variable)
- [Choosing the right macro](#choosing-the-right-macro)
- [Attributes and derives](#attributes-and-derives)
- [Implementing traits and methods](#implementing-traits-and-methods)
- [Limitations](#limitations)
- [License](#license)

---

## Installation

Add `objs` to your `Cargo.toml`:

```toml
[dependencies]
objs = "0.1"
```

All three macros are exported from the crate root, so you can bring in exactly what you need:

```rust
use objs::{obj, infer_obj, let_obj};
```

---

## Overview

| Macro | Produces | Type name | Types | Supports Attributes | Supports `impl` | Reusable type? |
|---|---|---|---|---|---|---|
| [`obj!`](#obj--anonymous-explicitly-typed-objects) | Anonymous object | `Object` (block-scoped) | Explicit, per-field | ✅ (before fields) | ✅ | ❌ (scoped to the macro's block) |
| [`infer_obj!`](#infer_obj--anonymous-type-inferred-objects) | Anonymous object | `Object` (block-scoped, generic) | Inferred from values | ✅ (before fields) | ❌ | ❌ (scoped to the macro's block) |
| [`let_obj!`](#let_obj--named-objects-bound-to-a-variable) | Named object + variable | Whatever name you choose | Explicit, per-field | ✅ (before object name) | ✅ | ✅ |

---

## `obj!` — anonymous, explicitly-typed objects

`obj!` builds a one-off object with explicitly annotated field types. It expands to a locally-scoped `struct Object { .. }` plus an initialized instance of it, all wrapped in a block expression.

### Syntax

```text
obj! {
    [#[attributes]]
    field_name: FieldType = value,
    ...
    [; impl [Trait] { .. } ...]
}
```

- Fields are comma-separated `name: Type = value` pairs. A trailing comma is optional, and the field list may be empty.
- Optional attributes (like `#[derive(Debug)]`) may precede the field list and are forwarded onto the generated struct.
- After a `;`, you may add one or more `impl [Trait] { .. }` blocks to give the object methods or trait implementations.

### Examples

Basic usage:

```rust
use objs::obj;

let point = obj! {
    x: i32 = 10,
    y: i32 = 20,
};

assert_eq!(point.x, 10);
assert_eq!(point.y, 20);
```

Implementing a trait:

```rust
use objs::obj;
use std::fmt;

let person = obj! {
    name: String = "Alice".to_string(),
    age: u32 = 30;
    impl fmt::Display {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{} is {}", self.name, self.age)
        }
    }
};

assert_eq!(format!("{}", person), "Alice is 30");
```

Adding inherent methods with a bare `impl { .. }` block (no trait):

```rust
use objs::obj;

let calculator = obj! {
    factor: i32 = 2;
    impl {
        fn multiply(&self, val: i32) -> i32 {
            val * self.factor
        }
    }
};

assert_eq!(calculator.multiply(5), 10);
```

An empty object is valid too:

```rust
use objs::obj;

let empty = obj! {};
drop(empty);
```

> **Note:** the generated struct is always named `Object` internally, but since the whole thing is wrapped in a block expression, this never collides with other `obj!` calls elsewhere in your code. You just can't refer to that type by name from outside the block — if you need that, use [`let_obj!`](#let_obj--named-objects-bound-to-a-variable) instead.

---

## `infer_obj!` — anonymous, type-inferred objects

`infer_obj!` is like `obj!`, but you skip writing out field types entirely. Each field becomes its own generic type parameter on the generated struct, and Rust infers the concrete type from the value you assign. This is handy for quick, throwaway objects where explicit types would just be noise.

### Syntax

```text
infer_obj! {
    [#[attributes]]
    field_name: value,
    ...
}
```

- Fields are comma-separated `name: value` pairs. A trailing comma is optional, and the field list may be empty.
- Optional attributes may precede the field list.

### Examples

```rust
use objs::infer_obj;

let mixed = infer_obj! {
    integer: 42,
    float: 3.14,
    text: "Hello",
};

assert_eq!(mixed.integer, 42);
assert_eq!(mixed.float, 3.14);
assert_eq!(mixed.text, "Hello");
```

Objects can be nested, since each field's value is just an ordinary expression:

```rust
use objs::infer_obj;

let complex = infer_obj! {
    id: 1,
    nested: infer_obj! {
        inner_val: "nested",
    },
};

assert_eq!(complex.id, 1);
assert_eq!(complex.nested.inner_val, "nested");
```

An empty object is valid too:

```rust
use objs::infer_obj;

let empty = infer_obj! {};
drop(empty);
```

> **Note:** `infer_obj!` does not support `impl` blocks — because the generated struct is generic over every field's type, implementing a trait or method on it would require naming those generic parameters yourself, which defeats the purpose of the macro. If you need behavior attached to the object, reach for [`obj!`](#obj--anonymous-explicitly-typed-objects) or [`let_obj!`](#let_obj--named-objects-bound-to-a-variable) instead. Also note that each field name doubles as a generic type parameter name, so field names must be valid, unique identifiers.

---

## `let_obj!` — named objects bound to a variable

`let_obj!` declares a real, nameable `struct` and binds an instance of it to a local variable — with the **same name** as the type — in a single step. Unlike `obj!` and `infer_obj!`, the generated type is left in scope, so you can construct additional instances of it later using normal struct-literal syntax.

### Syntax

```text
let_obj! {
    [#[attributes]]
    [mut] TypeName {
        field_name: FieldType = value,
        ...
    }
    [impl [Trait] { .. }]...
}
```

- `TypeName` becomes both the struct's type name and the bound variable's name.
- The optional `mut` keyword makes the variable mutable.
- Fields are comma-separated `name: Type = value` pairs, with an optional trailing comma. The field list may be empty.
- Optional attributes may precede `TypeName` and are forwarded to the struct definition.
- Zero or more `impl [Trait] { .. }` blocks may follow the field list.

### Examples

Immutable object:

```rust
use objs::let_obj;

let_obj! {
    User {
        id: u64 = 1001,
        active: bool = true,
    }
};

assert_eq!(User.id, 1001);
assert_eq!(User.active, true);
```

Mutable object:

```rust
use objs::let_obj;

let_obj! {
    mut Counter {
        count: i32 = 0,
    }
};

assert_eq!(Counter.count, 0);
Counter.count += 5;
assert_eq!(Counter.count, 5);
```

Implementing a trait, then reusing the generated type by name:

```rust
use objs::let_obj;

trait Greeter {
    fn greet(&self) -> String;
}

let_obj! {
    Bot {
        name: &'static str = "Robo",
    }
    impl Greeter {
        fn greet(&self) -> String {
            format!("Hello from {}", self.name)
        }
    }
};

assert_eq!(Bot.greet(), "Hello from Robo");

// `Bot` the type is still in scope, so it can be constructed again
// explicitly, just like any other struct.
let another_bot: Bot = Bot { name: "Android" };
assert_eq!(another_bot.name, "Android");
```

An empty object is valid too:

```rust
use objs::let_obj;

let_obj! {
    EmptyObj {}
};

let _copy = EmptyObj;
```

> **Note:** because the generated variable shares its name with the generated type, `let_obj!` applies `#[allow(non_camel_case_types)]` and `#[allow(non_snake_case)]` internally, so using a conventional `PascalCase` type name as a variable won't trigger lint warnings.

---

## Choosing the right macro

- Need a **quick, throwaway value** and don't care about the type's name? Use **`obj!`** (or **`infer_obj!`** if you don't want to write out field types).
- Need to **attach a trait implementation or methods**, but only within a single expression/block? Use **`obj!`**.
- Need the **type itself to be reusable** — as a function parameter, return type, or to construct more instances later? Use **`let_obj!`**.
- Want the **absolute least ceremony**, with field types inferred instead of written out? Use **`infer_obj!`**.

## Attributes and derives

All three macros forward attributes placed before the field list onto the generated struct, so standard derives work as expected:

```rust
use objs::obj;

let tagged = obj! {
    #[allow(dead_code)]
    secret: i32 = 99,
};

assert_eq!(tagged.secret, 99);
```

## Implementing traits and methods

Both `obj!` and `let_obj!` support trailing `impl` blocks:

- `impl { .. }` implements inherent methods directly on the generated object.
- `impl SomeTrait { .. }` implements `SomeTrait` for the generated object.

`infer_obj!` does not support `impl` blocks, since its generated struct is generic over every field's type (see [above](#infer_obj--anonymous-type-inferred-objects)).

## Limitations

- **`obj!` and `infer_obj!` types are not nameable.** Since both expand to a struct named `Object` inside a block expression, you cannot refer to that exact type from outside the macro invocation. Use `let_obj!` if you need a type you can name elsewhere (as a function signature, stored field, etc.).
- **`infer_obj!` field names must be valid generic parameter identifiers**, since each field name is reused as a generic type parameter on the generated struct.
- **`infer_obj!` cannot implement traits or methods**, for the same generic-parameter reason described above.
- These macros generate real structs at the call site, so normal Rust visibility, ownership, and lifetime rules apply — there is no dynamic typing or reflection involved.

## License

Licensed under the Apache License and the MIT License. See `LICENSE-APACHE2.txt` and `LICENSE-MIT.txt` for details.