batch-impl 0.9.1

A proc-macro library for batch generating trait impls with a powerful DSL
Documentation
//! dsl.rs §1-12 + §29: basic batch generation — concrete types, generics,
//! body merging, `.` lists, tuple generation, associated type bindings,
//! unsafe impls, fn types, attributes, complex passthrough, unsafe fn types.
//! (split from the former single-file `tests/dsl.rs`)

use batch_impl::batch_impl;
use std::rc::Rc;

// ============================================================
// 1. Basic: implement directly for concrete types
// ============================================================
#[batch_impl(usize, isize)]
trait Numeric {}

#[test]
fn basic_numeric() {
    fn check<T: Numeric>(_: &T) {}
    check(&0usize);
    check(&0isize);
}

// ============================================================
// 2. Generics: <T> Vec<T>
// ============================================================
#[batch_impl(<T> Vec<T>)]
trait Collection {}

#[test]
fn generic_vec() {
    fn check<T: Collection>(_: &T) {}
    check(&vec![1, 2, 3]);
    check(&vec!["a", "b"]);
}

// ============================================================
// 3. Shared + independent body merging
// ============================================================
#[batch_impl(
    [usize { fn name() -> &'static str { "usize" } },
     isize { fn name() -> &'static str { "isize" } }]
    { fn zero() -> Self { 0 } }
)]
trait Zero {
    fn zero() -> Self;
    fn name() -> &'static str;
}

#[test]
fn shared_independent_body() {
    assert_eq!(usize::zero(), 0);
    assert_eq!(isize::zero(), 0);
    assert_eq!(<usize as Zero>::name(), "usize");
    assert_eq!(<isize as Zero>::name(), "isize");
}

// ============================================================
// 4. . operator: [&, Box, Rc].u32 cartesian product
// ============================================================
#[batch_impl([&, Box, Rc].u32)]
trait RefOrOwnedEmpty {}

#[test]
fn caret_prefix_list() {
    fn check<T: RefOrOwnedEmpty>(_: &T) {}
    let v: u32 = 5;
    check(&(&v));
    check(&Box::new(v));
    check(&Rc::new(v));
}

// ============================================================
// 5. Tuple generation: ().3
// ============================================================
#[batch_impl(().3)]
trait Tuple3 {}

#[test]
fn tuple_pow_basic() {
    fn check<T: Tuple3>(_: &T) {}
    check(&(1u8, 2u16, 3u32));
}

// ============================================================
// 6. Range tuples: ().1..=3
// ============================================================
#[batch_impl(().1)]
trait Tuple1 {}
#[batch_impl(().2)]
trait Tuple2 {}
#[batch_impl(().3)]
trait Tuple3R {}

#[test]
fn tuple_range_pow() {
    fn t1<T: Tuple1>(_: &T) {}
    fn t2<T: Tuple2>(_: &T) {}
    fn t3<T: Tuple3R>(_: &T) {}
    t1(&(1u8,));
    t2(&(1u8, 2u16));
    t3(&(1u8, 2u16, 3u32));
}

// ============================================================
// 7. Associated type bindings: <T> Iter<Item=T> Vec<T> {...}
// ============================================================
#[batch_impl(<T> Iter<Item=T> Vec<T> {
    fn count(&self) -> usize { self.len() }
})]
trait Iter {
    type Item;
    fn count(&self) -> usize;
}

#[test]
fn assoc_type_binding() {
    assert_eq!(vec![1, 2, 3].count(), 3);
}

// ============================================================
// 8. unsafe impl: `unsafe` before TRAIT makes all impls unsafe
// ============================================================
/// # Safety
///
/// Marker trait for testing; no actual unsafe semantics.
#[batch_impl(usize, Box<u32>)]
unsafe trait UnsafeAll {}

#[test]
fn unsafe_trait_impls() {
    fn check<T: UnsafeAll>(_: &T) {}
    check(&0usize);
    check(&Box::new(0u32));
}

// ============================================================
// 9. Partial unsafe
// ============================================================
/// # Safety
///
/// Marker trait for testing; no actual unsafe semantics.
#[batch_impl(unsafe.usize, isize)]
unsafe trait PartialUnsafe {}

#[test]
fn partial_unsafe() {
    fn check<T: PartialUnsafe>(_: &T) {}
    check(&0usize);
    check(&0isize);
}

// ============================================================
// 10. fn types
// ============================================================
#[batch_impl(fn.(i32, u32))]
trait FnSimple {}

#[batch_impl(fn(i32, u32) String)]
trait FnWithReturn {}

#[test]
fn fn_types() {
    fn check_simple<T: FnSimple>(_: &T) {}
    fn check_ret<T: FnWithReturn>(_: &T) {}
    let f: fn(i32, u32) = |_, _| {};
    check_simple(&f);
    let fr: fn(i32, u32) -> String = |_, _| String::new();
    check_ret(&fr);
}

// `unsafe fn` is an unsafe fn type (the impl stays safe); `unsafe.fn` marks
// the impl unsafe — the two forms must not be confused.
#[batch_impl(unsafe fn(u8) -> u8)]
trait UnsafeFnType {}

/// # Safety
///
/// Marker trait for testing; no actual unsafe semantics.
#[batch_impl(unsafe.fn(u8) -> u8)]
unsafe trait UnsafeFnImpl {}

// `!` (never) as a fn return type: the `!` prefix punct is a passthrough
// block, and the trailing `{...}` attachment belongs to the impl, not the
// return type.
#[batch_impl(fn(u8) -> ! { #[allow(dead_code)] fn call(&self, _: u8) -> ! { unreachable!() } })]
trait NeverReturning {
    #[allow(dead_code)]
    fn call(&self, x: u8) -> !;
}

#[test]
fn unsafe_fn_vs_unsafe_impl() {
    fn check_type<T: UnsafeFnType>(_: &T) {}
    let f: unsafe fn(u8) -> u8 = |x| x;
    check_type(&f);

    unsafe fn check_impl<T: UnsafeFnImpl>(_: &T) {}
    let g: fn(u8) -> u8 = |x| x;
    unsafe { check_impl(&g) };

    fn check_never<T: NeverReturning>(_: &T) {}
    let n: fn(u8) -> ! = |_| unreachable!();
    check_never(&n);
}

// The space unit must accept every operand form `.` does: `&'a mut T`,
// HRTB `for<'a> fn(...)`, `extern "C" fn(...)` all stay one unit.
#[batch_impl(&'static mut u8 { fn tag(&self) -> &'static str { "m" } })]
trait RefMutLt {
    fn tag(&self) -> &'static str;
}

#[batch_impl(for<'a> fn(&'a u8) -> u8 { fn tag(&self) -> &'static str { "hrtb" } })]
trait HrtbFn {
    fn tag(&self) -> &'static str;
}

#[batch_impl(extern "C" fn(u8) -> u8 { fn tag(&self) -> &'static str { "c" } })]
trait ExternFn {
    fn tag(&self) -> &'static str;
}

#[test]
fn space_accepts_dot_operands() {
    fn check<T: RefMutLt>(t: T) {
        assert_eq!(t.tag(), "m");
    }
    check(Box::leak(Box::new(0u8)));

    fn check2<T: HrtbFn>(t: &T) {
        assert_eq!(t.tag(), "hrtb");
    }
    let g: for<'a> fn(&'a u8) -> u8 = |x| *x;
    check2(&g);

    fn check3<T: ExternFn>(t: &T) {
        assert_eq!(t.tag(), "c");
    }
    extern "C" fn c_fn(x: u8) -> u8 {
        x
    }
    let h: extern "C" fn(u8) -> u8 = c_fn;
    check3(&h);
}

// ============================================================
// 11. Attribute support: #[allow(dead_code)].usize
// ============================================================
#[batch_impl(#[allow(dead_code)].usize, isize)]
trait AttrSimple {}

#[test]
fn attr_support() {
    fn check<T: AttrSimple>(_: &T) {}
    check(&0usize);
    check(&0isize);
}

// ============================================================
// 12. Complex type passthrough
// ============================================================
#[batch_impl(
    (i32, String),
    &str,
    Box<dyn std::fmt::Display>,
    fn(i32) -> bool,
    dyn Fn() + Send + Sync
)]
trait ComplexMarker {}

#[test]
fn complex_passthrough() {
    fn check<T: ComplexMarker + ?Sized>(_: &T) {}
    check(&(1i32, String::from("x")));
    check(&"hi");
    let bd: Box<dyn std::fmt::Display> = Box::new(1i32);
    check(&bd);
    let ft: fn(i32) -> bool = |_| true;
    check(&ft);
    fn _dyn_check<T: ComplexMarker + ?Sized>() {}
    _dyn_check::<dyn Fn() + Send + Sync>();
}

// ============================================================
// 29. `unsafe fn(...)` types: `unsafe` modifies the fn type itself
//     (distinct from the unsafe impl marker `unsafe.T`; `unsafe X` errors when X is not a fn)
// ============================================================
#[batch_impl(unsafe fn(u32) -> u32)]
trait UnsafeFnMarker {}

#[batch_impl(unsafe fn.(u32, i32))]
trait UnsafeFnPow {}

#[batch_impl(unsafe fn.(u32, i32)   i64)]
trait UnsafeFnRet {}

#[test]
fn unsafe_fn_type() {
    fn check<T: UnsafeFnMarker>(_: &T) {}
    let f: unsafe fn(u32) -> u32 = |x| x;
    check(&f);

    fn check_pow<T: UnsafeFnPow>(_: &T) {}
    let g: unsafe fn(u32, i32) = |_, _| {};
    check_pow(&g);

    fn check_ret<T: UnsafeFnRet>(_: &T) {}
    let h: unsafe fn(u32, i32) -> i64 = |a, b| a as i64 + b as i64;
    check_ret(&h);
}

// ============================================================
// 30. `self` identity prefix: `self.T` = `T` — in a matrix it acts as
//     a bare-type placeholder (`[Box, self] u8` = `Box<u8>` + bare `u8`).
// ============================================================
#[batch_impl([Box, self] u8 { fn tag(&self) -> &'static str { "x" } })]
#[allow(dead_code)]
trait WrapOrBare {
    fn tag(&self) -> &'static str;
}

#[test]
fn self_identity_in_matrix() {
    fn check<T: WrapOrBare>(_: &T) {}
    check(&Box::new(0u8));
    check(&0u8);
}