1 unstable release
Uses new Rust 2024
new 0.1.0 | Apr 30, 2025 |
---|
#2805 in Rust patterns
32KB
707 lines
derive-into
A Rust derive macro for easily creating conversions between structs and enums.
Features
- Automate conversions between similar data structures
- Support for struct-to-struct, tuple struct, and enum conversions
- Field renaming capabilities
- Automatic handling of wrapped types with
From
/Into
implementations - Special handling for
Option
andVec
types - Support for both infallible (
From
) and fallible (TryFrom
) conversions - Fine-grained control with field-level attributes
Installation
Add to your Cargo.toml
:
[dependencies]
derive-into = "0.1.0"
Quick Start
use derive_into::Convert;
// Source struct with conversion attributes
#[derive(Convert)]
#[convert(into = "Destination")] // Generate Into<Destination> implementation
struct Source {
id: u32,
#[convert(rename = "full_name")] // Field will be mapped to "full_name" in target
name: String,
}
// Destination struct
struct Destination {
id: u32,
full_name: String,
}
// Usage
let source = Source {
id: 1,
name: "Example".to_string(),
};
let destination: Destination = source.into();
Conversion Types
The macro supports the following conversion types:
Attribute | Description |
---|---|
#[convert(into = "Type")] |
Implements Into<Type> for the struct/enum |
#[convert(try_from = "Type")] |
Implements TryFrom<Type> for the struct/enum |
#[convert(from = "Type")] |
Implements From<Type> for the struct/enum |
Struct-Level Attributes
Attribute | Description |
---|---|
#[convert(into = "Type")] |
Generate an Into<Type> implementation |
#[convert(try_from = "Type")] |
Generate a TryFrom<Type> implementation |
#[convert(from = "Type")] |
Generate a From<Type> implementation |
#[convert(default)] |
Use Default::default() for fields not explicitly mapped |
Field-Level Attributes
Attribute | Description |
---|---|
#[convert(rename = "new_name")] |
Map this field to a differently named field in the target type |
#[convert(unwrap)] |
Automatically unwrap an Option value (fails in try_from if None ) |
#[convert(skip)] |
Skip this field during conversion (target must provide a default) |
Enum Conversion
The macro supports enum-to-enum conversion with similar attribute control:
#[derive(Convert)]
#[convert(into = "TargetEnum")]
enum SourceEnum {
Variant1(u32),
#[convert(rename = "RenamedVariant")]
Variant2 {
value: String,
#[convert(rename = "renamed_field")]
field: u8,
},
Unit,
}
enum TargetEnum {
Variant1(u32),
RenamedVariant {
value: String,
renamed_field: u8,
},
Unit,
}
Type Conversions
The macro intelligently handles various type scenarios:
- Direct Mapping: Fields with identical types are directly copied
- Automatic Conversion: Fields with types that implement
From
/Into
are automatically converted - Container Types: Special handling for
Option<T>
andVec<T>
with inner type conversion - Tuple Structs: Support for conversions between tuple structs
Examples
Basic Struct Conversion
use derive_into::Convert;
#[derive(Convert)]
#[convert(into = "Target")]
struct Source {
id: u32,
name: String,
}
struct Target {
id: u32,
name: String,
}
// Usage
let source = Source { id: 1, name: "Example".to_string() };
let target: Target = source.into();
Handling Option and Vec Types
The macro automatically handles conversion of inner types for Option
and Vec
:
use derive_into::Convert;
#[derive(Debug, PartialEq, Default)]
struct Number(u8);
impl From<u8> for Number {
fn from(n: u8) -> Number {
Number(n)
}
}
#[derive(Convert)]
#[convert(into = "Target")]
struct Source {
// Option's inner type will be converted
opt_value: Option<u8>,
// Vec's inner type will be converted
vec_values: Vec<u8>,
}
struct Target {
opt_value: Option<Number>,
vec_values: Vec<Number>,
}
Using Unwrap for Options
use derive_into::Convert;
#[derive(Convert)]
#[convert(try_from = "Source")]
struct Target {
value: u32,
}
struct Source {
#[convert(unwrap)]
value: Option<u32>,
}
// This will succeed
let source = Source { value: Some(42) };
let target: Result<Target, _> = Target::try_from(source);
assert!(target.is_ok());
// This will fail because value is None
let source = Source { value: None };
let target: Result<Target, _> = Target::try_from(source);
assert!(target.is_err());
Using Default Values
use derive_into::Convert;
#[derive(Convert)]
#[convert(into = "Target", default)]
struct Source {
id: u32,
// No 'extra' field - will use default
}
#[derive(Default)]
struct Target {
id: u32,
extra: String, // Will use Default::default()
}
Advanced Usage
Custom Type Conversion
The macro leverages existing From
/Into
implementations for field types:
use derive_into::Convert;
struct UserId(u32);
impl From<u32> for UserId {
fn from(id: u32) -> Self {
UserId(id)
}
}
#[derive(Convert)]
#[convert(into = "UserRecord")]
struct User {
id: u32, // Will be converted to UserId
name: String,
}
struct UserRecord {
id: UserId, // Converted from u32
name: String,
}
License
This project is licensed under the MIT License - see the LICENSE file for details.
Dependencies
~2MB
~47K SLoC