Skip to content

Commit f3df653

Browse files
committed
Implement traits for Refinement manually.
1 parent 1e510a6 commit f3df653

File tree

10 files changed

+280
-51
lines changed

10 files changed

+280
-51
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "refinement-types"
3-
version = "0.1.0"
3+
version = "0.0.0"
44
authors = ["nekitdev <[email protected]>"]
55
edition = "2024"
66
description = "Refinement types."
@@ -28,6 +28,10 @@ optional = true
2828
version = "2.0.11"
2929
default-features = false
3030

31+
[dev-dependencies.refinement-types]
32+
features = ["regex", "serde"]
33+
path = "."
34+
3135
[features]
3236
default = ["std"]
3337
serde = ["dep:serde"]
@@ -36,5 +40,5 @@ alloc = []
3640
std = []
3741

3842
[package.metadata.docs.rs]
39-
features = ["regex", "serde", "std"]
43+
features = ["regex", "serde"]
4044
rustdoc-args = ["--cfg", "docsrs"]

README.md

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Or by directly specifying it in the configuration like so:
2121

2222
```toml
2323
[dependencies]
24-
refinement-types = "0.1.0"
24+
refinement-types = "0.0.0"
2525
```
2626

2727
Alternatively, you can add it directly from the source:
@@ -33,11 +33,70 @@ git = "https://github.com/nekitdev/refinement-types.git"
3333

3434
## Examples
3535

36-
TODO
36+
### Library
37+
38+
```rust
39+
// lib.rs
40+
41+
#![no_std]
42+
43+
use core::fmt;
44+
45+
use refinement_types::{Refinement, int::U8Closed, length::Closed, logic::And, str::IsAscii};
46+
47+
/// Represents device names.
48+
pub type Name<'n> = Refinement<&'n str, And<Closed<1, 32>, IsAscii>>;
49+
50+
/// Represents device charge, in percentage.
51+
pub type Charge = Refinement<u8, U8Closed<1, 100>>;
52+
53+
/// Represents devices.
54+
#[derive(Debug)]
55+
pub struct Device<'d> {
56+
/// The name of the device.
57+
name: Name<'d>,
58+
/// The charge of the device.
59+
charge: Charge,
60+
}
61+
62+
impl fmt::Display for Device<'_> {
63+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
64+
write!(
65+
formatter,
66+
"{name}: {charge}%",
67+
name = self.name,
68+
charge = self.charge
69+
)
70+
}
71+
}
72+
73+
impl<'d> Device<'d> {
74+
/// Constructs [`Self`].
75+
pub fn new(name: Name<'d>, charge: Charge) -> Self {
76+
Self { name, charge }
77+
}
78+
}
79+
```
80+
81+
### Binary
82+
83+
```rust
84+
// main.rs
85+
86+
use device::{Charge, Device, Name};
87+
use refinement_types::MessageError;
3788

38-
## Features
89+
fn main() -> Result<(), MessageError> {
90+
let charge = Charge::refine(69)?;
91+
let name = Name::refine("nekit")?;
3992

40-
TODO
93+
let device = Device::new(name, charge);
94+
95+
println!("{device}"); // nekit: 69%
96+
97+
Ok(())
98+
}
99+
```
41100

42101
## Documentation
43102

changelogging.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[context]
22
name = "refinement-types"
3-
version = "0.1.0"
3+
version = "0.0.0"
44
url = "https://github.com/nekitdev/refinement-types"
55

66
[formats]

src/char.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ macro_rules! predicate {
7171
Expected = $expected: expr,
7272
) => {
7373
paste! {
74-
#[derive(Debug, Error)]
74+
#[derive(Debug, Error, Default)]
7575
#[error($message)]
7676
#[doc = $error]
7777
pub struct [<Non $name Error>];

src/core.rs

Lines changed: 164 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
//! Core functionality.
22
3-
use core::{fmt, marker::PhantomData, ops::Deref};
3+
use core::{
4+
cmp::Ordering,
5+
fmt,
6+
hash::{Hash, Hasher},
7+
marker::PhantomData,
8+
ops::Deref,
9+
};
410

511
#[cfg(feature = "serde")]
612
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
@@ -79,19 +85,100 @@ impl<T: ?Sized, P: Predicate<T> + ?Sized> fmt::Display for Expected<T, P> {
7985
///
8086
/// Values of this type are guaranteed to contain values of type `T`
8187
/// that satisfy the predicate `P`.
82-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8388
pub struct Refinement<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized = NoContext> {
8489
value: T,
8590
predicate: PhantomData<P>,
8691
context: PhantomData<C>,
8792
}
8893

94+
impl<T: fmt::Debug, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Debug
95+
for Refinement<T, P, C>
96+
{
97+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98+
self.get().fmt(formatter)
99+
}
100+
}
101+
102+
impl<T: fmt::Display, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Display
103+
for Refinement<T, P, C>
104+
{
105+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106+
self.get().fmt(formatter)
107+
}
108+
}
109+
110+
impl<T: Clone, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Clone for Refinement<T, P, C> {
111+
fn clone(&self) -> Self {
112+
unsafe { Self::unchecked(self.get().clone()) }
113+
}
114+
}
115+
116+
impl<T: Copy, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Copy for Refinement<T, P, C> {}
117+
118+
impl<T: PartialEq, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> PartialEq
119+
for Refinement<T, P, C>
120+
{
121+
fn eq(&self, other: &Self) -> bool {
122+
self.get().eq(other.get())
123+
}
124+
}
125+
126+
impl<T: Eq, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Eq for Refinement<T, P, C> {}
127+
128+
impl<T: PartialOrd, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> PartialOrd
129+
for Refinement<T, P, C>
130+
{
131+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
132+
self.get().partial_cmp(other.get())
133+
}
134+
}
135+
136+
impl<T: Ord, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Ord for Refinement<T, P, C> {
137+
fn cmp(&self, other: &Self) -> Ordering {
138+
self.get().cmp(other.get())
139+
}
140+
}
141+
142+
impl<T: Hash, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Hash for Refinement<T, P, C> {
143+
fn hash<H: Hasher>(&self, hasher: &mut H) {
144+
self.get().hash(hasher);
145+
}
146+
}
147+
148+
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Deref for Refinement<T, P, C> {
149+
type Target = T;
150+
151+
fn deref(&self) -> &Self::Target {
152+
self.get()
153+
}
154+
}
155+
156+
impl<T: Default, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
157+
/// Refines the default value of type `T`.
158+
///
159+
/// # Errors
160+
///
161+
/// Returns [`struct@Error`] if the default value does not satisfy the predicate.
162+
pub fn try_default() -> Result<Self, Error<T, P, C>> {
163+
Self::refine(T::default())
164+
}
165+
166+
/// Constructs [`Self`] without checking the default value.
167+
///
168+
/// # Safety
169+
///
170+
/// The caller must ensure that the default value satisfies the predicate.
171+
pub unsafe fn unchecked_default() -> Self {
172+
unsafe { Self::unchecked(T::default()) }
173+
}
174+
}
175+
89176
#[cfg(feature = "serde")]
90177
impl<T: Serialize, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Serialize
91178
for Refinement<T, P, C>
92179
{
93180
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
94-
self.value.serialize(serializer)
181+
self.get().serialize(serializer)
95182
}
96183
}
97184

@@ -128,7 +215,7 @@ impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
128215
/// This error is constructed from the value that failed to satisfy the predicate
129216
/// and the error produced by the predicate.
130217
#[derive(Debug, Error)]
131-
#[error("expected {expected} [{context}]", expected = P::expected(), context = C::VALUE)]
218+
#[error("expected {expected} [{context}]", expected = P::expected(), context = Self::context())]
132219
pub struct Error<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized = NoContext> {
133220
/// The value that failed to satisfy the predicate.
134221
pub value: T,
@@ -141,6 +228,48 @@ pub struct Error<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized = NoContext> {
141228
pub context: PhantomData<C>,
142229
}
143230

231+
#[cfg(feature = "alloc")]
232+
use alloc::string::{String, ToString};
233+
234+
/// Represents errors that retain their message and invalid value.
235+
#[cfg(any(feature = "alloc", feature = "std"))]
236+
#[derive(Debug, Error)]
237+
#[error("{message}")]
238+
pub struct ErasedError<T> {
239+
/// The value that failed to satisfy the predicate.
240+
pub value: T,
241+
/// The expectation message.
242+
pub message: String,
243+
}
244+
245+
impl<T> ErasedError<T> {
246+
/// Constructs [`Self`].
247+
pub const fn new(value: T, message: String) -> Self {
248+
Self { value, message }
249+
}
250+
}
251+
252+
/// Represents errors that only retain their message.
253+
#[cfg(any(feature = "alloc", feature = "std"))]
254+
#[derive(Debug, Error)]
255+
#[error("{string}")]
256+
pub struct MessageError {
257+
/// The expectation message.
258+
pub string: String,
259+
}
260+
261+
impl MessageError {
262+
/// Constructs [`Self`].
263+
pub const fn new(string: String) -> Self {
264+
Self { string }
265+
}
266+
267+
/// Returns the expectation message.
268+
pub fn string(&self) -> &str {
269+
self.string.as_str()
270+
}
271+
}
272+
144273
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
145274
/// Constructs [`Self`].
146275
pub const fn new(value: T, error: P::Error) -> Self {
@@ -167,6 +296,34 @@ impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
167296
}
168297
}
169298

299+
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
300+
/// Erases the source error, retaining only the value and the expectation message.
301+
pub fn erase(self) -> ErasedError<T> {
302+
let string = self.to_string();
303+
304+
ErasedError::new(self.value, string)
305+
}
306+
}
307+
308+
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
309+
/// Erases the source error and discards the value, retaining only the expectation message.
310+
pub fn message(&self) -> MessageError {
311+
MessageError::new(self.to_string())
312+
}
313+
}
314+
315+
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> From<Error<T, P, C>> for ErasedError<T> {
316+
fn from(error: Error<T, P, C>) -> Self {
317+
error.erase()
318+
}
319+
}
320+
321+
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> From<Error<T, P, C>> for MessageError {
322+
fn from(error: Error<T, P, C>) -> Self {
323+
error.message()
324+
}
325+
}
326+
170327
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
171328
/// Returns the contained value and the received error.
172329
pub fn into_parts(self) -> (T, P::Error) {
@@ -206,7 +363,7 @@ impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
206363
///
207364
/// Returns [`struct@Error`] if the resulting value does not satisfy the predicate.
208365
pub fn map<F: FnOnce(T) -> T>(self, function: F) -> Result<Self, Error<T, P, C>> {
209-
Self::refine(function(self.value))
366+
Self::refine(function(self.take()))
210367
}
211368

212369
/// Replaces the value of the refinement.
@@ -218,8 +375,8 @@ impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
218375
Self::refine(value)
219376
}
220377

221-
/// Extracts the value from the refinement.
222-
pub fn extract(self) -> T {
378+
/// Takes the value from the refinement.
379+
pub fn take(self) -> T {
223380
self.value
224381
}
225382

@@ -228,30 +385,3 @@ impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
228385
&self.value
229386
}
230387
}
231-
232-
impl<T: Default, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
233-
/// Refines the default value of type `T`.
234-
///
235-
/// # Errors
236-
///
237-
/// Returns [`struct@Error`] if the default value does not satisfy the predicate.
238-
pub fn try_default() -> Result<Self, Error<T, P, C>> {
239-
Self::refine(T::default())
240-
}
241-
}
242-
243-
impl<T: fmt::Display, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Display
244-
for Refinement<T, P, C>
245-
{
246-
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
247-
self.value.fmt(formatter)
248-
}
249-
}
250-
251-
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Deref for Refinement<T, P, C> {
252-
type Target = T;
253-
254-
fn deref(&self) -> &Self::Target {
255-
self.get()
256-
}
257-
}

src/empty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub trait HasEmpty {
1414
}
1515

1616
/// Represents errors that occur when the provided value is non-empty.
17-
#[derive(Debug, Error)]
17+
#[derive(Debug, Error, Default)]
1818
#[error("received non-empty value")]
1919
pub struct NonEmptyError;
2020

0 commit comments

Comments
 (0)