#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use optionally_const_macros::FieldlessEnumConstType;
#[doc(hidden)]
pub mod hidden {
#[derive(Clone, Copy, PartialEq)]
pub struct ConstTypeBool<const VAL: bool>;
}
pub type ConstTypeBool<const VAL: bool> = hidden::ConstTypeBool<VAL>;
#[diagnostic::on_unimplemented(
message = "`OptionallyConst<T>` is implemented for `T: Clone, Copy, PartialEq`\
and *should* be implemented for `U: Const<T>`",
label = "Optionally const type instance expected here",
note = "If `T` doesn't implement `OptionallyConst<T>` (that is, if `{Self}`==`{T}`) \
and `{Self}` is local to the crate, \
consider adding `#[derive(Clone, Copy, PartialEq)]` on the definition of `T`."
)]
pub trait OptionallyConst<T>: Clone + Copy + PartialEq + Sized {
const MAYBE_CONST: Option<T>;
fn into_value(self) -> T;
fn try_from_value(value: T) -> Result<Self, T>;
fn try_from_another<U>(another: U) -> Result<Self, U>
where
U: OptionallyConst<T>,
T: Clone + Copy + PartialEq,
{
Self::try_from_value(another.into_value())
.ok()
.ok_or(another)
}
}
pub trait Const<T> {
const VALUE: T;
}
impl<const VAL: bool> Const<bool> for ConstTypeBool<VAL> {
const VALUE: bool = VAL;
}
impl<T> OptionallyConst<T> for T
where
T: Clone + Copy + PartialEq,
{
const MAYBE_CONST: Option<T> = None;
fn into_value(self) -> T {
self
}
fn try_from_value(value: T) -> Result<Self, T> {
Ok(value)
}
}
impl<const VAL: bool> OptionallyConst<bool> for ConstTypeBool<VAL> {
const MAYBE_CONST: Option<bool> = Some(VAL);
fn into_value(self) -> bool {
VAL
}
fn try_from_value(value: bool) -> Result<Self, bool> {
if value == VAL {
Ok(crate::hidden::ConstTypeBool::<VAL>)
} else {
Err(value)
}
}
}
#[macro_export]
macro_rules! const_type_instance {
(true $(: bool)?) => {
$crate::hidden::ConstTypeBool::<true>
};
(false $(: bool)?) => {
$crate::hidden::ConstTypeBool::<false>
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_const_type() {
let a: ConstTypeBool<true> = const_type_instance!(true);
let b: ConstTypeBool<false> = const_type_instance!(false);
let a_value: bool = a.into_value();
let b_value: bool = b.into_value();
assert_eq!(a_value, true);
assert_eq!(b_value, false);
}
fn print_flag<T: OptionallyConst<bool>>(flag: T) {
if let Some(flag) = T::MAYBE_CONST {
println!("flag is const: {flag}");
} else {
let flag: bool = flag.into_value();
println!("flag is not const: {flag}");
};
}
#[derive(Clone, Copy, PartialEq)]
enum MyEnum {
A,
B,
C,
}
impl std::fmt::Display for MyEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MyEnum::A => write!(f, "A"),
MyEnum::B => write!(f, "B"),
MyEnum::C => write!(f, "C"),
}
}
}
#[derive(Clone, Copy, PartialEq)]
struct MyEnumAConstType;
#[derive(Clone, Copy, PartialEq)]
struct MyEnumBConstType;
#[derive(Clone, Copy, PartialEq)]
struct MyEnumCConstType;
impl Const<MyEnum> for MyEnumAConstType {
const VALUE: MyEnum = MyEnum::A;
}
impl OptionallyConst<MyEnum> for MyEnumAConstType {
const MAYBE_CONST: Option<MyEnum> = Some(MyEnum::A);
fn into_value(self) -> MyEnum {
MyEnum::A
}
fn try_from_value(value: MyEnum) -> Result<Self, MyEnum> {
if matches!(value, MyEnum::A) {
Ok(MyEnumAConstType)
} else {
Err(value)
}
}
}
impl Const<MyEnum> for MyEnumBConstType {
const VALUE: MyEnum = MyEnum::B;
}
impl OptionallyConst<MyEnum> for MyEnumBConstType {
const MAYBE_CONST: Option<MyEnum> = Some(MyEnum::B);
fn into_value(self) -> MyEnum {
MyEnum::B
}
fn try_from_value(value: MyEnum) -> Result<Self, MyEnum> {
if matches!(value, MyEnum::B) {
Ok(MyEnumBConstType)
} else {
Err(value)
}
}
}
impl Const<MyEnum> for MyEnumCConstType {
const VALUE: MyEnum = MyEnum::C;
}
impl OptionallyConst<MyEnum> for MyEnumCConstType {
const MAYBE_CONST: Option<MyEnum> = Some(MyEnum::C);
fn into_value(self) -> MyEnum {
MyEnum::C
}
fn try_from_value(value: MyEnum) -> Result<Self, MyEnum> {
if matches!(value, MyEnum::C) {
Ok(MyEnumCConstType)
} else {
Err(value)
}
}
}
fn print_my_enum<T: OptionallyConst<MyEnum>>(value: T) {
if let Some(value) = T::MAYBE_CONST {
println!("value is const: {value}");
} else {
let value: MyEnum = value.into_value();
println!("value is not const: {value}");
};
}
#[test]
fn test_output_bool() {
print_flag(true);
print_flag(false);
print_flag(const_type_instance!(true));
print_flag(const_type_instance!(false));
}
#[test]
fn test_output_my_enum() {
print_my_enum(MyEnum::A);
print_my_enum(MyEnum::B);
print_my_enum(MyEnum::C);
print_my_enum(MyEnumAConstType);
print_my_enum(MyEnumBConstType);
print_my_enum(MyEnumCConstType);
}
}