| Crates.io | tagged-types-derive |
| lib.rs | tagged-types-derive |
| version | 0.2.4 |
| created_at | 2025-08-31 00:05:51.721896+00 |
| updated_at | 2025-09-07 23:37:40.294013+00 |
| description | Define derive procmacro for tagged types |
| homepage | |
| repository | https://github.com/poroh/tagged-types |
| max_upload_size | |
| id | 1818053 |
| size | 18,381 |
use tagged_types::TaggedType;
use core::net::IpAddr;
// Introduce Gateway type that has IpAddr as base type.
type Gateway = TaggedType<IpAddr, GatewayTag>;
// Define all required properties that needed for type.
#[derive(tagged_types::Tag)]
#[implement(Clone, Copy, PartialEq, Eq, Hash)]
#[transparent(Display, Debug, FromStr, Serialize, Deserialize)]
#[capability(inner_access, from_inner, value_map, cloned, as_ref)]
enum GatewayTag {}
// Alternatively to define ALL supported properties. Shortcut can
// be used:
// #[derive(tagged_types::Tag)]
// #[permissive]
// enum GatewayTag {}
// Use type as if it is Ipaddr
#[derive(serde::Serialize, serde::Deserialize)]
struct Route {
gateway: Gateway,
}
fn main() {
let gw = serde_json::from_str::<Route>(r#"{"gateway":"192.168.0.1"}"#)
.unwrap()
.gateway;
println!("gateway is {gw}");
let another_gw: Gateway = "192.168.0.2".parse().unwrap();
println!("another gateway is {another_gw}");
}
This library contains helpers to enforce stricter types. Possible application can be introduction of new types with limited capabilities in compare to base types.
For example. You can introduce type Password that don't have Display and Debug
traits. But still can be used for comparison and be deserialized using serde:
use tagged_types::TaggedType;
type Password = TaggedType<String, PasswordTag>;
#[derive(tagged_types::Tag)]
#[implement(Clone, Copy, PartialEq, Eq)]
#[transparent(Deserialize)]
enum PasswordTag {}
Another application can be usage of TaggedType to destinguish different identifiers.
use tagged_types::TaggedType;
type UserId = TaggedType<uuid::Uuid, UserIdTag>;
#[derive(tagged_types::Tag)]
#[permissive]
enum UserIdTag {}
type GroupId = TaggedType<uuid::Uuid, GroupIdTag>;
#[derive(tagged_types::Tag)]
#[permissive]
enum GroupIdTag {}
And of course more complex structures can be wrapped.
#[derive(Debug, Clone)]
struct Version {
major: u32,
minor: u32,
}
type SoftwareVersion = TaggedType<Version, SoftwareVersionTag>;
#[derive(tagged_types::Tag)]
#[permissive]
enum SoftwareVersionTag {}
type FirmwareVersion = TaggedType<Version, FirmwareVersionTag>;
#[derive(tagged_types::Tag)]
#[permissive]
enum FirmwareVersionTag {}
This library is designed to depend only on the standard library.
Optionally, it supports serialization and deserialization for the
underlying type (see the support_serde feature).
In many cases, we want strict types, but we don't want to spend a lot of time implementing boilerplate around them (serialization / deserialization / parsing / clone / copy, etc.).
This library makes it possible to introduce types with minimal effort and implements many traits for the new type when the underlying type implements them.
Let:
V is the value typeT is the tag typeConditionally implemented traits when the trait is implemented by the underlying type V:
Deref is implemented if ImplementDeref is implemeted for T, so all methods of V are available on
TaggedType<V, T>.Clone if ImplementClone is implemented for TCopy if ImplementCopy is implemented for THash if ImplementHash is implemented for TPartialEq if ImplementParitalEq is implemented for TEq if ImplementEq is implemented for TDefault if ImplementDefault is implemented for TConditionally implemented traits when the trait is implemented by the underlying
type V and enabled for the tag type T:
Debug if T implements TransparentDebug. In this case, Debug
formats the same as VDisplay if T implements TransparentDisplay. In this case,
Display formats the same as VFromStr if T implements TransparentFromStr. In this case,
FromStr parses the same as VAdditional capabilities that you can opt-in:
inner / into_inner if T implements InnerAccess marker trait.From<V> if T implements FromInner marker trait.map / try_map if T implements ValueMap marker trait.cloned if T implements Cloned marker trait.support_serdeConditionally implemented traits when implemented by the underlying type
V:
SerializeDeserializeprovide_permissiveProvides Permissive trait that automatically implements all defined
traits for T type except Deref. Deref is considered as
footgun. But you still can opt-in to it by adding ImplementDeref
marker trait to T.
provide_deriveProvides #[derive(tagged_type::Tag)] which provide helpers to avoid
manual implementation of traits. Example of all possible features:
type DefaultGateway = TaggedType<core::net::IpAddr, DefaultGatewayTag>;
#[derive(Tag)]
#[implement(Default, Eq, PartialEq, Hash, Clone, Copy)]
#[transparent(Debug, Display, FromStr, Serialize, Deserialize)]
#[capability(inner_access)]
enum DefaultGatewayTag {}
Or permissive:
type DefaultGateway = TaggedType<std::net::IpAddr, DefaultGatewayTag>;
#[derive(Tag)]
#[permissive]
enum DefaultGatewayTag {}