format-attr 0.2.1

a custom derive to implement Debug/Display easy
Documentation
use format_attr::{DebugAttr, DisplayAsDebug, DisplayAttr};

// Test: only fmt attribute (both Display and Debug use the same)
#[derive(DisplayAttr, DebugAttr)]
#[fmt("Point({}, {})", self.x, self.y)]
struct Point {
    x: i32,
    y: i32,
}

// Test: separate fmt_display and fmt_debug
#[derive(DisplayAttr, DebugAttr)]
#[fmt_display("User: {}", self.name)]
#[fmt_debug("User {{ name: {}, age: {} }}", self.name, self.age)]
struct User {
    name: String,
    age: u32,
}

// Test: only fmt_display (DisplayAttr uses it, DebugAttr should fallback to fmt - but there's no fmt!)
// This should fail if we try to derive DebugAttr without fmt or fmt_debug
// So let's test a case with only fmt_debug too

// Test: only fmt_display with DebugAttr using fmt_display as fallback
// Actually, the logic is: fmt_display for Display, fmt for both fallback
// So if we only have fmt_display, DisplayAttr works but DebugAttr needs fmt or fmt_debug

// Test: fmt_debug only - this won't work for DisplayAttr
// Let's use fmt for Display, fmt_debug for Debug
#[derive(DisplayAttr, DebugAttr)]
#[fmt("{} (v{})", self.name, self.version)]
#[fmt_debug("Package {{ name: {}, version: {} }}", self.name, self.version)]
struct Package {
    name: String,
    version: String,
}

// Test: fmt_display overrides fmt for Display, fmt used for Debug
#[derive(DisplayAttr, DebugAttr)]
#[fmt("{} - {} years old", self.name, self.age)]
#[fmt_display("{}", self.name)]
struct Person {
    name: String,
    age: u32,
}

// Test: fmt_debug overrides fmt for Debug, fmt used for Display
#[derive(DisplayAttr, DebugAttr)]
#[fmt("{} @ {}", self.name, self.email)]
#[fmt_debug("Contact {{ name: {}, email: {} }}", self.name, self.email)]
struct Contact {
    name: String,
    email: String,
}

#[test]
fn test_point_same_fmt() {
    let p = Point { x: 10, y: 20 };
    assert_eq!(format!("{}", p), "Point(10, 20)");
    assert_eq!(format!("{:?}", p), "Point(10, 20)");
}

#[test]
fn test_user_separate_fmt() {
    let u = User {
        name: "Alice".to_string(),
        age: 30,
    };
    // Display uses fmt_display
    assert_eq!(format!("{}", u), "User: Alice");
    // Debug uses fmt_debug
    assert_eq!(format!("{:?}", u), "User { name: Alice, age: 30 }");
}

#[test]
fn test_package_fmt_and_fmt_debug() {
    let pkg = Package {
        name: "my-crate".to_string(),
        version: "1.0.0".to_string(),
    };
    // Display uses fmt
    assert_eq!(format!("{}", pkg), "my-crate (v1.0.0)");
    // Debug uses fmt_debug
    assert_eq!(format!("{:?}", pkg), "Package { name: my-crate, version: 1.0.0 }");
}

#[test]
fn test_person_fmt_display_overrides() {
    let p = Person {
        name: "Bob".to_string(),
        age: 25,
    };
    // Display uses fmt_display (only name)
    assert_eq!(format!("{}", p), "Bob");
    // Debug uses fmt (name and age)
    assert_eq!(format!("{:?}", p), "Bob - 25 years old");
}

#[test]
fn test_contact_fmt_debug_overrides() {
    let c = Contact {
        name: "Charlie".to_string(),
        email: "charlie@example.com".to_string(),
    };
    // Display uses fmt
    assert_eq!(format!("{}", c), "Charlie @ charlie@example.com");
    // Debug uses fmt_debug
    assert_eq!(format!("{:?}", c), "Contact { name: Charlie, email: charlie@example.com }");
}

// Test: no arguments format string
#[derive(DisplayAttr, DebugAttr)]
#[fmt("Empty")]
struct EmptyStruct {}

#[derive(DisplayAttr, DebugAttr)]
#[fmt_display("StaticDisplay")]
#[fmt_debug("StaticDebug")]
struct StaticStruct {}

#[test]
fn test_no_args_fmt() {
    let e = EmptyStruct {};
    assert_eq!(format!("{}", e), "Empty");
    assert_eq!(format!("{:?}", e), "Empty");
}

#[test]
fn test_no_args_separate_fmt() {
    let s = StaticStruct {};
    assert_eq!(format!("{}", s), "StaticDisplay");
    assert_eq!(format!("{:?}", s), "StaticDebug");
}

// Test: DisplayAsDebug - Display uses Debug implementation
#[derive(DebugAttr, DisplayAsDebug)]
#[fmt_debug("DebugOutput({})", self.value)]
struct DisplayAsDebugStruct {
    value: i32,
}

// Test: DisplayAsDebug with std Debug derive
#[derive(std::fmt::Debug, DisplayAsDebug)]
struct StdDebugStruct {
    name: String,
}

#[test]
fn test_display_as_debug_with_debug_attr() {
    let s = DisplayAsDebugStruct { value: 42 };
    // Display should use Debug implementation
    assert_eq!(format!("{}", s), "DebugOutput(42)");
    assert_eq!(format!("{:?}", s), "DebugOutput(42)");
}

#[test]
fn test_display_as_debug_with_std_debug() {
    let s = StdDebugStruct {
        name: "test".to_string(),
    };
    // Display should use standard Debug implementation
    let display_output = format!("{}", s);
    let debug_output = format!("{:?}", s);
    assert_eq!(display_output, debug_output);
    assert!(display_output.contains("test"));
}