Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ categories = [
"development-tools::procedural-macro-helpers",
"rust-patterns",
]
description = "A lightweight library designed to make the newtype idiom more ergonomic to use."
description = "Ergonomic utilities for the Rust newtype idiom."
edition = "2024"
homepage = "https://github.com/berestovskyy/newtype-tools/releases"
keywords = ["conversion", "derive", "idiom", "iterator", "newtype"]
Expand All @@ -27,7 +27,5 @@ quote = "1.0.45"
rstest = "0.26.1"
rustversion = "1.0.22"
serde = { version = "1.0.228", features = ["derive"] }
# To debug macro parsing:
#syn = { version = "2.0.117", features = ["extra-traits", "full"] }
syn = "2.0.117"
syn = { version = "2.0.117", features = ["full"] }
trybuild = "1.0.116"
12 changes: 4 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
##
## Newtype-tools Makefile
##

MSRV := $(shell grep ^rust-version Cargo.toml | cut -d '"' -f 2)
NO_STD_TARGET := "thumbv7em-none-eabi"

Expand All @@ -12,23 +8,23 @@ check:: clippy fmt
@echo "All OK."

help::
@echo "Newtype-tools targets:"
@echo "Main targets:"
@echo " check Run quick checks: clippy, fmt, cargo test, cargo check."
@echo " help This help message."
@echo " ci Run CI pipeline locally: clippy, build, no_std, test, fmt, publish."
@echo " clean Remove generated artifacts."
@echo " Test targets:"
@echo "Test targets:"
@echo " clippy Run cargo clippy."
@echo " build Run cargo build."
@echo " no_std Run no_std check."
@echo " test Run cargo test."
@echo " trybuild Overwrite trybuild test results."
@echo " Misc targets:"
@echo "Misc targets:"
@echo " expand Show macro expansion for a specified test: TEST=from make expand"
@echo " fmt Run cargo fmt."
@echo " keepsorted Sort Rust derives alphabetically (cargo install keepsorted)."
@echo " publish Run the cargo publish dry run."
@echo " Code coverage targets:"
@echo "Code coverage targets:"
@echo " lcov Generate lcov code coverage report."
@echo " html Generate HTML code coverage report."
@echo " open Open HTML code coverage report."
Expand Down
76 changes: 44 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,19 @@ Newtype Tools
[![Crates.io]][Crates.io Link]
[![ChangeLog]][ChangeLog Link]

A lightweight library (~1K lines of code with minimum dependencies) designed to make
the [newtype idiom][newtype] more ergonomic to use.
Ergonomic utilities for the Rust [newtype idiom][newtype].

Motivation
----------

Instead of trying to be everything or deriving dozens of unused trait implementations,
this crate provides unique, simple, yet powerful tools for the `newtypes`.
This crate avoids the bloat of general-purpose "derive-all" libraries, offering instead
a minimalist set of powerful tools specifically for the [newtype idiom][newtype].

The crate focuses on three main areas to make `newtype` usage more enjoyable:
Development is focused on three core pillars:

1. Conversions between types.
2. Operations on `newtypes`.
3. Iteration over `newtype` ranges.
1. Conversions between `newtypes` and between `newtypes` and their inner types.
2. Operations directly on `newtype` values.
3. Iteration over ranges of `newtypes`.

Usage
-----
Expand All @@ -35,7 +34,7 @@ cargo add newtype-tools
Examples
--------

The simplest way to use the crate is to declare a tuple struct as a `newtype` kind:
The simplest way to use the crate is to define a tuple struct using the `newtype` attribute:

```rust
# #[cfg(feature = "derive")]
Expand Down Expand Up @@ -66,10 +65,12 @@ assert_eq!(apple2 / apple1 , 1);
# }
```

The crate supports two kinds of `newtypes`: `Amount` and `Id`. See below for more details.
The crate supports two main "presets": `Amount` and `Id`. See below for more details.

Rather than using the predefined sets of derives, the implementation allows
for the derivation of only the necessary traits. Conversion between types:
Instead of using predefined sets, the implementation allows for the derivation
of only the specific traits required for a given use case.

**`newtype` conversions:**

```rust
# #[cfg(feature = "derive")]
Expand All @@ -91,7 +92,7 @@ assert_eq!(oranges.0, 21);
# }
```

Operations on `newtypes`:
**`newtype` operations:**

```rust
# #[cfg(feature = "derive")]
Expand All @@ -113,7 +114,7 @@ assert!(apples == oranges);
# }
```

Iterations over `newtype` ranges:
**`newtype` range iteration:**

```rust
# #[cfg(feature = "derive")]
Expand All @@ -131,16 +132,15 @@ for apple in range.iter() {
# }
```

This will become even more ergonomic once the [Step][step] trait is stabilized.
Note: This will become even more ergonomic once the Rust [Step trait][step] is stabilized.

Newtype Kinds
-------------

The crate supports predefined sets of newtype properties. The concept is similar
to the `phantom_newtype` crate but avoids its limitations, as the newtype
generated here is a distinct Rust type. This allows new traits
to be implemented easily for the type and makes the set of derived traits
simple to extend.
The crate supports predefined sets of `newtype` properties. The concept is similar
to the `phantom_newtype` crate but avoids Rust orphan rule limitations by defining
the `newtype` locally. This allows additional traits to be implemented easily
and makes the set of derived traits simple to extend.

The supported `newtype` kinds are:

Expand Down Expand Up @@ -169,23 +169,35 @@ The supported `newtype` kinds are:
Alternatives
------------

1. `nutype` -- An impressive 12.5k lines of code, with 7.5k lines in proc-macros alone.
After trying to extend it, I realized it would be faster to simply write a new crate:
1. `derive_more` -- A large library (~10K SLoC) focused on supporting a wide range of traits
for any data type. It can be used alongside `newtype-tools` to derive additional traits as needed:
[crate](https://crates.io/crates/derive_more)
| [repo](https://github.com/JelteF/derive_more).

The unique value of this `newtype-tools` crate lies in its lightweight design,
support for property sets like `#[newtype(Amount)]`, and built-in range iteration
via `(Newtype(0)..Newtype(42)).iter()`.

2. `nutype` -- A comprehensive library (7.5K SLoC) primarily focused on `newtype` validation:
[crate](https://crates.io/crates/nutype)
and [repo](https://github.com/greyblake/nutype).
2. `phantom_newtype` -- Provides 19 trait implementations out of the box,
but lacks a mechanism for providing custom trait implementations:
| [repo](https://github.com/greyblake/nutype).

3. `phantom_newtype` -- Provides 19 trait implementations out of the box, but lacks a mechanism
for custom trait implementations due to the Rust orphan rule:
[crate](https://crates.io/crates/phantom_newtype)
and [repo](https://github.com/roman-kashitsyn/phantom-newtype).
3. `newtype_derive` -- Outdated and relies on the legacy `custom_derive!` declarative macro:
| [repo](https://github.com/roman-kashitsyn/phantom-newtype).

4. `newtype_derive` -- Legacy crate that relies on the `custom_derive!` declarative macro:
[crate](https://crates.io/crates/newtype_derive)
and [repo](https://github.com/DanielKeep/rust-custom-derive).
4. `newtype-derive-2018` -- Less outdated, but it's 2026:
| [repo](https://github.com/DanielKeep/rust-custom-derive).

5. `newtype-derive-2018` -- Less outdated, but still based on the declarative macro:
[crate](https://crates.io/crates/newtype-derive-2018)
and [repo](https://github.com/A1-Triard/newtype-derive-2018).
5. `newtype` -- yet another outdated crate with a similar approach:
| [repo](https://github.com/A1-Triard/newtype-derive-2018).

6. `newtype` -- An older crate following a similar approach but no longer actively maintained:
[crate](https://crates.io/crates/newtype)
and [repo](https://gitlab.com/jrobsonchase/newtype).
| [repo](https://gitlab.com/jrobsonchase/newtype).

References
----------
Expand Down
2 changes: 1 addition & 1 deletion newtype-tools-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod parse;
///
/// ```ignore
/// #[newtype(Amount)]
/// #[derive(Default)]
/// #[derive(serde::Serialize)]
/// struct Apples(u64);
/// ```
#[proc_macro_attribute]
Expand Down