![](https://img.shields.io/crates/v/tacit) Simple newtype macro to create strong ID types over other types. Make a strong ID out of a string: ```rust tacit!(id1::Id1 -> String); tacit!(id2::Id2 -> Arc); ``` Try it with [ulid](https://crates.io/crates/ulid) ```rust use ulid::Ulid; tacit!(id::Id -> Ulid); ``` You can also write a doc comment for your tacits: ```rust tacit!( /// My unique type! id::Id -> Ulid ); ``` Tacit works by generating two structs with the same name. The first part of the macro, `id::Id` generates a struct in a module named `id`, like so: ```rust mod id { #[derive(Clone, Copy, Debug...)] struct Id; } ``` which then uses that type to make a type alias over the `Tacit` struct: ```rust pub type Id = Tacit; ``` The first generic argument is the *representation*, the actual data in the type. The second argument is the *identifier*, which prevents Tacits of the same representation to not compare with each other or allow them to be passed as arguments to functions expecting different types. While `Tacit`s of different types do not compare, they can convert between other tacits that implement the same representation: ```rust tacit!(a::A -> Ulid); tacit!(b::B -> Ulid); let a: A = Ulid::new().into(); let b: B = a.cast(); ``` the `Tacit` struct implements a variety of traits when the representation implements them. Below is a list of the following: - `Clone` - `Copy` - `Debug`[1] - `Display`[1] - `Eq` - `PartialEq` - `Ord` - `PartialOrd` - `Hash` - `From` where `R` is the representation type. - `FromStr` where `FromStr::Err` is `R::FromStr::Err` - `serde::Serialize`[2] - `serde::Deserialize`[2] Note that, if the representation doesn't implement any of these, the Tacit won't have it implemented either. You can still use types that don't implement these, though, as they're implemented conditionally. This enables non-Copy types to be Tacits, while also allowing Copy types to be Copyable. Additionally, there are special `From`/`From` impls for `Arc` and `Rc` as I couldn't find a good way to implements a generic `From` for types that encase other types like `Arc` does. I figured this was a reasonable thing to hard-code since I'm essentially hardcoding over Rust's string literal syntax. [1] Tacit's display outputs are as follows:\ Given `tacit!(a::A -> Arc)`: - Debug: `Tacit(A, "abc")` - Display: `A(abc)` If you want the raw debug/display output without the tacit's name, call: - `tacit.repr_debug()` - `tacit.repr_str()` Note that the automatically generated identifier implements the `tacit::Identity` trait in order to display it's name. If you are not using an automatically generated ID, ensure your identifier type implements `Identity` as well, or else the entire `Tacit` cannot use `Debug` or `Display`. This seems to be a limitation in Rust's trait system and one I can't find a way to overcome, as ideally I'd like to simply only print the representation if the identifier has no display. [2] A note on `serde`: The Tacit will not serialize or deserialize *itself*, it is a direct passthrough to the representation type. This means instead of getting (from `tacit!(a::A -> u32)`): ```json Tacit { repr: 123 } ``` You will instead simply get `123`. Keep this in mind when handling serialization, as this essentially means Tacit will not help you out with deserializing types.