infiltrait

Crates.ioinfiltrait
lib.rsinfiltrait
version0.1.0
created_at2025-08-23 14:00:05.749802+00
updated_at2025-08-23 14:00:05.749802+00
descriptionA procedural macro that automatically generates trait definitions from implementation blocks
homepagehttps://github.com/aldenmoreton/infiltrait
repositoryhttps://github.com/aldenmoreton/infiltrait
max_upload_size
id1807540
size29,666
Alden Moreton (aldenmoreton)

documentation

README

Infiltrait

A Rust procedural macro that automatically generates trait definitions from implementation blocks.

Overview

The #[infiltrait] attribute macro allows you to define a trait and its implementation simultaneously by writing only the impl block. This eliminates the need to separately define the trait interface, reducing code duplication and keeping related code together.

Installation

Add this to your Cargo.toml:

[dependencies]
infiltrait = "0.1.0"

Quick Start

use infiltrait::infiltrait;

struct Calculator;

#[infiltrait]
impl Arithmetic for Calculator {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }

    fn multiply(&self, a: i32, b: i32) -> i32 {
        a * b
    }
}

// The macro automatically generates:
// trait Arithmetic {
//     fn add(&self, a: i32, b: i32) -> i32;
//     fn multiply(&self, a: i32, b: i32) -> i32;
// }

// Now you can use the trait normally:
fn calculate<T: Arithmetic>(calc: &T) -> i32 {
    let sum = calc.add(2, 3);
    calc.multiply(sum, 4)
}

Features

  • Zero Runtime Overhead: Pure compile-time code generation
  • Full Feature Support: Methods, associated types, and associated constants
  • Visibility Control: Respects pub, pub(crate), and other visibility modifiers
  • Safety Preservation: Maintains unsafe markers where appropriate
  • Clear Error Messages: Helpful compile-time diagnostics

Examples

Basic Methods

use infiltrait::infiltrait;

struct Logger;

#[infiltrait]
impl Logging for Logger {
    fn info(&self, message: &str) {
        println!("[INFO] {}", message);
    }

    fn error(&self, message: &str) {
        eprintln!("[ERROR] {}", message);
    }
}

Associated Types and Constants

use infiltrait::infiltrait;

struct Database;

#[infiltrait]
impl Storage for Database {
    type Item = String;
    const MAX_CAPACITY: usize = 1000;

    fn store(&mut self, item: Self::Item) -> Result<(), &'static str> {
        // Implementation details...
        Ok(())
    }

    fn capacity(&self) -> usize {
        Self::MAX_CAPACITY
    }
}

Public Traits

use infiltrait::infiltrait;

struct ApiService;

#[infiltrait]
pub impl PublicApi for ApiService {
    fn process_request(&self, data: &str) -> String {
        format!("Processed: {}", data)
    }
}

// The generated trait will also be public:
// pub trait PublicApi { ... }

Generic Implementations

use infiltrait::infiltrait;

struct Container<T> {
    value: T,
}

#[infiltrait]
impl<T: Clone> Wrapper<T> for Container<T> {
    fn get(&self) -> T {
        self.value.clone()
    }

    fn set(&mut self, value: T) {
        self.value = value;
    }
}

Unsafe Traits

use infiltrait::infiltrait;

struct RawPointer(*mut u8);

#[infiltrait]
unsafe impl UnsafeOperations for RawPointer {
    unsafe fn read(&self) -> u8 {
        *self.0
    }

    unsafe fn write(&mut self, value: u8) {
        *self.0 = value;
    }
}

Use Cases

Rapid Prototyping

When exploring API designs, infiltrait lets you focus on the implementation without getting bogged down in trait definitions:

use infiltrait::infiltrait;

struct GameEngine;

#[infiltrait]
impl GameLoop for GameEngine {
    fn update(&mut self, delta_time: f32) {
        // Game logic here...
    }

    fn render(&self) {
        // Rendering code here...
    }
}

Library Development

Create clean APIs where the trait and implementation are defined together:

use infiltrait::infiltrait;

pub struct HttpClient;

#[infiltrait]
pub impl HttpRequests for HttpClient {
    async fn get(&self, url: &str) -> Result<String, Box<dyn std::error::Error>> {
        // HTTP GET implementation
        Ok("response".to_string())
    }

    async fn post(&self, url: &str, body: &str) -> Result<String, Box<dyn std::error::Error>> {
        // HTTP POST implementation
        Ok("response".to_string())
    }
}

Testing and Mocking

Generate traits that can be easily mocked for testing:

use infiltrait::infiltrait;

struct FileSystem;

#[infiltrait]
impl FileOperations for FileSystem {
    fn read_file(&self, path: &str) -> std::io::Result<String> {
        std::fs::read_to_string(path)
    }

    fn write_file(&self, path: &str, content: &str) -> std::io::Result<()> {
        std::fs::write(path, content)
    }
}

// Easy to create a mock for testing:
struct MockFileSystem;

impl FileOperations for MockFileSystem {
    fn read_file(&self, _path: &str) -> std::io::Result<String> {
        Ok("mock content".to_string())
    }

    fn write_file(&self, _path: &str, _content: &str) -> std::io::Result<()> {
        Ok(())
    }
}

Limitations

  • Trait Names: Cannot contain lifetimes or generic parameters in the trait name itself
  • Implementation Only: Only works with trait implementations, not inherent impls
  • Single Trait: Each #[infiltrait] attribute generates exactly one trait

Error Handling

The macro provides clear error messages for common mistakes:

use infiltrait::infiltrait;

struct MyStruct;

// ❌ Error: Please name a trait to implement
#[infiltrait]
impl MyStruct {
    fn method(&self) {}
}

// ❌ Error: Trait may not contain lifetimes or generics
#[infiltrait]
impl MyTrait<T> for MyStruct {
    fn method(&self) {}
}

How It Works

The infiltrait macro:

  1. Parses the implementation block and extracts the trait name
  2. Converts each implementation item into its corresponding trait item:
    • Method implementations → Method signatures
    • Associated constants → Associated constant declarations
    • Associated types → Associated type declarations
  3. Generates both the trait definition and preserves the original implementation
  4. Applies the same visibility and safety modifiers to the generated trait

Contributing

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

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 3

cargo fmt