1 unstable release
0.1.0 | Aug 22, 2020 |
---|
#85 in #constants
Used in simple-irc-server
18KB
163 lines
#[const_table]
This crate provides an attribute macro to associate struct-type constants with enum variants.
[dependencies]
const-table = "0.1"
Syntax
Place #[const_table]
on an enum with at least two variants, where
- the first has named fields and defines the type of the associated constants, and
- all following have discriminant expressions of that type:
#[const_table]
pub enum Planet {
PlanetInfo {
pub mass: f32,
pub radius: f32,
},
Mercury = PlanetInfo { mass: 3.303e+23, radius: 2.4397e6 },
Venus = PlanetInfo { mass: 4.869e+24, radius: 6.0518e6 },
Earth = PlanetInfo { mass: 5.976e+24, radius: 6.37814e6 },
Mars = PlanetInfo { mass: 6.421e+23, radius: 3.3972e6 },
Jupiter = PlanetInfo { mass: 1.9e+27, radius: 7.1492e7 },
Saturn = PlanetInfo { mass: 5.688e+26, radius: 6.0268e7 },
Uranus = PlanetInfo { mass: 8.686e+25, radius: 2.5559e7 },
Neptune = PlanetInfo { mass: 1.024e+26, radius: 2.4746e7 },
}
This expands to the following:
#[repr(u32)]
#[derive(core::marker::Copy, core::clone::Clone, core::fmt::Debug, core::hash::Hash, core::cmp::PartialEq, core::cmp::Eq)]
pub enum Planet {
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Uranus,
Neptune,
}
pub struct PlanetInfo {
pub mass: f32,
pub radius: f32,
}
impl Planet {
const COUNT: usize = 8;
pub fn iter() -> impl core::iter::DoubleEndedIterator<Item = Self> {
// transmuting here is fine because... (see try_from)
(0..Self::COUNT).map(|i| unsafe { core::mem::transmute(i as u32) })
}
}
impl core::ops::Deref for Planet {
type Target = PlanetInfo;
fn deref(&self) -> &Self::Target {
use Planet::*;
const TABLE: [PlanetInfo; 8] = [
PlanetInfo { mass: 3.303e+23, radius: 2.4397e6 },
PlanetInfo { mass: 4.869e+24, radius: 6.0518e6 },
PlanetInfo { mass: 5.976e+24, radius: 6.37814e6 },
PlanetInfo { mass: 6.421e+23, radius: 3.3972e6 },
PlanetInfo { mass: 1.9e+27, radius: 7.1492e7 },
PlanetInfo { mass: 5.688e+26, radius: 6.0268e7 },
PlanetInfo { mass: 8.686e+25, radius: 2.5559e7 },
PlanetInfo { mass: 1.024e+26, radius: 2.4746e7 },
];
&TABLE[*self as usize]
}
}
impl core::convert::TryFrom<u32> for Planet {
type Error = u32;
fn try_from(i: u32) -> Result<Self, Self::Error> {
if (i as usize) < Self::COUNT {
// transmuting here is fine because all values in range are valid, since
// discriminants are assigned linearly starting at 0.
Ok(unsafe { core::mem::transmute(i) })
} else {
Err(i)
}
}
}
Note the automatically inserted repr
and derive
attributes. You may place a different repr
attribute as normal,
although only u8
, u16
, u32
and u64
are supported; an implementation of TryFrom<T>
is provided, where T
is
the chosen repr
type. You may also derive
additional traits on the enum.
Any attributes placed on the first variant will be placed on the corresponding struct in the expanded code.
Also, note that the macro places the discriminant expressions inside a scope that imports all variants of your enum. This makes it convenient to make the values refer to each other, e.g. in a graph-like structure.
Because the macro implements Deref
for your enum, you can access fields of the target type like Planet::Earth.mass
.
Finally, Planet::iter()
gives a DoubleEndedIterator
over all variants in declaration order, and Planet::COUNT
is
the total number of variants.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Dependencies
~1.5MB
~38K SLoC