Skip to content

Commit e0a0713

Browse files
kiendangtyranronJelteF
authored
Support for custom error type in FromStr derive (#494)
Resolves #112 Related to #396 Add support for a `#[from_str(error(error_ty[, error_fn]))]` attribute to specify a custom error type for `FromStr` derive for enums (or structs with no fields) in case where the string does not match any variant. A conversion function to convert a `derive_more::FromStrError` to the custom error type might optionally be provided. Otherwise the custom error type `T` must satisfy `derive_more::FromStrError: Into<T>`. [`strum`](https://github.com/Peternator7/strum) provides similar functionality. See Peternator7/strum#380. This approach could be used to set a custom error type for `TryInto` derive as well (#396). Co-authored-by: Kai Ren <[email protected]> Co-authored-by: Jelte Fennema-Nio <[email protected]>
1 parent 2b3b588 commit e0a0713

File tree

10 files changed

+681
-30
lines changed

10 files changed

+681
-30
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2929
([#477](https://github.com/JelteF/derive_more/pull/477))
3030
- Support `Deref` and `DerefMut` derives for enums.
3131
([#485](https://github.com/JelteF/derive_more/pull/485))
32+
- Support custom error in `FromStr` derive.
33+
([#494](https://github.com/JelteF/derive_more/pull/494))
3234

3335
### Changed
3436

impl/doc/from_str.md

Lines changed: 215 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ assert_eq!(
117117

118118
Code like this is generated:
119119
```rust
120+
# use core::str::FromStr;
121+
#
120122
# enum EnumNoFields {
121123
# Foo,
122124
# Bar,
@@ -126,7 +128,7 @@ Code like this is generated:
126128
#
127129
impl derive_more::core::str::FromStr for EnumNoFields {
128130
type Err = derive_more::FromStrError;
129-
fn from_str(s: &str) -> Result<Self, derive_more::FromStrError> {
131+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
130132
Ok(match s.to_lowercase().as_str() {
131133
"foo" => Self::Foo,
132134
"bar" => Self::Bar,
@@ -158,11 +160,13 @@ assert_eq!("FOO".parse::<Foo>().unwrap(), Foo);
158160

159161
Code like this is generated:
160162
```rust
163+
# use core::str::FromStr;
164+
#
161165
# struct Foo;
162166
#
163167
impl derive_more::core::str::FromStr for Foo {
164168
type Err = derive_more::FromStrError;
165-
fn from_str(s: &str) -> Result<Self, derive_more::FromStrError> {
169+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
166170
Ok(match s.to_lowercase().as_str() {
167171
"foo" => Self,
168172
_ => return Err(derive_more::FromStrError::new("Foo")),
@@ -228,3 +232,212 @@ assert_eq!("variant-two".parse::<Enum>().unwrap(), Enum::VariantTwo);
228232
> # assert_eq!("Bar".parse::<Enum>().unwrap(), Enum::Bar);
229233
> # assert_eq!("Ba_R".parse::<Enum>().unwrap(), Enum::Ba_R);
230234
> ```
235+
236+
237+
238+
239+
## Custom error
240+
241+
The `#[from_str(error(<ty>[, <conv>]))]` attribute can be used to convert the `FromStr`' `Err` type
242+
into a custom error type. If the conversion function is not provided, the custom error type must implement
243+
`From<FromStr::Err>`.
244+
245+
246+
### Forwarding
247+
248+
Given the following struct:
249+
```rust
250+
# use derive_more::{From, FromStr};
251+
#
252+
#[derive(From)]
253+
struct CustomError(core::num::ParseIntError);
254+
255+
#[derive(FromStr, Debug, Eq, PartialEq)]
256+
#[from_str(error(CustomError))]
257+
struct MyInt(i32);
258+
```
259+
Code like this is generated:
260+
```rust
261+
# use core::str::FromStr;
262+
# use derive_more::From;
263+
#
264+
# #[derive(From)]
265+
# struct CustomError(core::num::ParseIntError);
266+
#
267+
# struct MyInt(i32);
268+
#
269+
impl derive_more::core::str::FromStr for MyInt {
270+
type Err = CustomError;
271+
fn from_str(s: &str) -> Result<Self, Self::Err> {
272+
FromStr::from_str(s)
273+
.map(|v| Self(v))
274+
.map_err(Into::into)
275+
}
276+
}
277+
```
278+
279+
For the explicitly specified error conversion:
280+
```rust
281+
# use derive_more::FromStr;
282+
#
283+
struct CustomError(core::num::ParseIntError);
284+
285+
impl CustomError {
286+
fn new(err: core::num::ParseIntError) -> Self {
287+
Self(err)
288+
}
289+
}
290+
291+
#[derive(FromStr, Debug, Eq, PartialEq)]
292+
#[from_str(error(CustomError, CustomError::new))]
293+
struct MyInt(i32);
294+
```
295+
Code like this is generated:
296+
```rust
297+
# use core::str::FromStr;
298+
#
299+
# struct CustomError(core::num::ParseIntError);
300+
#
301+
# impl CustomError {
302+
# fn new(err: core::num::ParseIntError) -> Self {
303+
# Self(err)
304+
# }
305+
# }
306+
#
307+
# struct MyInt(i32);
308+
#
309+
impl derive_more::core::str::FromStr for MyInt {
310+
type Err = CustomError;
311+
fn from_str(s: &str) -> Result<Self, Self::Err> {
312+
FromStr::from_str(s)
313+
.map(|v| Self(v))
314+
.map_err(CustomError::new)
315+
}
316+
}
317+
```
318+
319+
Custom error for a newtype struct with one named field, *e.g*,
320+
```rust
321+
# use derive_more::{From, FromStr};
322+
#
323+
#[derive(From)]
324+
struct CustomError(core::num::ParseIntError);
325+
326+
#[derive(FromStr)]
327+
#[from_str(error(CustomError))]
328+
struct Point1D {
329+
x: i32,
330+
}
331+
```
332+
works similarly.
333+
334+
335+
### Flat representation
336+
337+
Custom error type is also supported for empty enums and unit structs.
338+
339+
Given the following enum:
340+
```rust
341+
# use derive_more::{From, FromStr};
342+
#
343+
#[derive(From)]
344+
struct CustomError(derive_more::FromStrError);
345+
346+
#[derive(FromStr, Debug, Eq, PartialEq)]
347+
#[from_str(error(CustomError))]
348+
enum EnumNoFields {
349+
Foo,
350+
Bar,
351+
Baz,
352+
}
353+
```
354+
Code like this is generated:
355+
```rust
356+
# use core::str::FromStr;
357+
# use derive_more::From;
358+
#
359+
# #[derive(From)]
360+
# struct CustomError(derive_more::FromStrError);
361+
#
362+
# enum EnumNoFields {
363+
# Foo,
364+
# Bar,
365+
# Baz,
366+
# }
367+
#
368+
impl derive_more::core::str::FromStr for EnumNoFields {
369+
type Err = CustomError;
370+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
371+
Ok(match s.to_lowercase().as_str() {
372+
"foo" => Self::Foo,
373+
"bar" => Self::Bar,
374+
"baz" => Self::Baz,
375+
_ => return Err(derive_more::FromStrError::new("EnumNoFields").into()),
376+
})
377+
}
378+
}
379+
```
380+
381+
For the explicitly specified error conversion:
382+
```rust
383+
# use derive_more::FromStr;
384+
#
385+
struct CustomError(derive_more::FromStrError);
386+
387+
impl CustomError {
388+
pub fn new(err: derive_more::FromStrError) -> Self {
389+
Self(err)
390+
}
391+
}
392+
393+
#[derive(FromStr, Debug, Eq, PartialEq)]
394+
#[from_str(error(CustomError, CustomError::new))]
395+
enum EnumNoFields {
396+
Foo,
397+
Bar,
398+
Baz,
399+
}
400+
```
401+
Code like this is generated:
402+
```rust
403+
# use core::str::FromStr;
404+
#
405+
# struct CustomError(derive_more::FromStrError);
406+
#
407+
# impl CustomError {
408+
# pub fn new(err: derive_more::FromStrError) -> Self {
409+
# Self(err)
410+
# }
411+
# }
412+
#
413+
# enum EnumNoFields {
414+
# Foo,
415+
# Bar,
416+
# Baz,
417+
# }
418+
#
419+
impl derive_more::core::str::FromStr for EnumNoFields {
420+
type Err = CustomError;
421+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
422+
Ok(match s.to_lowercase().as_str() {
423+
"foo" => Self::Foo,
424+
"bar" => Self::Bar,
425+
"baz" => Self::Baz,
426+
_ => return Err(CustomError::new(derive_more::FromStrError::new("EnumNoFields"))),
427+
})
428+
}
429+
}
430+
```
431+
432+
Custom error type for unit structs, *e.g*,
433+
```rust
434+
# use derive_more::{From, FromStr};
435+
#
436+
#[derive(From)]
437+
struct CustomError(derive_more::FromStrError);
438+
439+
#[derive(FromStr)]
440+
#[from_str(error(CustomError))]
441+
struct Foo;
442+
```
443+
works similarly.

0 commit comments

Comments
 (0)