Skip to content

Commit

Permalink
implement Head
Browse files Browse the repository at this point in the history
  • Loading branch information
tomoikey committed Sep 16, 2024
1 parent 6021b14 commit 500d214
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/rule/collection.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod exists;
mod for_all;
mod head;

pub use exists::*;
pub use for_all::*;
pub use head::*;
66 changes: 66 additions & 0 deletions src/rule/collection/head.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
mod collection;
mod string;

use crate::rule::Rule;
use crate::Refined;
use std::collections::{HashSet, VecDeque};
use std::marker::PhantomData;

/// A type that holds a value satisfying the `HeadRule`
pub type Head<RULE, ITERABLE> = Refined<HeadRule<RULE, ITERABLE>>;

/// A type that holds a Vec value satisfying the `HeadRule`
pub type HeadVec<RULE> = Head<RULE, Vec<<RULE as Rule>::Item>>;

/// A type that holds a VecDeque value satisfying the `HeadRule`
pub type HeadVecDeque<RULE> = Head<RULE, VecDeque<<RULE as Rule>::Item>>;

/// A type that holds a HashSet value satisfying the `HeadRule`
pub type HeadHashSet<RULE> = Head<RULE, HashSet<<RULE as Rule>::Item>>;

/// A type that holds a String value satisfying the `HeadRule`
pub type HeadString<RULE> = Head<RULE, String>;

/// Rule where the first element satisfies the condition
pub struct HeadRule<RULE, ITERABLE>
where
RULE: Rule,
{
_phantom_data: PhantomData<(RULE, ITERABLE)>,
}

#[cfg(test)]
mod tests {
use crate::rule::{HeadVec, NonEmptyStringRule};

#[test]
fn head_valid() -> anyhow::Result<()> {
let table = vec![
vec!["good morning".to_string(), "".to_string()],
vec!["hello".to_string(), "hello".to_string()],
];

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

Ok(())
}

#[test]
fn head_invalid() -> anyhow::Result<()> {
let table = vec![
vec![],
vec!["".to_string()],
vec!["".to_string(), "hello".to_string()],
];

for value in table {
let head_result = HeadVec::<NonEmptyStringRule>::new(value.clone());
assert!(head_result.is_err());
}

Ok(())
}
}
34 changes: 34 additions & 0 deletions src/rule/collection/head/collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::result::Error;
use crate::rule::collection::head::HeadRule;
use crate::rule::Rule;
use std::collections::VecDeque;

impl<RULE, ITEM> Rule for HeadRule<RULE, Vec<ITEM>>
where
RULE: Rule<Item = ITEM>,
{
type Item = Vec<ITEM>;

fn validate(target: &Self::Item) -> Result<(), Error> {
if let Some(head) = target.first() {
RULE::validate(head)
} else {
Err(Error::new("empty collection"))
}
}
}

impl<RULE, ITEM> Rule for HeadRule<RULE, VecDeque<ITEM>>
where
RULE: Rule<Item = ITEM>,
{
type Item = VecDeque<ITEM>;

fn validate(target: &Self::Item) -> Result<(), Error> {
if let Some(head) = target.front() {
RULE::validate(head)
} else {
Err(Error::new("empty collection"))
}
}
}
33 changes: 33 additions & 0 deletions src/rule/collection/head/string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::result::Error;
use crate::rule::collection::head::HeadRule;
use crate::rule::Rule;

impl<RULE> Rule for HeadRule<RULE, String>
where
RULE: Rule<Item = char>,
{
type Item = String;

fn validate(target: &Self::Item) -> Result<(), Error> {
if let Some(head) = target.chars().next() {
RULE::validate(&head)
} else {
Err(Error::new("empty string"))
}
}
}

impl<RULE> Rule for HeadRule<RULE, &'static str>
where
RULE: Rule<Item = char>,
{
type Item = &'static str;

fn validate(target: &Self::Item) -> Result<(), Error> {
if let Some(head) = target.chars().next() {
RULE::validate(&head)
} else {
Err(Error::new("empty string"))
}
}
}

0 comments on commit 500d214

Please sign in to comment.