Skip to content

Commit 1d85f8d

Browse files
committed
better inverse Refine<Rep> for Tag trait
1 parent 76cfbf4 commit 1d85f8d

File tree

2 files changed

+39
-27
lines changed

2 files changed

+39
-27
lines changed

rust/functora-tagged/src/lib.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![doc = include_str!("../README.md")]
12
use std::fmt::Debug;
23
use std::marker::PhantomData;
34
use std::str::FromStr;
@@ -6,20 +7,20 @@ use thiserror::Error;
67
#[derive(Debug)]
78
pub struct Tagged<Rep, Tag>(Rep, PhantomData<Tag>);
89

9-
pub trait Refine<Tag>: Sized {
10+
pub trait Refine<Rep>: Sized {
1011
type RefineError;
1112

12-
fn refine(self) -> Result<Self, Self::RefineError> {
13-
Ok(self)
13+
fn refine(rep: Rep) -> Result<Rep, Self::RefineError> {
14+
Ok(rep)
1415
}
1516
}
1617

1718
impl<Rep, Tag> Tagged<Rep, Tag> {
18-
pub fn new(rep: Rep) -> Result<Self, Rep::RefineError>
19+
pub fn new(rep: Rep) -> Result<Self, Tag::RefineError>
1920
where
20-
Rep: Refine<Tag>,
21+
Tag: Refine<Rep>,
2122
{
22-
rep.refine().map(|rep| Tagged(rep, PhantomData))
23+
Tag::refine(rep).map(|rep| Tagged(rep, PhantomData))
2324
}
2425
pub fn rep(&self) -> &Rep {
2526
&self.0
@@ -58,17 +59,19 @@ impl<Rep: Clone, Tag> Clone for Tagged<Rep, Tag> {
5859
#[derive(Debug, Error)]
5960
pub enum ParseError<Rep, Tag>
6061
where
61-
Rep: FromStr + Refine<Tag>,
62+
Rep: FromStr,
63+
Tag: Refine<Rep>,
6264
{
6365
#[error("Decode failed: {0}")]
6466
Decode(Rep::Err),
6567
#[error("Refine failed: {0}")]
66-
Refine(Rep::RefineError),
68+
Refine(Tag::RefineError),
6769
}
6870

6971
impl<Rep, Tag> FromStr for Tagged<Rep, Tag>
7072
where
71-
Rep: FromStr + Refine<Tag>,
73+
Rep: FromStr,
74+
Tag: Refine<Rep>,
7275
{
7376
type Err = ParseError<Rep, Tag>;
7477
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -100,8 +103,9 @@ impl<Rep: Serialize, Tag> Serialize for Tagged<Rep, Tag> {
100103
#[cfg(feature = "serde")]
101104
impl<'de, Rep, Tag> Deserialize<'de> for Tagged<Rep, Tag>
102105
where
103-
Rep: Deserialize<'de> + Refine<Tag>,
104-
Rep::RefineError: Display,
106+
Rep: Deserialize<'de>,
107+
Tag: Refine<Rep>,
108+
Tag::RefineError: Display,
105109
{
106110
fn deserialize<D>(
107111
deserializer: D,
@@ -165,8 +169,9 @@ mod diesel_impl {
165169

166170
impl<DB, Rep, Tag, ST> FromSql<ST, DB> for Tagged<Rep, Tag>
167171
where
168-
Rep: FromSql<ST, DB> + Refine<Tag>,
169-
Rep::RefineError: 'static + Error + Send + Sync,
172+
Rep: FromSql<ST, DB>,
173+
Tag: Refine<Rep>,
174+
Tag::RefineError: 'static + Error + Send + Sync,
170175
DB: Backend,
171176
{
172177
fn from_sql(
@@ -180,8 +185,9 @@ mod diesel_impl {
180185
impl<Rep, Tag, ST, DB> Queryable<ST, DB>
181186
for Tagged<Rep, Tag>
182187
where
183-
Rep: Queryable<ST, DB> + Refine<Tag>,
184-
Rep::RefineError: 'static + Error + Send + Sync,
188+
Rep: Queryable<ST, DB>,
189+
Tag: Refine<Rep>,
190+
Tag::RefineError: 'static + Error + Send + Sync,
185191
DB: Backend,
186192
{
187193
type Row = Rep::Row;

rust/functora-tagged/tests/integration.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ pub type Email = Tagged<NonEmpty<String>, EmailTag>;
2222
#[error("Empty value is not allowed")]
2323
pub struct EmptyError;
2424

25-
impl Refine<NonEmptyTag> for String {
25+
impl Refine<String> for NonEmptyTag {
2626
type RefineError = EmptyError;
27-
fn refine(self) -> Result<Self, Self::RefineError> {
28-
if self.is_empty() {
27+
fn refine(
28+
rep: String,
29+
) -> Result<String, Self::RefineError> {
30+
if rep.is_empty() {
2931
Err(EmptyError)
3032
} else {
31-
Ok(self)
33+
Ok(rep)
3234
}
3335
}
3436
}
@@ -37,12 +39,14 @@ impl Refine<NonEmptyTag> for String {
3739
#[error("Invalid UserId format")]
3840
pub struct UserIdError;
3941

40-
impl Refine<UserIdTag> for NonEmpty<String> {
42+
impl Refine<NonEmpty<String>> for UserIdTag {
4143
type RefineError = UserIdError;
42-
fn refine(self) -> Result<Self, Self::RefineError> {
43-
let txt = self.rep();
44+
fn refine(
45+
rep: NonEmpty<String>,
46+
) -> Result<NonEmpty<String>, Self::RefineError> {
47+
let txt = rep.rep();
4448
if txt.starts_with("user_") && txt.len() > 5 {
45-
Ok(self)
49+
Ok(rep)
4650
} else {
4751
Err(UserIdError)
4852
}
@@ -53,14 +57,16 @@ impl Refine<UserIdTag> for NonEmpty<String> {
5357
#[error("String is too short: {0}, minimum length: 3")]
5458
pub struct EmailError(usize);
5559

56-
impl Refine<EmailTag> for NonEmpty<String> {
60+
impl Refine<NonEmpty<String>> for EmailTag {
5761
type RefineError = EmailError;
58-
fn refine(self) -> Result<Self, Self::RefineError> {
59-
let len = self.clone().rep().len();
62+
fn refine(
63+
rep: NonEmpty<String>,
64+
) -> Result<NonEmpty<String>, Self::RefineError> {
65+
let len = rep.clone().rep().len();
6066
if len < 3 {
6167
Err(EmailError(len))
6268
} else {
63-
Ok(self)
69+
Ok(rep)
6470
}
6571
}
6672
}

0 commit comments

Comments
 (0)