use crate::TwosComplement;
#[cfg(feature = "hut")]
use hut::{self, AsUsage, AsUsagePage};
use alloc::{string::String, vec, vec::Vec};
macro_rules! impl_from {
($tipo:ty, $tipo_expr:expr, $to:ty) => {
impl From<$tipo> for $to {
fn from(f: $tipo) -> $to {
f.0
}
}
impl From<&$tipo> for $to {
fn from(f: &$tipo) -> $to {
f.0
}
}
impl From<$to> for $tipo {
fn from(f: $to) -> Self {
$tipo_expr(f)
}
}
};
}
macro_rules! impl_fmt {
($tipo:ty, $to:ty) => {
impl core::fmt::Display for $tipo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let v: $to = self.into();
write!(f, "{v}")
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct UsagePage(pub(crate) u16);
impl_from!(UsagePage, UsagePage, u16);
impl_fmt!(UsagePage, u16);
#[cfg(feature = "hut")]
impl From<&hut::UsagePage> for UsagePage {
fn from(hut: &hut::UsagePage) -> UsagePage {
UsagePage(hut.usage_page_value())
}
}
#[cfg(feature = "hut")]
impl From<hut::UsagePage> for UsagePage {
fn from(hut: hut::UsagePage) -> UsagePage {
UsagePage(hut.usage_page_value())
}
}
#[cfg(feature = "hut")]
impl From<&hut::Usage> for UsagePage {
fn from(hut: &hut::Usage) -> UsagePage {
UsagePage(hut.usage_page_value())
}
}
#[cfg(feature = "hut")]
impl From<hut::Usage> for UsagePage {
fn from(hut: hut::Usage) -> UsagePage {
UsagePage::from(&hut)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct LogicalMinimum(pub(crate) i32);
impl_from!(LogicalMinimum, LogicalMinimum, i32);
impl_fmt!(LogicalMinimum, i32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct LogicalMaximum(pub(crate) i32);
impl_from!(LogicalMaximum, LogicalMaximum, i32);
impl_fmt!(LogicalMaximum, i32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct PhysicalMinimum(pub(crate) i32);
impl_from!(PhysicalMinimum, PhysicalMinimum, i32);
impl_fmt!(PhysicalMinimum, i32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct PhysicalMaximum(pub(crate) i32);
impl_from!(PhysicalMaximum, PhysicalMaximum, i32);
impl_fmt!(PhysicalMaximum, i32);
#[derive(Debug, Clone, Copy)]
pub enum UnitSystem {
None,
SILinear,
SIRotation,
EnglishLinear,
EnglishRotation,
}
impl From<UnitSystem> for u32 {
fn from(system: UnitSystem) -> u32 {
match system {
UnitSystem::None => 0,
UnitSystem::SILinear => 1,
UnitSystem::SIRotation => 2,
UnitSystem::EnglishLinear => 3,
UnitSystem::EnglishRotation => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Units {
None,
Centimeter { exponent: i8 },
Radians { exponent: i8 },
Inch { exponent: i8 },
Degrees { exponent: i8 },
Gram { exponent: i8 },
Slug { exponent: i8 },
Seconds { exponent: i8 },
Kelvin { exponent: i8 },
Fahrenheit { exponent: i8 },
Ampere { exponent: i8 },
Candela { exponent: i8 },
}
impl core::fmt::Display for Units {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let (unit, exp) = match self {
Units::None => ("", &0i8),
Units::Centimeter { exponent } => ("cm", exponent),
Units::Radians { exponent } => ("rad", exponent),
Units::Inch { exponent } => ("in", exponent),
Units::Degrees { exponent } => ("deg", exponent),
Units::Gram { exponent } => ("g", exponent),
Units::Slug { exponent } => ("slug", exponent),
Units::Seconds { exponent } => ("s", exponent),
Units::Kelvin { exponent } => ("K", exponent),
Units::Fahrenheit { exponent } => ("F", exponent),
Units::Ampere { exponent } => ("A", exponent),
Units::Candela { exponent } => ("cd", exponent),
};
let (unit, exp) = match exp {
0 => ("", 0),
exp @ -8..=7 => (unit, *exp),
_ => ("", 0),
};
const SUP: [&str; 16] = [
"⁻⁸", "⁻⁷", "⁻⁶", "⁻⁵", "⁻⁴", "⁻³", "⁻²", "⁻¹", "", "", "²", "³", "⁴", "⁵", "⁶", "⁷",
];
let exp = SUP[(exp + 8) as usize];
write!(f, "{unit}{exp}")
}
}
impl From<Units> for u32 {
fn from(u: Units) -> u32 {
let (system, nibble, exponent) = match u {
Units::None => (UnitSystem::None, 0, 0),
Units::Centimeter { exponent } => (UnitSystem::SILinear, 1, exponent),
Units::Radians { exponent } => (UnitSystem::SIRotation, 1, exponent),
Units::Inch { exponent } => (UnitSystem::EnglishLinear, 1, exponent),
Units::Degrees { exponent } => (UnitSystem::EnglishRotation, 1, exponent),
Units::Gram { exponent } => (UnitSystem::None, 2, exponent),
Units::Slug { exponent } => (UnitSystem::None, 2, exponent),
Units::Seconds { exponent } => (UnitSystem::None, 3, exponent),
Units::Kelvin { exponent } => (UnitSystem::None, 4, exponent),
Units::Fahrenheit { exponent } => (UnitSystem::None, 4, exponent),
Units::Ampere { exponent } => (UnitSystem::None, 5, exponent),
Units::Candela { exponent } => (UnitSystem::None, 6, exponent),
};
let exp: u32 = match exponent {
exp @ 0..=7 => exp as u32,
exp @ -8..=-1 => (16 + exp) as u32,
_ => 0u32,
};
u32::from(system) | (exp << (nibble * 4))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Unit(pub(crate) u32);
impl_from!(Unit, Unit, u32);
impl Unit {
fn nibbles(&self) -> Vec<u8> {
core::ops::Range { start: 0, end: 32 }
.step_by(4)
.map(|shift| ((self.0 & (0b1111 << shift)) >> shift) as u8)
.collect()
}
pub fn units(&self) -> Option<Vec<Units>> {
let units: Vec<Units> = vec![
self.length(),
self.mass(),
self.time(),
self.temperature(),
self.current(),
self.luminosity(),
]
.into_iter()
.filter(|u| !matches!(u, Units::None))
.collect();
if units.is_empty() {
None
} else {
Some(units)
}
}
pub fn system(&self) -> UnitSystem {
match self.nibbles().first() {
None | Some(0) => UnitSystem::None,
Some(1) => UnitSystem::SILinear,
Some(2) => UnitSystem::SIRotation,
Some(3) => UnitSystem::EnglishLinear,
Some(4) => UnitSystem::EnglishRotation,
Some(n) => panic!("invalid size {n} for the UnitSystem nibble"),
}
}
pub fn length(&self) -> Units {
match self.nibbles().get(1) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
match self.system() {
UnitSystem::None => Units::None,
UnitSystem::SILinear => Units::Centimeter { exponent },
UnitSystem::SIRotation => Units::Radians { exponent },
UnitSystem::EnglishLinear => Units::Inch { exponent },
UnitSystem::EnglishRotation => Units::Degrees { exponent },
}
}
}
}
pub fn mass(&self) -> Units {
match self.nibbles().get(2) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
match self.system() {
UnitSystem::None => Units::None,
UnitSystem::SILinear => Units::Gram { exponent },
UnitSystem::SIRotation => Units::Gram { exponent },
UnitSystem::EnglishLinear => Units::Slug { exponent },
UnitSystem::EnglishRotation => Units::Slug { exponent },
}
}
}
}
pub fn time(&self) -> Units {
match self.nibbles().get(3) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
Units::Seconds { exponent }
}
}
}
pub fn temperature(&self) -> Units {
match self.nibbles().get(4) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
match self.system() {
UnitSystem::None => Units::None,
UnitSystem::SILinear => Units::Kelvin { exponent },
UnitSystem::SIRotation => Units::Kelvin { exponent },
UnitSystem::EnglishLinear => Units::Fahrenheit { exponent },
UnitSystem::EnglishRotation => Units::Fahrenheit { exponent },
}
}
}
}
pub fn current(&self) -> Units {
match self.nibbles().get(5) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
Units::Ampere { exponent }
}
}
}
pub fn luminosity(&self) -> Units {
match self.nibbles().get(6) {
None | Some(0) => Units::None,
Some(n) => {
let exponent = n.twos_comp(4);
Units::Candela { exponent }
}
}
}
}
impl core::fmt::Display for Unit {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let units = self
.units()
.unwrap_or_default()
.iter()
.map(|u| alloc::format!("{u}"))
.collect::<Vec<String>>()
.join("");
write!(f, "{units}")
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct UnitExponent(pub(crate) i32);
impl UnitExponent {
pub fn exponent(&self) -> i8 {
match self.0 {
n @ 0..=7 => n as i8,
n @ 8..=15 => -16 + n as i8,
n => n as i8,
}
}
}
impl_from!(UnitExponent, UnitExponent, i32);
impl_fmt!(UnitExponent, i32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReportSize(pub(crate) usize);
impl_from!(ReportSize, ReportSize, usize);
impl_fmt!(ReportSize, usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ReportId(pub(crate) u8);
impl From<&ReportId> for ReportId {
fn from(report_id: &ReportId) -> ReportId {
ReportId(u8::from(report_id))
}
}
impl_from!(ReportId, ReportId, u8);
impl_fmt!(ReportId, u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReportCount(pub(crate) usize);
impl_from!(ReportCount, ReportCount, usize);
impl_fmt!(ReportCount, usize);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct UsageId(pub(crate) u16);
impl_from!(UsageId, UsageId, u16);
impl_fmt!(UsageId, u16);
#[cfg(feature = "hut")]
impl From<&hut::Usage> for UsageId {
fn from(hut: &hut::Usage) -> UsageId {
UsageId(hut.usage_id_value())
}
}
#[cfg(feature = "hut")]
impl From<hut::Usage> for UsageId {
fn from(hut: hut::Usage) -> UsageId {
UsageId::from(&hut)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct UsageMinimum(pub(crate) u32);
impl UsageMinimum {
pub fn usage_id(&self) -> UsageId {
UsageId::from((self.0 & 0xffff) as u16)
}
pub fn usage_page(&self) -> UsagePage {
UsagePage((self.0 >> 16) as u16)
}
}
impl_from!(UsageMinimum, UsageMinimum, u32);
impl_fmt!(UsageMinimum, u32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct UsageMaximum(pub(crate) u32);
impl UsageMaximum {
pub fn usage_id(&self) -> UsageId {
UsageId::from((self.0 & 0xffff) as u16)
}
pub fn usage_page(&self) -> UsagePage {
UsagePage((self.0 >> 16) as u16)
}
}
impl_from!(UsageMaximum, UsageMaximum, u32);
impl_fmt!(UsageMaximum, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StringIndex(pub(crate) u32);
impl_from!(StringIndex, StringIndex, u32);
impl_fmt!(StringIndex, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StringMinimum(pub(crate) u32);
impl_from!(StringMinimum, StringMinimum, u32);
impl_fmt!(StringMinimum, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StringMaximum(pub(crate) u32);
impl_from!(StringMaximum, StringMaximum, u32);
impl_fmt!(StringMaximum, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DesignatorIndex(pub(crate) u32);
impl_from!(DesignatorIndex, DesignatorIndex, u32);
impl_fmt!(DesignatorIndex, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DesignatorMinimum(pub(crate) u32);
impl_from!(DesignatorMinimum, DesignatorMinimum, u32);
impl_fmt!(DesignatorMinimum, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DesignatorMaximum(pub(crate) u32);
impl_from!(DesignatorMaximum, DesignatorMaximum, u32);
impl_fmt!(DesignatorMaximum, u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Delimiter(pub(crate) u32);
impl_from!(Delimiter, Delimiter, u32);
impl_fmt!(Delimiter, u32);
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_units() {
for nibble in 1..=6 {
for system in 1..=2 {
let exp = 2;
let v = system | exp << (4 * nibble);
let u = Unit::from(v);
match system {
1 => assert!(matches!(u.system(), UnitSystem::SILinear)),
2 => assert!(matches!(u.system(), UnitSystem::SIRotation)),
_ => panic!("Invalid system value"),
};
match nibble {
1 => match system {
0x1 => assert!(matches!(u.length(), Units::Centimeter { exponent: 2 })),
0x2 => assert!(matches!(u.length(), Units::Radians { exponent: 2 })),
_ => panic!("Invalid system value"),
},
2 => assert!(matches!(u.mass(), Units::Gram { exponent: 2 })),
3 => assert!(matches!(u.time(), Units::Seconds { exponent: 2 })),
4 => assert!(matches!(u.temperature(), Units::Kelvin { exponent: 2 })),
5 => assert!(matches!(u.current(), Units::Ampere { exponent: 2 })),
6 => assert!(matches!(u.luminosity(), Units::Candela { exponent: 2 })),
_ => panic!("Invalid nibble value"),
}
}
for system in 3..=4 {
let exp = 0xd; let v = system | exp << (4 * nibble);
let u = Unit::from(v);
match system {
3 => assert!(matches!(u.system(), UnitSystem::EnglishLinear)),
4 => assert!(matches!(u.system(), UnitSystem::EnglishRotation)),
_ => panic!("Invalid system value"),
};
match nibble {
1 => match system {
0x3 => assert!(matches!(u.length(), Units::Inch { exponent: -3 })),
0x4 => assert!(matches!(u.length(), Units::Degrees { exponent: -3 })),
_ => panic!("Invalid system value"),
},
2 => assert!(matches!(u.mass(), Units::Slug { exponent: -3 })),
3 => assert!(matches!(u.time(), Units::Seconds { exponent: -3 })),
4 => assert!(matches!(
u.temperature(),
Units::Fahrenheit { exponent: -3 }
)),
5 => assert!(matches!(u.current(), Units::Ampere { exponent: -3 })),
6 => assert!(matches!(u.luminosity(), Units::Candela { exponent: -3 })),
_ => panic!("Invalid nibble value"),
}
}
}
let u = Unit::from(0xE121);
assert!(matches!(u.system(), UnitSystem::SILinear));
assert!(matches!(u.length(), Units::Centimeter { exponent: 2 }));
assert!(matches!(u.mass(), Units::Gram { exponent: 1 }));
assert!(matches!(u.time(), Units::Seconds { exponent: -2 }));
assert!(matches!(u.current(), Units::None));
assert!(matches!(u.luminosity(), Units::None));
assert!(matches!(u.temperature(), Units::None));
assert_eq!(format!("{u}"), "cm²gs⁻²");
let u = Unit::from(0xE012);
assert!(matches!(u.system(), UnitSystem::SIRotation));
assert!(matches!(u.length(), Units::Radians { exponent: 1 }));
assert!(matches!(u.mass(), Units::None));
assert!(matches!(u.time(), Units::Seconds { exponent: -2 }));
assert!(matches!(u.current(), Units::None));
assert!(matches!(u.temperature(), Units::None));
assert!(matches!(u.luminosity(), Units::None));
assert_eq!(format!("{u}"), "rads⁻²");
let u = Unit::from(0x00F0D121);
assert!(matches!(u.system(), UnitSystem::SILinear));
assert!(matches!(u.length(), Units::Centimeter { exponent: 2 }));
assert!(matches!(u.mass(), Units::Gram { exponent: 1 }));
assert!(matches!(u.time(), Units::Seconds { exponent: -3 }));
assert!(matches!(u.current(), Units::Ampere { exponent: -1 }));
assert!(matches!(u.temperature(), Units::None));
assert!(matches!(u.luminosity(), Units::None));
assert_eq!(format!("{u}"), "cm²gs⁻³A⁻¹");
}
#[test]
fn test_unit_conversion() {
for exp in 1..=15 {
for nibble in 1..=6 {
let v = 0x1u32 | (exp << (nibble * 4)); let unit = Unit::from(v);
assert_eq!(u32::from(unit), v);
let uval = u32::from(unit.system())
| unit
.units()
.unwrap()
.iter()
.fold(0, |acc, u| acc | u32::from(*u));
assert_eq!(uval, v);
}
}
}
#[test]
fn unit_exponent() {
let testvals = vec![
(0xf, -1), (0xe, -2), (0x8, -8), (0x7, 7), (0x1, 1), (0xff, -1), (0xfd, -3), (0x10, 16), ];
for (v, expected) in testvals {
let exponent = UnitExponent::from(v);
assert_eq!(exponent.exponent(), expected);
}
}
#[cfg(feature = "hut")]
#[test]
fn hut() {
let up = UsagePage::from(hut::UsagePage::GenericDesktop);
assert_eq!(u16::from(up), 0x1);
let up = UsagePage::from(&hut::UsagePage::GenericDesktop);
assert_eq!(u16::from(up), 0x1);
let u = UsagePage::from(hut::GenericDesktop::X.usage());
assert_eq!(u16::from(u), 0x1);
let u = UsagePage::from(&hut::GenericDesktop::X.usage());
assert_eq!(u16::from(u), 0x1);
let u = UsageId::from(hut::GenericDesktop::X.usage());
assert_eq!(u16::from(u), 0x30);
let u = UsageId::from(&hut::GenericDesktop::X.usage());
assert_eq!(u16::from(u), 0x30);
}
}