1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! ## About
//! This crate implements traits and types that allows you to implement type-pinned length
//! constraints in your API.
//!
//!
//! ## Why?
//! How often have you seen APIs like this?
//! ```ignore
//! // BAD EXAMPLE!
//!
//! fn encrypt(buf: &mut[u8], plaintext: &[u8], key: &[u8], nonce: &[u8])
//! 	-> Result<usize, Box<dyn Error + 'static>>
//! {
//! 	// Validate parameters
//! 	if plaintext.len() > 65_635 { Err("Plaintext is too large")? }
//! 	if buf.len() < plaintext.len() + 16 { Err("Buffer is smaller than plaintext length")? }
//! 	if key.len() != 32 { Err("Invalid key size")? }
//! 	if nonce.len() != 12 { Err("Invalid nonce size")? }
//!
//! 	// Do sth.
//! 	unimplemented!()
//! }
//! ```
//! As you can see, this API is pretty opaque and requires a lot of manual checks.
//!
//! Of course s.o. could use array references:
//! ```ignore
//! // MEH EXAMPLE...
//!
//! fn encrypt(buf: &mut[u8], plaintext: &[u8], key: &[u8; 32], nonce: &[u8; 12])
//! 	-> Result<usize, Box<dyn Error + 'static>>
//! {
//! 	// Validate parameters
//! 	if plaintext.len() > 65_635 { Err("Plaintext is too large")? }
//! 	if buf.len() < plaintext.len() + 16 { Err("Buffer is smaller than plaintext length")? }
//!
//! 	// Do sth.
//! 	unimplemented!()
//! }
//! ```
//! But array references also have their disadvantages. They are not suitable for multiple valid
//! lengths (allow anything in `16..=32`) nor can they represent relative relationships. Also
//! converting between other data types and arrays can get annoying.
//!
//! `len_constraints` tries to solve this problem:
//! ```
//! // GOOD EXAMPLE :D
//!
//! use std::{ convert::TryInto, error::Error };
//! use len_constraints::{
//! 	slice_mut::RelativeMut, slice::{ Fixed, Ranged },
//! 	type_math::{ Add, _0, _12, _16, _32, _65536 }
//! };
//!
//! fn encrypt(buf: RelativeMut<u8, Add, _16>, plaintext: Ranged<u8, _0, _65536>,
//! 	key: Fixed<u8, _32>, nonce: Fixed<u8, _12>) -> Result<usize, Box<Error + 'static>>
//! {
//! 	// Get buffer (we do this here because there may not be a relationship at an earlier stage)
//! 	let buf = buf.slice_mut(plaintext.len())?;
//!
//! 	// Do sth.
//! 	Ok(7)
//! }
//!
//! fn main() -> Result<(), Box<Error + 'static>> {
//! 	// Parameters
//! 	let mut buf: &mut[u8] = &mut[0; 9 + 16];
//! 	let plaintext: &[u8] = b"Testolope";
//! 	let key: &[u8] = &[0; 32];
//! 	let nonce = "12 byte Nonc".as_bytes();
//!
//! 	// Call function
//! 	encrypt(buf.into(), plaintext.try_into()?, key.try_into()?, nonce.try_into()?)?;
//! 	Ok(())
//! }
//! ```
//! As you can see, we now can describe complex relationships in the function signature – this makes
//! the API more transparent and removes the need for manual (and error-prone) parameter validation.


/// The `TypeNum` and `Operator` traits which can be used as type arguments as well as some
/// predefined numbers and operators.
#[macro_use] pub mod type_math;
/// Some wrappers for immutable slices with various length constraints
pub mod slice;
/// Some wrappers for mutable slices with various length constraints
pub mod slice_mut;
#[macro_use] mod constraint_macro;

pub use self::type_math::{ TypeNum, Operator };
use std::{
	convert::TryFrom, error::Error,
	fmt::{ self, Display, Formatter }
};


/// A constraint violation error
#[derive(Debug)]
pub struct ConstraintViolation {
	#[doc(hidden)]
	pub constraint: String,
	#[doc(hidden)]
	pub by: i128
}
impl ConstraintViolation {
	/// Creates a new error in case a fixed constraint was violated
	pub fn fixed<Val: TypeNum>(len: usize) -> Self {
		// Compute diff
		let by = i128::try_from(len).unwrap() - i128::try_from(Val::VALUE).unwrap();
		assert_ne!(by, 0, "Cannot construct `ConstraintViolation` for valid constraint");
		
		Self{ constraint: format!("{:?}", Val::default()), by }
	}
	/// Creates a new error in case a range constraint was violated
	pub fn ranged<Start: TypeNum, End: TypeNum>(len: usize) -> Self {
		// Prepare values
		let len = i128::try_from(len).unwrap();
		let (start, end) =
			(i128::try_from(Start::VALUE).unwrap(), i128::try_from(End::VALUE).unwrap());
		
		// Compute diff
		let by = match (start, end) {
			(start, _) if len < start => len - start,
			(_, end) if len >= end => len - (end - 1),
			_ => panic!("Cannot construct `ConstraintViolation` for valid constraint")
		};
		
		Self{ constraint: format!("Range<{:?} .. {:?}>", Start::default(), End::default()), by }
	}
	/// Creates a new error in case a relative constraint was violated
	pub fn relative<Op: Operator, By: TypeNum>(len: usize, other: usize) -> Self {
		// Compute the absolute length of the relative constraint
		let abs_len = Op::r#do(other, By::VALUE)
			.expect("Cannot construct `ConstraintViolation` for illegal constraint");
		
		// Compute diff
		let by = i128::try_from(len).unwrap() - i128::try_from(abs_len).unwrap();
		assert_ne!(by, 0, "Cannot construct `ConstraintViolation` for valid constraint");
		
		Self{ constraint: format!("{:?}({:?})", Op::default(), By::default()), by }
	}
}
impl Display for ConstraintViolation {
	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
		let sign = match self.by.is_positive() {
			true => "+",
			false => ""
		};
		write!(f, "The length constraint `{}` was violated by {}{}", self.constraint, sign, self.by)
	}
}
impl Error for ConstraintViolation {}