Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,23 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
args: --workspace -- -D warnings

examples:
name: Examples
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable

- name: Run examples
run: |
for example in examples/*/; do
echo "Running $example"
cargo run --manifest-path "${example}Cargo.toml"
done
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Unreleased
## v0.5.0 - 2026-02-xx
- Add `skip_derive` attribute to opt out of default trait implementations (fixes #19).
- Add `attrs` attribute to pass extra attributes to the generated kind enum (e.g., `#[kinded(attrs(serde(rename_all = "snake_case")))]`)
- Add per-variant `attrs` attribute to pass attributes to individual kind variants (e.g., `#[kinded(attrs(default))]`) (fixes #22)
- Make `kind()` method `const fn`, allowing usage in const contexts (fixes #12)
Expand Down
86 changes: 84 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ members = [
"kinded_macros",
"sandbox",
"test_suite",
"examples/basic",
"examples/enumset_example",
]
35 changes: 35 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
all: fmt test-all clippy examples typos

test-all: test test-doc

test:
cargo test --workspace

test-doc:
cd kinded && cargo test --doc
cd kinded_macros && cargo test --doc

fmt:
cargo fmt

clippy:
cargo clippy --workspace -- -D warnings

examples:
#!/usr/bin/env bash
set -euxo pipefail
ROOT_DIR=$(pwd)
for EXAMPLE in $(ls examples); do
cd "$ROOT_DIR/examples/$EXAMPLE"
cargo run
done

watch:
bacon

watch-sandbox:
bacon run --path sandbox

typos:
which typos >/dev/null || cargo install typos-cli
typos
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,44 @@ let mut drink_kinds = HashSet::new();
drink_kinds.insert(DrinkKind::Mate);
```

### Skip default traits

In some cases you may need to opt out of default trait implementations.
For example, when using `kinded` with crates like `enumset` that provide their own trait implementations,
you can use `skip_derive(..)` to avoid conflicts:

```rs
use kinded::Kinded;

#[derive(Kinded)]
#[kinded(skip_derive(Display, FromStr))]
enum Task {
Download { url: String },
Process(Vec<u8>),
}

// Display and FromStr are not implemented for TaskKind
// You can provide your own custom implementations if needed
```

The following traits can be skipped:
- Derived traits: `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`
- Implemented traits: `Display`, `FromStr`, `From`

You can combine `skip_derive` with `derive` to replace default traits:

```rs
use kinded::Kinded;

#[derive(Kinded)]
#[kinded(skip_derive(Display), derive(Hash))]
enum Expr {
Literal(i64),
Variable(String),
BinaryOp { left: Box<Expr>, op: char, right: Box<Expr> },
}
```

### Extra attributes

If you're using derive macros from other libraries like Serde or Strum, you may want to add
Expand Down Expand Up @@ -298,7 +336,7 @@ But I am Ukrainian. The first 25 years of my life I spent in [Kharkiv](https://e
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.
Some of them have managed to evacuate to EU. Some others are trying to live "normal lives" 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).
Expand Down
7 changes: 7 additions & 0 deletions examples/basic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "basic"
version = "0.1.0"
edition = "2024"

[dependencies]
kinded = { path = "../../kinded" }
67 changes: 67 additions & 0 deletions examples/basic/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use kinded::Kinded;

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

fn main() {
// Create enum variants with associated data
let espresso = Drink::Coffee("Espresso".to_owned());
let green_tea = Drink::Tea {
variety: "Sencha".to_owned(),
caffeine: true,
};
let mate = Drink::Mate;

// Use .kind() to get the kind without associated data
assert_eq!(espresso.kind(), DrinkKind::Coffee);
assert_eq!(green_tea.kind(), DrinkKind::Tea);
assert_eq!(mate.kind(), DrinkKind::Mate);

// Get all kind variants
assert_eq!(
DrinkKind::all(),
[DrinkKind::Mate, DrinkKind::Coffee, DrinkKind::Tea]
);

// Pattern match on kind and access original data
let description = match &espresso {
Drink::Mate => "Just mate".to_owned(),
Drink::Coffee(name) => format!("Coffee: {name}"),
Drink::Tea { variety, caffeine } => {
format!("Tea: {variety}, caffeine: {caffeine}")
}
};
assert_eq!(description, "Coffee: Espresso");

// Display trait
assert_eq!(DrinkKind::Coffee.to_string(), "Coffee");
assert_eq!(DrinkKind::Tea.to_string(), "Tea");
assert_eq!(DrinkKind::Mate.to_string(), "Mate");

// FromStr trait
assert_eq!("Tea".parse::<DrinkKind>().unwrap(), DrinkKind::Tea);
assert_eq!("Coffee".parse::<DrinkKind>().unwrap(), DrinkKind::Coffee);
assert_eq!("Mate".parse::<DrinkKind>().unwrap(), DrinkKind::Mate);

// FromStr is case-insensitive
assert_eq!("tea".parse::<DrinkKind>().unwrap(), DrinkKind::Tea);
assert_eq!("TEA".parse::<DrinkKind>().unwrap(), DrinkKind::Tea);

// From trait - convert from Drink to DrinkKind
assert_eq!(DrinkKind::from(&espresso), DrinkKind::Coffee);
assert_eq!(DrinkKind::from(&green_tea), DrinkKind::Tea);
assert_eq!(DrinkKind::from(&mate), DrinkKind::Mate);

// Kind implements Copy, Clone, PartialEq, Eq, Debug
let kind = DrinkKind::Coffee;
let kind_copy = kind; // Copy
#[allow(clippy::clone_on_copy)]
let kind_clone = kind.clone(); // Clone
assert_eq!(kind, kind_copy); // PartialEq
assert_eq!(kind, kind_clone);
assert_eq!(format!("{:?}", kind), "Coffee"); // Debug
}
8 changes: 8 additions & 0 deletions examples/enumset_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "enumset_example"
version = "0.1.0"
edition = "2024"

[dependencies]
kinded = { path = "../../kinded" }
enumset = "1"
Loading
Loading