Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl/collection #20

Merged
merged 20 commits into from
Sep 18, 2024
Merged
237 changes: 199 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ type TargetAgeRule = And![TargetAge18OrMore, TargetAge80OrLess];

# Iterator

I have also prepared several useful refined types for Iterators.
`refined_type` has several useful refined types for Iterators.

## `ForAll`

Expand Down Expand Up @@ -345,6 +345,198 @@ fn example_12() -> anyhow::Result<()> {
}
```

# `Head`

`Head` is a rule that applies a specific rule to the first element in the Iterator.

```rust
fn example_13() -> anyhow::Result<()> {
let table = vec![
(vec!["good morning".to_string(), "".to_string()], true), // PASS
(vec!["hello".to_string(), "hello".to_string()], true), // PASS
(vec![], false), // FAIL
(vec!["".to_string()], false), // FAIL
(vec!["".to_string(), "hello".to_string()], false), // FAIL
];

for (value, ok) in table {
let head = HeadVec::<NonEmptyStringRule>::new(value.clone());
assert_eq!(head.is_ok(), ok);
}

Ok(())
}
```

# `Last`

`Last` is a rule that applies a specific rule to the last element in the Iterator.

```rust
fn example_14() -> anyhow::Result<()> {
let table = vec![
(vec!["".to_string(), "hello".to_string()], true), // PASS
(vec!["good morning".to_string(), "hello".to_string()], true), // PASS
(vec![], false), // FAIL
(vec!["".to_string()], false), // FAIL
(vec!["hello".to_string(), "".to_string()], false), // FAIL
];

for (value, ok) in table {
let last = LastVec::<NonEmptyStringRule>::new(value.clone());
assert_eq!(last.is_ok(), ok);
}

Ok(())
}
```

# `Tail`

`Tail` is a rule that applies a specific rule to all elements except the first element in the Iterator.

```rust
fn example_15() -> anyhow::Result<()> {
let table = vec![
(vec!["hey".to_string(), "hello".to_string(), "world".to_string()], true),
(vec!["hey".to_string(), "hello".to_string(), "".to_string()], false),
(vec!["hey".to_string(), "".to_string(), "world".to_string()], false),
(vec!["hey".to_string(), "".to_string(), "".to_string()], false),
(vec!["".to_string(), "hello".to_string(), "world".to_string()], true),
(vec!["".to_string(), "hello".to_string(), "".to_string()], false),
(vec!["".to_string(), "".to_string(), "world".to_string()], false),
(vec!["".to_string(), "".to_string(), "".to_string()], false),
];

for (value, ok) in table {
let tail = TailVec::<NonEmptyStringRule>::new(value.clone());
assert_eq!(tail.is_ok(), ok);
}

Ok(())
}
```

# `Init`

`Init` is a rule that applies a specific rule to all elements except the last element in the Iterator.

```rust
fn example_16() -> anyhow::Result<()> {
let table = vec![
(vec!["hey".to_string(), "hello".to_string(), "world".to_string()], true),
(vec!["hey".to_string(), "hello".to_string(), "".to_string()], true),
(vec!["hey".to_string(), "".to_string(), "world".to_string()], false),
(vec!["hey".to_string(), "".to_string(), "".to_string()], false),
(vec!["".to_string(), "hello".to_string(), "world".to_string()], false),
(vec!["".to_string(), "hello".to_string(), "".to_string()], false),
(vec!["".to_string(), "".to_string(), "world".to_string()], false),
(vec!["".to_string(), "".to_string(), "".to_string()], false),
];

for (value, ok) in table {
let init = InitVec::<NonEmptyStringRule>::new(value.clone());
assert_eq!(init.is_ok(), ok);
}

Ok(())
}
```

# `Index`

`Index` is a rule that applies a specific rule to the element at a specific index in the Iterator.

```rust
fn example_17() -> anyhow::Result<()> {
let table = vec![
(vec!["good morning".to_string(), "hello".to_string()], true),
(vec!["good morning".to_string(), "".to_string()], false),
(vec!["".to_string(), "hello".to_string()], true),
(vec!["".to_string(), "".to_string()], false),
];

for (value, expected) in table {
let refined = Index1Vec::<NonEmptyStringRule>::new(value.clone());
assert_eq!(refined.is_ok(), expected);
}

Ok(())
}
```

# `Reverse`

`Reverse` is a rule that applies a specific rule to all elements in the Iterator in reverse order.
`refined_type` crate has `Index0` to `Index10` by default.

```rust
fn example_18() -> Result<(), Error<Vec<i32>>> {
let table = vec![
(vec!["good morning".to_string(), "hello".to_string()], true),
(vec!["good morning".to_string(), "".to_string()], false),
(vec!["".to_string(), "hello".to_string()], true),
(vec!["".to_string(), "".to_string()], false),
];

for (value, expected) in table {
let refined = Reverse::<Index0VecRule<NonEmptyStringRule>, _>::new(value.clone());
assert_eq!(refined.is_ok(), expected);
}

Ok(())
}
```

if you need more, you can define it like this.

```rust
define_index_refined!(11, 12, 13);
define_index_rule!(11, 12, 13);
```

# `Skip`

`Skip` is a rule that applies a specific rule to the elements of the Iterator while skipping the elements according
to `SkipOption`.

```rust
fn example_19() -> Result<(), Error<Vec<i32>>> {
let table = vec![
(vec!["hey".to_string(), "hello".to_string(), "world".to_string()], true),
(vec!["hey".to_string(), "hello".to_string(), "".to_string()], false),
(vec!["hey".to_string(), "".to_string(), "world".to_string()], false),
(vec!["hey".to_string(), "".to_string(), "".to_string()], false),
(vec!["".to_string(), "hello".to_string(), "world".to_string()], true),
(vec!["".to_string(), "hello".to_string(), "".to_string()], false),
(vec!["".to_string(), "".to_string(), "world".to_string()], false),
(vec!["".to_string(), "".to_string(), "".to_string()], false),
];

for (value, ok) in table {
let init = SkipVec::<NonEmptyStringRule, SkipFirst<_>>::new(value.clone());
assert_eq!(init.is_ok(), ok);
}

Ok(())
}
```

if you need more skip option, you can define it like this.

```rust
pub struct NoSkip<T> {
_phantom_data: std::marker::PhantomData<T>,
}

impl<ITEM> SkipOption for NoSkip<ITEM> {
type Item = ITEM;
fn should_skip(_: usize, _: &Self::Item) -> bool {
false
}
}
```

---

## `into_iter()` and `iter()`
Expand All @@ -356,7 +548,7 @@ Feel free to explore the capabilities of the Iterator you’ve been given!
### `into_iter()`

```rust
fn example_11() -> anyhow::Result<()> {
fn example_20() -> anyhow::Result<()> {
let ne_vec = NonEmptyVec::new(vec![1, 2, 3])?;
let ne_vec: NonEmptyVec<i32> = ne_vec.into_iter().map(|n| n * 2).map(|n| n * 3).collect();
assert_eq!(ne_vec.into_value(), vec![6, 12, 18]);
Expand All @@ -367,7 +559,7 @@ fn example_11() -> anyhow::Result<()> {
### `iter()`

```rust
fn example_12() -> anyhow::Result<()> {
fn example_21() -> anyhow::Result<()> {
let ne_vec = NonEmptyVec::new(vec![1, 2, 3])?;
let ne_vec: NonEmptyVec<i32> = ne_vec.iter().map(|n| n * 2).map(|n| n * 3).collect();
assert_eq!(ne_vec.into_value(), vec![6, 12, 18]);
Expand All @@ -378,53 +570,22 @@ fn example_12() -> anyhow::Result<()> {
### `NonEmptyVec` to `NonEmptyVecDeque` using `collect()`

```rust
fn example_13() -> anyhow::Result<()> {
fn example_22() -> anyhow::Result<()> {
let ne_vec = NonEmptyVec::new(vec![1, 2, 3])?;
let ne_vec_deque: NonEmptyVecDeque<i32> = ne_vec.into_iter().collect();
assert_eq!(ne_vec_deque.into_value(), vec![1, 2, 3]);
Ok(())
}
```

# Add Trait

I have implemented the `Add` trait for a part of the `Refined` that I provided. Therefore, operations can be performed
without downgrading the type level.

### NonEmptyString

```rust
fn example_14() -> anyhow::Result<()> {
let non_empty_string_1 = NonEmptyString::new("Hello".to_string())?;
let non_empty_string_2 = NonEmptyString::new("World".to_string())?;
let non_empty_string = non_empty_string_1 + non_empty_string_2; // This is also `NonEmptyString` type

assert_eq!(non_empty_string.into_value(), "HelloWorld");
Ok(())
}
```

### NonEmptyVec

```rust
fn example_15() -> anyhow::Result<()> {
let ne_vec_1 = NonEmptyVec::new(vec![1, 2, 3])?;
let ne_vec_2 = NonEmptyVec::new(vec![4, 5, 6])?;
let ne_vec = ne_vec_1 + ne_vec_2; // This is also `NonEmptyVec` type

assert_eq!(ne_vec.into_value(), vec![1, 2, 3, 4, 5, 6]);
Ok(())
}
```

# Length

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

### String

```rust
fn example_16() -> Result<(), Error> {
fn example_23() -> Result<(), Error> {
length_greater_than!(5);
length_equal!(5, 10);
length_less_than!(10);
Expand Down Expand Up @@ -459,7 +620,7 @@ fn example_16() -> Result<(), Error> {

```rust
#[test]
fn example_17() -> anyhow::Result<()> {
fn example_24() -> anyhow::Result<()> {
length_greater_than!(5);
length_equal!(5, 10);
length_less_than!(10);
Expand Down Expand Up @@ -516,7 +677,7 @@ by `refined_type`, you can easily do so using `LengthDefinition`.

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

#[derive(Debug, PartialEq)]
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ pub use refined::Refined;
mod refined;
pub mod result;
pub mod rule;

pub use result::Result;
35 changes: 22 additions & 13 deletions src/refined.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};

use crate::result::Error;
use crate::rule::Rule;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Debug, Display, Formatter};

/// Refined is a versatile type in ensuring that `T` satisfies the conditions of `RULE` (predicate type)
/// # Example
Expand Down Expand Up @@ -57,13 +56,23 @@ impl<RULE, T> Refined<RULE>
where
RULE: Rule<Item = T>,
{
pub fn new(value: T) -> Result<Self, Error> {
RULE::validate(&value).map_err(|e| Error::new(e.to_string()))?;
pub fn new(value: T) -> Result<Self, Error<T>> {
let value = RULE::validate(value).map_err(|e| {
let message = e.to_string();
Error::new(e.into_value(), message)
})?;
Ok(Self { value })
}

pub fn unsafe_new(value: T) -> Self {
RULE::validate(&value).expect("initialization by `unsafe_new` failed");
pub fn unsafe_new(value: T) -> Self
where
T: Debug,
{
let value = RULE::validate(value).expect("initialization by `unsafe_new` failed");
Self { value }
}

pub(crate) fn new_unchecked(value: T) -> Self {
Self { value }
}

Expand Down Expand Up @@ -109,7 +118,7 @@ mod test {
}

#[test]
fn test_refined_non_empty_string_ok() -> Result<(), Error> {
fn test_refined_non_empty_string_ok() -> Result<(), Error<String>> {
let non_empty_string = Refined::<NonEmptyStringRule>::new("Hello".to_string())?;
assert_eq!(non_empty_string.value, "Hello");
Ok(())
Expand All @@ -123,14 +132,14 @@ mod test {
}

#[test]
fn test_refined_display() -> Result<(), Error> {
fn test_refined_display() -> Result<(), Error<String>> {
let non_empty_string = Refined::<NonEmptyStringRule>::new("Hello".to_string())?;
assert_eq!(format!("{}", non_empty_string), "Hello");
Ok(())
}

#[test]
fn test_refined_serialize_json_string() -> anyhow::Result<()> {
fn test_refined_serialize_json_string() -> Result<(), Error<String>> {
let non_empty_string = Refined::<NonEmptyStringRule>::new("hello".to_string())?;

let actual = json!(non_empty_string);
Expand All @@ -140,7 +149,7 @@ mod test {
}

#[test]
fn test_refined_serialize_json_struct() -> anyhow::Result<()> {
fn test_refined_serialize_json_struct() -> Result<(), Error<String>> {
type NonEmptyString = Refined<NonEmptyStringRule>;
#[derive(Serialize)]
struct Human {
Expand Down Expand Up @@ -191,8 +200,8 @@ mod test {
let actual = serde_json::from_str::<Human>(&json)?;

let expected = Human {
name: NonEmptyString::new("john".to_string())?,
friends: NonEmptyVec::new(vec!["tom".to_string(), "taro".to_string()])?,
name: NonEmptyString::unsafe_new("john".to_string()),
friends: NonEmptyVec::unsafe_new(vec!["tom".to_string(), "taro".to_string()]),
age: 8,
};
assert_eq!(actual, expected);
Expand Down
Loading
Loading