mcarton / rust-derivative

A set of alternative `derive` attributes for Rust
Apache License 2.0
422 stars 46 forks source link

Skip enum fields for `Debug` implementation #76

Closed GeorgeHahn closed 3 years ago

GeorgeHahn commented 4 years ago

One common pattern in rust is to represent commands with enums. Multiple related commands map to different enum members and their parameters map to fields. Associated input/output buffers or other data structures may be included as enum fields. These often require a custom Debug implementation to display the control parameters but omit large buffers or non-debug data structures. This would be a nice pattern to support.

Examples with proposed syntax options

Enums with named fields

enum Command {
    #[derivative(Debug(skip("buffer"))]
    Rebuild { target: IsDebug, buffer: NotDebug },
}

Enums with unnamed fields

// Explicit by field
enum Command {
    Rebuild(IsDebug, #[derivative(Debug="skip")] NotDebug),
}

// Explicit by type
enum Command {
    #[derivative(Debug(skip(NotDebug)))]
    Rebuild(IsDebug, NotDebug),
}

// Implicit
enum Command {
    #[derivative(Debug="skip_non_debug")]
    Rebuild(IsDebug, NotDebug),
}

Generated code

impl Debug for Command {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Command::Rebuild(val, _) => f.debug_tuple("Rebuild").field(&val).finish(),
        }
    }
}
mcarton commented 3 years ago

This is already possible, eg.:

use derivative; // 2.1.1

#[derive(derivative::Derivative)]
#[derivative(Debug)]
enum Command {
    Rebuild {
        target: String,
        #[derivative(Debug = "ignore")] // ← this is what you want
        buffer: NotDebug,
    },
}

struct NotDebug;

fn main() {
    println!(
        "{:?}",
        Command::Rebuild {
            target: "foo".to_string(),
            buffer: NotDebug
        }
    );
}

Permalink to the playground