Skip to content

Commit

Permalink
refactoring length
Browse files Browse the repository at this point in the history
  • Loading branch information
tomoikey committed Oct 23, 2024
1 parent 05525e4 commit 1145287
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 382 deletions.
134 changes: 57 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,113 +657,93 @@ fn example_22() -> anyhow::Result<()> {

You can impose constraints on objects that have a length, such as `String` or `Vec`.

### String
## `LengthMinMax`
`LengthMinMax` is a type that signifies the target has a length between a certain number and another number.

```rust
fn example_23() -> Result<(), Error> {
length_greater_than!(5);
length_equal!(5, 10);
length_less_than!(10);
fn length_min_max_example() -> Result<(), Error<String>> {
type Password = LengthMinMax<5, 10, String>;

type Password = Refined<From5To10Rule<String>>;
let password = Password::new("123456".to_string())?;
assert_eq!(password.into_value(), "123456");

type From5To10Rule<T> = And![
Or![LengthEqualRule5<T>, LengthGreaterThanRule5<T>],
Or![LengthLessThanRule10<T>, LengthEqualRule10<T>]
];

// length is 8. so, this is valid
let raw_password = "password";
let password = Password::new(raw_password.to_string())?;
assert_eq!(password.into_value(), "password");
let password = Password::new("1234".to_string());
assert!(password.is_err());

// length is 4. so, this is invalid
let raw_password = "pswd";
let password = Password::new(raw_password.to_string());
let password = Password::new("12345678901".to_string());
assert!(password.is_err());

// length is 17. so, this is invalid
let raw_password = "password password";
let password = Password::new(raw_password.to_string());
Ok(())
}
```

## `LengthGreater`
`LengthGreater` is a type that signifies the target has a length greater than a certain number.

```rust
fn length_greater_example() -> Result<(), Error<String>> {
type Password = LengthGreater<5, String>;

let password = Password::new("123456".to_string())?;
assert_eq!(password.into_value(), "123456");

let password = Password::new("1234".to_string());
assert!(password.is_err());

Ok(())
}
```

### Vec
## `LengthLess`
`LengthLess` is a type that signifies the target has a length less than a certain number.

```rust
#[test]
fn example_24() -> anyhow::Result<()> {
length_greater_than!(5);
length_equal!(5, 10);
length_less_than!(10);
fn length_less_example() -> Result<(), Error<String>> {
type Password = LengthLess<10, String>;

type Friends = Refined<From5To10Rule<Vec<String>>>;
let password = Password::new("123456".to_string())?;
assert_eq!(password.into_value(), "123456");

type From5To10Rule<T> = And![
Or![LengthEqualRule5<T>, LengthGreaterThanRule5<T>],
Or![LengthLessThanRule10<T>, LengthEqualRule10<T>],
];
let password = Password::new("12345678901".to_string());
assert!(password.is_err());

// length is 6. so, this is valid
let raw_friends = vec![
"Tom".to_string(),
"Taro".to_string(),
"Jiro".to_string(),
"Hanako".to_string(),
"Sachiko".to_string(),
"Yoshiko".to_string(),
];
let friends = Friends::new(raw_friends.clone())?;
assert_eq!(friends.into_value(), raw_friends);

// length is 2. so, this is invalid
let raw_friends = vec!["Tom".to_string(), "Taro".to_string()];
let friends = Friends::new(raw_friends.clone());
assert!(friends.is_err());

// length is 11. so, this is invalid
let raw_friends = vec![
"Tom".to_string(),
"Taro".to_string(),
"Jiro".to_string(),
"Hanako".to_string(),
"Sachiko".to_string(),
"Yuiko".to_string(),
"Taiko".to_string(),
"John".to_string(),
"Jane".to_string(),
"Jack".to_string(),
"Jill".to_string(),
];
let friends = Friends::new(raw_friends.clone());
assert!(friends.is_err());
Ok(())
}
```

## `LengthEqual`
`LengthEqual` is a type that signifies the target has a length equal to a certain number.

```rust
fn length_equal_example() -> Result<(), Error<String>> {
type Password = LengthEqual<5, String>;

let password = Password::new("12345".to_string())?;
assert_eq!(password.into_value(), "12345");

let password = Password::new("1234".to_string());
assert!(password.is_err());

Ok(())
}
```

### Custom Length
## Custom Length

You can define a length for any type. Therefore, if you want to implement a length that is not provided
by `refined_type`, you can easily do so using `LengthDefinition`.

```rust
#[test]
fn example_25() -> anyhow::Result<()> {
length_equal!(5);

#[derive(Debug, PartialEq)]
struct Hello;
impl LengthDefinition for Hello {
fn length(&self) -> usize {
5
}
#[derive(Debug, PartialEq)]
struct Hello;
impl LengthDefinition for Hello {
fn length(&self) -> usize {
5
}
}

let hello = Refined::<LengthEqualRule5<Hello>>::new(Hello)?;
fn custom_length_example() -> Result<(), Error<Hello>> {
let hello = Refined::<LengthEqualRule<5, Hello>>::new(Hello)?;
assert_eq!(hello.into_value(), Hello);
Ok(())
}
Expand Down
6 changes: 6 additions & 0 deletions src/rule/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ mod equal;
mod grater;
mod length_definition;
mod less;
mod min_max;

pub use equal::*;
pub use grater::*;
pub use less::*;
pub use min_max::*;
74 changes: 26 additions & 48 deletions src/rule/length/equal.rs
Original file line number Diff line number Diff line change
@@ -1,83 +1,61 @@
/// This macro generates a rule that checks if the length of the target is equal to `N`
/// # Example
/// ```rust
/// use refined_type::length_equal;
/// length_equal!(5);
///
/// let target = "12345";
/// let refined = LengthEqual5::new(target).unwrap();
/// assert_eq!(refined.into_value(), "12345");
///
/// let target = "1234";
/// let refined = LengthEqual5::new(target);
/// assert!(refined.is_err());
/// ```
#[macro_export]
macro_rules! length_equal {
($length:literal) => {
$crate::paste::item! {
/// A type that holds a value satisfying the LengthEqualN rule.
#[allow(dead_code)]
pub type [<LengthEqual $length>]<ITEM> = $crate::Refined<[<LengthEqualRule $length>]<ITEM>>;
use crate::result::Error;
use crate::rule::{LengthDefinition, Rule};
use crate::Refined;

/// Rule where the length of the input value is equal to N
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct [<LengthEqualRule $length>]<ITEM> {
_phantom: ::std::marker::PhantomData<ITEM>,
}
/// A type that holds a value satisfying the `LengthEqualRule`
pub type LengthEqual<const LENGTH: usize, ITEM> = Refined<LengthEqualRule<LENGTH, ITEM>>;

impl<ITEM> $crate::rule::Rule for [<LengthEqualRule $length>]<ITEM> where ITEM: $crate::rule::LengthDefinition {
type Item = ITEM;
fn validate(target: Self::Item) -> Result<Self::Item, $crate::result::Error<Self::Item>> {
if target.length() == $length {
Ok(target)
} else {
Err($crate::result::Error::new(target, format!("target length is not equal to {}", $length)))
}
}
}
/// Rule where the input `ITEM` has a length equal to `LENGTH`
pub struct LengthEqualRule<const LENGTH: usize, ITEM> {
_phantom: std::marker::PhantomData<ITEM>,
}

impl<const LENGTH: usize, ITEM: LengthDefinition> Rule for LengthEqualRule<LENGTH, ITEM> {
type Item = ITEM;
fn validate(target: Self::Item) -> Result<Self::Item, Error<Self::Item>> {
if target.length() == LENGTH {
Ok(target)
} else {
Err(Error::new(
target,
format!("target length is not equal to {}", LENGTH),
))
}
};
($length:literal, $($lengths:literal),+) => {
length_equal!($length);
length_equal!($($lengths),+);
};
}
}

#[cfg(test)]
mod tests {
use crate::result::Error;

length_equal!(5, 10);
use crate::rule::length::equal::LengthEqual;

#[test]
fn test_length_equal_5() -> Result<(), Error<&'static str>> {
let target = "12345";
let refined = LengthEqual5::new(target)?;
let refined = LengthEqual::<5, _>::new(target)?;
assert_eq!(refined.into_value(), "12345");
Ok(())
}

#[test]
fn test_length_equal_5_fail() {
let target = "1234";
let refined = LengthEqual5::new(target);
let refined = LengthEqual::<5, _>::new(target);
assert!(refined.is_err());
}

#[test]
fn test_length_equal_10() -> Result<(), Error<&'static str>> {
let target = "1234567890";
let refined = LengthEqual10::new(target)?;
let refined = LengthEqual::<10, _>::new(target)?;
assert_eq!(refined.into_value(), "1234567890");
Ok(())
}

#[test]
fn test_length_equal_10_fail() {
let target = "123456789";
let refined = LengthEqual10::new(target);
let refined = LengthEqual::<10, _>::new(target);
assert!(refined.is_err());
}
}
73 changes: 26 additions & 47 deletions src/rule/length/grater.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,61 @@
/// This macro generates a rule that checks if the length of the target is greater than `N`
/// # Example
/// ```rust
/// use refined_type::length_greater_than;
/// length_greater_than!(5);
///
/// let target = "123456";
/// let refined = LengthGreaterThan5::new(target).unwrap();
/// assert_eq!(refined.into_value(), "123456");
///
/// let target = "12345";
/// let refined = LengthGreaterThan5::new(target);
/// assert!(refined.is_err());
#[macro_export]
macro_rules! length_greater_than {
($length:literal) => {
$crate::paste::item! {
/// A type that holds a value satisfying the LengthGreaterThanN rule.
#[allow(dead_code)]
pub type [<LengthGreaterThan $length>]<ITEM> = $crate::Refined<[<LengthGreaterThanRule $length>]<ITEM>>;
use crate::result::Error;
use crate::rule::{LengthDefinition, Rule};
use crate::Refined;

/// Rule where the length of the input value is greater than N
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct [<LengthGreaterThanRule $length>]<ITEM> {
_phantom: ::std::marker::PhantomData<ITEM>,
}
/// A type that holds a value satisfying the `LengthGreaterRule`
pub type LengthGreater<const THAN: usize, ITEM> = Refined<LengthGreaterRule<THAN, ITEM>>;

impl<ITEM> $crate::rule::Rule for [<LengthGreaterThanRule $length>]<ITEM> where ITEM: $crate::rule::LengthDefinition {
type Item = ITEM;
fn validate(target: Self::Item) -> Result<Self::Item, $crate::result::Error<Self::Item>> {
if target.length() > $length {
Ok(target)
} else {
Err($crate::result::Error::new(target, format!("target length is not greater than {}", $length)))
}
}
}
/// Rule where the input `ITEM` has a length greater than `THAN`
pub struct LengthGreaterRule<const THAN: usize, ITEM> {
_phantom: std::marker::PhantomData<ITEM>,
}

impl<const THAN: usize, ITEM: LengthDefinition> Rule for LengthGreaterRule<THAN, ITEM> {
type Item = ITEM;
fn validate(target: Self::Item) -> Result<Self::Item, Error<Self::Item>> {
if target.length() > THAN {
Ok(target)
} else {
Err(Error::new(
target,
format!("target length is not greater than {}", THAN),
))
}
};
($length:literal, $($lengths:literal),+) => {
length_greater_than!($length);
length_greater_than!($($lengths),+);
};
}
}

#[cfg(test)]
mod tests {
use crate::result::Error;

length_greater_than!(5, 10);
use crate::rule::length::grater::LengthGreater;

#[test]
fn test_length_greater_than_5() -> Result<(), Error<&'static str>> {
let target = "123456";
let refined = LengthGreaterThan5::new(target)?;
let refined = LengthGreater::<5, _>::new(target)?;
assert_eq!(refined.into_value(), "123456");
Ok(())
}

#[test]
fn test_length_greater_than_5_fail() {
let target = "1234";
let refined = LengthGreaterThan5::new(target);
let refined = LengthGreater::<5, _>::new(target);
assert!(refined.is_err());
}

#[test]
fn test_length_greater_than_10() -> Result<(), Error<&'static str>> {
let target = "12345678901";
let refined = LengthGreaterThan10::new(target)?;
let refined = LengthGreater::<10, _>::new(target)?;
assert_eq!(refined.into_value(), "12345678901");
Ok(())
}

#[test]
fn test_length_greater_than_10_fail() {
let target = "123456789";
let refined = LengthGreaterThan10::new(target);
let refined = LengthGreater::<10, _>::new(target);
assert!(refined.is_err());
}
}
Loading

0 comments on commit 1145287

Please sign in to comment.