4 releases

0.2.0 Oct 10, 2022
0.1.2 May 14, 2021
0.1.1 Apr 16, 2021
0.1.0 Apr 16, 2021

#1254 in Rust patterns

MIT license

18KB
148 lines

Crate newstr

Simple macros for declaring String-base new types.

MIT License Minimum Rust Version crates.io docs.rs Build Audit GitHub stars

This crate provides simple macros that generate String based new types. The two primary macros implement the validity rules for the new type by either 1) providing a predicate that is used by the is_valid associated function, or 2) providing a function to parse and return a string which is then called by FromStr::from_str.

Both of these methods produce a new type, with the following:

  1. An associated predicate function is_valid that returns true if the string provided would be a valid value for the type.
  2. This type derives implementations of Clone, Debug, PartialEq, PartialOrd, Eq, Ord, and Hash.
  3. An implementation of Display for T that simply returns the inner value.
  4. An implementation of From<T> for String.
  5. An implementation of AsRef for T with the target type str.
  6. An implementation of Deref for T with the target type str.
  7. An implementation of FromStr.

Additional user-required traits can also be added to the macro to be derived by the implementation.

Example

The following example constructs a new string type that implements an Identifier value. This value must be ASCII, alphanumeric, the '_' character and must not be empty.

use std::fmt::{Display, Formatter};
use std::str::FromStr;

fn is_identifier_value(s: &str) -> bool {
    !s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}

is_valid_newstring!(Identifier, is_identifier_value);

assert!(!Identifier::is_valid(""));
assert!(!Identifier::is_valid("hi!"));
assert!(!Identifier::is_valid("hello world"));
assert!(!Identifier::is_valid("9.99"));

assert_eq!(
    Identifier::from_str("hi").unwrap().to_string(),
    String::from("hi")
);
assert_eq!(
    Identifier::from_str("hello_world").unwrap().to_string(),
    String::from("hello_world")
);

The macro new_unchecked will add a constructor to the type that allows a trusted client to bypass the validity checks.

new_unchecked!(pub(crate) Identifier);

assert_eq!(
    Identifier::from_str("hi").unwrap(),
    Identifier::new_unchecked("hi")
);

Dependencies

In the example above you can see the necessary use-statements for the trait implementations the macros generate. Unless you use regex_is_valid there are no crate dependencies; if you do you will need to add lazy_static and regex dependencies.

Changes

Version 0.2.0

  • Changes to the macro API;
    • use ; instead of , to separate the additional derive macros,
    • use full paths inside all macros, no need for the use_required macro any more,
    • clients will need to import any trait they then use such as FromStr.
  • Added new_unchecked macro for validity bypass.

Version 0.1.2

  • Added additional, optional, parameter to both macros that allows additional traits to be added to the derive attribute on the generated struct.
  • Refactored the macros to reduce repetition.
  • Added some additional tests.

Version 0.1.1

  • Added new use_required macro.
  • Removed unnecessary feature 'regex_is_valid', the build doesn't require this to avoid bloat.
  • Made lazy_static and regex dev dependencies, if you don't use them, you don't need them.
  • Added dependency on cargo-husky for Git cleanliness.

Version 0.1.0

  • Initial version.

No runtime deps