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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased
- Add `attrs` attribute to pass extra attributes to the generated kind enum (e.g., `#[kinded(attrs(serde(rename_all = "snake_case")))]`)
- Make `kind()` method `const fn`, allowing usage in const contexts (fixes #12)

## v0.4.1 - 2026-01-29
- Add `#[kinded(rename = "...")]` attribute for variants to customize display/parse names.
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ enum DrinkKind {
}

impl Drink {
fn kind(&self) -> DrinkKind {
const fn kind(&self) -> DrinkKind {
match self {
Drink::Mate => DrinkKind::Mate,
Drink::Coffee(..) => DrinkKind::Coffee,
Expand All @@ -46,6 +46,22 @@ impl Drink {
}
```

## Const context

The `kind()` method is a `const fn`, so it can be used in const contexts:

```rs
use kinded::Kinded;

#[derive(Kinded)]
enum Status {
Active,
Inactive,
}

const ACTIVE_KIND: StatusKind = Status::Active.kind();
```

## Kinded trait

The library provides `Kinded` trait:
Expand Down
29 changes: 24 additions & 5 deletions kinded/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,37 @@
//! }
//!
//! impl Drink {
//! fn kind(&self) -> DrinkKind {
//! Drink::Mate => DrinkKind::Mate,
//! Drink::Coffee(..) => DrinkKind::Coffee,
//! Drink::Tea { .. } => DrinkKind::Tea,
//! const fn kind(&self) -> DrinkKind {
//! match self {
//! Drink::Mate => DrinkKind::Mate,
//! Drink::Coffee(..) => DrinkKind::Coffee,
//! Drink::Tea { .. } => DrinkKind::Tea,
//! }
//! }
//! }
//! ```
//!
//! ## Const context
//!
//! The `kind()` method is a `const fn`, so it can be used in const contexts:
//!
//! ```
//! use kinded::Kinded;
//!
//! #[derive(Kinded)]
//! enum Status {
//! Active,
//! Inactive,
//! }
//!
//! const ACTIVE_KIND: StatusKind = Status::Active.kind();
//! ```
//!
//! ## Kinded trait
//!
//! The library provides `Kinded` trait:
//!
//! ```rs
//! ```ignore
//! pub trait Kinded {
//! type Kind: PartialEq + Eq + Debug + Clone + Copy;
//!
Expand Down
2 changes: 1 addition & 1 deletion kinded_macros/src/generate/main_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn gen_fn_kind(meta: &Meta) -> TokenStream {
.map(|variant| gen_match_branch(name, &kind_name, variant));

quote!(
pub fn kind(&self) -> #kind_name { // pub fn kind(&self) -> DrinkKind {
pub const fn kind(&self) -> #kind_name { // pub const fn kind(&self) -> DrinkKind {
match self { // match self {
#(#match_branches),* // Drink::Coffee(..) => DrinkKind::Coffee,
} // }
Expand Down
74 changes: 74 additions & 0 deletions test_suite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,80 @@ fn should_work_with_lifetimes() {
assert_eq!(identifier.kind(), IdentifierKind::Name);
}

mod const_kind {
use kinded::Kinded;

#[derive(Kinded)]
enum Status {
Active,
Inactive,
Pending(i32),
Custom { value: i32 },
}

/// Test that kind() can be used in const context with unit variant
const ACTIVE_KIND: StatusKind = Status::Active.kind();

/// Test that kind() can be used in const context with unit variant (another)
const INACTIVE_KIND: StatusKind = Status::Inactive.kind();

#[test]
fn should_work_in_const_context_unit_variant() {
assert_eq!(ACTIVE_KIND, StatusKind::Active);
assert_eq!(INACTIVE_KIND, StatusKind::Inactive);
}

#[test]
fn should_work_in_const_fn() {
const fn get_kind(status: &Status) -> StatusKind {
status.kind()
}

const STATUS: Status = Status::Active;
const KIND: StatusKind = get_kind(&STATUS);
assert_eq!(KIND, StatusKind::Active);
}

#[test]
fn should_work_in_const_match() {
const fn is_active(status: &Status) -> bool {
matches!(status.kind(), StatusKind::Active)
}

const RESULT: bool = is_active(&Status::Active);
assert!(RESULT);
}

/// Test with generic enum
#[derive(Kinded)]
enum Maybe<T> {
Just(T),
Nothing,
}

const NOTHING_KIND: MaybeKind = Maybe::<i32>::Nothing.kind();

#[test]
fn should_work_with_generic_enum_in_const() {
assert_eq!(NOTHING_KIND, MaybeKind::Nothing);
}

/// Test with custom kind name
#[derive(Kinded)]
#[kinded(kind = SimpleStatus)]
enum ComplexStatus {
Ok,
Error(i32),
}

const OK_KIND: SimpleStatus = ComplexStatus::Ok.kind();

#[test]
fn should_work_with_custom_kind_name_in_const() {
assert_eq!(OK_KIND, SimpleStatus::Ok);
}
}

mod rename {
extern crate alloc;
use alloc::string::ToString;
Expand Down
Loading