#injection #field #struct-fields #visibility #proc-macro #rules #injectable

macro injectables

A procedural macro library that enables field injection between Rust structs through #[injectable] and #[inject_fields] attributes. The library handles visibility rules, generic type resolution, and detects circular dependencies during compile time to ensure safe composition. This enables a declarative approach to struct composition where fields from one or more source structs can be automatically injected into target structs while respecting Rust's ownership and visibility rules.

1 unstable release

0.1.0 Oct 25, 2024

#1511 in Rust patterns

MIT/Apache

335KB
455 lines

injectables

My Image

A Rust procedural macro library that enables field injection between structs through declarative attributes. This library allows you to compose structs by automatically injecting fields from one or more source structs while respecting Rust's visibility rules and ownership semantics.

Features

  • 🔒 Respects Rust's visibility rules (pub, pub(crate), private)
  • 🧬 Supports generic types with concrete type resolution
  • ⚡ Compile-time dependency injection and validation
  • 🔍 Detects circular dependencies and invalid injections at compile time
  • 🌳 Supports nested/transitive injections
  • 📦 Zero runtime overhead

Installation

Add this to your Cargo.toml:

[dependencies]
injectables = "0.1.0"

Basic Usage

use injectables::{injectable, inject_fields};

// Mark source struct as injectable
#[injectable]
pub struct Base {
    pub id: u64,
}

// Inject fields from Base into Document
#[inject_fields(Base)]
pub struct Document {
    pub title: String,
}

fn main() {
    let doc = Document {
        title: "Test".to_string(),
        id: 1,  // Field injected from Base
    };
    
    assert_eq!(doc.id, 1);
    assert_eq!(doc.title, "Test");
}

Advanced Features

Generic Type Support

The library handles generic types with concrete type specifications:

#[injectable]
pub struct GenericBase<T> {
    pub data: T,
}

#[inject_fields(GenericBase<String>)]
pub struct Container {
    pub name: String,
}

let container = Container {
    name: "Test".to_string(),
    data: "Hello".to_string(),  // Injected with concrete String type
};

Nested Injections

Fields can be injected transitively through multiple structs:

#[injectable]
pub struct A {
    pub id: u64,
}

#[injectable]
#[inject_fields(A)]
pub struct B {
    pub name: String,
}

#[inject_fields(B)]
pub struct C {
    pub description: String,
}

// C will have fields: description, name (from B), and id (from A)

Visibility Rules

The library respects Rust's visibility rules:

mod inner {
    #[injectable]
    pub struct Private {
        id: u64,           // private field
        pub name: String,  // public field
        pub(crate) age: u32, // crate-visible field
    }
}

// Can only inject visible fields based on module boundaries
#[inject_fields(inner::Private)]
pub struct Public {
    pub value: String,
    // Can access `name` and `age`, but not `id`
}

Compile-Time Validations

The library performs several compile-time checks to ensure correct usage:

  • ❌ Prevents circular dependencies between structs
  • ❌ Detects duplicate field names
  • ❌ Validates visibility access rules
  • ❌ Ensures source structs are marked as #[injectable]
  • ❌ Prevents injection into enums or non-struct types
  • ❌ Validates generic type parameters

Current Limitations

  1. Only works with named struct fields (not tuple structs)
  2. Cannot inject fields into enums
  3. Source structs must be marked with #[injectable] before being used in inject_fields
  4. Injected fields maintain their original visibility rules
  5. When using generic types, concrete types must be specified in inject_fields

Error Messages

The library provides clear compile-time error messages:

  • When attempting circular injections:

    error: Circular injection chain detected: A already depends on B
    
  • When accessing private fields from invalid module:

    error: Cannot access private field 'id' from struct 'Private' defined in module 'other_module'
    

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Licensed under either of:

at your option.

Safety

This crate uses #![forbid(unsafe_code)] to ensure 100% safe Rust.

Dependencies