parse-display

Crates.io Docs.rs Actions Status

This crate provides derive macro Display and FromStr. These macros use common helper attributes to specify the format.

Install

Add this to your Cargo.toml:

toml [dependencies] parse-display = "0.6.0"

Example

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display("{a}-{b}")]

struct MyStruct { a: u32, b: u32, } asserteq!(MyStruct { a:10, b:20 }.tostring(), "10-20"); assert_eq!("10-20".parse(), Ok(MyStruct { a:10, b:20 }));

[derive(Display, FromStr, PartialEq, Debug)]

[display(style = "snake_case")]

enum MyEnum { VarA, VarB, } asserteq!(MyEnum::VarA.tostring(), "vara"); asserteq!("var_a".parse(), Ok(MyEnum::VarA)); ```

Helper attributes

Helper attributes can be written in the following positions.

| attribute | struct | enum | variant | field | | ------------------------------------------------------------- | ------ | ---- | ------- | ----- | | #[display("...")] | ✔ | ✔ | ✔ | ✔ | | #[display(style = "...")] | | ✔ | ✔ | | | #[display(bound(...))] | ✔ | ✔ | ✔ | ✔ | | #[from_str(bound(...))] | ✔ | ✔ | ✔ | ✔ | | #[from_str(regex = "...")] | ✔ | ✔ | ✔ | ✔ | | #[from_str(new = ...)] | ✔ | | ✔ | | | #[from_str(ignore)] | | | ✔ | | | #[from_str(default)] | ✔ | | | ✔ | | #[from_str(default_fields(...))] | ✔ | ✔ | ✔ | |

#[derive(Display)] use #[display].
#[derive(FromStr)] use both #[display] and #[from_str].

key = value style parameter can be specified only once for each key.
key(value1, value2, ...) style parameter can be specified multiple times.

#[display("...")]

Specifies the format using a syntax similar to std::format!(). However, unlike std::format!(), field name is specified in {}.

Struct format

By writing #[display("..")], you can specify the format used by Display and FromStr.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display("{a}-{b}")]

struct MyStruct { a: u32, b: u32, } asserteq!(MyStruct { a:10, b:20 }.tostring(), "10-20"); assert_eq!("10-20".parse(), Ok(MyStruct { a:10, b:20 }));

[derive(Display, FromStr, PartialEq, Debug)]

[display("{0}+{1}")]

struct MyTuple(u32, u32); asserteq!(MyTuple(10, 20).tostring(), "10+20"); assert_eq!("10+20".parse(), Ok(MyTuple(10, 20))); ```

Newtype pattern

If the struct has only one field, the format can be omitted. In this case, the only field is used.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

struct NewType(u32); asserteq!(NewType(10).tostring(), "10"); assert_eq!("10".parse(), Ok(NewType(10))); ```

Enum format

In enum, you can specify the format for each variant.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

enum MyEnum { #[display("aaa")] VarA, #[display("bbb")] VarB, } asserteq!(MyEnum::VarA.tostring(), "aaa"); asserteq!(MyEnum::VarB.tostring(), "bbb"); asserteq!("aaa".parse(), Ok(MyEnum::VarA)); asserteq!("bbb".parse(), Ok(MyEnum::VarB)); ```

In enum format, {} means variant name. Variant name style (e.g. snake_case, camelCase, ...) can be specified by #[from_str(style = "...")].

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

enum MyEnum { #[display("aaa-{}")] VarA, #[display("bbb-{}")] VarB, } asserteq!(MyEnum::VarA.tostring(), "aaa-VarA"); asserteq!(MyEnum::VarB.tostring(), "bbb-VarB"); asserteq!("aaa-VarA".parse(), Ok(MyEnum::VarA)); asserteq!("bbb-VarB".parse(), Ok(MyEnum::VarB));

[derive(Display, FromStr, PartialEq, Debug)]

[display(style = "snake_case")]

enum MyEnumSnake { #[display("{}")] VarA, } asserteq!(MyEnumSnake::VarA.tostring(), "vara"); asserteq!("var_a".parse(), Ok(MyEnumSnake::VarA)); ```

By writing a format on enum instead of variant, you can specify the format common to multiple variants.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display("xxx-{}")]

enum MyEnum { VarA, VarB, } asserteq!(MyEnum::VarA.tostring(), "xxx-VarA"); asserteq!(MyEnum::VarB.tostring(), "xxx-VarB"); asserteq!("xxx-VarA".parse(), Ok(MyEnum::VarA)); asserteq!("xxx-VarB".parse(), Ok(MyEnum::VarB)); ```

Unit variants

If all variants has no field, format can be omitted. In this case, variant name is used.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

enum MyEnum { VarA, VarB, } asserteq!(MyEnum::VarA.tostring(), "VarA"); asserteq!(MyEnum::VarB.tostring(), "VarB"); asserteq!("VarA".parse(), Ok(MyEnum::VarA)); asserteq!("VarB".parse(), Ok(MyEnum::VarB)); ```

Field format

You can specify the format of the field. In field format, {} means the field itself.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display("{a}, {b}")]

struct MyStruct { #[display("a is {}")] a: u32, #[display("b is {}")] b: u32, } asserteq!(MyStruct { a:10, b:20 }.tostring(), "a is 10, b is 20"); assert_eq!("a is 10, b is 20".parse(), Ok(MyStruct { a:10, b:20 }));

[derive(Display, FromStr, PartialEq, Debug)]

[display("{0}, {1}")]

struct MyTuple(#[display("first is {}")] u32, #[display("next is {}")] u32); asserteq!(MyTuple(10, 20).tostring(), "first is 10, next is 20"); assert_eq!("first is 10, next is 20".parse(), Ok(MyTuple(10, 20)));

[derive(Display, FromStr, PartialEq, Debug)]

enum MyEnum { #[display("this is A {0}")] VarA(#[display("{}")] u32), } asserteq!(MyEnum::VarA(10).tostring(), "this is A 10"); asserteq!("this is A 10_".parse(), Ok(MyEnum::VarA(10))); ```

Display field chain

You can use "field chain", e.g. {x.a} .

```rust use parse_display::{Display, FromStr};

[derive(PartialEq, Debug, Default)]

struct MyStruct { a: u32, b: u32, }

[derive(FromStr, Display, PartialEq, Debug)]

[display("{x.a}")]

struct FieldChain { #[fromstr(default)] x: MyStruct, } asserteq!(FieldChain { x:MyStruct { a:10, b:20 } }.tostring(), "10"); asserteq!("10".parse(), Ok(FieldChain { x:MyStruct { a:10, b:0 } })); ```

When using "field chain", you need to use #[from_str(default)] to implement FromStr.

Format parameter

Like std::format!(), format parameter can be specified.

```rust use parse_display::{Display, FromStr};

[derive(Display, PartialEq, Debug)]

[display("{a:>04}")]

struct WithFormatParameter { a: u32, } asserteq!(WithFormatParameter { a:5 }.tostring(), "0005"); ```

#[display(style = "...")]

By writing #[display(style = "...")], you can specify the variant name style. The following styles are available.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display(style = "snake_case")]

enum MyEnum { VarA, VarB, } asserteq!(MyEnum::VarA.tostring(), "vara"); asserteq!("var_a".parse(), Ok(MyEnum::VarA));

[derive(Display, FromStr, PartialEq, Debug)]

enum StyleExample { #[display(style = "none")] VarA1, #[display(style = "none")] varA2, #[display(style = "lowercase")] VarB, #[display(style = "UPPERCASE")] VarC, #[display(style = "snakecase")] VarD, #[display(style = "SNAKECASE")] VarE, #[display(style = "camelCase")] VarF, #[display(style = "CamelCase")] VarG1, #[display(style = "CamelCase")] varG2, #[display(style = "kebab-case")] VarH, #[display(style = "KEBAB-CASE")] VarI, #[display(style = "Title Case")] VarJ, #[display(style = "Title case")] VarK, #[display(style = "title case")] VarL, #[display(style = "TITLE CASE")] VarM, } asserteq!(StyleExample::VarA1.tostring(), "VarA1"); asserteq!(StyleExample::varA2.tostring(), "varA2"); asserteq!(StyleExample::VarB.tostring(), "varb"); asserteq!(StyleExample::VarC.tostring(), "VARC"); asserteq!(StyleExample::VarD.tostring(), "vard"); asserteq!(StyleExample::VarE.tostring(), "VARE"); asserteq!(StyleExample::VarF.tostring(), "varF"); asserteq!(StyleExample::VarG1.tostring(), "VarG1"); asserteq!(StyleExample::varG2.tostring(), "VarG2"); asserteq!(StyleExample::VarH.tostring(), "var-h"); asserteq!(StyleExample::VarI.tostring(), "VAR-I"); asserteq!(StyleExample::VarJ.tostring(), "Var J"); asserteq!(StyleExample::VarK.tostring(), "Var k"); asserteq!(StyleExample::VarL.tostring(), "var l"); asserteq!(StyleExample::VarM.tostring(), "VAR M"); ```

#[display(bound(...))]

By default, the type of field used in the format is added to the trait bound.

In Rust prior to 1.59, this behavior causes a compile error if you use fields of non public type in public struct.

```rust

![deny(privateinpublic)]

use parse_display::Display;

// private type Inner<T> in public interface (error E0446)

[derive(Display)]

pub struct Outer(Inner);

[derive(Display)]

struct Inner(T); ```

By writing #[display(bound(...))], you can override the default behavior.

Specify trait bound type

By specifying the type, you can specify the type that need to implement Display and FromStr.

```rust use parse_display::{Display, FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display(bound(T))]

pub struct Outer(Inner);

[derive(Display, FromStr, PartialEq, Debug)]

struct Inner(T);

asserteq!(Outer(Inner(10)).tostring(), "10"); assert_eq!("10".parse(), Ok(Outer(Inner(10)))); ```

Specify where predicate

You can also specify the where predicate.

```rust use parse_display::Display;

[derive(Display)]

[display(bound(T : std::fmt::Debug))]

pub struct Outer(Inner);

[derive(Display)]

[display("{0:?}")]

struct Inner(T);

asserteq!(Outer(Inner(10)).tostring(), "10"); ```

No trait bounds

You can also remove all trait bounds.

```rust use parse_display::Display;

[derive(Display)]

[display(bound())]

pub struct Outer(Inner);

[derive(Display)]

[display("ABC")]

struct Inner(T);

asserteq!(Outer(Inner(10)).tostring(), "ABC"); ```

Default trait bounds

.. means default (automatically generated) trait bounds.

The following example specifies T1 as a trait bound in addition to the default trait bound T2.

```rust use parse_display::Display;

pub struct Inner(T);

[derive(Display)]

[display("{0.0}, {1}", bound(T1, ..))]

pub struct Outer(Inner, T2);

asserteq!(Outer(Inner(10), 20).tostring(), "10, 20"); ```

#[from_str(bound(...))]

You can use a different trait bound for Display and FromStr by specifying both #[display(bound(...))] and #[from_str(bound(...))].

```rust use parse_display::*; use std::{fmt::Display, str::FromStr};

[derive(Display, FromStr, PartialEq, Debug)]

[display(bound("T : Display"))]

[from_str(bound("T : FromStr"))]

pub struct Outer(Inner);

[derive(Display, FromStr, PartialEq, Debug)]

struct Inner(T);

asserteq!(Outer(Inner(10)).tostring(), "10"); assert_eq!("10".parse(), Ok(Outer(Inner(10)))); ```

#[from_str(regex = "...")]

Specify the format of the string to be input with FromStr. #[display("...")] is ignored, when this attribute is specified.

Capture name

The capture name corresponds to the field name.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[fromstr(regex = "(?P[0-9]+)_(?P[0-9]+)")]

struct MyStruct { a: u8, b: u8, }

asserteq!("10_20".parse(), Ok(MyStruct { a:10, b:20 })); ```

Field regex

Set #[display("...")] to struct and set #[from_str(regex = "...")] to field, regex is used in the position where field name is specified in #[display("...")].

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[display("{a}__{b}")]

struct MyStruct { #[from_str(regex = "[0-9]+")] a: u8,

#[fromstr(regex = "[0-9]+")] b: u8, } asserteq!("10__20".parse(), Ok(MyStruct { a:10, b:20 })); ```

If #[from_str(regex = "...")] is not set to field , it operates in the same way as when #[from_str(regex = ".*?")] is set.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[display("{a}{b}")]

struct MyStruct { a: String, b: String, } assert_eq!("abcdef".parse(), Ok(MyStruct { a:"".into(), b:"abcdef".into() })); ```

Variant name

In the regex specified for enum or variant, empty name capture means variant name.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[fromstr(regex = "(?P<>)_")]

enum MyEnum { VarA,

#[fromstr(regex = "xxx(?P<>)xxx")] VarB, } asserteq!("VarA".parse(), Ok(MyEnum::VarA)); assert_eq!("xxxVarBxxx".parse(), Ok(MyEnum::VarB)); ```

Regex field chain

You can use "field chain" in regex.

```rust use parse_display::FromStr;

[derive(PartialEq, Debug, Default)]

struct MyStruct { a: u32, }

[derive(FromStr, PartialEq, Debug)]

[fromstr(regex = "__(?P[0-9]+)")]

struct FieldChain { #[fromstr(default)] x: MyStruct, } asserteq!("_10".parse(), Ok(FieldChain { x:MyStruct { a:10 } })); ```

When using "field chain", you need to use #[from_str(default)].

#[from_str(new = ...)]

If #[from_str(new = ...)] is specified, the value will be initialized with the specified expression instead of the constructor.

The expression must return a value that implement [IntoResult] (e.g. Self, Option<Self>, Result<Self, E>).

In the expression, you can use a variable with the same name as the field name.

```rust use parse_display::FromStr;

[derive(FromStr, Debug, PartialEq)]

[from_str(new = Self::new(value))]

struct MyNonZeroUSize { value: usize, }

impl MyNonZeroUSize { fn new(value: usize) -> Option { if value == 0 { None } else { Some(Self { value }) } } }

asserteq!("1".parse(), Ok(MyNonZeroUSize { value: 1 })); asserteq!("0".parse::().is_err(), true); ```

In tuple struct, variables are named with a leading underscore and their index. (e.g. _0, _1).

```rust use parse_display::FromStr;

[derive(FromStr, Debug, PartialEq)]

[fromstr(new = Self::new(0))]

struct MyNonZeroUSize(usize);

impl MyNonZeroUSize { fn new(value: usize) -> Option { if value == 0 { None } else { Some(Self(value)) } } }

asserteq!("1".parse(), Ok(MyNonZeroUSize(1))); asserteq!("0".parse::().is_err(), true); ```

#[from_str(ignore)]

Specifying this attribute for a variant will not generate FromStr implementation for that variant.

```rust use parse_display::FromStr;

[derive(Debug, Eq, PartialEq)]

struct CanNotFromStr;

[derive(FromStr, Debug, Eq, PartialEq)]

[allow(dead_code)]

enum HasIgnore { #[from_str(ignore)] A(CanNotFromStr), #[display("{0}")] B(u32), }

assert_eq!("1".parse(), Ok(HasIgnore::B(1))); ```

#[from_str(default)]

If this attribute is specified, the default value is used for fields not included in the input.

If an attribute is specified for struct, the struct's default value is used.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[display("{b}")]

[from_str(default)]

struct MyStruct { a: u32, b: u32, }

impl Default for MyStruct { fn default() -> Self { Self { a:99, b:99 } } } assert_eq!("10".parse(), Ok(MyStruct { a:99, b:10 })); ```

If an attribute is specified for field, the field type's default value is used.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[display("{b}")]

struct MyStruct { #[from_str(default)] a: u32, b: u32, }

impl Default for MyStruct { fn default() -> Self { Self { a:99, b:99 } } } assert_eq!("10".parse(), Ok(MyStruct { a:0, b:10 })); ```

#[from_str(default_fields(...))]

You can use #[from_str(default_fields(...))] if you want to set default values for the same-named fields of multiple variants.

```rust use parse_display::FromStr;

[derive(FromStr, PartialEq, Debug)]

[display("{}-{a}")]

[fromstr(defaultfields("b", "c"))]

enum MyEnum { VarA { a:u8, b:u8, c:u8 }, VarB { a:u8, b:u8, c:u8 }, }

asserteq!("VarA-10".parse(), Ok(MyEnum::VarA { a:10, b:0, c:0 })); asserteq!("VarB-10".parse(), Ok(MyEnum::VarB { a:10, b:0, c:0 })); ```

License

This project is dual licensed under Apache-2.0/MIT. See the two LICENSE-* files for details.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.