# Light ranged integers
Ranged integers for stable Rust compiler.
# Why
The concept of constraining an integer variable to a specific range comes from solid languages like [Ada](https://en.wikibooks.org/wiki/Ada_Programming/Types/range).
In Rust we have the [ranged_integers](https://docs.rs/ranged_integers/latest/ranged_integers/) package, which is really powerful, with automatic type size and automatic interval limits extension; however, it currently needs a Nightly compiler along with enabling some experimental compiler features, as it needs more advanced control of constant expressions and generics.
While having more compile time automation is welcome, runtime checking is good enough for most situations.
In fact, even in Ada range types are mostly runtime checked, excluding the Range declaration and the very first initialization. If runtime checking is good enough for Ada's high safety standards, it's probably good enough for your use case, but that's up to you to decide.
Compared to Rust ranged_integers package, this project has no automatic type size detection and boundary limit extension/shrinking, so it works on a stable compiler version, has only one dependency (paste macro) and a very small codebase.
# Usage
A Range type guarantees that the contained number fits the range from MIN to MAX, extremes included. MIN must be smaller than MAX, or it will refuse to compile.
The library offers Range types, declared in this form
```rust,ignore
RangedU16<MIN, MAX, OpMode>
```
where:
- `MIN` is a lower limit of the interval
- `MAX` is the upper limit of the interval
- `OpMode` selects how to behave when a number goes out of range
```rust
use light_ranged_integers::{RangedU16, op_mode::Panic};
// Create a value of 3 in a range from 1 to 6, extremes included
let n1 = RangedU16::<1,6, Panic>::new(3);
```
It's also possible to check the initialization value fits the range
at compile time
```rust
// Create a value of 3 in a range from 1 to 6, extremes included
// This is checked at compile time
use light_ranged_integers::RangedU16;
let n1 = RangedU16::<1,6>::const_new::<3>();
```
## Modes
When you declare a Ranged type, you can select a behaviour to apply when the number goes outside range.
There are currently three modes: `Panic`, `Clamp` and `Wrap`.
### Panic
Panic mode is the default. If a number falls outside the range, the program panics.
```rust,should_panic
use light_ranged_integers::{RangedU16, op_mode::Panic};
// Create a ranged u16, interval [1,6], in Panic mode.
let n1 = RangedU16::<1,6, Panic>::new(5);
// As Panic mode is the default, you can also initialize using only the range
let n1 = RangedU16::<1,6>::new(5);
let res = n1+5;// This line will panic, as 5+5=10 is out of range
```
### Clamp
In Clamp mode, a number not fitting the interval is clamped to its closest extreme.
For example, a 10 will be clamped to 6 when you try to fit it into a [1, 6] interval.
```rust
use light_ranged_integers::{RangedU16, op_mode::Clamp};
// Create a ranged u16, interval [1,6], in Clamp mode.
let n1 = RangedU16::<1,6, Clamp>::new(3);
assert_eq!(n1,3);
// Here 10 is clamped to a 6 to fit into range
let n2 = RangedU16::<1,6, Clamp>::new(10);
assert_eq!(n2,6);
// We're in clamp mode, so 3+6=9 is clamped at 6 automatically
assert_eq!(n1+n2, 6)
```
### Wrap
(WORK IN PROGRESS) Wrap mode.
In this mode, out of range numbers are wrapped to fit the interval.
## Range limits
Range limits are not expanded or shrinked automatically in order to be able to use stable Rust and to prevent unexpected range change.
```rust
use light_ranged_integers::{RangedU16, op_mode::Clamp};
let n1 = RangedU16::<1,6, Clamp>::new(3);
let n2 = RangedU16::<1,6, Clamp>::new(6);
// Expand the limit of each operand to 1-12
// Then 3+6=9 fits the range and 9 is not clamped
assert_eq!(
n1.limit::<1,12>()
+
n2.limit::<1,12>(),
9
);
```
The library offers some additional safety restrictions.
You can't compile code that computes operations in which the operands have different range.
We can't assume the desired output range, if it should be the first one, the second one, a shrinked range or an extended range. It's better to not compile and let the user manually set the limits.
```rust,compile_fail
use light_ranged_integers::RangedU8;
// Does NOT compile, different ranges
RangedU8::<0,1>::new(0) + RangedU8::<0,2>::new(0)
```
Also you can't do operations between values with different Modes (i.e. can't add a Clamp mode value with a Panic mode one).
We can't assume the desired mode to use, so it's better to not compile at all.
```rust,compile_fail
use light_ranged_integers::{RangedU8, op_mode::{Clamp, Panic}};
// Does NOT compile, different mode (Panic vs Clamp)
RangedU8::<0, 1, Panic>::new(0) + RangedU8::<0, 1, Clamp>::new(0)
```
# Copyright
This software is free and open source software, as defined by its license, this is **NOT** public domain, make sure to respect the license terms.
You can find the license text in the [COPYING](https://gitlab.com/MassiminoilTrace/light-ranged-integers/-/blob/master/COPYING) file.
Copyright © 2024 Massimo Gismondi
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
## Dependencies
This project leverages the [paste](https://github.com/dtolnay/paste) crate, published under either of Apache License, Version 2.0 or MIT license at your option.