[−][src]Crate len_constraints
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?
// 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:
// 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.
Re-exports
pub use self::type_math::TypeNum; |
pub use self::type_math::Operator; |
Modules
slice | Some wrappers for immutable slices with various length constraints |
slice_mut | Some wrappers for mutable slices with various length constraints |
type_math | The |
Macros
constraints | Convert multiple variables in-place into their constrained representation |
type_num | Creates a new type number representing |
Structs
ConstraintViolation | A constraint violation error |