<h1 align="center"> Kinded </h1>
<p align="center">
<a href="https://github.com/greyblake/kinded/actions/workflows/ci.yml" rel="nofollow"><img src="https://github.com/greyblake/kinded/actions/workflows/ci.yml/badge.svg" alt="Nutype Build Status"></a>
<a href="https://docs.rs/kinded" rel="nofollow"><img src="https://docs.rs/kinded/badge.svg" alt="Nutype Documentation"></a>
<a href="https://github.com/greyblake/kinded/discussions"><img src="https://img.shields.io/github/discussions/greyblake/kinded"/></a>
<p>

<p align="center">Generate Rust enum variants without associated data</p>


## Get Started

```rs
use kinded::Kinded;

#[derive(Kinded)]
enum Drink {
    Mate,
    Coffee(String),
    Tea { variety: String, caffeine: bool }
}

let drink = Drink::Coffee("Espresso".to_owned());
assert_eq!(drink.kind(), DrinkKind::Coffee);
```

Note, the definition of `DrinkKind` enum is generated automatically as well as `Drink::kind()` method.
To put it simply you get something similar to the following:

```rs
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DrinkKind {
    Mate,
    Coffee,
    Tea
}

impl Drink {
    fn kind(&self) -> DrinkKind {
        Drink::Mate => DrinkKind::Mate,
        Drink::Coffee(..) => DrinkKind::Coffee,
        Drink::Tea { .. } => DrinkKind::Tea,
    }
}
```

## Kinded trait

The library provides `Kinded` trait:

```rs
pub trait Kinded {
    type Kind: PartialEq + Eq + Debug + Clone + Copy;

    fn kind(&self) -> Self::Kind;
}
```

From the example above, the derived implementation of `Kinded` for `Drink` resembles the following:

```rs
impl Kinded for Drink {
    type Kind = DrinkKind;

    fn kind(&self) -> DrinkKind { /* implementation */ }
}
```

The `Kinded` trait allows to build abstract functions that can be used with different enum types.

## Get all kind variants

The kind type gets implementation of `::all()` associated function, which returns a vector with all kind variants:

```rs
assert_eq!(DrinkKind::all(), [DrinkKind::Mate, DrinkKind::Coffee, DrinkKind::Tea]);
```


## Attributes

### Custom kind type name

By default the kind type name is generated by adding postfix `Kind` to the original enum name.
This can be customized with `kind = ` attribute:

```rs
use kinded::Kinded;

#[derive(Kinded)]
#[kinded(kind = SimpleDrink)]
enum Drink {
    Mate,
    Coffee(String),
    Tea { variety: String, caffeine: bool }
}
```

### Derive traits

By default the kind type implements the following traits: `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Display`, `FromStr`, `From<T>`, `From<&T>`.

Extra traits can be derived with `derive(..)` attribute:

```rs
use kinded::Kinded;
use std::collections::HashSet;

#[derive(Kinded)]
#[kinded(derive(Hash))]
enum Drink {
    Mate,
    Coffee(String),
    Tea { variety: String, caffeine: bool }
}

let mut drink_kinds = HashSet::new();
drink_kinds.insert(DrinkKind::Mate);
```

### Display trait

Implementation of `Display` trait can be customized in the `serde` fashion:

```rs
use kinded::Kinded;

#[derive(Kinded)]
#[kinded(display = "snake_case")]
enum Drink {
    VeryHotBlackTea,
    Milk { fat: f64 },
}

let tea = DrinkKind::VeryHotBlackTea;
assert_eq!(tea.to_string(), "very_hot_black_tea");
```

The possible values are `"snake_case"`, `"camelCase"`, `"PascalCase"`, `"SCREAMING_SNAKE_CASE"`, `"kebab-case"`, `"SCREAMING-KEBAB-CASE"`, `"Title Case"`, `"lowercase"`, `"UPPERCASE"`.

### FromStr trait

The kind type implements `FromStr` trait. The implementation tries it's best to parse, checking all the possible cases mentioned above.

```rs
use kinded::Kinded;

#[derive(Kinded)]
#[kinded(display = "snake_case")]
enum Drink {
    VeryHotBlackTea,
    Milk { fat: f64 },
}

assert_eq!(
    "VERY_HOT_BLACK_TEA".parse::<DrinkKind>().unwrap(),
    DrinkKind::VeryHotBlackTea
);

assert_eq!(
    "veryhotblacktea".parse::<DrinkKind>().unwrap(),
    DrinkKind::VeryHotBlackTea
);
```


## A note about enum-kinds

There is a very similar crate [enum-kinds](https://github.com/Soft/enum-kinds) that does almost the same job.

Here is what makes `kinded` different:
* It provides `Kinded` trait, on top of which users can build abstractions.
* Generates customizable implementation of `Display` trait.
* Generates implementation of `FromStr` trait.
* Generates `kind()` function to extra ergonomics.

## A note about the war in Ukraine 🇺🇦

Today I live in Berlin, I have the luxury to live a physically safe life.
But I am Ukrainian. The first 25 years of my life I spent in [Kharkiv](https://en.wikipedia.org/wiki/Kharkiv),
the second-largest city in Ukraine, 60km away from the border with russia. Today about [a third of my home city is destroyed](https://www.youtube.com/watch?v=ihoufBFSZds) by russians.
My parents, my relatives and my friends had to survive the artillery and air attack, living for over a month in basements.

Some of them have managed to evacuate to EU. Some others are trying to live "normal lifes" in Kharkiv, doing there daily duties.
And some are at the front line right now, risking their lives every second to protect the rest.

I encourage you to donate to [Charity foundation of Serhiy Prytula](https://prytulafoundation.org/en).
Just pick the project you like and donate. This is one of the best-known foundations, you can watch a [little documentary](https://www.youtube.com/watch?v=VlmWqoeub1Q) about it.
Your contribution to the Ukrainian military force is a contribution to my calmness, so I can spend more time developing the project.

Thank you.


## License

MIT © [Serhii Potapov](https://www.greyblake.com)
