use TermelOccupancy::{Continuation, Origin, Reserved, Unoccupied};
#[doc = crate::_tags!(term text layout bit)]
#[doc = crate::_doc_meta!{location("sys/os/term/grid")}]
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum TermelOccupancy {
#[default]
Unoccupied = 0,
Origin = 1,
Continuation = 2,
Reserved = 3,
}
impl TermelOccupancy {
#[must_use]
pub const fn from_u8(value: u8) -> Self {
match value {
0 => Self::Unoccupied,
1 => Self::Origin,
2 => Self::Continuation,
_ => Self::Reserved,
}
}
}
crate::bitfield! {
#[doc = crate::_tags!(term text layout bit)]
#[doc = crate::_doc_meta!{
location("sys/os/term/grid"),
test_size_of(TermelMeta = 1),
}]
#[must_use]
pub struct TermelMeta(u8) {
KIND = 0..=1;
VALUE = 2..=5;
}
impl {
pub const MAX_SPAN: u8 = 15;
#[must_use]
pub const fn origin(width: u8) -> Option<Self> {
if width == 0 || width > Self::MAX_SPAN { None }
else { Some( Self::new().with_kind(Origin as u8).with_value(width)) }
}
#[must_use]
pub const fn continuation(origin_offset: u8) -> Option<Self> {
if origin_offset == 0 || origin_offset > Self::MAX_SPAN { None }
else { Some( Self::new().with_kind(Continuation as u8).with_value(origin_offset)) }
}
#[must_use]
pub const fn occupancy(self) -> TermelOccupancy {
TermelOccupancy::from_u8(self.get_kind())
}
pub const fn is_occupied(self) -> bool {
matches!( self.occupancy(), Origin | Continuation)
}
#[must_use]
pub const fn is_unoccupied(self) -> bool { matches!(self.occupancy(), Unoccupied) }
#[must_use]
pub const fn is_origin(self) -> bool { matches!(self.occupancy(), Origin) }
#[must_use]
pub const fn is_continuation(self) -> bool { matches!(self.occupancy(), Continuation) }
#[must_use]
pub const fn width(self) -> Option<u8> {
if self.is_origin() { Some(self.get_value()) } else { None }
}
#[must_use]
pub const fn origin_offset(self) -> Option<u8> {
if self.is_continuation() { Some(self.get_value()) } else { None }
}
#[must_use]
pub const fn is_canonical(self) -> bool {
if self.bits() & !Self::mask() != 0 { return false; }
match self.occupancy() {
Unoccupied => self.get_value() == 0,
Origin | Continuation => { self.get_value() != 0 }
Reserved => false,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size() {
assert_eq!(size_of::<TermelMeta>(), 1);
}
#[test]
fn empty() {
let meta = TermelMeta::new();
assert!(meta.is_empty());
assert_eq!(meta.width(), None);
assert_eq!(meta.origin_offset(), None);
assert!(meta.is_canonical());
}
#[test]
fn origin() {
let meta = TermelMeta::origin(4).unwrap();
assert!(meta.is_origin());
assert_eq!(meta.width(), Some(4));
assert_eq!(meta.origin_offset(), None);
assert!(meta.is_canonical());
}
#[test]
fn continuation() {
let meta = TermelMeta::continuation(3).unwrap();
assert!(meta.is_continuation());
assert_eq!(meta.width(), None);
assert_eq!(meta.origin_offset(), Some(3));
assert!(meta.is_canonical());
}
#[test]
fn span_limits() {
assert_eq!(TermelMeta::origin(0), None);
assert_eq!(TermelMeta::origin(16), None);
assert_eq!(TermelMeta::continuation(0), None);
assert_eq!(TermelMeta::continuation(16), None);
assert_eq!(TermelMeta::origin(TermelMeta::MAX_SPAN).unwrap().width(), Some(15),);
}
#[test]
fn occupied_sequence() {
let cells = [
TermelMeta::origin(4).unwrap(),
TermelMeta::continuation(1).unwrap(),
TermelMeta::continuation(2).unwrap(),
TermelMeta::continuation(3).unwrap(),
];
assert_eq!(cells[0].width(), Some(4));
assert_eq!(cells[1].origin_offset(), Some(1));
assert_eq!(cells[2].origin_offset(), Some(2));
assert_eq!(cells[3].origin_offset(), Some(3));
}
}