From 85f2cde2a9c9a8f0a8bb1e52e4997eed64f38b99 Mon Sep 17 00:00:00 2001 From: Andriy Berestovskyy Date: Thu, 23 Apr 2026 12:44:36 +0200 Subject: [PATCH 1/4] chore: add CI comments --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 783a256..394aa11 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,6 +10,7 @@ on: env: CARGO_TERM_COLOR: always +# Allow just one concurrent job per branch. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -36,6 +37,7 @@ jobs: matrix: os: - ubuntu-latest + # Run on macOS and Windows if requested. - ${{ contains(github.event.pull_request.labels.*.name, 'CI-run-on-macos-latest') && 'macos-latest' || '' }} - ${{ contains(github.event.pull_request.labels.*.name, From d8fc8179ea368343f8857a43fc6fb61ebdce2a70 Mon Sep 17 00:00:00 2001 From: Andriy Berestovskyy Date: Thu, 23 Apr 2026 16:43:14 +0200 Subject: [PATCH 2/4] refactor: move ops tests --- newtype-tools/tests/add_assign.rs | 41 ------------------------- newtype-tools/tests/bitand_assign.rs | 41 ------------------------- newtype-tools/tests/bitor_assign.rs | 41 ------------------------- newtype-tools/tests/bitxor_assign.rs | 41 ------------------------- newtype-tools/tests/div_assign.rs | 41 ------------------------- newtype-tools/tests/mul_assign.rs | 41 ------------------------- newtype-tools/tests/ops.rs | 12 ++++++++ newtype-tools/tests/{ => ops}/add.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/bitand.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/bitor.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/bitxor.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/div.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/mul.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/rem.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/shl.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/shr.rs | 38 +++++++++++++++++++++++ newtype-tools/tests/{ => ops}/sub.rs | 37 ++++++++++++++++++++++ newtype-tools/tests/rem_assign.rs | 41 ------------------------- newtype-tools/tests/shl_assign.rs | 41 ------------------------- newtype-tools/tests/shr_assign.rs | 41 ------------------------- newtype-tools/tests/sub_assign.rs | 41 ------------------------- 21 files changed, 391 insertions(+), 410 deletions(-) delete mode 100644 newtype-tools/tests/add_assign.rs delete mode 100644 newtype-tools/tests/bitand_assign.rs delete mode 100644 newtype-tools/tests/bitor_assign.rs delete mode 100644 newtype-tools/tests/bitxor_assign.rs delete mode 100644 newtype-tools/tests/div_assign.rs delete mode 100644 newtype-tools/tests/mul_assign.rs create mode 100644 newtype-tools/tests/ops.rs rename newtype-tools/tests/{ => ops}/add.rs (57%) rename newtype-tools/tests/{ => ops}/bitand.rs (57%) rename newtype-tools/tests/{ => ops}/bitor.rs (57%) rename newtype-tools/tests/{ => ops}/bitxor.rs (57%) rename newtype-tools/tests/{ => ops}/div.rs (57%) rename newtype-tools/tests/{ => ops}/mul.rs (57%) rename newtype-tools/tests/{ => ops}/rem.rs (57%) rename newtype-tools/tests/{ => ops}/shl.rs (57%) rename newtype-tools/tests/{ => ops}/shr.rs (57%) rename newtype-tools/tests/{ => ops}/sub.rs (57%) delete mode 100644 newtype-tools/tests/rem_assign.rs delete mode 100644 newtype-tools/tests/shl_assign.rs delete mode 100644 newtype-tools/tests/shr_assign.rs delete mode 100644 newtype-tools/tests/sub_assign.rs diff --git a/newtype-tools/tests/add_assign.rs b/newtype-tools/tests/add_assign.rs deleted file mode 100644 index 38c1f8e..0000000 --- a/newtype-tools/tests/add_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42 + 21 * 2; - -#[test] -fn add_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(add_assign(Oranges, with = "|apples, oranges| apples.0 += oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples += Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples += &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_add_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(add_assign(Oranges, with = "|apples, oranges| apples.0 += oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::AddAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples += oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples += &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/bitand_assign.rs b/newtype-tools/tests/bitand_assign.rs deleted file mode 100644 index ba9c11b..0000000 --- a/newtype-tools/tests/bitand_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42; - -#[test] -fn bitand_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(bitand_assign(Oranges, with = "|apples, oranges| apples.0 &= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples &= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples &= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_bitand_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(bitand_assign(Oranges, with = "|apples, oranges| apples.0 &= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::BitAndAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples &= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples &= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/bitor_assign.rs b/newtype-tools/tests/bitor_assign.rs deleted file mode 100644 index b85148f..0000000 --- a/newtype-tools/tests/bitor_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42; - -#[test] -fn bitor_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(bitor_assign(Oranges, with = "|apples, oranges| apples.0 |= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples |= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples |= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_bitor_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(bitor_assign(Oranges, with = "|apples, oranges| apples.0 |= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::BitOrAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples |= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples |= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/bitxor_assign.rs b/newtype-tools/tests/bitxor_assign.rs deleted file mode 100644 index 19a9136..0000000 --- a/newtype-tools/tests/bitxor_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 0; - -#[test] -fn bitxor_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(bitxor_assign(Oranges, with = "|apples, oranges| apples.0 ^= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples ^= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples ^= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_bitxor_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(bitxor_assign(Oranges, with = "|apples, oranges| apples.0 ^= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::BitXorAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples ^= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples ^= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/div_assign.rs b/newtype-tools/tests/div_assign.rs deleted file mode 100644 index e70b11e..0000000 --- a/newtype-tools/tests/div_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 1; - -#[test] -fn div_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(div_assign(Oranges, with = "|apples, oranges| apples.0 /= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples /= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples /= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_div_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(div_assign(Oranges, with = "|apples, oranges| apples.0 /= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::DivAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples /= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples /= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/mul_assign.rs b/newtype-tools/tests/mul_assign.rs deleted file mode 100644 index 89efbff..0000000 --- a/newtype-tools/tests/mul_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42 * 21 * 2; - -#[test] -fn mul_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(mul_assign(Oranges, with = "|apples, oranges| apples.0 *= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples *= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples *= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_mul_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(mul_assign(Oranges, with = "|apples, oranges| apples.0 *= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::MulAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples *= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples *= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/ops.rs b/newtype-tools/tests/ops.rs new file mode 100644 index 0000000..11e39a9 --- /dev/null +++ b/newtype-tools/tests/ops.rs @@ -0,0 +1,12 @@ +mod ops { + mod add; + mod bitand; + mod bitor; + mod bitxor; + mod div; + mod mul; + mod rem; + mod shl; + mod shr; + mod sub; +} diff --git a/newtype-tools/tests/add.rs b/newtype-tools/tests/ops/add.rs similarity index 57% rename from newtype-tools/tests/add.rs rename to newtype-tools/tests/ops/add.rs index cebcfd9..a14dd3e 100644 --- a/newtype-tools/tests/add.rs +++ b/newtype-tools/tests/ops/add.rs @@ -52,3 +52,41 @@ fn generic_add() { let res = &Apples(42) + &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn add_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(add_assign(Oranges, with = "|apples, oranges| apples.0 += oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples += Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples += &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_add_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(add_assign(Oranges, with = "|apples, oranges| apples.0 += oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::AddAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples += oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples += &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/bitand.rs b/newtype-tools/tests/ops/bitand.rs similarity index 57% rename from newtype-tools/tests/bitand.rs rename to newtype-tools/tests/ops/bitand.rs index e2658dd..44a08d6 100644 --- a/newtype-tools/tests/bitand.rs +++ b/newtype-tools/tests/ops/bitand.rs @@ -52,3 +52,41 @@ fn generic_bitand() { let res = &Apples(42) & &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn bitand_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(bitand_assign(Oranges, with = "|apples, oranges| apples.0 &= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples &= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples &= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_bitand_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(bitand_assign(Oranges, with = "|apples, oranges| apples.0 &= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::BitAndAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples &= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples &= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/bitor.rs b/newtype-tools/tests/ops/bitor.rs similarity index 57% rename from newtype-tools/tests/bitor.rs rename to newtype-tools/tests/ops/bitor.rs index bc37f06..6fe80a6 100644 --- a/newtype-tools/tests/bitor.rs +++ b/newtype-tools/tests/ops/bitor.rs @@ -52,3 +52,41 @@ fn generic_bitor() { let res = &Apples(42) | &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn bitor_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(bitor_assign(Oranges, with = "|apples, oranges| apples.0 |= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples |= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples |= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_bitor_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(bitor_assign(Oranges, with = "|apples, oranges| apples.0 |= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::BitOrAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples |= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples |= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/bitxor.rs b/newtype-tools/tests/ops/bitxor.rs similarity index 57% rename from newtype-tools/tests/bitxor.rs rename to newtype-tools/tests/ops/bitxor.rs index 5fb0b50..8681355 100644 --- a/newtype-tools/tests/bitxor.rs +++ b/newtype-tools/tests/ops/bitxor.rs @@ -52,3 +52,41 @@ fn generic_bitxor() { let res = &Apples(42) ^ &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn bitxor_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(bitxor_assign(Oranges, with = "|apples, oranges| apples.0 ^= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples ^= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples ^= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_bitxor_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(bitxor_assign(Oranges, with = "|apples, oranges| apples.0 ^= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::BitXorAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples ^= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples ^= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/div.rs b/newtype-tools/tests/ops/div.rs similarity index 57% rename from newtype-tools/tests/div.rs rename to newtype-tools/tests/ops/div.rs index fdc85b1..04f8db1 100644 --- a/newtype-tools/tests/div.rs +++ b/newtype-tools/tests/ops/div.rs @@ -52,3 +52,41 @@ fn generic_div() { let res = &Apples(42) / &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn div_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(div_assign(Oranges, with = "|apples, oranges| apples.0 /= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples /= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples /= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_div_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(div_assign(Oranges, with = "|apples, oranges| apples.0 /= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::DivAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples /= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples /= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/mul.rs b/newtype-tools/tests/ops/mul.rs similarity index 57% rename from newtype-tools/tests/mul.rs rename to newtype-tools/tests/ops/mul.rs index 501f498..cd79a16 100644 --- a/newtype-tools/tests/mul.rs +++ b/newtype-tools/tests/ops/mul.rs @@ -52,3 +52,41 @@ fn generic_mul() { let res = &Apples(42) * &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn mul_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(mul_assign(Oranges, with = "|apples, oranges| apples.0 *= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples *= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples *= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_mul_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(mul_assign(Oranges, with = "|apples, oranges| apples.0 *= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::MulAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples *= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples *= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/rem.rs b/newtype-tools/tests/ops/rem.rs similarity index 57% rename from newtype-tools/tests/rem.rs rename to newtype-tools/tests/ops/rem.rs index 03fe5cd..332bc3d 100644 --- a/newtype-tools/tests/rem.rs +++ b/newtype-tools/tests/ops/rem.rs @@ -52,3 +52,41 @@ fn generic_rem() { let res = &Apples(42) % &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn rem_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(rem_assign(Oranges, with = "|apples, oranges| apples.0 %= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples %= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples %= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_rem_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(rem_assign(Oranges, with = "|apples, oranges| apples.0 %= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::RemAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples %= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples %= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/shl.rs b/newtype-tools/tests/ops/shl.rs similarity index 57% rename from newtype-tools/tests/shl.rs rename to newtype-tools/tests/ops/shl.rs index 37f2ec8..6dd85bb 100644 --- a/newtype-tools/tests/shl.rs +++ b/newtype-tools/tests/ops/shl.rs @@ -52,3 +52,41 @@ fn generic_shl() { let res = &Apples(42_u64) << &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn shl_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(shl_assign(Oranges, with = "|apples, oranges| apples.0 <<= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples <<= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples <<= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_shl_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(shl_assign(Oranges, with = "|apples, oranges| apples.0 <<= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::ShlAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples <<= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples <<= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/shr.rs b/newtype-tools/tests/ops/shr.rs similarity index 57% rename from newtype-tools/tests/shr.rs rename to newtype-tools/tests/ops/shr.rs index 208b8c9..1104bb9 100644 --- a/newtype-tools/tests/shr.rs +++ b/newtype-tools/tests/ops/shr.rs @@ -52,3 +52,41 @@ fn generic_shr() { let res = &Apples(42_u64) >> &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } + +#[test] +fn shr_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(shr_assign(Oranges, with = "|apples, oranges| apples.0 >>= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples >>= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples >>= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_shr_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(shr_assign(Oranges, with = "|apples, oranges| apples.0 >>= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::ShrAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples >>= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples >>= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/sub.rs b/newtype-tools/tests/ops/sub.rs similarity index 57% rename from newtype-tools/tests/sub.rs rename to newtype-tools/tests/ops/sub.rs index 0d5dfe6..27a83cb 100644 --- a/newtype-tools/tests/sub.rs +++ b/newtype-tools/tests/ops/sub.rs @@ -52,3 +52,40 @@ fn generic_sub() { let res = &Apples(42) - &Oranges(21); assert_eq!(res, Weight(EXPECTED_WEIGHT)); } +#[test] +fn sub_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + /// Doc comment. + #[newtype(sub_assign(Oranges, with = "|apples, oranges| apples.0 -= oranges.0 as u64 * 2"))] + struct Apples(u64); + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + apples -= Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + apples -= &Oranges(21); + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); +} + +#[test] +fn generic_sub_assign() { + #[derive(Debug, newtype_tools::Newtype, PartialEq)] + #[newtype(sub_assign(Oranges, with = "|apples, oranges| apples.0 -= oranges.0 as u64 * 2"))] + struct Apples(T) + where + T: std::ops::SubAssign + Clone; + #[derive(Debug, PartialEq)] + struct Oranges(u32); + + let mut apples = Apples(42); + let oranges = Oranges(21); + apples -= oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + let mut apples = Apples(42); + let oranges = Oranges(21); + apples -= &oranges; + assert_eq!(apples, Apples(EXPECTED_WEIGHT)); + assert_eq!(oranges, Oranges(21)); +} diff --git a/newtype-tools/tests/rem_assign.rs b/newtype-tools/tests/rem_assign.rs deleted file mode 100644 index d9ff1c5..0000000 --- a/newtype-tools/tests/rem_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 0; - -#[test] -fn rem_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(rem_assign(Oranges, with = "|apples, oranges| apples.0 %= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples %= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples %= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_rem_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(rem_assign(Oranges, with = "|apples, oranges| apples.0 %= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::RemAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples %= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples %= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/shl_assign.rs b/newtype-tools/tests/shl_assign.rs deleted file mode 100644 index 383fea2..0000000 --- a/newtype-tools/tests/shl_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42 << (21 * 2); - -#[test] -fn shl_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(shl_assign(Oranges, with = "|apples, oranges| apples.0 <<= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples <<= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples <<= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_shl_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(shl_assign(Oranges, with = "|apples, oranges| apples.0 <<= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::ShlAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples <<= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples <<= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/shr_assign.rs b/newtype-tools/tests/shr_assign.rs deleted file mode 100644 index 5082c9d..0000000 --- a/newtype-tools/tests/shr_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 42 >> (21 * 2); - -#[test] -fn shr_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(shr_assign(Oranges, with = "|apples, oranges| apples.0 >>= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples >>= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples >>= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_shr_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(shr_assign(Oranges, with = "|apples, oranges| apples.0 >>= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::ShrAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples >>= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples >>= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} diff --git a/newtype-tools/tests/sub_assign.rs b/newtype-tools/tests/sub_assign.rs deleted file mode 100644 index 50736b1..0000000 --- a/newtype-tools/tests/sub_assign.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![cfg(feature = "derive")] - -const EXPECTED_WEIGHT: u64 = 0; - -#[test] -fn sub_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - /// Doc comment. - #[newtype(sub_assign(Oranges, with = "|apples, oranges| apples.0 -= oranges.0 as u64 * 2"))] - struct Apples(u64); - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - apples -= Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - apples -= &Oranges(21); - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); -} - -#[test] -fn generic_sub_assign() { - #[derive(Debug, newtype_tools::Newtype, PartialEq)] - #[newtype(sub_assign(Oranges, with = "|apples, oranges| apples.0 -= oranges.0 as u64 * 2"))] - struct Apples(T) - where - T: std::ops::SubAssign + Clone; - #[derive(Debug, PartialEq)] - struct Oranges(u32); - - let mut apples = Apples(42); - let oranges = Oranges(21); - apples -= oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - let mut apples = Apples(42); - let oranges = Oranges(21); - apples -= &oranges; - assert_eq!(apples, Apples(EXPECTED_WEIGHT)); - assert_eq!(oranges, Oranges(21)); -} From 2fc9aa13f0368158e0f3bb654da37e22f2c52f01 Mon Sep 17 00:00:00 2001 From: Andriy Berestovskyy Date: Thu, 23 Apr 2026 16:59:35 +0200 Subject: [PATCH 3/4] build: add `make expand` --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 3eb2b70..1ca3fd4 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ help:: @echo " test Run cargo test." @echo " trybuild Overwrite trybuild test results." @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." @@ -62,6 +63,9 @@ trybuild:: ######################################################################## ## Misc Targets +expand:: + cargo expand -p newtype-tools --test ${TEST} + fmt:: cargo +nightly fmt -- --config reorder_impl_items=true,error_on_unformatted=true,error_on_line_overflow=true From b9f7e58ce3aa4bdb4d4f7cf25bc66b56915b46f3 Mon Sep 17 00:00:00 2001 From: Andriy Berestovskyy Date: Fri, 24 Apr 2026 09:49:39 +0200 Subject: [PATCH 4/4] feat: implement newtype Amount attribute + refactoring --- Cargo.lock | 7 + Cargo.toml | 5 +- README.md | 4 +- newtype-tools-derive/src/expand.rs | 276 +++++++++++----- newtype-tools-derive/src/lib.rs | 297 ++++++++++++++--- newtype-tools-derive/src/parse.rs | 304 +++++++----------- newtype-tools-derive/src/parse/tests.rs | 29 -- newtype-tools/Cargo.toml | 1 + newtype-tools/src/iter.rs | 26 +- newtype-tools/src/lib.rs | 4 +- newtype-tools/tests/from.rs | 4 +- newtype-tools/tests/into.rs | 4 +- newtype-tools/tests/iter.rs | 42 +-- newtype-tools/tests/iter_trait.rs | 28 +- newtype-tools/tests/newtype_amount.rs | 252 +++++++++++++++ .../tests/{newtype.rs => newtype_derive.rs} | 4 +- newtype-tools/tests/ops/add.rs | 4 +- newtype-tools/tests/ops/bitand.rs | 4 +- newtype-tools/tests/ops/bitor.rs | 4 +- newtype-tools/tests/ops/bitxor.rs | 4 +- newtype-tools/tests/ops/div.rs | 4 +- newtype-tools/tests/ops/mul.rs | 4 +- newtype-tools/tests/ops/rem.rs | 4 +- newtype-tools/tests/ops/shl.rs | 4 +- newtype-tools/tests/ops/shr.rs | 4 +- newtype-tools/tests/ops/sub.rs | 4 +- newtype-tools/tests/trybuild.rs | 11 +- newtype-tools/tests/trybuild/newtype/enum.rs | 4 - .../tests/trybuild/newtype/enum.stderr | 5 - .../tests/trybuild/newtype/named-struct.rs | 6 - .../trybuild/newtype/named-struct.stderr | 8 - .../trybuild/newtype/struct-list-invalid.rs | 5 - .../newtype/struct-list-invalid.stderr | 5 - .../newtype/struct-list-name-value.rs | 5 - .../newtype/struct-list-name-value.stderr | 5 - .../newtype/struct-list-path-invalid.rs | 5 - .../newtype/struct-list-path-invalid.stderr | 5 - .../newtype/struct-list-path-valid.rs | 5 - .../newtype/struct-list-path-valid.stderr | 5 - .../trybuild/newtype/struct-name-value.rs | 5 - .../trybuild/newtype/struct-name-value.stderr | 11 - .../tests/trybuild/newtype/struct-path.rs | 5 - .../tests/trybuild/newtype/struct-path.stderr | 5 - .../tests/trybuild/newtype/tuple-struct.rs | 4 - .../trybuild/newtype/tuple-struct.stderr | 5 - newtype-tools/tests/trybuild/newtype/union.rs | 6 - .../tests/trybuild/newtype/union.stderr | 5 - .../tests/trybuild/newtype/unit-struct.rs | 4 - .../tests/trybuild/newtype/unit-struct.stderr | 5 - .../tests/trybuild/newtype_attribute.rs | 38 +++ .../tests/trybuild/newtype_attribute.stderr | 39 +++ .../tests/trybuild/newtype_derive.rs | 68 ++++ .../tests/trybuild/newtype_derive.stderr | 74 +++++ 53 files changed, 1145 insertions(+), 530 deletions(-) delete mode 100644 newtype-tools-derive/src/parse/tests.rs create mode 100644 newtype-tools/tests/newtype_amount.rs rename newtype-tools/tests/{newtype.rs => newtype_derive.rs} (94%) delete mode 100644 newtype-tools/tests/trybuild/newtype/enum.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/enum.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/named-struct.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/named-struct.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-invalid.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-invalid.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-name-value.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-name-value.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-path-valid.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-list-path-valid.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-name-value.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-name-value.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-path.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/struct-path.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/tuple-struct.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/tuple-struct.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/union.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/union.stderr delete mode 100644 newtype-tools/tests/trybuild/newtype/unit-struct.rs delete mode 100644 newtype-tools/tests/trybuild/newtype/unit-struct.stderr create mode 100644 newtype-tools/tests/trybuild/newtype_attribute.rs create mode 100644 newtype-tools/tests/trybuild/newtype_attribute.stderr create mode 100644 newtype-tools/tests/trybuild/newtype_derive.rs create mode 100644 newtype-tools/tests/trybuild/newtype_derive.stderr diff --git a/Cargo.lock b/Cargo.lock index 05ed630..7eb55f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,7 @@ version = "0.1.0-beta.9" dependencies = [ "newtype-tools-derive", "rstest", + "rustversion", "trybuild", ] @@ -223,6 +224,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "semver" version = "1.0.28" diff --git a/Cargo.toml b/Cargo.toml index 1b7b81a..808a8c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,5 +25,8 @@ newtype-tools-derive = { version = "0.1.0-beta.9", path = "newtype-tools-derive" proc-macro2 = "1.0.106" quote = "1.0.45" rstest = "0.26.1" -syn = { version = "2.0.117", features = ["extra-traits", "full"] } +rustversion = "1.0.22" +# To debug macro parsing: +#syn = { version = "2.0.117", features = ["extra-traits", "full"] } +syn = "2.0.117" trybuild = "1.0.116" diff --git a/README.md b/README.md index 1f435f5..cd0f008 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ use newtype_tools::Newtype; #[derive(Newtype)] #[newtype( - into(Oranges, with = "|apples| Oranges((apples.0 / 2) as u32)") + into(Oranges, with = |apples| Oranges((apples.0 / 2) as u32)) )] struct Apples(u64); struct Oranges(u32); @@ -65,7 +65,7 @@ use newtype_tools::Newtype; #[derive(Debug, Newtype)] #[newtype( - partial_eq(Oranges, with = "|apples, oranges| apples.0 == oranges.0 as u64 * 2") + partial_eq(Oranges, with = |apples, oranges| apples.0 == oranges.0 as u64 * 2) )] struct Apples(u64); struct Oranges(u32); diff --git a/newtype-tools-derive/src/expand.rs b/newtype-tools-derive/src/expand.rs index 0d3bed1..dc6c180 100644 --- a/newtype-tools-derive/src/expand.rs +++ b/newtype-tools-derive/src/expand.rs @@ -1,103 +1,155 @@ -use crate::ParseResult; +use crate::{Newtype, NewtypeDerives, NewtypeKind}; -/// Expands all parsed derives into a token stream. -pub(crate) fn expand_derive(res: &ParseResult) -> syn::Result { +/// Expands the parsed `newtype` attribute into a token stream. +/// +/// ```ignore +/// #[newtype(Amount)] +/// struct Apples(u64); +/// ``` +pub(crate) fn expand_newtype( + attr: Newtype, + kind: NewtypeKind, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let newtype = &attr.newtype; + let inner_ty = &attr.inner_ty; + let standard_derives = quote::quote!(Clone, Copy, Debug, Default, PartialEq, PartialOrd); + let standard_derives = if is_int_type(inner_ty) { + quote::quote!( + #standard_derives, Eq, Ord, Hash + ) + } else { + standard_derives + }; + let mut derives: proc_macro::TokenStream = match kind { + NewtypeKind::Amount => quote::quote! { + #[automatically_derived] + #[derive(newtype_tools::Newtype, #standard_derives)] + #[newtype( + add(#newtype, output = #newtype, with = |l, r| #newtype(l.0 + r.0)), + add_assign(#newtype, with = |this, other| this.0 += other.0), + sub(#newtype, output = #newtype, with = |l, r| #newtype(l.0 - r.0)), + sub_assign(#newtype, with = |this, other| this.0 -= other.0), + mul(#inner_ty, output = #newtype, with = |l, inner| #newtype(l.0 * inner)), + mul_assign(#inner_ty, with = |this, inner| this.0 *= inner), + div(#newtype, output = #inner_ty, with = |l, r| l.0 / r.0) + )] + // Guarantees the memory layout is identical to the inner type. + #[repr(transparent)] + } + .into(), + }; + derives.extend(item); + derives +} + +/// Expands all parsed attributes of a `Newtype` derive into a token stream. +/// +/// ```ignore +/// #[derive(Newtype)] +/// #[newtype(from(Oranges, with = "|oranges| Apples(oranges.0 as u64 * 2)"))] +/// struct Apples(u64); +/// ``` +pub(crate) fn expand_newtype_derives( + newtype_derives: (Newtype, NewtypeDerives), +) -> proc_macro::TokenStream { let add = ( - syn::parse_quote!(std::ops::Add), + syn::parse_quote!(core::ops::Add), syn::parse_quote!(add), - syn::parse_quote!(std::ops::AddAssign), + syn::parse_quote!(core::ops::AddAssign), syn::parse_quote!(add_assign), ); let band = ( - syn::parse_quote!(std::ops::BitAnd), + syn::parse_quote!(core::ops::BitAnd), syn::parse_quote!(bitand), - syn::parse_quote!(std::ops::BitAndAssign), + syn::parse_quote!(core::ops::BitAndAssign), syn::parse_quote!(bitand_assign), ); let bor = ( - syn::parse_quote!(std::ops::BitOr), + syn::parse_quote!(core::ops::BitOr), syn::parse_quote!(bitor), - syn::parse_quote!(std::ops::BitOrAssign), + syn::parse_quote!(core::ops::BitOrAssign), syn::parse_quote!(bitor_assign), ); let bxor = ( - syn::parse_quote!(std::ops::BitXor), + syn::parse_quote!(core::ops::BitXor), syn::parse_quote!(bitxor), - syn::parse_quote!(std::ops::BitXorAssign), + syn::parse_quote!(core::ops::BitXorAssign), syn::parse_quote!(bitxor_assign), ); let div = ( - syn::parse_quote!(std::ops::Div), + syn::parse_quote!(core::ops::Div), syn::parse_quote!(div), - syn::parse_quote!(std::ops::DivAssign), + syn::parse_quote!(core::ops::DivAssign), syn::parse_quote!(div_assign), ); let mul = ( - syn::parse_quote!(std::ops::Mul), + syn::parse_quote!(core::ops::Mul), syn::parse_quote!(mul), - syn::parse_quote!(std::ops::MulAssign), + syn::parse_quote!(core::ops::MulAssign), syn::parse_quote!(mul_assign), ); let rem = ( - syn::parse_quote!(std::ops::Rem), + syn::parse_quote!(core::ops::Rem), syn::parse_quote!(rem), - syn::parse_quote!(std::ops::RemAssign), + syn::parse_quote!(core::ops::RemAssign), syn::parse_quote!(rem_assign), ); let shl = ( - syn::parse_quote!(std::ops::Shl), + syn::parse_quote!(core::ops::Shl), syn::parse_quote!(shl), - syn::parse_quote!(std::ops::ShlAssign), + syn::parse_quote!(core::ops::ShlAssign), syn::parse_quote!(shl_assign), ); let shr = ( - syn::parse_quote!(std::ops::Shr), + syn::parse_quote!(core::ops::Shr), syn::parse_quote!(shr), - syn::parse_quote!(std::ops::ShrAssign), + syn::parse_quote!(core::ops::ShrAssign), syn::parse_quote!(shr_assign), ); let sub = ( - syn::parse_quote!(std::ops::Sub), + syn::parse_quote!(core::ops::Sub), syn::parse_quote!(sub), - syn::parse_quote!(std::ops::SubAssign), + syn::parse_quote!(core::ops::SubAssign), syn::parse_quote!(sub_assign), ); let mut tokens = proc_macro2::TokenStream::new(); - tokens.extend(expand_newtype_trait(res)); - tokens.extend(expand_from(res)); - tokens.extend(expand_try_from(res)); - tokens.extend(expand_into(res)); - tokens.extend(expand_try_into(res)); - tokens.extend(expand_bin_op(add.0, add.1, res, &res.add)); - tokens.extend(expand_assign_op(add.2, add.3, res, &res.add_assign)); - tokens.extend(expand_bin_op(band.0, band.1, res, &res.bitand)); - tokens.extend(expand_assign_op(band.2, band.3, res, &res.bitand_assign)); - tokens.extend(expand_bin_op(bor.0, bor.1, res, &res.bitor)); - tokens.extend(expand_assign_op(bor.2, bor.3, res, &res.bitor_assign)); - tokens.extend(expand_bin_op(bxor.0, bxor.1, res, &res.bitxor)); - tokens.extend(expand_assign_op(bxor.2, bxor.3, res, &res.bitxor_assign)); - tokens.extend(expand_bin_op(div.0, div.1, res, &res.div)); - tokens.extend(expand_assign_op(div.2, div.3, res, &res.div_assign)); - tokens.extend(expand_bin_op(mul.0, mul.1, res, &res.mul)); - tokens.extend(expand_assign_op(mul.2, mul.3, res, &res.mul_assign)); - tokens.extend(expand_bin_op(rem.0, rem.1, res, &res.rem)); - tokens.extend(expand_assign_op(rem.2, rem.3, res, &res.rem_assign)); - tokens.extend(expand_bin_op(shl.0, shl.1, res, &res.shl)); - tokens.extend(expand_assign_op(shl.2, shl.3, res, &res.shl_assign)); - tokens.extend(expand_bin_op(shr.0, shr.1, res, &res.shr)); - tokens.extend(expand_assign_op(shr.2, shr.3, res, &res.shr_assign)); - tokens.extend(expand_partial_eq(res)); - tokens.extend(expand_bin_op(sub.0, sub.1, res, &res.sub)); - tokens.extend(expand_assign_op(sub.2, sub.3, res, &res.sub_assign)); - Ok(tokens.into()) + let d = &newtype_derives; + tokens.extend(expand_newtype_trait(d)); + tokens.extend(expand_from(d)); + tokens.extend(expand_try_from(d)); + tokens.extend(expand_into(d)); + tokens.extend(expand_try_into(d)); + tokens.extend(expand_bin_op(add.0, add.1, d, &d.1.add)); + tokens.extend(expand_assign_op(add.2, add.3, d, &d.1.add_assign)); + tokens.extend(expand_bin_op(band.0, band.1, d, &d.1.bitand)); + tokens.extend(expand_assign_op(band.2, band.3, d, &d.1.bitand_assign)); + tokens.extend(expand_bin_op(bor.0, bor.1, d, &d.1.bitor)); + tokens.extend(expand_assign_op(bor.2, bor.3, d, &d.1.bitor_assign)); + tokens.extend(expand_bin_op(bxor.0, bxor.1, d, &d.1.bitxor)); + tokens.extend(expand_assign_op(bxor.2, bxor.3, d, &d.1.bitxor_assign)); + tokens.extend(expand_bin_op(div.0, div.1, d, &d.1.div)); + tokens.extend(expand_assign_op(div.2, div.3, d, &d.1.div_assign)); + tokens.extend(expand_bin_op(mul.0, mul.1, d, &d.1.mul)); + tokens.extend(expand_assign_op(mul.2, mul.3, d, &d.1.mul_assign)); + tokens.extend(expand_bin_op(rem.0, rem.1, d, &d.1.rem)); + tokens.extend(expand_assign_op(rem.2, rem.3, d, &d.1.rem_assign)); + tokens.extend(expand_bin_op(shl.0, shl.1, d, &d.1.shl)); + tokens.extend(expand_assign_op(shl.2, shl.3, d, &d.1.shl_assign)); + tokens.extend(expand_bin_op(shr.0, shr.1, d, &d.1.shr)); + tokens.extend(expand_assign_op(shr.2, shr.3, d, &d.1.shr_assign)); + tokens.extend(expand_partial_eq(d)); + tokens.extend(expand_bin_op(sub.0, sub.1, d, &d.1.sub)); + tokens.extend(expand_assign_op(sub.2, sub.3, d, &d.1.sub_assign)); + tokens.into() } /// Expands newtype trait definition into a token stream. -fn expand_newtype_trait(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let inner_ty = &res.inner_ty; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); +fn expand_newtype_trait(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let inner_ty = &newtype_derives.0.inner_ty; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); quote::quote! { #[automatically_derived] impl #impl_generics newtype_tools::Newtype for #newtype #newtype_generics #r#where { @@ -119,10 +171,12 @@ fn expand_newtype_trait(res: &ParseResult) -> proc_macro2::TokenStream { } /// Expands all `from` derives into a token stream. -fn expand_from(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); - res.from +fn expand_from(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); + newtype_derives + .1 + .from .iter() .map(|(from_ty, expr)| { quote::quote! { @@ -141,10 +195,12 @@ fn expand_from(res: &ParseResult) -> proc_macro2::TokenStream { } /// Expands all `try_from` derives into a token stream. -fn expand_try_from(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); - res.try_from +fn expand_try_from(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); + newtype_derives + .1 + .try_from .iter() .map(|(try_from_ty, error_ty, expr)| { quote::quote! { @@ -165,10 +221,12 @@ fn expand_try_from(res: &ParseResult) -> proc_macro2::TokenStream { /// Expands all `into` derives into a token stream. /// Note, that it still produces the `from` derives, but with reversed types. -fn expand_into(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); - res.into +fn expand_into(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); + newtype_derives + .1 + .into .iter() .map(|(output_ty, expr)| { quote::quote! { @@ -188,10 +246,12 @@ fn expand_into(res: &ParseResult) -> proc_macro2::TokenStream { /// Expands all `try_into` derives into a token stream. /// Note, that it still produces the `try_from` derives, but with reversed types. -fn expand_try_into(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); - res.try_into +fn expand_try_into(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); + newtype_derives + .1 + .try_into .iter() .map(|(output_ty, error_ty, expr)| { quote::quote! { @@ -211,10 +271,12 @@ fn expand_try_into(res: &ParseResult) -> proc_macro2::TokenStream { } /// Expands all `partial_eq` derives into a token stream. -fn expand_partial_eq(res: &ParseResult) -> proc_macro2::TokenStream { - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); - res.partial_eq +fn expand_partial_eq(newtype_derives: &(Newtype, NewtypeDerives)) -> proc_macro2::TokenStream { + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); + newtype_derives + .1 + .partial_eq .iter() .map(|(other_ty, expr)| { quote::quote! { @@ -237,14 +299,14 @@ fn expand_partial_eq(res: &ParseResult) -> proc_macro2::TokenStream { fn expand_bin_op( r#trait: syn::Path, method: syn::Ident, - res: &ParseResult, + newtype_derives: &(Newtype, NewtypeDerives), ops: &[(syn::Type, syn::Type, syn::Expr)], ) -> proc_macro2::TokenStream { if ops.is_empty() { return proc_macro2::TokenStream::new(); } - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); ops.iter() .map(|(rhs_ty, output_ty, expr)| { quote::quote! { @@ -287,14 +349,14 @@ fn expand_bin_op( fn expand_assign_op( r#trait: syn::Path, method: syn::Ident, - res: &ParseResult, + newtype_derives: &(Newtype, NewtypeDerives), ops: &[(syn::Type, syn::Expr)], ) -> proc_macro2::TokenStream { if ops.is_empty() { return proc_macro2::TokenStream::new(); } - let newtype = &res.newtype; - let (impl_generics, newtype_generics, r#where) = &res.generics.split_for_impl(); + let newtype = &newtype_derives.0.newtype; + let (impl_generics, newtype_generics, r#where) = &newtype_derives.0.generics.split_for_impl(); ops.iter() .map(|(rhs_ty, expr)| { quote::quote! { @@ -315,3 +377,57 @@ fn expand_assign_op( }) .collect() } + +/// Returns `true` if the newtype inner representation is a known integer type. +fn is_int_type(inner_ty: &syn::Type) -> bool { + let syn::Type::Path(tp) = inner_ty else { + return false; + }; + if tp.qself.is_some() || tp.path.segments.len() != 1 { + return false; + } + match &tp.path.segments[0].ident { + ident if ident == "i8" => true, + ident if ident == "u8" => true, + ident if ident == "i16" => true, + ident if ident == "u16" => true, + ident if ident == "i32" => true, + ident if ident == "u32" => true, + ident if ident == "i64" => true, + ident if ident == "u64" => true, + ident if ident == "isize" => true, + ident if ident == "usize" => true, + ident if ident == "i128" => true, + ident if ident == "u128" => true, + _ => false, + } +} + +#[cfg(test)] +mod tests { + #[test] + fn is_int_type() { + use super::is_int_type; + assert!(is_int_type(&syn::parse_quote!(i8))); + assert!(is_int_type(&syn::parse_quote!(u8))); + assert!(is_int_type(&syn::parse_quote!(i16))); + assert!(is_int_type(&syn::parse_quote!(u16))); + assert!(is_int_type(&syn::parse_quote!(i32))); + assert!(is_int_type(&syn::parse_quote!(u32))); + assert!(is_int_type(&syn::parse_quote!(i64))); + assert!(is_int_type(&syn::parse_quote!(u64))); + assert!(is_int_type(&syn::parse_quote!(isize))); + assert!(is_int_type(&syn::parse_quote!(usize))); + assert!(is_int_type(&syn::parse_quote!(i128))); + assert!(is_int_type(&syn::parse_quote!(u128))); + assert!(!is_int_type(&syn::parse_quote!(bool))); + assert!(!is_int_type(&syn::parse_quote!(char))); + assert!(!is_int_type(&syn::parse_quote!(str))); + assert!(!is_int_type(&syn::parse_quote!(String))); + assert!(!is_int_type(&syn::parse_quote!(Other))); + assert!(!is_int_type(&syn::parse_quote!(T))); + + assert!(!is_int_type(&syn::parse_quote!(dyn T))); + assert!(!is_int_type(&syn::parse_quote!(::Output))); + } +} diff --git a/newtype-tools-derive/src/lib.rs b/newtype-tools-derive/src/lib.rs index cb47b71..2447880 100644 --- a/newtype-tools-derive/src/lib.rs +++ b/newtype-tools-derive/src/lib.rs @@ -5,28 +5,96 @@ use proc_macro::TokenStream; mod expand; mod parse; -/// Expands the derive macro into a `TokenStream`. -#[proc_macro_derive(Newtype, attributes(newtype))] -pub fn derive(input: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); - parse_input_and_expand_derive(input).unwrap_or_else(|err| err.to_compile_error().into()) +/// Parses and expands a `newtype` attribute kind into a token stream. +/// The idea is similar to the `phantom_newtype` crate, but has no its limitations, +/// as the `newtype` is a real new Rust type. New traits could be easily implemented +/// for such a `newtype`, and the set of derived traits could be easily extended: +/// +/// ```ignore +/// #[newtype(Amount)] +/// #[derive(Default)] +/// struct Apples(u64); +/// ``` +/// +/// The supported `newtype` kinds are: +/// +/// | Trait | `#[newtype(Amount)]` | `#[newtype(Id)]` | +/// |-------------------|:--------------------:|:----------------:| +/// | `PartialEq` | ✔ | ✔ | +/// | `PartialOrd` | ✔ | ✔ | +/// | `From` | ✔ | ✔ | +/// | `Add` | ✔ | ✘ | +/// | `AddAssign` | ✔ | ✘ | +/// | `Sub` | ✔ | ✘ | +/// | `SubAssign` | ✔ | ✘ | +/// | `Mul` | ✔ | ✘ | +/// | `MulAssign` | ✔ | ✘ | +/// | `Div` | ✔ | ✘ | +#[proc_macro_attribute] +pub fn newtype(attr: TokenStream, item: TokenStream) -> TokenStream { + let kind = match parse::parse_newtype_kind(attr.into()) { + Ok(kind) => kind, + Err(err) => return err.to_compile_error().into(), + }; + let input = item.clone(); + let newtype = match parse::parse_newtype(input) { + Ok(newtype) => newtype, + Err(err) => return err.to_compile_error().into(), + }; + expand::expand_newtype(newtype, kind, item) } -/// Expands the `DeriveInput` into a `syn::Result`. -fn parse_input_and_expand_derive(input: syn::DeriveInput) -> syn::Result { - let derive = parse::parse_input(input)?; - expand::expand_derive(&derive) +/// Parses and expands a `Newtype` derive into a token stream. +/// +/// ```ignore +/// #[derive(Newtype)] +/// #[newtype(from(Oranges, with = "|oranges| Apples(oranges.0 as u64 * 2)"))] +/// struct Apples(u64); +/// ``` +#[proc_macro_derive(Newtype, attributes(newtype))] +pub fn newtype_derive(input: TokenStream) -> TokenStream { + let newtype_derives = match parse::parse_newtype_derives(input.into()) { + Ok(newtype_derives) => newtype_derives, + Err(err) => return err.to_compile_error().into(), + }; + expand::expand_newtype_derives(newtype_derives) } -/// All parsed newtype derives. -#[derive(Debug)] -struct ParseResult { +/// Structured representation of a `newtype`. +/// +/// ```ignore +/// #[newtype(Amount)] +/// struct Apples(u64); +/// ``` +struct Newtype { /// Top-level newtype identifier. newtype: syn::Ident, /// Inner type field type. inner_ty: syn::Type, /// Newtype generics. generics: syn::Generics, +} + +impl Newtype { + /// Creates a new `Newtype` instance. + fn new(newtype: syn::Ident, inner_ty: syn::Type, generics: syn::Generics) -> Self { + Self { + newtype, + inner_ty, + generics, + } + } +} + +/// Structured representation of all `Newtype` derives. +/// +/// ```ignore +/// #[derive(Newtype)] +/// #[newtype(from(Oranges, with = "|oranges| Apples(oranges.0 as u64 * 2)"))] +/// struct Apples(u64); +/// ``` +#[derive(Default)] +struct NewtypeDerives { /// Tuples of `(from type, conversion expression)`. from: Vec<(syn::Type, syn::Expr)>, /// Tuples of `(from type, error type, conversion expression)`. @@ -79,38 +147,179 @@ struct ParseResult { sub_assign: Vec<(syn::Type, syn::Expr)>, } -impl ParseResult { - /// Creates a new `ParseResult` instance. - fn new(newtype: syn::Ident, inner_ty: syn::Type, generics: syn::Generics) -> Self { - Self { - newtype, - inner_ty, - generics, - from: Vec::default(), - try_from: Vec::default(), - into: Vec::default(), - try_into: Vec::default(), - add: Vec::default(), - add_assign: Vec::default(), - bitand: Vec::default(), - bitand_assign: Vec::default(), - bitor: Vec::default(), - bitor_assign: Vec::default(), - bitxor: Vec::default(), - bitxor_assign: Vec::default(), - div: Vec::default(), - div_assign: Vec::default(), - mul: Vec::default(), - mul_assign: Vec::default(), - rem: Vec::default(), - rem_assign: Vec::default(), - shl: Vec::default(), - shl_assign: Vec::default(), - shr: Vec::default(), - shr_assign: Vec::default(), - partial_eq: Vec::default(), - sub: Vec::default(), - sub_assign: Vec::default(), +/// Defines `newtype` attribute kind. +/// +/// ```ignore +/// #[newtype(Amount)] +/// struct Apples(u64); +/// ``` +#[derive(Debug, PartialEq)] +enum NewtypeKind { + Amount, +} + +impl core::fmt::Display for NewtypeKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Amount => f.write_str("Amount"), } } } + +impl TryFrom<&syn::Ident> for NewtypeKind { + type Error = syn::Error; + + fn try_from(value: &syn::Ident) -> Result { + match value { + ident if ident == "Amount" => Ok(Self::Amount), + _ => Err(syn::Error::new_spanned(value, "expected 'Amount'")), + } + } +} + +/// Defines `Newtype` derive attribute type. +/// +/// ```ignore +/// #[derive(Newtype)] +/// #[newtype(from(Oranges, with = "|oranges| Apples(oranges.0 as u64 * 2)"))] +/// struct Apples(u64); +/// ``` +#[derive(Debug, PartialEq)] +enum DeriveType { + From, + TryFrom, + Into, + TryInto, + Add, + AddAssign, + BitAnd, + BitAndAssign, + BitOr, + BitOrAssign, + BitXor, + BitXorAssign, + Div, + DivAssign, + Mul, + MulAssign, + Rem, + RemAssign, + Shl, + ShlAssign, + Shr, + ShrAssign, + PartialEq, + Sub, + SubAssign, +} + +impl core::fmt::Display for DeriveType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::From => f.write_str("from"), + Self::TryFrom => f.write_str("try_from"), + Self::Into => f.write_str("into"), + Self::TryInto => f.write_str("try_into"), + Self::Add => f.write_str("add"), + Self::AddAssign => f.write_str("add_assign"), + Self::BitAnd => f.write_str("bitand"), + Self::BitAndAssign => f.write_str("bitand_assign"), + Self::BitOr => f.write_str("bitor"), + Self::BitOrAssign => f.write_str("bitor_assign"), + Self::BitXor => f.write_str("bitxor"), + Self::BitXorAssign => f.write_str("bitxor_assign"), + Self::Div => f.write_str("div"), + Self::DivAssign => f.write_str("div_assign"), + Self::Mul => f.write_str("mul"), + Self::MulAssign => f.write_str("mul_assign"), + Self::Rem => f.write_str("rem"), + Self::RemAssign => f.write_str("rem_assign"), + Self::Shl => f.write_str("shl"), + Self::ShlAssign => f.write_str("shl_assign"), + Self::Shr => f.write_str("shr"), + Self::ShrAssign => f.write_str("shr_assign"), + Self::PartialEq => f.write_str("partial_eq"), + Self::Sub => f.write_str("sub"), + Self::SubAssign => f.write_str("sub_assign"), + } + } +} + +impl TryFrom> for DeriveType { + type Error = syn::Error; + + fn try_from(value: Option<&syn::Ident>) -> Result { + match value { + Some(ident) if ident == "from" => Ok(Self::From), + Some(ident) if ident == "try_from" => Ok(Self::TryFrom), + Some(ident) if ident == "into" => Ok(Self::Into), + Some(ident) if ident == "try_into" => Ok(Self::TryInto), + Some(ident) if ident == "add" => Ok(Self::Add), + Some(ident) if ident == "add_assign" => Ok(Self::AddAssign), + Some(ident) if ident == "bitand" => Ok(Self::BitAnd), + Some(ident) if ident == "bitand_assign" => Ok(Self::BitAndAssign), + Some(ident) if ident == "bitor" => Ok(Self::BitOr), + Some(ident) if ident == "bitor_assign" => Ok(Self::BitOrAssign), + Some(ident) if ident == "bitxor" => Ok(Self::BitXor), + Some(ident) if ident == "bitxor_assign" => Ok(Self::BitXorAssign), + Some(ident) if ident == "div" => Ok(Self::Div), + Some(ident) if ident == "div_assign" => Ok(Self::DivAssign), + Some(ident) if ident == "mul" => Ok(Self::Mul), + Some(ident) if ident == "mul_assign" => Ok(Self::MulAssign), + Some(ident) if ident == "rem" => Ok(Self::Rem), + Some(ident) if ident == "rem_assign" => Ok(Self::RemAssign), + Some(ident) if ident == "shl" => Ok(Self::Shl), + Some(ident) if ident == "shl_assign" => Ok(Self::ShlAssign), + Some(ident) if ident == "shr" => Ok(Self::Shr), + Some(ident) if ident == "shr_assign" => Ok(Self::ShrAssign), + Some(ident) if ident == "partial_eq" => Ok(Self::PartialEq), + Some(ident) if ident == "sub" => Ok(Self::Sub), + Some(ident) if ident == "sub_assign" => Ok(Self::SubAssign), + _ => Err(syn::Error::new_spanned( + value, + "expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, \ + `bitor(_assign)`, `bitxor(_assign)`, `div(_assign)`, `mul(_assign)`, \ + `rem(_assign)`, `shl(_assign)`, `shr(_assign)`, `partial_eq`, or `sub(_assign)`", + )), + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn newtype_kind_display_roundtrip() { + use super::NewtypeKind; + assert_eq!(format!("{}", NewtypeKind::Amount), "Amount"); + } + + #[test] + fn derive_type_display_roundtrip() { + use super::DeriveType; + assert_eq!(format!("{}", DeriveType::From), "from"); + assert_eq!(format!("{}", DeriveType::TryFrom), "try_from"); + assert_eq!(format!("{}", DeriveType::Into), "into"); + assert_eq!(format!("{}", DeriveType::TryInto), "try_into"); + assert_eq!(format!("{}", DeriveType::Add), "add"); + assert_eq!(format!("{}", DeriveType::AddAssign), "add_assign"); + assert_eq!(format!("{}", DeriveType::BitAnd), "bitand"); + assert_eq!(format!("{}", DeriveType::BitAndAssign), "bitand_assign"); + assert_eq!(format!("{}", DeriveType::BitOr), "bitor"); + assert_eq!(format!("{}", DeriveType::BitOrAssign), "bitor_assign"); + assert_eq!(format!("{}", DeriveType::BitXor), "bitxor"); + assert_eq!(format!("{}", DeriveType::BitXorAssign), "bitxor_assign"); + assert_eq!(format!("{}", DeriveType::Div), "div"); + assert_eq!(format!("{}", DeriveType::DivAssign), "div_assign"); + assert_eq!(format!("{}", DeriveType::Mul), "mul"); + assert_eq!(format!("{}", DeriveType::MulAssign), "mul_assign"); + assert_eq!(format!("{}", DeriveType::Rem), "rem"); + assert_eq!(format!("{}", DeriveType::RemAssign), "rem_assign"); + assert_eq!(format!("{}", DeriveType::Shl), "shl"); + assert_eq!(format!("{}", DeriveType::ShlAssign), "shl_assign"); + assert_eq!(format!("{}", DeriveType::Shr), "shr"); + assert_eq!(format!("{}", DeriveType::ShrAssign), "shr_assign"); + assert_eq!(format!("{}", DeriveType::PartialEq), "partial_eq"); + assert_eq!(format!("{}", DeriveType::Sub), "sub"); + assert_eq!(format!("{}", DeriveType::SubAssign), "sub_assign"); + } +} diff --git a/newtype-tools-derive/src/parse.rs b/newtype-tools-derive/src/parse.rs index d97bdff..0dac4bd 100644 --- a/newtype-tools-derive/src/parse.rs +++ b/newtype-tools-derive/src/parse.rs @@ -1,7 +1,4 @@ -use crate::ParseResult; - -#[cfg(test)] -mod tests; +use crate::{DeriveType, Newtype, NewtypeDerives, NewtypeKind}; mod kw { syn::custom_keyword!(error); @@ -12,24 +9,63 @@ mod kw { /// Newtype attribute name. const NEWTYPE_ATTR_NAME: &str = "newtype"; -/// Parses all attributes and produce their structured representation. -pub(crate) fn parse_input(input: syn::DeriveInput) -> syn::Result { - let inner_ty = parse_derive_input_data(input.data)?; - let mut res = ParseResult::new(input.ident, inner_ty, input.generics); - for attr in input.attrs { +/// Parses `newtype` attribute kind. +/// +/// ```ignore +/// #[newtype(Amount)] +/// struct Apples(u64); +/// ``` +pub(crate) fn parse_newtype_kind(attr: proc_macro2::TokenStream) -> syn::Result { + let attr_span = syn::spanned::Spanned::span(&attr); + let parser = |input: syn::parse::ParseStream| parse_lit_or::(&input); + use syn::parse::Parser; + let ident = parser + .parse2(attr) + .map_err(|_| syn::Error::new(attr_span, "expected `#[newtype(NewtypeKind)]`"))?; + NewtypeKind::try_from(&ident) +} + +/// Parses `newtype` attribute and produces its structured representation. +/// +/// ```ignore +/// #[newtype(Amount)] +/// struct Apples(u64); +/// ``` +pub(crate) fn parse_newtype(input: proc_macro::TokenStream) -> syn::Result { + let derive_input = syn::parse::(input)?; + let inner_ty = parse_derive_input_data(&derive_input.data)?; + let attribute = Newtype::new(derive_input.ident, inner_ty, derive_input.generics); + Ok(attribute) +} + +/// Parses all attributes of a `Newtype` derive and produces their structured representation. +/// +/// ```ignore +/// #[derive(Newtype)] +/// #[newtype(from(Oranges, with = "|oranges| Apples(oranges.0 as u64 * 2)"))] +/// struct Apples(u64); +/// ``` +pub(crate) fn parse_newtype_derives( + input: proc_macro2::TokenStream, +) -> syn::Result<(Newtype, NewtypeDerives)> { + let derive_input = syn::parse2::(input)?; + let inner_ty = parse_derive_input_data(&derive_input.data)?; + let attr = Newtype::new(derive_input.ident, inner_ty, derive_input.generics); + let mut derives = NewtypeDerives::default(); + for attr in derive_input.attrs { // Just skip all other top-level attributes. if !attr.path().is_ident(NEWTYPE_ATTR_NAME) { continue; } - parse_top_level_meta(attr.meta, &mut res)?; + parse_top_level_meta(attr.meta, &mut derives)?; } - Ok(res) + Ok((attr, derives)) } /// Parses the first struct field to get the inner type. /// /// For `struct Newtype(type)` returns `type`. -fn parse_derive_input_data(data: syn::Data) -> syn::Result { +pub(crate) fn parse_derive_input_data(data: &syn::Data) -> syn::Result { let msg = "expected `struct Newtype(inner_type)`"; let field = match &data { @@ -49,7 +85,7 @@ fn parse_derive_input_data(data: syn::Data) -> syn::Result { } /// Parses a single top-level attribute's meta and fills in its structured representation. -fn parse_top_level_meta(meta: syn::Meta, res: &mut ParseResult) -> syn::Result<()> { +fn parse_top_level_meta(meta: syn::Meta, res: &mut NewtypeDerives) -> syn::Result<()> { match meta { // `#[newtype]` syn::Meta::Path(path) => parse_top_level_path(path, res)?, @@ -63,7 +99,7 @@ fn parse_top_level_meta(meta: syn::Meta, res: &mut ParseResult) -> syn::Result<( /// Parses a single top-level path attribute and fills in its structured representation: /// `#[newtype]` -fn parse_top_level_path(path: syn::Path, _res: &mut ParseResult) -> syn::Result<()> { +fn parse_top_level_path(path: syn::Path, _res: &mut NewtypeDerives) -> syn::Result<()> { Err(syn::Error::new_spanned( path, "expected `#[newtype(attr1, attr2)]`", @@ -72,7 +108,7 @@ fn parse_top_level_path(path: syn::Path, _res: &mut ParseResult) -> syn::Result< /// Parses a single top-level list attribute and fills in its structured representation: /// `#[newtype(attr1, attr2)]` -fn parse_top_level_list(list: syn::MetaList, res: &mut ParseResult) -> syn::Result<()> { +fn parse_top_level_list(list: syn::MetaList, res: &mut NewtypeDerives) -> syn::Result<()> { let args = list.parse_args_with( syn::punctuated::Punctuated::::parse_terminated, )?; @@ -86,7 +122,7 @@ fn parse_top_level_list(list: syn::MetaList, res: &mut ParseResult) -> syn::Resu /// `#[newtype = value]` fn parse_top_level_name_value( name_value: syn::MetaNameValue, - _res: &mut ParseResult, + _res: &mut NewtypeDerives, ) -> syn::Result<()> { Err(syn::Error::new_spanned( name_value, @@ -95,15 +131,15 @@ fn parse_top_level_name_value( } /// Parses a single nested attribute's meta and fills in its structured representation. -fn parse_nested_meta(meta: &syn::Meta, res: &mut ParseResult) -> syn::Result<()> { - let attr_type = AttrType::try_from(meta.path().get_ident())?; +fn parse_nested_meta(meta: &syn::Meta, res: &mut NewtypeDerives) -> syn::Result<()> { + let derive_type = DeriveType::try_from(meta.path().get_ident())?; match meta { // `#[newtype(attr)]` - syn::Meta::Path(path) => parse_nested_path(attr_type, path, res)?, + syn::Meta::Path(path) => parse_nested_path(derive_type, path, res)?, // `#[newtype(attr1, attr2)]` - syn::Meta::List(list) => parse_nested_list(attr_type, list, res)?, + syn::Meta::List(list) => parse_nested_list(derive_type, list, res)?, // `#[newtype(attr = value)]` - syn::Meta::NameValue(name_value) => parse_nested_name_value(attr_type, name_value, res)?, + syn::Meta::NameValue(name_value) => parse_nested_name_value(derive_type, name_value, res)?, } Ok(()) } @@ -111,38 +147,38 @@ fn parse_nested_meta(meta: &syn::Meta, res: &mut ParseResult) -> syn::Result<()> /// Parses a single nested path attribute and fills in its structured representation: /// `#[newtype(attr)]` fn parse_nested_path( - attr_type: AttrType, + derive_type: DeriveType, path: &syn::Path, - _res: &mut ParseResult, + _res: &mut NewtypeDerives, ) -> syn::Result<()> { - match attr_type { - AttrType::From - | AttrType::TryFrom - | AttrType::Into - | AttrType::TryInto - | AttrType::Add - | AttrType::AddAssign - | AttrType::BitAnd - | AttrType::BitAndAssign - | AttrType::BitOr - | AttrType::BitOrAssign - | AttrType::BitXor - | AttrType::BitXorAssign - | AttrType::Div - | AttrType::DivAssign - | AttrType::Mul - | AttrType::MulAssign - | AttrType::Rem - | AttrType::RemAssign - | AttrType::Shl - | AttrType::ShlAssign - | AttrType::Shr - | AttrType::ShrAssign - | AttrType::PartialEq - | AttrType::Sub - | AttrType::SubAssign => Err(syn::Error::new_spanned( + match derive_type { + DeriveType::From + | DeriveType::TryFrom + | DeriveType::Into + | DeriveType::TryInto + | DeriveType::Add + | DeriveType::AddAssign + | DeriveType::BitAnd + | DeriveType::BitAndAssign + | DeriveType::BitOr + | DeriveType::BitOrAssign + | DeriveType::BitXor + | DeriveType::BitXorAssign + | DeriveType::Div + | DeriveType::DivAssign + | DeriveType::Mul + | DeriveType::MulAssign + | DeriveType::Rem + | DeriveType::RemAssign + | DeriveType::Shl + | DeriveType::ShlAssign + | DeriveType::Shr + | DeriveType::ShrAssign + | DeriveType::PartialEq + | DeriveType::Sub + | DeriveType::SubAssign => Err(syn::Error::new_spanned( path, - format!("expected `#[newtype({attr_type}(...))]`"), + format!("expected `#[newtype({derive_type}(...))]`"), )), } } @@ -150,49 +186,49 @@ fn parse_nested_path( /// Parses a single nested list attribute and fills in its structured representation: /// `#[newtype(attr(attr1, attr2))]` fn parse_nested_list( - attr_type: AttrType, + derive_type: DeriveType, list: &syn::MetaList, - res: &mut ParseResult, + res: &mut NewtypeDerives, ) -> syn::Result<()> { - match attr_type { - AttrType::From => parse_type_with(list, &mut res.from), - AttrType::TryFrom => parse_type_error_with(list, &mut res.try_from), - AttrType::Into => parse_type_with(list, &mut res.into), - AttrType::TryInto => parse_type_error_with(list, &mut res.try_into), - AttrType::Add => parse_type_output_with(list, &mut res.add), - AttrType::AddAssign => parse_type_with(list, &mut res.add_assign), - AttrType::BitAnd => parse_type_output_with(list, &mut res.bitand), - AttrType::BitAndAssign => parse_type_with(list, &mut res.bitand_assign), - AttrType::BitOr => parse_type_output_with(list, &mut res.bitor), - AttrType::BitOrAssign => parse_type_with(list, &mut res.bitor_assign), - AttrType::BitXor => parse_type_output_with(list, &mut res.bitxor), - AttrType::BitXorAssign => parse_type_with(list, &mut res.bitxor_assign), - AttrType::Div => parse_type_output_with(list, &mut res.div), - AttrType::DivAssign => parse_type_with(list, &mut res.div_assign), - AttrType::Mul => parse_type_output_with(list, &mut res.mul), - AttrType::MulAssign => parse_type_with(list, &mut res.mul_assign), - AttrType::Rem => parse_type_output_with(list, &mut res.rem), - AttrType::RemAssign => parse_type_with(list, &mut res.rem_assign), - AttrType::Shl => parse_type_output_with(list, &mut res.shl), - AttrType::ShlAssign => parse_type_with(list, &mut res.shl_assign), - AttrType::Shr => parse_type_output_with(list, &mut res.shr), - AttrType::ShrAssign => parse_type_with(list, &mut res.shr_assign), - AttrType::PartialEq => parse_type_with(list, &mut res.partial_eq), - AttrType::Sub => parse_type_output_with(list, &mut res.sub), - AttrType::SubAssign => parse_type_with(list, &mut res.sub_assign), + match derive_type { + DeriveType::From => parse_type_with(list, &mut res.from), + DeriveType::TryFrom => parse_type_error_with(list, &mut res.try_from), + DeriveType::Into => parse_type_with(list, &mut res.into), + DeriveType::TryInto => parse_type_error_with(list, &mut res.try_into), + DeriveType::Add => parse_type_output_with(list, &mut res.add), + DeriveType::AddAssign => parse_type_with(list, &mut res.add_assign), + DeriveType::BitAnd => parse_type_output_with(list, &mut res.bitand), + DeriveType::BitAndAssign => parse_type_with(list, &mut res.bitand_assign), + DeriveType::BitOr => parse_type_output_with(list, &mut res.bitor), + DeriveType::BitOrAssign => parse_type_with(list, &mut res.bitor_assign), + DeriveType::BitXor => parse_type_output_with(list, &mut res.bitxor), + DeriveType::BitXorAssign => parse_type_with(list, &mut res.bitxor_assign), + DeriveType::Div => parse_type_output_with(list, &mut res.div), + DeriveType::DivAssign => parse_type_with(list, &mut res.div_assign), + DeriveType::Mul => parse_type_output_with(list, &mut res.mul), + DeriveType::MulAssign => parse_type_with(list, &mut res.mul_assign), + DeriveType::Rem => parse_type_output_with(list, &mut res.rem), + DeriveType::RemAssign => parse_type_with(list, &mut res.rem_assign), + DeriveType::Shl => parse_type_output_with(list, &mut res.shl), + DeriveType::ShlAssign => parse_type_with(list, &mut res.shl_assign), + DeriveType::Shr => parse_type_output_with(list, &mut res.shr), + DeriveType::ShrAssign => parse_type_with(list, &mut res.shr_assign), + DeriveType::PartialEq => parse_type_with(list, &mut res.partial_eq), + DeriveType::Sub => parse_type_output_with(list, &mut res.sub), + DeriveType::SubAssign => parse_type_with(list, &mut res.sub_assign), } } /// Parses a single nested name-value attribute and fills in its structured representation. /// `#[newtype(attr = value)]` fn parse_nested_name_value( - attr_type: AttrType, + derive_type: DeriveType, name_value: &syn::MetaNameValue, - _res: &mut ParseResult, + _res: &mut NewtypeDerives, ) -> syn::Result<()> { Err(syn::Error::new_spanned( name_value, - format!("expected `#[newtype({attr_type}(...))]`"), + format!("expected `#[newtype({derive_type}(...))]`"), )) } @@ -273,104 +309,12 @@ where input.parse::() } -/// Attribute types. -#[derive(Debug, PartialEq)] -enum AttrType { - From, - TryFrom, - Into, - TryInto, - Add, - AddAssign, - BitAnd, - BitAndAssign, - BitOr, - BitOrAssign, - BitXor, - BitXorAssign, - Div, - DivAssign, - Mul, - MulAssign, - Rem, - RemAssign, - Shl, - ShlAssign, - Shr, - ShrAssign, - PartialEq, - Sub, - SubAssign, -} - -impl std::fmt::Display for AttrType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::From => f.write_str("from"), - Self::TryFrom => f.write_str("try_from"), - Self::Into => f.write_str("into"), - Self::TryInto => f.write_str("try_into"), - Self::Add => f.write_str("add"), - Self::AddAssign => f.write_str("add_assign"), - Self::BitAnd => f.write_str("bitand"), - Self::BitAndAssign => f.write_str("bitand_assign"), - Self::BitOr => f.write_str("bitor"), - Self::BitOrAssign => f.write_str("bitor_assign"), - Self::BitXor => f.write_str("bitxor"), - Self::BitXorAssign => f.write_str("bitxor_assign"), - Self::Div => f.write_str("div"), - Self::DivAssign => f.write_str("div_assign"), - Self::Mul => f.write_str("mul"), - Self::MulAssign => f.write_str("mul_assign"), - Self::Rem => f.write_str("rem"), - Self::RemAssign => f.write_str("rem_assign"), - Self::Shl => f.write_str("shl"), - Self::ShlAssign => f.write_str("shl_assign"), - Self::Shr => f.write_str("shr"), - Self::ShrAssign => f.write_str("shr_assign"), - Self::PartialEq => f.write_str("partial_eq"), - Self::Sub => f.write_str("sub"), - Self::SubAssign => f.write_str("sub_assign"), - } - } -} - -impl TryFrom> for AttrType { - type Error = syn::Error; - - fn try_from(value: Option<&syn::Ident>) -> Result { - match value { - Some(i) if i == "from" => Ok(Self::From), - Some(i) if i == "try_from" => Ok(Self::TryFrom), - Some(i) if i == "into" => Ok(Self::Into), - Some(i) if i == "try_into" => Ok(Self::TryInto), - Some(i) if i == "add" => Ok(Self::Add), - Some(i) if i == "add_assign" => Ok(Self::AddAssign), - Some(i) if i == "bitand" => Ok(Self::BitAnd), - Some(i) if i == "bitand_assign" => Ok(Self::BitAndAssign), - Some(i) if i == "bitor" => Ok(Self::BitOr), - Some(i) if i == "bitor_assign" => Ok(Self::BitOrAssign), - Some(i) if i == "bitxor" => Ok(Self::BitXor), - Some(i) if i == "bitxor_assign" => Ok(Self::BitXorAssign), - Some(i) if i == "div" => Ok(Self::Div), - Some(i) if i == "div_assign" => Ok(Self::DivAssign), - Some(i) if i == "mul" => Ok(Self::Mul), - Some(i) if i == "mul_assign" => Ok(Self::MulAssign), - Some(i) if i == "rem" => Ok(Self::Rem), - Some(i) if i == "rem_assign" => Ok(Self::RemAssign), - Some(i) if i == "shl" => Ok(Self::Shl), - Some(i) if i == "shl_assign" => Ok(Self::ShlAssign), - Some(i) if i == "shr" => Ok(Self::Shr), - Some(i) if i == "shr_assign" => Ok(Self::ShrAssign), - Some(i) if i == "partial_eq" => Ok(Self::PartialEq), - Some(i) if i == "sub" => Ok(Self::Sub), - Some(i) if i == "sub_assign" => Ok(Self::SubAssign), - _ => Err(syn::Error::new_spanned( - value, - "expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, \ - `bitor(_assign)`, `bitxor(_assign)`, `mul(_assign)`, `partial_eq`, `sub(_assign)`, \ - `iter`", - )), - } +#[cfg(test)] +mod tests { + #[test] + fn parse_newtype_derives() { + let input = quote::quote! { fn not_a_struct() {} }; + let result = super::parse_newtype_derives(input); + assert!(result.is_err()); } } diff --git a/newtype-tools-derive/src/parse/tests.rs b/newtype-tools-derive/src/parse/tests.rs deleted file mode 100644 index 0c40926..0000000 --- a/newtype-tools-derive/src/parse/tests.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[test] -fn attr_type_display_roundtrip() { - use super::AttrType; - assert_eq!(format!("{}", AttrType::From), "from"); - assert_eq!(format!("{}", AttrType::TryFrom), "try_from"); - assert_eq!(format!("{}", AttrType::Into), "into"); - assert_eq!(format!("{}", AttrType::TryInto), "try_into"); - assert_eq!(format!("{}", AttrType::Add), "add"); - assert_eq!(format!("{}", AttrType::AddAssign), "add_assign"); - assert_eq!(format!("{}", AttrType::BitAnd), "bitand"); - assert_eq!(format!("{}", AttrType::BitAndAssign), "bitand_assign"); - assert_eq!(format!("{}", AttrType::BitOr), "bitor"); - assert_eq!(format!("{}", AttrType::BitOrAssign), "bitor_assign"); - assert_eq!(format!("{}", AttrType::BitXor), "bitxor"); - assert_eq!(format!("{}", AttrType::BitXorAssign), "bitxor_assign"); - assert_eq!(format!("{}", AttrType::Div), "div"); - assert_eq!(format!("{}", AttrType::DivAssign), "div_assign"); - assert_eq!(format!("{}", AttrType::Mul), "mul"); - assert_eq!(format!("{}", AttrType::MulAssign), "mul_assign"); - assert_eq!(format!("{}", AttrType::Rem), "rem"); - assert_eq!(format!("{}", AttrType::RemAssign), "rem_assign"); - assert_eq!(format!("{}", AttrType::Shl), "shl"); - assert_eq!(format!("{}", AttrType::ShlAssign), "shl_assign"); - assert_eq!(format!("{}", AttrType::Shr), "shr"); - assert_eq!(format!("{}", AttrType::ShrAssign), "shr_assign"); - assert_eq!(format!("{}", AttrType::PartialEq), "partial_eq"); - assert_eq!(format!("{}", AttrType::Sub), "sub"); - assert_eq!(format!("{}", AttrType::SubAssign), "sub_assign"); -} diff --git a/newtype-tools/Cargo.toml b/newtype-tools/Cargo.toml index 696e7e2..0b3dab6 100644 --- a/newtype-tools/Cargo.toml +++ b/newtype-tools/Cargo.toml @@ -22,6 +22,7 @@ newtype-tools-derive = { workspace = true, optional = true } newtype-tools-derive = { workspace = true } rstest = { workspace = true } trybuild = { workspace = true } +rustversion = { workspace = true } [features] default = ["derive"] diff --git a/newtype-tools/src/iter.rs b/newtype-tools/src/iter.rs index 32ce02d..ef9d661 100644 --- a/newtype-tools/src/iter.rs +++ b/newtype-tools/src/iter.rs @@ -1,4 +1,4 @@ -/// Blanket `Step` implementation for all `Newtype`s. +/// Blanket `Step` implementation for all `newtypes`. impl Step for T where T: crate::Newtype + Clone + PartialOrd + MinMax, @@ -25,7 +25,7 @@ pub trait Iter where N: crate::Newtype, N::Inner: crate::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { /// Converts a range bounds into a `newtype` `Iterator` instance. fn iter(&self) -> crate::Iterator; @@ -36,7 +36,7 @@ impl Iter for R where N: crate::Newtype, N::Inner: crate::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { fn iter(&self) -> crate::Iterator { crate::Iterator::from(self) @@ -48,7 +48,7 @@ pub trait IntoIter where N: crate::Newtype, N::Inner: crate::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { /// Converts a range bounds into a `newtype` `Iterator` instance. fn into_iter(self) -> crate::Iterator; @@ -59,14 +59,14 @@ impl IntoIter for R where N: crate::Newtype, N::Inner: crate::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { fn into_iter(self) -> crate::Iterator { crate::Iterator::from(&self) } } -/// Blanket `Iterator` implementation for all `Newtype`s. +/// Blanket `Iterator` implementation for all `newtypes`. #[derive(Clone)] pub struct Iterator where @@ -88,7 +88,7 @@ where } /// Creates a new `Iterator` instance based on the given range. - /// The internal `Newtype` representation must implement `Step` trait. + /// The internal `newtype` representation must implement `Step` trait. /// /// # Example /// ``` @@ -105,9 +105,9 @@ where /// # } /// ``` #[inline] - pub fn from>(range: &R) -> Self { + pub fn from>(range: &R) -> Self { use crate::iter::Step; - use std::ops::Bound; + use core::ops::Bound; let start = match range.start_bound() { Bound::Included(s) => s.as_ref().clone(), Bound::Excluded(s) => Step::forward(s.as_ref().clone(), 1), @@ -122,7 +122,7 @@ where } } -impl std::iter::Iterator for Iterator +impl core::iter::Iterator for Iterator where T: crate::Newtype, T::Inner: Step, @@ -191,7 +191,7 @@ where { } -impl std::iter::FusedIterator for Iterator +impl core::iter::FusedIterator for Iterator where T: crate::Newtype, T::Inner: Step, @@ -200,7 +200,7 @@ where //////////////////////////////////////////////////////////////////////// // The following complexity should go away once the `Step` trait is stable. -// Mostly it's a copy-paste from the unstable `std::iter::Step` trait. +// Mostly it's a copy-paste from the unstable `core::iter::Step` trait. /// A helper trait to support inner values ranges. pub trait MinMax { @@ -223,7 +223,7 @@ impl_min_max!( u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize ); -/// A copy of the unstable `std::iter::Step` trait required for the `iter` +/// A copy of the unstable `core::iter::Step` trait required for the `iter` /// derive. pub trait Step: Clone + PartialOrd + MinMax + Sized { /// Returns the bounds on the number of *successor* steps required to get from `start` to `end` diff --git a/newtype-tools/src/lib.rs b/newtype-tools/src/lib.rs index c1e9f59..c8303e3 100644 --- a/newtype-tools/src/lib.rs +++ b/newtype-tools/src/lib.rs @@ -7,8 +7,10 @@ pub use iter::Iter; pub use iter::Iterator; #[cfg(feature = "derive")] pub use newtype_tools_derive::Newtype; +#[cfg(feature = "derive")] +pub use newtype_tools_derive::newtype; -/// `Newtype` trait defines the internal representation of the `newtype`. +/// `Newtype` trait defines the internal representation of a `newtype`. /// /// This trait is automatically derived for all types annotated with `#[derive(Newtype)]` /// along with the `From` and `AsRef` traits to convert diff --git a/newtype-tools/tests/from.rs b/newtype-tools/tests/from.rs index 00523e2..aaf1202 100644 --- a/newtype-tools/tests/from.rs +++ b/newtype-tools/tests/from.rs @@ -39,7 +39,7 @@ fn generic_from() { #[test] fn try_from() { - use std::num::TryFromIntError; + use core::num::TryFromIntError; #[derive(newtype_tools::Newtype)] #[newtype(try_from( u64, @@ -55,7 +55,7 @@ fn try_from() { #[test] fn generic_try_from() { - use std::num::TryFromIntError; + use core::num::TryFromIntError; #[derive(newtype_tools::Newtype)] #[newtype(try_from( Oranges, diff --git a/newtype-tools/tests/into.rs b/newtype-tools/tests/into.rs index 3eede4c..a9c7f8c 100644 --- a/newtype-tools/tests/into.rs +++ b/newtype-tools/tests/into.rs @@ -35,7 +35,7 @@ fn generic_into() { #[test] fn try_into() { - use std::num::TryFromIntError; + use core::num::TryFromIntError; #[derive(newtype_tools::Newtype)] #[newtype(try_into( Oranges, @@ -55,7 +55,7 @@ fn try_into() { #[test] fn generic_try_into() { - use std::num::TryFromIntError; + use core::num::TryFromIntError; #[derive(newtype_tools::Newtype)] #[newtype(try_into( Oranges, diff --git a/newtype-tools/tests/iter.rs b/newtype-tools/tests/iter.rs index d5a1573..5eee14c 100644 --- a/newtype-tools/tests/iter.rs +++ b/newtype-tools/tests/iter.rs @@ -8,7 +8,7 @@ impl newtype_tools::iter::MinMax for Gold { } impl TryFrom for usize { - type Error = std::num::TryFromIntError; + type Error = core::num::TryFromIntError; fn try_from(value: Gold) -> Result { usize::try_from(value.0) @@ -51,7 +51,7 @@ impl TryFrom for usize { #[case::gold_a_max2_b1_n1(Gold(i8::MAX - 2), Gold(1), 1)] #[case::gold_a1_b_max2_n1(Gold(1), Gold(i8::MAX - 2), 0)] #[case::gold_a1_b1_n_max2(Gold(1), Gold(1), i8::MAX as usize - 2)] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn steps_between_invariants(#[case] a: T, #[case] b: T, #[case] n: usize) where T: newtype_tools::iter::Step + Copy, @@ -83,7 +83,7 @@ where /// Cover `Step::steps_between` subtraction overflow. #[rstest::rstest] #[case::i128_a_min_b_max(i128::MIN, i128::MAX)] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn steps_between_overflow(#[case] a: T, #[case] b: T) where T: newtype_tools::iter::Step, @@ -119,7 +119,7 @@ where #[case::i64_a_max_n_max_m_max(i64::MAX, usize::MAX, usize::MAX)] #[case::i128_a_max_n1_m1(i128::MAX, 1, 1)] #[case::u128_a_max_n1_m1(u128::MAX, 1, 1)] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn forward_checked_invariants( #[case] a: T, #[case] n: usize, @@ -161,7 +161,7 @@ fn forward_checked_invariants( #[case] a: T, #[case] n: usize, @@ -233,7 +233,7 @@ fn step_integer_impls_forward_backward_checked() { #[case::i64_a_max_n_max_m_max(i64::MAX, usize::MAX, usize::MAX)] #[case::i128_a_max_n1_m1(i128::MAX, 1, 1)] #[case::u128_a_max_n1_m1(u128::MAX, 1, 1)] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn backward_checked_invariants( #[case] a: T, #[case] n: usize, @@ -259,9 +259,9 @@ fn backward_checked_invariants>(range: R) { + fn test>(range: R) { let mut iter = newtype_tools::Iterator::from(&range); assert_eq!(iter.len(), 3); assert_eq!(iter.size_hint(), (3, Some(3))); @@ -286,9 +286,9 @@ fn iter() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn infinite_iter() { - fn test>(range: R) { + fn test>(range: R) { let mut iter = newtype_tools::Iterator::from(&range); assert_eq!(iter.len(), usize::MAX); assert_eq!(iter.size_hint(), (usize::MAX, Some(usize::MAX))); @@ -304,15 +304,15 @@ fn infinite_iter() { test(Apples(1)..); test(( - std::ops::Bound::Excluded(Apples(0)), - std::ops::Bound::Unbounded, + core::ops::Bound::Excluded(Apples(0)), + core::ops::Bound::Unbounded, )); } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn empty_iter() { - fn test>(range: R) { + fn test>(range: R) { let mut iter = newtype_tools::Iterator::from(&range); assert_eq!(iter.len(), 0); assert_eq!(iter.size_hint(), (0, Some(0))); @@ -329,7 +329,7 @@ fn empty_iter() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn custom_inner_type() { #[derive(Clone, Debug, Default, PartialEq, PartialOrd)] struct CustomInner(u64); @@ -374,11 +374,11 @@ fn custom_inner_type() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn generic_iter() { - fn test>>(range: R) + fn test>>(range: R) where - T: std::fmt::Debug + Into + From + newtype_tools::iter::Step, + T: core::fmt::Debug + Into + From + newtype_tools::iter::Step, { let mut iter = newtype_tools::Iterator::from(&range); assert_eq!(iter.len(), 3); @@ -406,11 +406,11 @@ fn generic_iter() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn generic_infinite_iter() { - fn test>>(range: R) + fn test>>(range: R) where - T: std::fmt::Debug + Into + From + newtype_tools::iter::Step, + T: core::fmt::Debug + Into + From + newtype_tools::iter::Step, { let mut iter = newtype_tools::Iterator::from(&range); assert_eq!(iter.len(), usize::MAX); diff --git a/newtype-tools/tests/iter_trait.rs b/newtype-tools/tests/iter_trait.rs index aea0205..f16948e 100644 --- a/newtype-tools/tests/iter_trait.rs +++ b/newtype-tools/tests/iter_trait.rs @@ -1,7 +1,7 @@ #![cfg(feature = "derive")] #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn iter_trait() { fn test(mut iter: newtype_tools::Iterator) { assert_eq!(iter.len(), 3); @@ -33,7 +33,7 @@ fn iter_trait() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn infinite_iter_trait() { fn test(mut iter: newtype_tools::Iterator) { assert_eq!(iter.len(), usize::MAX); @@ -54,22 +54,22 @@ fn infinite_iter_trait() { test((Apples(1)..).into_iter()); test( ( - std::ops::Bound::Excluded(Apples(0)), - std::ops::Bound::Unbounded, + core::ops::Bound::Excluded(Apples(0)), + core::ops::Bound::Unbounded, ) .iter(), ); test( ( - std::ops::Bound::Excluded(Apples(0)), - std::ops::Bound::Unbounded, + core::ops::Bound::Excluded(Apples(0)), + core::ops::Bound::Unbounded, ) .into_iter(), ); } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn empty_iter_trait() { fn test(mut iter: newtype_tools::Iterator) { assert_eq!(iter.len(), 0); @@ -90,7 +90,7 @@ fn empty_iter_trait() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn custom_inner_type_trait() { #[derive(Clone, Debug, Default, PartialEq, PartialOrd)] struct CustomInner(u64); @@ -121,7 +121,7 @@ fn custom_inner_type_trait() { where N: newtype_tools::Newtype, N::Inner: newtype_tools::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { fn iter(&self) -> newtype_tools::Iterator; } @@ -130,7 +130,7 @@ fn custom_inner_type_trait() { where N: newtype_tools::Newtype, N::Inner: newtype_tools::iter::Step, - R: std::ops::RangeBounds, + R: core::ops::RangeBounds, { fn iter(&self) -> newtype_tools::Iterator { newtype_tools::Iterator::from(self) @@ -155,11 +155,11 @@ fn custom_inner_type_trait() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn generic_iter_trait() { fn test(mut iter: newtype_tools::Iterator) where - N: newtype_tools::Newtype + Clone + From + PartialEq + PartialOrd + std::fmt::Debug, + N: newtype_tools::Newtype + Clone + From + PartialEq + PartialOrd + core::fmt::Debug, N::Inner: newtype_tools::iter::Step, { assert_eq!(iter.len(), 3); @@ -193,11 +193,11 @@ fn generic_iter_trait() { } #[rstest::rstest] -#[timeout(std::time::Duration::from_secs(1))] +#[timeout(core::time::Duration::from_secs(1))] fn generic_infinite_iter_trait() { fn test(mut iter: newtype_tools::Iterator) where - N: newtype_tools::Newtype + Clone + From + PartialEq + PartialOrd + std::fmt::Debug, + N: newtype_tools::Newtype + Clone + From + PartialEq + PartialOrd + core::fmt::Debug, N::Inner: newtype_tools::iter::Step, { assert_eq!(iter.len(), usize::MAX); diff --git a/newtype-tools/tests/newtype_amount.rs b/newtype-tools/tests/newtype_amount.rs new file mode 100644 index 0000000..60ea5c2 --- /dev/null +++ b/newtype-tools/tests/newtype_amount.rs @@ -0,0 +1,252 @@ +#![cfg(feature = "derive")] + +// Manual integer newtype trait definition. +#[derive(Clone, Copy, Debug, Eq, Hash, newtype_tools::Newtype, Ord, PartialEq, PartialOrd)] +#[newtype( + add(ManInt, output = "ManInt", with = "|a1, a2| ManInt(a1.0 + a2.0)"), + add_assign(ManInt, with = "|this, other| this.0 += other.0"), + sub(ManInt, output = "ManInt", with = "|a1, a2| ManInt(a1.0 - a2.0)"), + sub_assign(ManInt, with = "|this, other| this.0 -= other.0"), + mul(i64, output = "ManInt", with = "|a, r| ManInt(a.0 * r)"), + mul_assign(i64, with = "|this, r| this.0 *= r"), + div(ManInt, output = "i64", with = "|a1, a2| a1.0 / a2.0") +)] +#[repr(transparent)] +struct ManInt(i64); + +// Attribute integer newtype trait definition. +#[newtype_tools::newtype(Amount)] +/// Doc comment. +struct AttrInt(i64); + +#[rstest::rstest] +#[case::i64(2_i64, 3_i64, 2_i64)] +#[case::man_int(ManInt(2_i64), ManInt(3_i64), 2_i64)] +#[case::attr_int(AttrInt(2_i64), AttrInt(3_i64), 2_i64)] +#[timeout(core::time::Duration::from_secs(1))] +fn int_newtype_amount(#[case] a: T, #[case] b: T, #[case] repr_a: R) +where + T: Clone + + Copy + + PartialEq + + PartialOrd + + Ord + + core::hash::Hash + + core::fmt::Debug + + From + + core::ops::Add + + core::ops::AddAssign + + core::ops::Sub + + core::ops::SubAssign + + core::ops::Mul + + core::ops::MulAssign + + core::ops::Div, + R: Copy + PartialOrd, +{ + // PartialEq + assert!(a != b); + // PartialOrd + assert!(a < b); + // Ord + assert_eq!(a.min(b), a); + // Hash + let mut hasher = std::hash::DefaultHasher::new(); + let prev_a = a; + a.hash(&mut hasher); + assert_eq!(prev_a, a); + // From + let res = T::from(repr_a); + assert!(res == a); + // Add + let res = a + b; + assert!(res > a); + // AddAssign + let mut res = a; + res += b; + assert!(res > a); + // Sub + let res = a - b; + assert!(res < a); + // SubAssign + let mut res = a; + res -= b; + assert!(res < a); + // Mul + let res = a * repr_a; + assert!(res > a); + // MulAssign + let mut res = a; + res *= repr_a; + assert!(res > a); + // Div + let res = a / b; + assert!(res < repr_a); +} + +// Manual floating point newtype trait definition. +#[derive(Clone, Copy, Debug, newtype_tools::Newtype, PartialEq, PartialOrd)] +#[newtype( + add(ManFloat, output = "ManFloat", with = "|a1, a2| ManFloat(a1.0 + a2.0)"), + add_assign(ManFloat, with = "|this, other| this.0 += other.0"), + sub(ManFloat, output = "ManFloat", with = "|a1, a2| ManFloat(a1.0 - a2.0)"), + sub_assign(ManFloat, with = "|this, other| this.0 -= other.0"), + mul(f64, output = "ManFloat", with = "|a, r| ManFloat(a.0 * r)"), + mul_assign(f64, with = "|this, r| this.0 *= r"), + div(ManFloat, output = "f64", with = "|a1, a2| a1.0 / a2.0") +)] +#[repr(transparent)] +struct ManFloat(f64); + +// Attribute floating point newtype trait definition. +#[newtype_tools::newtype(Amount)] +/// Doc comment. +struct AttrFloat(f64); + +#[rstest::rstest] +#[case::f64(2.0_f64, 3.0_f64, 2.0_f64)] +#[case::man_float(ManFloat(2.0_f64), ManFloat(3.0_f64), 2.0_f64)] +#[case::attr_float(AttrFloat(2.0_f64), AttrFloat(3.0_f64), 2.0_f64)] +#[timeout(core::time::Duration::from_secs(1))] +fn float_newtype_amount(#[case] a: T, #[case] b: T, #[case] repr_a: R) +where + T: Clone + + Copy + + PartialEq + + PartialOrd + + core::fmt::Debug + + From + + core::ops::Add + + core::ops::AddAssign + + core::ops::Sub + + core::ops::SubAssign + + core::ops::Mul + + core::ops::MulAssign + + core::ops::Div, + R: Copy + PartialOrd, +{ + // PartialEq + assert!(a != b); + // PartialOrd + assert!(a < b); + // Ord: is not implemented for f64 + // Hash: is not implemented for f64 + // From + let res = T::from(repr_a); + assert!(res == a); + // Add + let res = a + b; + assert!(res > a); + // AddAssign + let mut res = a; + res += b; + assert!(res > a); + // Sub + let res = a - b; + assert!(res < a); + // SubAssign + let mut res = a; + res -= b; + assert!(res < a); + // Mul + let res = a * repr_a; + assert!(res > a); + // MulAssign + let mut res = a; + res *= repr_a; + assert!(res > a); + // Div + let res = a / b; + assert!(res < repr_a); +} + +// Manual generic newtype trait definition. +#[derive(Clone, Copy, Debug, newtype_tools::Newtype, PartialEq, PartialOrd)] +#[newtype( + add(ManGeneric, output = "ManGeneric", with = "|a1, a2| ManGeneric(a1.0 + a2.0)"), + add_assign(ManGeneric, with = "|this, other| this.0 += other.0"), + sub(ManGeneric, output = "ManGeneric", with = "|a1, a2| ManGeneric(a1.0 - a2.0)"), + sub_assign(ManGeneric, with = "|this, other| this.0 -= other.0"), + mul(R, output = "ManGeneric", with = "|a, r| ManGeneric(a.0 * *r)"), + mul_assign(R, with = "|this, r| this.0 *= *r"), + div(ManGeneric, output = "R", with = "|a1, a2| a1.0 / a2.0") +)] +#[repr(transparent)] +struct ManGeneric(T) +where + T: Clone + + Copy + + PartialEq + + PartialOrd + + core::fmt::Debug + + From + + core::ops::Add + + core::ops::AddAssign + + core::ops::Sub + + core::ops::SubAssign + + core::ops::Mul + + core::ops::MulAssign + + core::ops::Div, + R: Copy + PartialOrd; + +// Attribute generic newtype trait definition. +#[newtype_tools::newtype(Amount)] +/// Doc comment. +struct AttrGeneric(f64); + +#[rstest::rstest] +#[case::generic(2.0_f64, 3.0_f64, 2.0_f64)] +#[case::man_generic(ManGeneric(2.0_f64), ManGeneric(3.0_f64), 2.0_f64)] +#[case::attr_generic(AttrGeneric(2.0_f64), AttrGeneric(3.0_f64), 2.0_f64)] +#[timeout(core::time::Duration::from_secs(1))] +fn generic_float_newtype_amount(#[case] a: T, #[case] b: T, #[case] repr_a: R) +where + T: Clone + + Copy + + PartialEq + + PartialOrd + + core::fmt::Debug + + From + + core::ops::Add + + core::ops::AddAssign + + core::ops::Sub + + core::ops::SubAssign + + core::ops::Mul + + for<'a> core::ops::Mul<&'a R, Output = T> + + core::ops::MulAssign + + core::ops::Div, + R: Copy + PartialOrd + core::ops::Mul + core::ops::MulAssign, +{ + // PartialEq + assert!(a != b); + // PartialOrd + assert!(a < b); + // Ord: is not implemented for f64 + // Hash: is not implemented for f64 + // From + let res = T::from(repr_a); + assert!(res == a); + // Add + let res = a + b; + assert!(res > a); + // AddAssign + let mut res = a; + res += b; + assert!(res > a); + // Sub + let res = a - b; + assert!(res < a); + // SubAssign + let mut res = a; + res -= b; + assert!(res < a); + // Mul + let res = a * repr_a; + assert!(res > a); + // MulAssign + let mut res = a; + res *= repr_a; + assert!(res > a); + // Div + let res = a / b; + assert!(res < repr_a); +} diff --git a/newtype-tools/tests/newtype.rs b/newtype-tools/tests/newtype_derive.rs similarity index 94% rename from newtype-tools/tests/newtype.rs rename to newtype-tools/tests/newtype_derive.rs index 1867aaf..f1dede1 100644 --- a/newtype-tools/tests/newtype.rs +++ b/newtype-tools/tests/newtype_derive.rs @@ -1,7 +1,7 @@ #![cfg(feature = "derive")] #[test] -fn newtype() { +fn newtype_derive() { #[derive(newtype_tools::Newtype)] #[repr(transparent)] struct Apples(u64); @@ -18,7 +18,7 @@ fn newtype() { } #[test] -fn generic_newtype() { +fn generic_newtype_derive() { #[derive(newtype_tools::Newtype)] #[repr(transparent)] struct Apples(T) diff --git a/newtype-tools/tests/ops/add.rs b/newtype-tools/tests/ops/add.rs index a14dd3e..c5b85a9 100644 --- a/newtype-tools/tests/ops/add.rs +++ b/newtype-tools/tests/ops/add.rs @@ -37,7 +37,7 @@ fn generic_add() { ))] struct Apples(T) where - T: std::ops::Add + Clone; + T: core::ops::Add + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_add_assign() { #[newtype(add_assign(Oranges, with = "|apples, oranges| apples.0 += oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::AddAssign + Clone; + T: core::ops::AddAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/bitand.rs b/newtype-tools/tests/ops/bitand.rs index 44a08d6..37b5015 100644 --- a/newtype-tools/tests/ops/bitand.rs +++ b/newtype-tools/tests/ops/bitand.rs @@ -37,7 +37,7 @@ fn generic_bitand() { ))] struct Apples(T) where - T: std::ops::BitAnd + Clone; + T: core::ops::BitAnd + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_bitand_assign() { #[newtype(bitand_assign(Oranges, with = "|apples, oranges| apples.0 &= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::BitAndAssign + Clone; + T: core::ops::BitAndAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/bitor.rs b/newtype-tools/tests/ops/bitor.rs index 6fe80a6..24de8fa 100644 --- a/newtype-tools/tests/ops/bitor.rs +++ b/newtype-tools/tests/ops/bitor.rs @@ -37,7 +37,7 @@ fn generic_bitor() { ))] struct Apples(T) where - T: std::ops::BitOr + Clone; + T: core::ops::BitOr + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_bitor_assign() { #[newtype(bitor_assign(Oranges, with = "|apples, oranges| apples.0 |= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::BitOrAssign + Clone; + T: core::ops::BitOrAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/bitxor.rs b/newtype-tools/tests/ops/bitxor.rs index 8681355..1a66905 100644 --- a/newtype-tools/tests/ops/bitxor.rs +++ b/newtype-tools/tests/ops/bitxor.rs @@ -37,7 +37,7 @@ fn generic_bitxor() { ))] struct Apples(T) where - T: std::ops::BitXor + Clone; + T: core::ops::BitXor + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_bitxor_assign() { #[newtype(bitxor_assign(Oranges, with = "|apples, oranges| apples.0 ^= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::BitXorAssign + Clone; + T: core::ops::BitXorAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/div.rs b/newtype-tools/tests/ops/div.rs index 04f8db1..50071db 100644 --- a/newtype-tools/tests/ops/div.rs +++ b/newtype-tools/tests/ops/div.rs @@ -37,7 +37,7 @@ fn generic_div() { ))] struct Apples(T) where - T: std::ops::Div + Clone; + T: core::ops::Div + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_div_assign() { #[newtype(div_assign(Oranges, with = "|apples, oranges| apples.0 /= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::DivAssign + Clone; + T: core::ops::DivAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/mul.rs b/newtype-tools/tests/ops/mul.rs index cd79a16..4e4b0f8 100644 --- a/newtype-tools/tests/ops/mul.rs +++ b/newtype-tools/tests/ops/mul.rs @@ -37,7 +37,7 @@ fn generic_mul() { ))] struct Apples(T) where - T: std::ops::Mul + Clone; + T: core::ops::Mul + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_mul_assign() { #[newtype(mul_assign(Oranges, with = "|apples, oranges| apples.0 *= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::MulAssign + Clone; + T: core::ops::MulAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/rem.rs b/newtype-tools/tests/ops/rem.rs index 332bc3d..ce9efba 100644 --- a/newtype-tools/tests/ops/rem.rs +++ b/newtype-tools/tests/ops/rem.rs @@ -37,7 +37,7 @@ fn generic_rem() { ))] struct Apples(T) where - T: std::ops::Rem + Clone; + T: core::ops::Rem + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_rem_assign() { #[newtype(rem_assign(Oranges, with = "|apples, oranges| apples.0 %= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::RemAssign + Clone; + T: core::ops::RemAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/shl.rs b/newtype-tools/tests/ops/shl.rs index 6dd85bb..029fc76 100644 --- a/newtype-tools/tests/ops/shl.rs +++ b/newtype-tools/tests/ops/shl.rs @@ -37,7 +37,7 @@ fn generic_shl() { ))] struct Apples(T) where - T: std::ops::Shl + Clone; + T: core::ops::Shl + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_shl_assign() { #[newtype(shl_assign(Oranges, with = "|apples, oranges| apples.0 <<= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::ShlAssign + Clone; + T: core::ops::ShlAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/shr.rs b/newtype-tools/tests/ops/shr.rs index 1104bb9..6261659 100644 --- a/newtype-tools/tests/ops/shr.rs +++ b/newtype-tools/tests/ops/shr.rs @@ -37,7 +37,7 @@ fn generic_shr() { ))] struct Apples(T) where - T: std::ops::Shr + Clone; + T: core::ops::Shr + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -76,7 +76,7 @@ fn generic_shr_assign() { #[newtype(shr_assign(Oranges, with = "|apples, oranges| apples.0 >>= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::ShrAssign + Clone; + T: core::ops::ShrAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/ops/sub.rs b/newtype-tools/tests/ops/sub.rs index 27a83cb..6e90126 100644 --- a/newtype-tools/tests/ops/sub.rs +++ b/newtype-tools/tests/ops/sub.rs @@ -37,7 +37,7 @@ fn generic_sub() { ))] struct Apples(T) where - T: std::ops::Sub + Clone; + T: core::ops::Sub + Clone; #[derive(Debug)] struct Oranges(u32); #[derive(Debug, PartialEq)] @@ -75,7 +75,7 @@ fn generic_sub_assign() { #[newtype(sub_assign(Oranges, with = "|apples, oranges| apples.0 -= oranges.0 as u64 * 2"))] struct Apples(T) where - T: std::ops::SubAssign + Clone; + T: core::ops::SubAssign + Clone; #[derive(Debug, PartialEq)] struct Oranges(u32); diff --git a/newtype-tools/tests/trybuild.rs b/newtype-tools/tests/trybuild.rs index 404a76e..e6328db 100644 --- a/newtype-tools/tests/trybuild.rs +++ b/newtype-tools/tests/trybuild.rs @@ -1,9 +1,18 @@ #![cfg(feature = "derive")] +// There is a small discrepancy between the stable and nightly in span reporting. +// ```ignore +// error: expected `#[newtype(NewtypeKind)]` +// --> tests/trybuild/newtype_attribute.rs:34:30 +// | +// 34 | #[newtype_tools::newtype(Amount, Amount)] +// | ^^^^^^ +// ``` +#[rustversion::stable] #[test] fn newtype_trybuild() { let t = trybuild::TestCases::new(); + t.compile_fail("tests/trybuild/*.rs"); t.compile_fail("tests/trybuild/conversions/*.rs"); - t.compile_fail("tests/trybuild/newtype/*.rs"); t.compile_fail("tests/trybuild/ops/*.rs"); } diff --git a/newtype-tools/tests/trybuild/newtype/enum.rs b/newtype-tools/tests/trybuild/newtype/enum.rs deleted file mode 100644 index 2d79557..0000000 --- a/newtype-tools/tests/trybuild/newtype/enum.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[derive(newtype_tools::Newtype)] -enum Enum {} - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/enum.stderr b/newtype-tools/tests/trybuild/newtype/enum.stderr deleted file mode 100644 index 866a5ec..0000000 --- a/newtype-tools/tests/trybuild/newtype/enum.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `struct Newtype(inner_type)` - --> tests/trybuild/newtype/enum.rs:2:1 - | -2 | enum Enum {} - | ^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/named-struct.rs b/newtype-tools/tests/trybuild/newtype/named-struct.rs deleted file mode 100644 index 8c8aefa..0000000 --- a/newtype-tools/tests/trybuild/newtype/named-struct.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(newtype_tools::Newtype)] -struct Oranges { - inner: u32, -} - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/named-struct.stderr b/newtype-tools/tests/trybuild/newtype/named-struct.stderr deleted file mode 100644 index d8494bb..0000000 --- a/newtype-tools/tests/trybuild/newtype/named-struct.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected `struct Newtype(inner_type)` - --> tests/trybuild/newtype/named-struct.rs:2:16 - | -2 | struct Oranges { - | ________________^ -3 | | inner: u32, -4 | | } - | |_^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-invalid.rs b/newtype-tools/tests/trybuild/newtype/struct-list-invalid.rs deleted file mode 100644 index 62d8765..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-invalid.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype(;)] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-invalid.stderr b/newtype-tools/tests/trybuild/newtype/struct-list-invalid.stderr deleted file mode 100644 index dd0f1c9..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-invalid.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected identifier - --> tests/trybuild/newtype/struct-list-invalid.rs:2:11 - | -2 | #[newtype(;)] - | ^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-name-value.rs b/newtype-tools/tests/trybuild/newtype/struct-list-name-value.rs deleted file mode 100644 index 74492dd..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-name-value.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype(from = Oranges)] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-name-value.stderr b/newtype-tools/tests/trybuild/newtype/struct-list-name-value.stderr deleted file mode 100644 index 5f168ba..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-name-value.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `#[newtype(from(...))]` - --> tests/trybuild/newtype/struct-list-name-value.rs:2:11 - | -2 | #[newtype(from = Oranges)] - | ^^^^^^^^^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.rs b/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.rs deleted file mode 100644 index e809622..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype(invalid_path)] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.stderr b/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.stderr deleted file mode 100644 index 378bf4e..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-path-invalid.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, `bitor(_assign)`, `bitxor(_assign)`, `mul(_assign)`, `partial_eq`, `sub(_assign)`, `iter` - --> tests/trybuild/newtype/struct-list-path-invalid.rs:2:11 - | -2 | #[newtype(invalid_path)] - | ^^^^^^^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.rs b/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.rs deleted file mode 100644 index 75aaeae..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype(from)] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.stderr b/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.stderr deleted file mode 100644 index d311c9e..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-list-path-valid.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `#[newtype(from(...))]` - --> tests/trybuild/newtype/struct-list-path-valid.rs:2:11 - | -2 | #[newtype(from)] - | ^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-name-value.rs b/newtype-tools/tests/trybuild/newtype/struct-name-value.rs deleted file mode 100644 index 09d40de..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-name-value.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype = value] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-name-value.stderr b/newtype-tools/tests/trybuild/newtype/struct-name-value.stderr deleted file mode 100644 index 621ab62..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-name-value.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: expected `#[newtype(attr1, attr2)]` - --> tests/trybuild/newtype/struct-name-value.rs:2:3 - | -2 | #[newtype = value] - | ^^^^^^^^^^^^^^^ - -error: attribute value must be a literal - --> tests/trybuild/newtype/struct-name-value.rs:2:13 - | -2 | #[newtype = value] - | ^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/struct-path.rs b/newtype-tools/tests/trybuild/newtype/struct-path.rs deleted file mode 100644 index 4fa58cc..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-path.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(newtype_tools::Newtype)] -#[newtype] -struct Oranges(u32); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/struct-path.stderr b/newtype-tools/tests/trybuild/newtype/struct-path.stderr deleted file mode 100644 index f130f77..0000000 --- a/newtype-tools/tests/trybuild/newtype/struct-path.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `#[newtype(attr1, attr2)]` - --> tests/trybuild/newtype/struct-path.rs:2:3 - | -2 | #[newtype] - | ^^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/tuple-struct.rs b/newtype-tools/tests/trybuild/newtype/tuple-struct.rs deleted file mode 100644 index af04498..0000000 --- a/newtype-tools/tests/trybuild/newtype/tuple-struct.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[derive(newtype_tools::Newtype)] -struct N(u64, u64); - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/tuple-struct.stderr b/newtype-tools/tests/trybuild/newtype/tuple-struct.stderr deleted file mode 100644 index 3ef216f..0000000 --- a/newtype-tools/tests/trybuild/newtype/tuple-struct.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `struct Newtype(inner_type)` - --> tests/trybuild/newtype/tuple-struct.rs:2:9 - | -2 | struct N(u64, u64); - | ^^^^^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/union.rs b/newtype-tools/tests/trybuild/newtype/union.rs deleted file mode 100644 index 17905ed..0000000 --- a/newtype-tools/tests/trybuild/newtype/union.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(newtype_tools::Newtype)] -union U { - u: u32, -} - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/union.stderr b/newtype-tools/tests/trybuild/newtype/union.stderr deleted file mode 100644 index bb5e02e..0000000 --- a/newtype-tools/tests/trybuild/newtype/union.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `struct Newtype(inner_type)` - --> tests/trybuild/newtype/union.rs:2:1 - | -2 | union U { - | ^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype/unit-struct.rs b/newtype-tools/tests/trybuild/newtype/unit-struct.rs deleted file mode 100644 index 0ef2500..0000000 --- a/newtype-tools/tests/trybuild/newtype/unit-struct.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[derive(newtype_tools::Newtype)] -struct Oranges; - -fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype/unit-struct.stderr b/newtype-tools/tests/trybuild/newtype/unit-struct.stderr deleted file mode 100644 index 0a5d14c..0000000 --- a/newtype-tools/tests/trybuild/newtype/unit-struct.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `struct Newtype(inner_type)` - --> tests/trybuild/newtype/unit-struct.rs:2:1 - | -2 | struct Oranges; - | ^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype_attribute.rs b/newtype-tools/tests/trybuild/newtype_attribute.rs new file mode 100644 index 0000000..5de9d7b --- /dev/null +++ b/newtype-tools/tests/trybuild/newtype_attribute.rs @@ -0,0 +1,38 @@ +/// `#[newtype(Amount)]` + +mod ok { + #[newtype_tools::newtype(Amount)] + struct Oranges(u32); +} + +mod missing_type { + #[newtype_tools::newtype] + struct Oranges(u32); +} + +mod missing_kind { + #[newtype_tools::newtype()] + struct Oranges(u32); +} + +mod invalid_kind { + #[newtype_tools::newtype(InvalidKind)] + struct Oranges(u32); +} + +mod invalid_newtype { + #[newtype_tools::newtype(Amount)] + struct Oranges(); +} + +mod invalid_derive { + #[newtype_tools::newtype(Amount)] + fn not_a_struct() {} +} + +mod extra_comma_kind { + #[newtype_tools::newtype(Amount, Amount)] + struct Oranges(u32); +} + +fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype_attribute.stderr b/newtype-tools/tests/trybuild/newtype_attribute.stderr new file mode 100644 index 0000000..a8e4f11 --- /dev/null +++ b/newtype-tools/tests/trybuild/newtype_attribute.stderr @@ -0,0 +1,39 @@ +error: expected `#[newtype(NewtypeKind)]` + --> tests/trybuild/newtype_attribute.rs:9:5 + | +9 | #[newtype_tools::newtype] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `newtype_tools::newtype` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected `#[newtype(NewtypeKind)]` + --> tests/trybuild/newtype_attribute.rs:14:5 + | +14 | #[newtype_tools::newtype()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `newtype_tools::newtype` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected 'Amount' + --> tests/trybuild/newtype_attribute.rs:19:30 + | +19 | #[newtype_tools::newtype(InvalidKind)] + | ^^^^^^^^^^^ + +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_attribute.rs:25:19 + | +25 | struct Oranges(); + | ^^ + +error: expected one of: `struct`, `enum`, `union` + --> tests/trybuild/newtype_attribute.rs:30:5 + | +30 | fn not_a_struct() {} + | ^^ + +error: expected `#[newtype(NewtypeKind)]` + --> tests/trybuild/newtype_attribute.rs:34:30 + | +34 | #[newtype_tools::newtype(Amount, Amount)] + | ^^^^^^ diff --git a/newtype-tools/tests/trybuild/newtype_derive.rs b/newtype-tools/tests/trybuild/newtype_derive.rs new file mode 100644 index 0000000..8266fd7 --- /dev/null +++ b/newtype-tools/tests/trybuild/newtype_derive.rs @@ -0,0 +1,68 @@ +/// `#[derive(Newtype)]` + +mod enum_invalid { + #[derive(newtype_tools::Newtype)] + enum Enum {} +} + +mod named_struct { + #[derive(newtype_tools::Newtype)] + struct Oranges { + inner: u32, + } +} + +mod struct_list { + #[derive(newtype_tools::Newtype)] + #[newtype(;)] + struct Oranges(u32); +} + +mod struct_list_name_value { + #[derive(newtype_tools::Newtype)] + #[newtype(from = Oranges)] + struct Oranges(u32); +} + +mod struct_list_path { + #[derive(newtype_tools::Newtype)] + #[newtype(from)] + struct Oranges(u32); +} + +mod struct_list_path_invalid { + #[derive(newtype_tools::Newtype)] + #[newtype(invalid_path)] + struct Oranges(u32); +} + +mod struct_name_value { + #[derive(newtype_tools::Newtype)] + #[newtype = value] + struct Oranges(u32); +} + +mod struct_path { + #[derive(newtype_tools::Newtype)] + #[newtype] + struct Oranges(u32); +} + +mod tuple_struct { + #[derive(newtype_tools::Newtype)] + struct N(u64, u64); +} + +mod union { + #[derive(newtype_tools::Newtype)] + union U { + u: u32, + } +} + +mod unit_struct { + #[derive(newtype_tools::Newtype)] + struct Oranges; +} + +fn main() {} diff --git a/newtype-tools/tests/trybuild/newtype_derive.stderr b/newtype-tools/tests/trybuild/newtype_derive.stderr new file mode 100644 index 0000000..0c8a031 --- /dev/null +++ b/newtype-tools/tests/trybuild/newtype_derive.stderr @@ -0,0 +1,74 @@ +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_derive.rs:5:5 + | +5 | enum Enum {} + | ^^^^ + +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_derive.rs:10:20 + | +10 | struct Oranges { + | ____________________^ +11 | | inner: u32, +12 | | } + | |_____^ + +error: expected identifier + --> tests/trybuild/newtype_derive.rs:17:15 + | +17 | #[newtype(;)] + | ^ + +error: expected `#[newtype(from(...))]` + --> tests/trybuild/newtype_derive.rs:23:15 + | +23 | #[newtype(from = Oranges)] + | ^^^^^^^^^^^^^^ + +error: expected `#[newtype(from(...))]` + --> tests/trybuild/newtype_derive.rs:29:15 + | +29 | #[newtype(from)] + | ^^^^ + +error: expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, `bitor(_assign)`, `bitxor(_assign)`, `div(_assign)`, `mul(_assign)`, `rem(_assign)`, `shl(_assign)`, `shr(_assign)`, `partial_eq`, or `sub(_assign)` + --> tests/trybuild/newtype_derive.rs:35:15 + | +35 | #[newtype(invalid_path)] + | ^^^^^^^^^^^^ + +error: expected `#[newtype(attr1, attr2)]` + --> tests/trybuild/newtype_derive.rs:41:7 + | +41 | #[newtype = value] + | ^^^^^^^^^^^^^^^ + +error: expected `#[newtype(attr1, attr2)]` + --> tests/trybuild/newtype_derive.rs:47:7 + | +47 | #[newtype] + | ^^^^^^^ + +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_derive.rs:53:13 + | +53 | struct N(u64, u64); + | ^^^^^^^^^^ + +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_derive.rs:58:5 + | +58 | union U { + | ^^^^^ + +error: expected `struct Newtype(inner_type)` + --> tests/trybuild/newtype_derive.rs:65:5 + | +65 | struct Oranges; + | ^^^^^^ + +error: attribute value must be a literal + --> tests/trybuild/newtype_derive.rs:41:17 + | +41 | #[newtype = value] + | ^^^^^