deranged-0.3.11/.cargo_vcs_info.json0000644000000001360000000000100126670ustar { "git": { "sha1": "c25ef2cefe36d92ebf3de6df2faaf293881f7349" }, "path_in_vcs": "" }deranged-0.3.11/Cargo.toml0000644000000032500000000000100106650ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.67.0" name = "deranged" version = "0.3.11" authors = ["Jacob Pratt "] include = [ "src/**/*", "LICENSE-*", "README.md", ] description = "Ranged integers" readme = "README.md" keywords = [ "integer", "int", "range", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jhpratt/deranged" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docs_rs", ] targets = ["x86_64-unknown-linux-gnu"] [dependencies.num-traits] version = "0.2.15" optional = true default-features = false [dependencies.powerfmt] version = "0.2.0" optional = true default-features = false [dependencies.quickcheck] version = "1.0.3" optional = true default-features = false [dependencies.rand] version = "0.8.4" optional = true default-features = false [dependencies.serde] version = "1.0.126" optional = true default-features = false [dev-dependencies.rand] version = "0.8.4" [dev-dependencies.serde_json] version = "1.0.86" [features] alloc = [] default = ["std"] num = ["dep:num-traits"] powerfmt = ["dep:powerfmt"] quickcheck = [ "dep:quickcheck", "alloc", ] rand = ["dep:rand"] serde = ["dep:serde"] std = ["alloc"] deranged-0.3.11/Cargo.toml.orig000064400000000000000000000021451046102023000143500ustar 00000000000000[package] name = "deranged" version = "0.3.11" authors = ["Jacob Pratt "] edition = "2021" rust-version = "1.67.0" repository = "https://github.com/jhpratt/deranged" keywords = ["integer", "int", "range"] readme = "README.md" license = "MIT OR Apache-2.0" description = "Ranged integers" include = ["src/**/*", "LICENSE-*", "README.md"] [features] default = ["std"] alloc = [] num = ["dep:num-traits"] powerfmt = ["dep:powerfmt"] quickcheck = ["dep:quickcheck", "alloc"] rand = ["dep:rand"] serde = ["dep:serde"] std = ["alloc"] [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "docs_rs"] [dependencies] num-traits = { version = "0.2.15", optional = true, default-features = false } powerfmt = { version = "0.2.0", optional = true, default-features = false } quickcheck = { version = "1.0.3", default-features = false, optional = true } rand = { version = "0.8.4", optional = true, default-features = false } serde = { version = "1.0.126", optional = true, default-features = false } [dev-dependencies] rand = "0.8.4" serde_json = "1.0.86" deranged-0.3.11/LICENSE-Apache000064400000000000000000000261251046102023000136510ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Jacob Pratt et al. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. deranged-0.3.11/LICENSE-MIT000064400000000000000000000020461046102023000131150ustar 00000000000000Copyright (c) 2022 Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. deranged-0.3.11/README.md000064400000000000000000000001201046102023000127270ustar 00000000000000# Deranged This crate is a proof-of-concept implementation of ranged integers. deranged-0.3.11/src/lib.rs000064400000000000000000001677061046102023000134030ustar 00000000000000#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] #![deny( anonymous_parameters, clippy::all, clippy::missing_safety_doc, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks, illegal_floating_point_literal_pattern, late_bound_lifetime_arguments, patterns_in_fns_without_body, rust_2018_idioms, trivial_casts, trivial_numeric_casts, unreachable_pub, unsafe_op_in_unsafe_fn, unused_extern_crates )] #![warn( clippy::dbg_macro, clippy::decimal_literal_representation, clippy::get_unwrap, clippy::nursery, clippy::pedantic, clippy::todo, clippy::unimplemented, clippy::unwrap_used, clippy::use_debug, missing_copy_implementations, missing_debug_implementations, unused_qualifications, variant_size_differences )] #![allow( path_statements, // used for static assertions clippy::inline_always, clippy::missing_errors_doc, clippy::must_use_candidate, clippy::redundant_pub_crate, )] #![doc(test(attr(deny(warnings))))] #[cfg(test)] mod tests; mod traits; mod unsafe_wrapper; #[cfg(feature = "alloc")] #[allow(unused_extern_crates)] extern crate alloc; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt; use core::num::IntErrorKind; use core::str::FromStr; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "powerfmt")] use powerfmt::smart_display; use crate::unsafe_wrapper::Unsafe; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TryFromIntError; impl fmt::Display for TryFromIntError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("out of range integral type conversion attempted") } } #[cfg(feature = "std")] impl Error for TryFromIntError {} #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParseIntError { kind: IntErrorKind, } impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. // This function is not const because the counterpart of stdlib isn't #[allow(clippy::missing_const_for_fn)] #[inline(always)] pub fn kind(&self) -> &IntErrorKind { &self.kind } } impl fmt::Display for ParseIntError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { IntErrorKind::Empty => "cannot parse integer from empty string", IntErrorKind::InvalidDigit => "invalid digit found in string", IntErrorKind::PosOverflow => "number too large to fit in target type", IntErrorKind::NegOverflow => "number too small to fit in target type", IntErrorKind::Zero => "number would be zero for non-zero type", _ => "Unknown Int error kind", } .fmt(f) } } #[cfg(feature = "std")] impl Error for ParseIntError {} macro_rules! const_try_opt { ($e:expr) => { match $e { Some(value) => value, None => return None, } }; } macro_rules! if_signed { (true $($x:tt)*) => { $($x)*}; (false $($x:tt)*) => {}; } macro_rules! if_unsigned { (true $($x:tt)*) => {}; (false $($x:tt)*) => { $($x)* }; } macro_rules! article { (true) => { "An" }; (false) => { "A" }; } macro_rules! unsafe_unwrap_unchecked { ($e:expr) => {{ let opt = $e; debug_assert!(opt.is_some()); match $e { Some(value) => value, None => core::hint::unreachable_unchecked(), } }}; } /// Informs the optimizer that a condition is always true. If the condition is false, the behavior /// is undefined. /// /// # Safety /// /// `b` must be `true`. #[inline] const unsafe fn assume(b: bool) { debug_assert!(b); if !b { // Safety: The caller must ensure that `b` is true. unsafe { core::hint::unreachable_unchecked() } } } macro_rules! impl_ranged { ($( $type:ident { mod_name: $mod_name:ident internal: $internal:ident signed: $is_signed:ident unsigned: $unsigned_type:ident optional: $optional_type:ident } )*) => {$( #[doc = concat!( article!($is_signed), " `", stringify!($internal), "` that is known to be in the range `MIN..=MAX`.", )] #[repr(transparent)] #[derive(Clone, Copy, Eq, Ord, Hash)] pub struct $type( Unsafe<$internal>, ); #[doc = concat!( "A `", stringify!($type), "` that is optional. Equivalent to [`Option<", stringify!($type), ">`] with niche value optimization.", )] /// #[doc = concat!( "If `MIN` is [`", stringify!($internal), "::MIN`] _and_ `MAX` is [`", stringify!($internal) ,"::MAX`] then compilation will fail. This is because there is no way to represent \ the niche value.", )] /// /// This type is useful when you need to store an optional ranged value in a struct, but /// do not want the overhead of an `Option` type. This reduces the size of the struct /// overall, and is particularly useful when you have a large number of optional fields. /// Note that most operations must still be performed on the [`Option`] type, which is #[doc = concat!("obtained with [`", stringify!($optional_type), "::get`].")] #[repr(transparent)] #[derive(Clone, Copy, Eq, Hash)] pub struct $optional_type( $internal, ); impl $type<0, 0> { #[inline(always)] pub const fn exact() -> $type { // Safety: The value is the only one in range. unsafe { $type::new_unchecked(VALUE) } } } impl $type { /// The smallest value that can be represented by this type. // Safety: `MIN` is in range by definition. pub const MIN: Self = Self::new_static::(); /// The largest value that can be represented by this type. // Safety: `MAX` is in range by definition. pub const MAX: Self = Self::new_static::(); /// Creates a ranged integer without checking the value. /// /// # Safety /// /// The value must be within the range `MIN..=MAX`. #[inline(always)] pub const unsafe fn new_unchecked(value: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the value is in range. unsafe { $crate::assume(MIN <= value && value <= MAX); Self(Unsafe::new(value)) } } /// Returns the value as a primitive type. #[inline(always)] pub const fn get(self) -> $internal { ::ASSERT; // Safety: A stored value is always in range. unsafe { $crate::assume(MIN <= *self.0.get() && *self.0.get() <= MAX) }; *self.0.get() } #[inline(always)] pub(crate) const fn get_ref(&self) -> &$internal { ::ASSERT; let value = self.0.get(); // Safety: A stored value is always in range. unsafe { $crate::assume(MIN <= *value && *value <= MAX) }; value } /// Creates a ranged integer if the given value is in the range `MIN..=MAX`. #[inline(always)] pub const fn new(value: $internal) -> Option { ::ASSERT; if value < MIN || value > MAX { None } else { // Safety: The value is in range. Some(unsafe { Self::new_unchecked(value) }) } } /// Creates a ranged integer with a statically known value. **Fails to compile** if the /// value is not in range. #[inline(always)] pub const fn new_static() -> Self { <($type, $type) as $crate::traits::StaticIsValid>::ASSERT; // Safety: The value is in range. unsafe { Self::new_unchecked(VALUE) } } /// Creates a ranged integer with the given value, saturating if it is out of range. #[inline] pub const fn new_saturating(value: $internal) -> Self { ::ASSERT; if value < MIN { Self::MIN } else if value > MAX { Self::MAX } else { // Safety: The value is in range. unsafe { Self::new_unchecked(value) } } } /// Expand the range that the value may be in. **Fails to compile** if the new range is /// not a superset of the current range. pub const fn expand( self, ) -> $type { <$type as $crate::traits::RangeIsValid>::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; <($type, $type) as $crate::traits::ExpandIsValid> ::ASSERT; // Safety: The range is widened. unsafe { $type::new_unchecked(self.get()) } } /// Attempt to narrow the range that the value may be in. Returns `None` if the value /// is outside the new range. **Fails to compile** if the new range is not a subset of /// the current range. pub const fn narrow< const NEW_MIN: $internal, const NEW_MAX: $internal, >(self) -> Option<$type> { <$type as $crate::traits::RangeIsValid>::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; <($type, $type) as $crate::traits::NarrowIsValid> ::ASSERT; $type::::new(self.get()) } /// Converts a string slice in a given base to an integer. /// /// The string is expected to be an optional `+` or `-` sign followed by digits. Leading /// and trailing whitespace represent an error. Digits are a subset of these characters, /// depending on `radix`: /// /// - `0-9` /// - `a-z` /// - `A-Z` /// /// # Panics /// /// Panics if `radix` is not in the range `2..=36`. /// /// # Examples /// /// Basic usage: /// /// ```rust #[doc = concat!("# use deranged::", stringify!($type), ";")] #[doc = concat!( "assert_eq!(", stringify!($type), "::<5, 10>::from_str_radix(\"A\", 16), Ok(", stringify!($type), "::new_static::<10>()));", )] /// ``` #[inline] pub fn from_str_radix(src: &str, radix: u32) -> Result { ::ASSERT; match $internal::from_str_radix(src, radix) { Ok(value) if value > MAX => { Err(ParseIntError { kind: IntErrorKind::PosOverflow }) } Ok(value) if value < MIN => { Err(ParseIntError { kind: IntErrorKind::NegOverflow }) } // Safety: If the value was out of range, it would have been caught in a // previous arm. Ok(value) => Ok(unsafe { Self::new_unchecked(value) }), Err(e) => Err(ParseIntError { kind: e.kind().clone() }), } } /// Checked integer addition. Computes `self + rhs`, returning `None` if the resulting /// value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_add(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_add(rhs))) } /// Unchecked integer addition. Computes `self + rhs`, assuming that the result is in /// range. /// /// # Safety /// /// The result of `self + rhs` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_add(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_add(rhs))) } } /// Checked integer addition. Computes `self - rhs`, returning `None` if the resulting /// value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_sub(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_sub(rhs))) } /// Unchecked integer subtraction. Computes `self - rhs`, assuming that the result is in /// range. /// /// # Safety /// /// The result of `self - rhs` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_sub(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_sub(rhs))) } } /// Checked integer addition. Computes `self * rhs`, returning `None` if the resulting /// value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_mul(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_mul(rhs))) } /// Unchecked integer multiplication. Computes `self * rhs`, assuming that the result is /// in range. /// /// # Safety /// /// The result of `self * rhs` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_mul(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_mul(rhs))) } } /// Checked integer addition. Computes `self / rhs`, returning `None` if `rhs == 0` or /// if the resulting value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_div(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_div(rhs))) } /// Unchecked integer division. Computes `self / rhs`, assuming that `rhs != 0` and that /// the result is in range. /// /// # Safety /// /// `self` must not be zero and the result of `self / rhs` must be in the range /// `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_div(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range and that `rhs` is not // zero. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_div(rhs))) } } /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` if /// `rhs == 0` or if the resulting value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_div_euclid(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_div_euclid(rhs))) } /// Unchecked Euclidean division. Computes `self.div_euclid(rhs)`, assuming that /// `rhs != 0` and that the result is in range. /// /// # Safety /// /// `self` must not be zero and the result of `self.div_euclid(rhs)` must be in the /// range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_div_euclid(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range and that `rhs` is not // zero. unsafe { Self::new_unchecked( unsafe_unwrap_unchecked!(self.get().checked_div_euclid(rhs)) ) } } if_unsigned!($is_signed /// Remainder. Computes `self % rhs`, statically guaranteeing that the returned value /// is in range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn rem( self, rhs: $type, ) -> $type<0, RHS_VALUE> { ::ASSERT; // Safety: The result is guaranteed to be in range due to the nature of remainder on // unsigned integers. unsafe { $type::new_unchecked(self.get() % rhs.get()) } }); /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0` or /// if the resulting value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_rem(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_rem(rhs))) } /// Unchecked remainder. Computes `self % rhs`, assuming that `rhs != 0` and that the /// result is in range. /// /// # Safety /// /// `self` must not be zero and the result of `self % rhs` must be in the range /// `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_rem(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range and that `rhs` is not // zero. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_rem(rhs))) } } /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` if /// `rhs == 0` or if the resulting value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_rem_euclid(self, rhs: $internal) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_rem_euclid(rhs))) } /// Unchecked Euclidean remainder. Computes `self.rem_euclid(rhs)`, assuming that /// `rhs != 0` and that the result is in range. /// /// # Safety /// /// `self` must not be zero and the result of `self.rem_euclid(rhs)` must be in the /// range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_rem_euclid(self, rhs: $internal) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range and that `rhs` is not // zero. unsafe { Self::new_unchecked( unsafe_unwrap_unchecked!(self.get().checked_rem_euclid(rhs)) ) } } /// Checked negation. Computes `-self`, returning `None` if the resulting value is out /// of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_neg(self) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_neg())) } /// Unchecked negation. Computes `-self`, assuming that `-self` is in range. /// /// # Safety /// /// The result of `-self` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_neg(self) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_neg())) } } /// Negation. Computes `self.neg()`, **failing to compile** if the result is not /// guaranteed to be in range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const fn neg(self) -> Self { ::ASSERT; ::ASSERT; // Safety: The compiler asserts that the result is in range. unsafe { self.unchecked_neg() } } /// Checked shift left. Computes `self << rhs`, returning `None` if the resulting value /// is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_shl(self, rhs: u32) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_shl(rhs))) } /// Unchecked shift left. Computes `self << rhs`, assuming that the result is in range. /// /// # Safety /// /// The result of `self << rhs` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shl(rhs))) } } /// Checked shift right. Computes `self >> rhs`, returning `None` if /// the resulting value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_shr(self, rhs: u32) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_shr(rhs))) } /// Unchecked shift right. Computes `self >> rhs`, assuming that the result is in range. /// /// # Safety /// /// The result of `self >> rhs` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shr(rhs))) } } if_signed!($is_signed /// Checked absolute value. Computes `self.abs()`, returning `None` if the resulting /// value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_abs(self) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_abs())) } /// Unchecked absolute value. Computes `self.abs()`, assuming that the result is in /// range. /// /// # Safety /// /// The result of `self.abs()` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_abs(self) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_abs())) } } /// Absolute value. Computes `self.abs()`, **failing to compile** if the result is not /// guaranteed to be in range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const fn abs(self) -> Self { ::ASSERT; ::ASSERT; // Safety: The compiler asserts that the result is in range. unsafe { self.unchecked_abs() } }); /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if the resulting /// value is out of range. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn checked_pow(self, exp: u32) -> Option { ::ASSERT; Self::new(const_try_opt!(self.get().checked_pow(exp))) } /// Unchecked exponentiation. Computes `self.pow(exp)`, assuming that the result is in /// range. /// /// # Safety /// /// The result of `self.pow(exp)` must be in the range `MIN..=MAX`. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const unsafe fn unchecked_pow(self, exp: u32) -> Self { ::ASSERT; // Safety: The caller must ensure that the result is in range. unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_pow(exp))) } } /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_add(self, rhs: $internal) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_add(rhs)) } /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_sub(self, rhs: $internal) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_sub(rhs)) } if_signed!($is_signed /// Saturating integer negation. Computes `self - rhs`, saturating at the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_neg(self) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_neg()) }); if_signed!($is_signed /// Saturating absolute value. Computes `self.abs()`, saturating at the numeric bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_abs(self) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_abs()) }); /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_mul(self, rhs: $internal) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_mul(rhs)) } /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at the /// numeric bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn saturating_pow(self, exp: u32) -> Self { ::ASSERT; Self::new_saturating(self.get().saturating_pow(exp)) } /// Compute the `rem_euclid` of this type with its unsigned type equivalent // Not public because it doesn't match stdlib's "method_unsigned implemented only for signed type" tradition. // Also because this isn't implemented for normal types in std. // TODO maybe make public anyway? It is useful. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned const fn rem_euclid_unsigned( rhs: $internal, range_len: $unsigned_type ) -> $unsigned_type { #[allow(unused_comparisons)] if rhs >= 0 { (rhs as $unsigned_type) % range_len } else { // Let ux refer to an n bit unsigned and ix refer to an n bit signed integer. // Can't write -ux or ux::abs() method. This gets around compilation error. // `wrapping_sub` is to handle rhs = ix::MIN since ix::MIN = -ix::MAX-1 let rhs_abs = ($internal::wrapping_sub(0, rhs)) as $unsigned_type; // Largest multiple of range_len <= type::MAX is lowest if range_len * 2 > ux::MAX -> range_len >= ux::MAX / 2 + 1 // Also = 0 in mod range_len arithmetic. // Sub from this large number rhs_abs (same as sub -rhs = -(-rhs) = add rhs) to get rhs % range_len // ix::MIN = -2^(n-1) so 0 <= rhs_abs <= 2^(n-1) // ux::MAX / 2 + 1 = 2^(n-1) so this subtraction will always be a >= 0 after subtraction // Thus converting rhs signed negative to equivalent positive value in mod range_len arithmetic ((($unsigned_type::MAX / range_len) * range_len) - (rhs_abs)) % range_len } } /// Wrapping integer addition. Computes `self + rhs`, wrapping around the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned pub const fn wrapping_add(self, rhs: $internal) -> Self { ::ASSERT; // Forward to internal type's impl if same as type. if MIN == $internal::MIN && MAX == $internal::MAX { // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. return unsafe { Self::new_unchecked(self.get().wrapping_add(rhs)) } } let inner = self.get(); // Won't overflow because of std impl forwarding. let range_len = MAX.abs_diff(MIN) + 1; // Calculate the offset with proper handling for negative rhs let offset = Self::rem_euclid_unsigned(rhs, range_len); let greater_vals = MAX.abs_diff(inner); // No wrap if offset <= greater_vals { // Safety: // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) // if inner < 0: Same as >=0 with caveat: // `(signed as unsigned).wrapping_add(unsigned) as signed` is the same as // `signed::checked_add_unsigned(unsigned).unwrap()` or `wrapping_add_unsigned` // (the difference doesn't matter since it won't overflow), // but unsigned integers don't have either method so it won't compile that way. unsafe { Self::new_unchecked( ((inner as $unsigned_type).wrapping_add(offset)) as $internal ) } } // Wrap else { // Safety: // - offset < range_len by rem_euclid (MIN + ... safe) // - offset > greater_vals from if statement (offset - (greater_vals + 1) safe) // // again using `(signed as unsigned).wrapping_add(unsigned) as signed` = `checked_add_unsigned` trick unsafe { Self::new_unchecked( ((MIN as $unsigned_type).wrapping_add( offset - (greater_vals + 1) )) as $internal ) } } } /// Wrapping integer subtraction. Computes `self - rhs`, wrapping around the numeric /// bounds. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned pub const fn wrapping_sub(self, rhs: $internal) -> Self { ::ASSERT; // Forward to internal type's impl if same as type. if MIN == $internal::MIN && MAX == $internal::MAX { // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. return unsafe { Self::new_unchecked(self.get().wrapping_sub(rhs)) } } let inner = self.get(); // Won't overflow because of std impl forwarding. let range_len = MAX.abs_diff(MIN) + 1; // Calculate the offset with proper handling for negative rhs let offset = Self::rem_euclid_unsigned(rhs, range_len); let lesser_vals = MIN.abs_diff(inner); // No wrap if offset <= lesser_vals { // Safety: // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) // if inner < 0: Same as >=0 with caveat: // `(signed as unsigned).wrapping_sub(unsigned) as signed` is the same as // `signed::checked_sub_unsigned(unsigned).unwrap()` or `wrapping_sub_unsigned` // (the difference doesn't matter since it won't overflow below 0), // but unsigned integers don't have either method so it won't compile that way. unsafe { Self::new_unchecked( ((inner as $unsigned_type).wrapping_sub(offset)) as $internal ) } } // Wrap else { // Safety: // - offset < range_len by rem_euclid (MAX - ... safe) // - offset > lesser_vals from if statement (offset - (lesser_vals + 1) safe) // // again using `(signed as unsigned).wrapping_sub(unsigned) as signed` = `checked_sub_unsigned` trick unsafe { Self::new_unchecked( ((MAX as $unsigned_type).wrapping_sub( offset - (lesser_vals + 1) )) as $internal ) } } } } impl $optional_type { /// The value used as the niche. Must not be in the range `MIN..=MAX`. const NICHE: $internal = match (MIN, MAX) { ($internal::MIN, $internal::MAX) => panic!("type has no niche"), ($internal::MIN, _) => $internal::MAX, (_, _) => $internal::MIN, }; /// An optional ranged value that is not present. #[allow(non_upper_case_globals)] pub const None: Self = Self(Self::NICHE); /// Creates an optional ranged value that is present. #[allow(non_snake_case)] #[inline(always)] pub const fn Some(value: $type) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; Self(value.get()) } /// Returns the value as the standard library's [`Option`] type. #[inline(always)] pub const fn get(self) -> Option<$type> { <$type as $crate::traits::RangeIsValid>::ASSERT; if self.0 == Self::NICHE { None } else { // Safety: A stored value that is not the niche is always in range. Some(unsafe { $type::new_unchecked(self.0) }) } } /// Creates an optional ranged integer without checking the value. /// /// # Safety /// /// The value must be within the range `MIN..=MAX`. As the value used for niche /// value optimization is unspecified, the provided value must not be the niche /// value. #[inline(always)] pub const unsafe fn some_unchecked(value: $internal) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; // Safety: The caller must ensure that the value is in range. unsafe { $crate::assume(MIN <= value && value <= MAX) }; Self(value) } /// Obtain the inner value of the struct. This is useful for comparisons. #[inline(always)] pub(crate) const fn inner(self) -> $internal { <$type as $crate::traits::RangeIsValid>::ASSERT; self.0 } #[inline(always)] pub const fn get_primitive(self) -> Option<$internal> { <$type as $crate::traits::RangeIsValid>::ASSERT; Some(const_try_opt!(self.get()).get()) } /// Returns `true` if the value is the niche value. #[inline(always)] pub const fn is_none(self) -> bool { <$type as $crate::traits::RangeIsValid>::ASSERT; self.get().is_none() } /// Returns `true` if the value is not the niche value. #[inline(always)] pub const fn is_some(self) -> bool { <$type as $crate::traits::RangeIsValid>::ASSERT; self.get().is_some() } } impl fmt::Debug for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::Debug for $optional_type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { <$type as $crate::traits::RangeIsValid>::ASSERT; self.get().fmt(f) } } impl fmt::Display for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } #[cfg(feature = "powerfmt")] impl< const MIN: $internal, const MAX: $internal, > smart_display::SmartDisplay for $type { type Metadata = <$internal as smart_display::SmartDisplay>::Metadata; #[inline(always)] fn metadata( &self, f: smart_display::FormatterOptions, ) -> smart_display::Metadata<'_, Self> { ::ASSERT; self.get_ref().metadata(f).reuse() } #[inline(always)] fn fmt_with_metadata( &self, f: &mut fmt::Formatter<'_>, metadata: smart_display::Metadata<'_, Self>, ) -> fmt::Result { ::ASSERT; self.get().fmt_with_metadata(f, metadata.reuse()) } } impl Default for $optional_type { #[inline(always)] fn default() -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; Self::None } } impl AsRef<$internal> for $type { #[inline(always)] fn as_ref(&self) -> &$internal { ::ASSERT; &self.get_ref() } } impl Borrow<$internal> for $type { #[inline(always)] fn borrow(&self) -> &$internal { ::ASSERT; &self.get_ref() } } impl< const MIN_A: $internal, const MAX_A: $internal, const MIN_B: $internal, const MAX_B: $internal, > PartialEq<$type> for $type { #[inline(always)] fn eq(&self, other: &$type) -> bool { ::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; self.get() == other.get() } } impl< const MIN_A: $internal, const MAX_A: $internal, const MIN_B: $internal, const MAX_B: $internal, > PartialEq<$optional_type> for $optional_type { #[inline(always)] fn eq(&self, other: &$optional_type) -> bool { <$type as $crate::traits::RangeIsValid>::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; self.inner() == other.inner() } } impl< const MIN_A: $internal, const MAX_A: $internal, const MIN_B: $internal, const MAX_B: $internal, > PartialOrd<$type> for $type { #[inline(always)] fn partial_cmp(&self, other: &$type) -> Option { ::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; self.get().partial_cmp(&other.get()) } } impl< const MIN_A: $internal, const MAX_A: $internal, const MIN_B: $internal, const MAX_B: $internal, > PartialOrd<$optional_type> for $optional_type { #[inline] fn partial_cmp(&self, other: &$optional_type) -> Option { <$type as $crate::traits::RangeIsValid>::ASSERT; <$type as $crate::traits::RangeIsValid>::ASSERT; if self.is_none() && other.is_none() { Some(Ordering::Equal) } else if self.is_none() { Some(Ordering::Less) } else if other.is_none() { Some(Ordering::Greater) } else { self.inner().partial_cmp(&other.inner()) } } } impl< const MIN: $internal, const MAX: $internal, > Ord for $optional_type { #[inline] fn cmp(&self, other: &Self) -> Ordering { <$type as $crate::traits::RangeIsValid>::ASSERT; if self.is_none() && other.is_none() { Ordering::Equal } else if self.is_none() { Ordering::Less } else if other.is_none() { Ordering::Greater } else { self.inner().cmp(&other.inner()) } } } impl fmt::Binary for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::LowerHex for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::UpperHex for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::LowerExp for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::UpperExp for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl fmt::Octal for $type { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::ASSERT; self.get().fmt(f) } } impl From<$type> for $internal { #[inline(always)] fn from(value: $type) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; value.get() } } impl< const MIN: $internal, const MAX: $internal, > From<$type> for $optional_type { #[inline(always)] fn from(value: $type) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; Self::Some(value) } } impl< const MIN: $internal, const MAX: $internal, > From>> for $optional_type { #[inline(always)] fn from(value: Option<$type>) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; match value { Some(value) => Self::Some(value), None => Self::None, } } } impl< const MIN: $internal, const MAX: $internal, > From<$optional_type> for Option<$type> { #[inline(always)] fn from(value: $optional_type) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; value.get() } } impl TryFrom<$internal> for $type { type Error = TryFromIntError; #[inline] fn try_from(value: $internal) -> Result { ::ASSERT; Self::new(value).ok_or(TryFromIntError) } } impl FromStr for $type { type Err = ParseIntError; #[inline] fn from_str(s: &str) -> Result { ::ASSERT; let value = s.parse::<$internal>().map_err(|e| ParseIntError { kind: e.kind().clone() })?; if value < MIN { Err(ParseIntError { kind: IntErrorKind::NegOverflow }) } else if value > MAX { Err(ParseIntError { kind: IntErrorKind::PosOverflow }) } else { // Safety: The value was previously checked for validity. Ok(unsafe { Self::new_unchecked(value) }) } } } #[cfg(feature = "serde")] impl serde::Serialize for $type { #[inline(always)] fn serialize(&self, serializer: S) -> Result { ::ASSERT; self.get().serialize(serializer) } } #[cfg(feature = "serde")] impl< const MIN: $internal, const MAX: $internal, > serde::Serialize for $optional_type { #[inline(always)] fn serialize(&self, serializer: S) -> Result { <$type as $crate::traits::RangeIsValid>::ASSERT; self.get().serialize(serializer) } } #[cfg(feature = "serde")] impl< 'de, const MIN: $internal, const MAX: $internal, > serde::Deserialize<'de> for $type { #[inline] fn deserialize>(deserializer: D) -> Result { ::ASSERT; let internal = <$internal>::deserialize(deserializer)?; Self::new(internal).ok_or_else(|| ::invalid_value( serde::de::Unexpected::Other("integer"), #[cfg(feature = "std")] { &format!("an integer in the range {}..={}", MIN, MAX).as_ref() }, #[cfg(not(feature = "std"))] { &"an integer in the valid range" } )) } } #[cfg(feature = "serde")] impl< 'de, const MIN: $internal, const MAX: $internal, > serde::Deserialize<'de> for $optional_type { #[inline] fn deserialize>(deserializer: D) -> Result { <$type as $crate::traits::RangeIsValid>::ASSERT; Ok(Self::Some($type::::deserialize(deserializer)?)) } } #[cfg(feature = "rand")] impl< const MIN: $internal, const MAX: $internal, > rand::distributions::Distribution<$type> for rand::distributions::Standard { #[inline] fn sample(&self, rng: &mut R) -> $type { <$type as $crate::traits::RangeIsValid>::ASSERT; $type::new(rng.gen_range(MIN..=MAX)).expect("rand failed to generate a valid value") } } #[cfg(feature = "rand")] impl< const MIN: $internal, const MAX: $internal, > rand::distributions::Distribution<$optional_type> for rand::distributions::Standard { #[inline] fn sample(&self, rng: &mut R) -> $optional_type { <$type as $crate::traits::RangeIsValid>::ASSERT; rng.gen::>>().into() } } #[cfg(feature = "num")] impl num_traits::Bounded for $type { #[inline(always)] fn min_value() -> Self { ::ASSERT; Self::MIN } #[inline(always)] fn max_value() -> Self { ::ASSERT; Self::MAX } } #[cfg(feature = "quickcheck")] impl quickcheck::Arbitrary for $type { #[inline] fn arbitrary(g: &mut quickcheck::Gen) -> Self { ::ASSERT; // Safety: The `rem_euclid` call and addition ensure that the value is in range. unsafe { Self::new_unchecked($internal::arbitrary(g).rem_euclid(MAX - MIN + 1) + MIN) } } #[inline] fn shrink(&self) -> ::alloc::boxed::Box> { ::alloc::boxed::Box::new( self.get() .shrink() .filter_map(Self::new) ) } } #[cfg(feature = "quickcheck")] impl< const MIN: $internal, const MAX: $internal, > quickcheck::Arbitrary for $optional_type { #[inline] fn arbitrary(g: &mut quickcheck::Gen) -> Self { <$type as $crate::traits::RangeIsValid>::ASSERT; Option::<$type>::arbitrary(g).into() } #[inline] fn shrink(&self) -> ::alloc::boxed::Box> { ::alloc::boxed::Box::new(self.get().shrink().map(Self::from)) } } )*}; } impl_ranged! { RangedU8 { mod_name: ranged_u8 internal: u8 signed: false unsigned: u8 optional: OptionRangedU8 } RangedU16 { mod_name: ranged_u16 internal: u16 signed: false unsigned: u16 optional: OptionRangedU16 } RangedU32 { mod_name: ranged_u32 internal: u32 signed: false unsigned: u32 optional: OptionRangedU32 } RangedU64 { mod_name: ranged_u64 internal: u64 signed: false unsigned: u64 optional: OptionRangedU64 } RangedU128 { mod_name: ranged_u128 internal: u128 signed: false unsigned: u128 optional: OptionRangedU128 } RangedUsize { mod_name: ranged_usize internal: usize signed: false unsigned: usize optional: OptionRangedUsize } RangedI8 { mod_name: ranged_i8 internal: i8 signed: true unsigned: u8 optional: OptionRangedI8 } RangedI16 { mod_name: ranged_i16 internal: i16 signed: true unsigned: u16 optional: OptionRangedI16 } RangedI32 { mod_name: ranged_i32 internal: i32 signed: true unsigned: u32 optional: OptionRangedI32 } RangedI64 { mod_name: ranged_i64 internal: i64 signed: true unsigned: u64 optional: OptionRangedI64 } RangedI128 { mod_name: ranged_i128 internal: i128 signed: true unsigned: u128 optional: OptionRangedI128 } RangedIsize { mod_name: ranged_isize internal: isize signed: true unsigned: usize optional: OptionRangedIsize } } deranged-0.3.11/src/tests.rs000064400000000000000000000632621046102023000137670ustar 00000000000000use std::hash::Hash; use crate::{ IntErrorKind, OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI64, OptionRangedI8, OptionRangedIsize, OptionRangedU128, OptionRangedU16, OptionRangedU32, OptionRangedU64, OptionRangedU8, OptionRangedUsize, ParseIntError, RangedI128, RangedI16, RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, RangedU32, RangedU64, RangedU8, RangedUsize, TryFromIntError, }; macro_rules! if_signed { (signed $($x:tt)*) => { $($x)* }; (unsigned $($x:tt)*) => {}; } macro_rules! if_unsigned { (signed $($x:tt)*) => {}; (unsigned $($x:tt)*) => { $($x)* }; } #[test] fn errors() { assert_eq!( TryFromIntError.to_string(), "out of range integral type conversion attempted" ); assert_eq!(TryFromIntError.clone(), TryFromIntError); assert_eq!(format!("{TryFromIntError:?}"), "TryFromIntError"); assert_eq!( ParseIntError { kind: IntErrorKind::Empty, } .to_string(), "cannot parse integer from empty string" ); assert_eq!( ParseIntError { kind: IntErrorKind::InvalidDigit, } .to_string(), "invalid digit found in string" ); assert_eq!( ParseIntError { kind: IntErrorKind::PosOverflow, } .to_string(), "number too large to fit in target type" ); assert_eq!( ParseIntError { kind: IntErrorKind::NegOverflow, } .to_string(), "number too small to fit in target type" ); assert_eq!( ParseIntError { kind: IntErrorKind::Zero, } .to_string(), "number would be zero for non-zero type" ); assert_eq!( format!( "{:?}", ParseIntError { kind: IntErrorKind::Empty } ), "ParseIntError { kind: Empty }" ); assert_eq!( ParseIntError { kind: IntErrorKind::Empty } .clone(), ParseIntError { kind: IntErrorKind::Empty } ); assert_eq!( ParseIntError { kind: IntErrorKind::Empty } .kind(), &IntErrorKind::Empty ); } macro_rules! tests { ($($signed:ident $opt:ident $t:ident $inner:ident),* $(,)?) => { #[test] fn derives() {$( assert_eq!($t::<5, 10>::MIN.clone(), $t::<5, 10>::MIN); let mut hasher = std::collections::hash_map::DefaultHasher::new(); $t::<5, 10>::MIN.hash(&mut hasher); assert_eq!( $t::<5, 10>::MIN.cmp(&$t::<5, 10>::MAX), std::cmp::Ordering::Less ); assert_eq!($opt::<5, 10>::None.clone(), $opt::<5, 10>::None); $opt::<5, 10>::None.hash(&mut hasher); )*} #[test] fn expand() {$( let expanded: $t::<0, 20> = $t::<5, 10>::MAX.expand(); assert_eq!(expanded, $t::<0, 20>::new_static::<10>()); )*} #[test] fn narrow() {$( let narrowed: Option<$t::<10, 20>> = $t::<0, 20>::new_static::<10>().narrow(); assert_eq!(narrowed, Some($t::<10, 20>::MIN)); )*} #[test] fn new() {$( assert!($t::<5, 10>::new(10).is_some()); assert!($t::<5, 10>::new(11).is_none()); )*} #[test] fn new_static() {$( let six: $t::<5, 10> = $t::<5, 10>::new_static::<6>(); assert_eq!(Some(six), $t::<5, 10>::new(6)); )*} #[test] fn some_unchecked() {$( // Safety: The value is in range. unsafe { assert_eq!($opt::<5, 10>::some_unchecked(10), $opt::Some($t::<5, 10>::MAX)); } )*} #[test] fn is_some() {$( assert!($opt::<5, 10>::Some($t::<5, 10>::MAX).is_some()); )*} #[test] fn is_none() {$( assert!($opt::<5, 10>::None.is_none()); )*} #[test] fn default() {$( assert_eq!($opt::<5, 10>::default(), $opt::<5, 10>::None); )*} #[test] fn get() {$( assert_eq!($t::<5, 10>::MAX.get(), 10); assert_eq!($opt::<5, 10>::None.get(), None); assert_eq!($opt::Some($t::<5, 10>::MAX).get(), Some($t::<5, 10>::MAX)); )*} #[test] fn get_primitive() {$( assert_eq!($opt::Some($t::<5, 10>::MAX).get_primitive(), Some(10)); assert_eq!($opt::<5, 10>::None.get_primitive(), None); )*} #[test] fn get_ref() {$( assert_eq!($t::<5, 10>::MAX.get_ref(), &10); )*} #[test] fn new_saturating() {$( assert_eq!($t::<5, 10>::new_saturating(11), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::new_saturating(0), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::new_saturating(9), $t::<5, 10>::new_static::<9>()); )*} #[test] fn from_str_radix() {$( assert_eq!($t::<5, 10>::from_str_radix("10", 10), Ok($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::from_str_radix("5", 10), Ok($t::<5, 10>::MIN)); assert_eq!( $t::<5, 10>::from_str_radix("4", 10), Err(ParseIntError { kind: IntErrorKind::NegOverflow }), ); assert_eq!( $t::<5, 10>::from_str_radix("11", 10), Err(ParseIntError { kind: IntErrorKind::PosOverflow }), ); assert_eq!( $t::<5, 10>::from_str_radix("", 10), Err(ParseIntError { kind: IntErrorKind::Empty }), ); )*} #[test] fn checked_add() {$( assert_eq!($t::<5, 10>::MAX.checked_add(1), None); assert_eq!($t::<5, 10>::MAX.checked_add(0), Some($t::<5, 10>::MAX)); )*} #[test] fn unchecked_add() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MIN.unchecked_add(5), $t::<5, 10>::MAX); } )*} #[test] fn checked_sub() {$( assert_eq!($t::<5, 10>::MIN.checked_sub(1), None); assert_eq!($t::<5, 10>::MIN.checked_sub(0), Some($t::<5, 10>::MIN)); )*} #[test] fn unchecked_sub() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_sub(5), $t::<5, 10>::MIN); } )*} #[test] fn checked_mul() {$( assert_eq!($t::<5, 10>::MAX.checked_mul(2), None); assert_eq!($t::<5, 10>::MAX.checked_mul(1), Some($t::<5, 10>::MAX)); )*} #[test] fn unchecked_mul() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_mul(1), $t::<5, 10>::MAX); } )*} #[test] fn checked_div() {$( assert_eq!($t::<5, 10>::MAX.checked_div(3), None); assert_eq!($t::<5, 10>::MAX.checked_div(2), $t::<5, 10>::new(5)); assert_eq!($t::<5, 10>::MAX.checked_div(1), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MAX.checked_div(0), None); )*} #[test] fn unchecked_div() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_div(1), $t::<5, 10>::MAX); } )*} #[test] fn checked_div_euclid() {$( assert_eq!($t::<5, 10>::MAX.checked_div_euclid(3), None); assert_eq!($t::<5, 10>::MAX.checked_div_euclid(2), $t::<5, 10>::new(5)); assert_eq!($t::<5, 10>::MAX.checked_div_euclid(1), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MAX.checked_div_euclid(0), None); )*} #[test] fn unchecked_div_euclid() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_div_euclid(1), $t::<5, 10>::MAX); } )*} #[test] fn rem() {$(if_unsigned! { $signed assert_eq!($t::<5, 10>::MAX.rem($t::exact::<3>()), $t::<0, 3>::new_static::<1>()); assert_eq!($t::<5, 10>::MAX.rem($t::exact::<5>()), $t::<0, 5>::MIN); })*} #[test] fn checked_rem() {$( assert_eq!($t::<5, 10>::MAX.checked_rem(11), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MAX.checked_rem(5), None); )*} #[test] fn unchecked_rem() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_rem(11), $t::<5, 10>::MAX); } )*} #[test] fn checked_rem_euclid() {$( assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(11), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(5), None); )*} #[test] fn unchecked_rem_euclid() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_rem_euclid(11), $t::<5, 10>::MAX); } )*} #[test] fn checked_neg() {$( assert_eq!($t::<5, 10>::MIN.checked_neg(), None); assert_eq!($t::<0, 10>::MIN.checked_neg(), Some($t::<0, 10>::MIN)); )*} #[test] fn unchecked_neg() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<0, 10>::MIN.unchecked_neg(), $t::<0, 10>::MIN); } )*} #[test] fn neg() {$( if_signed! { $signed assert_eq!($t::<-10, 10>::MIN.neg(), $t::<-10, 10>::MAX); })*} #[test] fn checked_shl() {$( assert_eq!($t::<5, 10>::MAX.checked_shl(1), None); assert_eq!($t::<5, 10>::MAX.checked_shl(0), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MIN.checked_shl(1), Some($t::<5, 10>::MAX)); )*} #[test] fn unchecked_shl() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_shl(0), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::MIN.unchecked_shl(1), $t::<5, 10>::MAX); } )*} #[test] fn checked_shr() {$( assert_eq!($t::<5, 10>::MAX.checked_shr(2), None); assert_eq!($t::<5, 10>::MAX.checked_shr(1), Some($t::<5, 10>::MIN)); assert_eq!($t::<5, 10>::MAX.checked_shr(0), Some($t::<5, 10>::MAX)); )*} #[test] fn unchecked_shr() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_shr(1), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MAX.unchecked_shr(0), $t::<5, 10>::MAX); } )*} #[test] fn checked_abs() {$( if_signed! { $signed assert_eq!($t::<5, 10>::MAX.checked_abs(), Some($t::<5, 10>::MAX)); assert_eq!($t::<-10, 10>::MIN.checked_abs(), Some($t::<-10, 10>::MAX)); assert_eq!($t::<-10, 0>::MIN.checked_abs(), None); })*} #[test] fn unchecked_abs() { $(if_signed! { $signed // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_abs(), $t::<5, 10>::MAX); assert_eq!($t::<-10, 10>::MIN.unchecked_abs(), $t::<-10, 10>::MAX); } })*} #[test] fn abs() { $(if_signed! { $signed assert_eq!($t::<-5, 10>::MIN.abs().get(), 5); })*} #[test] fn checked_pow() {$( assert_eq!($t::<5, 10>::MAX.checked_pow(0), None); assert_eq!($t::<5, 10>::MAX.checked_pow(1), Some($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::MAX.checked_pow(2), None); )*} #[test] fn unchecked_pow() {$( // Safety: The result is in range. unsafe { assert_eq!($t::<5, 10>::MAX.unchecked_pow(1), $t::<5, 10>::MAX); } )*} #[test] fn saturating_add() {$( assert_eq!($t::<5, 10>::MAX.saturating_add(0), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::MAX.saturating_add(1), $t::<5, 10>::MAX); )*} #[test] fn wrapping_add() { $( assert_eq!($t::<5, 10>::MAX.wrapping_add(0), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::MAX.wrapping_add(1), $t::<5, 10>::MIN); assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MAX.wrapping_add(1), $t::<{ $inner::MIN }, { $inner::MAX }>::MIN); for i in 1..127 { assert_eq!( $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::MAX.wrapping_add(i), $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::new($inner::MIN + i - 1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, $inner::MAX + i )) ); } )* $(if_signed! { $signed for i in 1..=127 { assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(126-i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(-5+i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); } for i in -127..=-1 { assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(126+i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(-5-i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); } assert_eq!($t::<-5, 126>::MIN.wrapping_add(-128), $t::<-5,126>::new(-1).unwrap_or_else(|| panic!("adding 128+{} does not yield -1", $inner::MIN))); assert_eq!($t::<-5, 10>::MAX.wrapping_add(0), $t::<-5, 10>::MAX); assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-3), $t::<-5, -3>::MAX); assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-30), $t::<-5, -3>::MAX); assert_eq!($t::<-5, -3>::MIN.wrapping_add(30), $t::<-5, -3>::MIN); assert_eq!($t::<-5, -3>::MIN.wrapping_add(-30), $t::<-5, -3>::MIN); assert_eq!($t::<-5, 10>::MAX.wrapping_add(25), $t::<-5, 10>::MIN.wrapping_add(24)); assert_eq!($t::<-5, 10>::MIN.wrapping_add(24), $t::<-5, 10>::MIN.wrapping_add(8)); assert_eq!($t::<-5, 10>::MAX.wrapping_add(1), $t::<-5, 10>::MIN); assert_eq!($t::<-5, 10>::MIN.wrapping_add(-1), $t::<-5, 10>::MAX); assert_eq!($t::<-5, 127>::MIN.wrapping_add(-1), $t::<-5, 127>::MAX); assert_eq!($t::<-127, 126>::MIN.wrapping_add(-1), $t::<-127, 126>::MAX); assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_add(-1), $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); })* } #[test] fn wrapping_sub() { $( assert_eq!($t::<5, 10>::MIN.wrapping_sub(0), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.wrapping_sub(1), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::new(5 + 1).unwrap().wrapping_sub(1), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MAX.wrapping_sub(1), $t::<5, 10>::new(10 - 1).unwrap()); assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_sub(1), $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); for i in 1..127 { assert_eq!( $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::MIN.wrapping_sub(i), $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::new($inner::MAX - i + 1).unwrap_or_else(|| panic!("failed test at iteration {i}")) ); } )* $(if_signed! { $signed for i in -127..=127 { assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::MIN.wrapping_sub(-i), "failed test at {i}"); assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::MIN.wrapping_sub(i), "failed test at {i}"); } assert_eq!( $t::<-5, 126>::MIN.wrapping_add(127).wrapping_add(1), $t::<-5,126>::MIN.wrapping_sub(-128) ); assert_eq!( $t::<-5, 126>::MIN.wrapping_add(-128), $t::<-5,126>::MIN.wrapping_sub(127).wrapping_sub(1) ); })* } #[test] fn saturating_sub() {$( assert_eq!($t::<5, 10>::MIN.saturating_sub(0), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.saturating_sub(1), $t::<5, 10>::MIN); )*} #[test] fn saturating_neg() {$(if_signed! { $signed assert_eq!($t::<5, 10>::MIN.saturating_neg(), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MAX.saturating_neg(), $t::<5, 10>::MIN); assert_eq!($t::<-10, 0>::MIN.saturating_neg(), $t::<-10, 0>::MAX); assert_eq!($t::<-10, 0>::MAX.saturating_neg(), $t::<-10, 0>::MAX); })*} #[test] fn saturating_abs() {$(if_signed! { $signed assert_eq!($t::<5, 10>::MIN.saturating_abs(), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MAX.saturating_abs(), $t::<5, 10>::MAX); assert_eq!($t::<-10, 0>::MIN.saturating_abs(), $t::<-10, 0>::MAX); assert_eq!($t::<-10, 0>::MAX.saturating_abs(), $t::<-10, 0>::MAX); })*} #[test] fn saturating_mul() {$( assert_eq!($t::<5, 10>::MIN.saturating_mul(0), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.saturating_mul(1), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.saturating_mul(2), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::MIN.saturating_mul(3), $t::<5, 10>::MAX); )*} #[test] fn saturating_pow() {$( assert_eq!($t::<5, 10>::MIN.saturating_pow(0), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.saturating_pow(1), $t::<5, 10>::MIN); assert_eq!($t::<5, 10>::MIN.saturating_pow(2), $t::<5, 10>::MAX); assert_eq!($t::<5, 10>::MIN.saturating_pow(3), $t::<5, 10>::MAX); )*} #[test] fn as_ref() {$( assert_eq!($t::<5, 10>::MIN.as_ref(), &5); assert_eq!($t::<5, 10>::MAX.as_ref(), &10); )*} #[test] fn borrow() { use std::borrow::Borrow; $( assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MIN), &5); assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MAX), &10); )* } #[test] fn formatting() {$( let val = $t::<5, 10>::MAX; assert_eq!(format!("{}", val), "10"); assert_eq!(format!("{:?}", val), "10"); assert_eq!(format!("{:b}", val), "1010"); assert_eq!(format!("{:o}", val), "12"); assert_eq!(format!("{:x}", val), "a"); assert_eq!(format!("{:X}", val), "A"); assert_eq!(format!("{:e}", val), "1e1"); assert_eq!(format!("{:E}", val), "1E1"); assert_eq!(format!("{:?}", $opt::Some($t::<5, 10>::MAX)), "Some(10)"); assert_eq!(format!("{:?}", $opt::<5, 10>::None), "None"); )*} #[test] fn ord() {$( assert!($t::<5, 10>::MIN < $t::<5, 10>::MAX); assert!($t::<5, 10>::MIN <= $t::<5, 10>::MAX); assert!($t::<5, 10>::MAX > $t::<5, 10>::MIN); assert!($t::<5, 10>::MAX >= $t::<5, 10>::MIN); let none = $opt::<5, 10>::None; let five = $opt::Some($t::<5, 10>::MIN); let ten = $opt::Some($t::<5, 10>::MAX); assert_eq!(none.cmp(&none), std::cmp::Ordering::Equal); assert_eq!(five.cmp(&five), std::cmp::Ordering::Equal); assert_eq!(ten.cmp(&ten), std::cmp::Ordering::Equal); assert_eq!(none.cmp(&five), std::cmp::Ordering::Less); assert_eq!(five.cmp(&ten), std::cmp::Ordering::Less); assert_eq!(none.cmp(&ten), std::cmp::Ordering::Less); assert_eq!(ten.cmp(&none), std::cmp::Ordering::Greater); let none = $opt::<0, 10>::None; let zero = $opt::Some($t::<0, 10>::MIN); let ten = $opt::Some($t::<0, 10>::MAX); assert_eq!(none.partial_cmp(&none), Some(std::cmp::Ordering::Equal)); assert_eq!(none.partial_cmp(&zero), Some(std::cmp::Ordering::Less)); assert_eq!(zero.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); assert_eq!(none.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); assert_eq!(ten.partial_cmp(&none), Some(std::cmp::Ordering::Greater)); )*} #[test] fn from() {$( assert_eq!($inner::from($t::<5, 10>::MAX), 10); assert_eq!($inner::from($t::<5, 10>::MIN), 5); assert_eq!($opt::from($t::<5, 10>::MAX), $opt::Some($t::<5, 10>::MAX)); assert_eq!($opt::from(Some($t::<5, 10>::MAX)), $opt::Some($t::<5, 10>::MAX)); assert_eq!($opt::<5, 10>::from(None), $opt::<5, 10>::None); assert_eq!(Option::from($opt::Some($t::<5, 10>::MAX)), Some($t::<5, 10>::MAX)); assert_eq!(Option::<$t<5, 10>>::from($opt::<5, 10>::None), None); )*} #[test] fn try_from() {$( assert_eq!($t::<5, 10>::try_from(10), Ok($t::<5, 10>::MAX)); assert_eq!($t::<5, 10>::try_from(5), Ok($t::<5, 10>::MIN)); assert_eq!($t::<5, 10>::try_from(4), Err(TryFromIntError)); assert_eq!($t::<5, 10>::try_from(11), Err(TryFromIntError)); )*} #[test] fn from_str() {$( assert_eq!("10".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MAX)); assert_eq!("5".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MIN)); assert_eq!("4".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::NegOverflow })); assert_eq!("11".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::PosOverflow })); assert_eq!("".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::Empty })); )*} #[cfg(feature = "serde")] #[test] fn serde() -> serde_json::Result<()> { $( let val = $t::<5, 10>::MAX; let serialized = serde_json::to_string(&val)?; assert_eq!(serialized, "10"); let deserialized: $t<5, 10> = serde_json::from_str(&serialized)?; assert_eq!(deserialized, val); assert!(serde_json::from_str::<$t<5, 10>>("").is_err()); assert!(serde_json::from_str::<$t<5, 10>>("4").is_err()); assert!(serde_json::from_str::<$t<5, 10>>("11").is_err()); let val = $opt::<5, 10>::Some($t::<5, 10>::MAX); let serialized = serde_json::to_string(&val)?; assert_eq!(serialized, "10"); let deserialized: $opt<5, 10> = serde_json::from_str(&serialized)?; assert_eq!(deserialized, val); assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); let val = $opt::<5, 10>::None; let serialized = serde_json::to_string(&val)?; assert_eq!(serialized, "null"); assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); )* Ok(()) } #[cfg(feature = "rand")] #[test] fn rand() {$( let rand_val: $t<5, 10> = rand::random(); assert!(rand_val >= $t::<5, 10>::MIN); assert!(rand_val <= $t::<5, 10>::MAX); let rand: $opt<5, 10> = rand::random(); if let Some(rand) = rand.get() { assert!(rand >= $t::<5, 10>::MIN); assert!(rand <= $t::<5, 10>::MAX); } )*} #[cfg(feature = "num")] #[test] fn num() {$( assert_eq!(<$t<5, 10> as num_traits::Bounded>::min_value(), $t::<5, 10>::MIN); assert_eq!(<$t<5, 10> as num_traits::Bounded>::max_value(), $t::<5, 10>::MAX); )*} #[cfg(feature = "quickcheck")] #[test] fn quickcheck() {$( #[allow(trivial_casts)] quickcheck::quickcheck((|val| { val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX }) as fn($t<5, 10>) -> bool); #[allow(trivial_casts)] quickcheck::quickcheck((|val| { if let Some(val) = val.get() { val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX } else { true } }) as fn($opt<5, 10>) -> bool); )*} }; } tests![ signed OptionRangedI8 RangedI8 i8, signed OptionRangedI16 RangedI16 i16, signed OptionRangedI32 RangedI32 i32, signed OptionRangedI64 RangedI64 i64, signed OptionRangedI128 RangedI128 i128, signed OptionRangedIsize RangedIsize isize, unsigned OptionRangedU8 RangedU8 u8, unsigned OptionRangedU16 RangedU16 u16, unsigned OptionRangedU32 RangedU32 u32, unsigned OptionRangedU64 RangedU64 u64, unsigned OptionRangedU128 RangedU128 u128, unsigned OptionRangedUsize RangedUsize usize, ]; deranged-0.3.11/src/traits.rs000064400000000000000000000064111046102023000141240ustar 00000000000000use crate::{ RangedI128, RangedI16, RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, RangedU32, RangedU64, RangedU8, RangedUsize, }; macro_rules! declare_traits { ($($trait_name:ident),* $(,)?) => {$( pub(crate) trait $trait_name { const ASSERT: (); } )*}; } macro_rules! impl_traits_for_all { ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( impl RangeIsValid for $ranged_ty { const ASSERT: () = assert!(MIN <= MAX); } impl< const CURRENT_MIN: $inner_ty, const CURRENT_MAX: $inner_ty, const NEW_MIN: $inner_ty, const NEW_MAX: $inner_ty, > ExpandIsValid for ($ranged_ty, $ranged_ty) { const ASSERT: () = { assert!(NEW_MIN <= CURRENT_MIN); assert!(NEW_MAX >= CURRENT_MAX); }; } impl< const CURRENT_MIN: $inner_ty, const CURRENT_MAX: $inner_ty, const NEW_MIN: $inner_ty, const NEW_MAX: $inner_ty, > NarrowIsValid for ($ranged_ty, $ranged_ty) { const ASSERT: () = { assert!(NEW_MIN >= CURRENT_MIN); assert!(NEW_MAX <= CURRENT_MAX); }; } impl< const VALUE: $inner_ty, const MIN: $inner_ty, const MAX: $inner_ty, > StaticIsValid for ($ranged_ty, $ranged_ty) { const ASSERT: () = { assert!(VALUE >= MIN); assert!(VALUE <= MAX); }; } )*}; } macro_rules! impl_traits_for_signed { ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( impl AbsIsSafe for $ranged_ty { const ASSERT: () = { assert!(MIN != <$inner_ty>::MIN); assert!(-MIN <= MAX); }; } impl NegIsSafe for $ranged_ty { const ASSERT: () = { assert!(MIN != <$inner_ty>::MIN); assert!(-MIN <= MAX); assert!(-MAX >= MIN); }; } impl_traits_for_all!($ranged_ty $inner_ty); )*}; } macro_rules! impl_traits_for_unsigned { ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( impl AbsIsSafe for $ranged_ty { const ASSERT: () = (); } impl NegIsSafe for $ranged_ty { const ASSERT: () = assert!(MAX == 0); } impl_traits_for_all!($ranged_ty $inner_ty); )*}; } declare_traits![ RangeIsValid, AbsIsSafe, NegIsSafe, ExpandIsValid, NarrowIsValid, StaticIsValid, ]; impl_traits_for_signed! { RangedI8 i8, RangedI16 i16, RangedI32 i32, RangedI64 i64, RangedI128 i128, RangedIsize isize, } impl_traits_for_unsigned! { RangedU8 u8, RangedU16 u16, RangedU32 u32, RangedU64 u64, RangedU128 u128, RangedUsize usize, } deranged-0.3.11/src/unsafe_wrapper.rs000064400000000000000000000010531046102023000156340ustar 00000000000000#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct Unsafe(T); impl core::fmt::Debug for Unsafe { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.0.fmt(f) } } impl Unsafe { pub(crate) const unsafe fn new(value: T) -> Self { Self(value) } pub(crate) const fn get(&self) -> &T { &self.0 } } impl core::ops::Deref for Unsafe { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } }