use core::fmt;
use fixedbitset::FixedBitSet;
use alloc::borrow::Cow;
#[derive(Default, Debug)]
pub struct MetaData {
access: Access,
name: Cow<'static, str>,
error_message: Option<Cow<'static, str>>,
}
impl MetaData {
pub fn new<S>() -> Self {
Self { access: Access::new(), name: Cow::from(super::type_name::normalized::<S>()), error_message: None }
}
#[inline(always)]
pub fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
#[inline(always)]
pub fn set_error_message(&mut self, error: Cow<'static, str>) {
self.error_message = Some(error);
}
#[inline(always)]
pub fn error_message(&self) -> Option<Cow<'static, str>> {
self.error_message.clone()
}
#[inline(always)]
pub(crate) fn access_mut(&mut self) -> &mut Access {
&mut self.access
}
#[inline(always)]
pub(crate) fn access(&self) -> &Access {
&self.access
}
}
#[derive(Default)]
pub struct Access {
config_writes: FixedBitSet,
config_read_and_writes: FixedBitSet,
reads_all_configs: bool,
writes_all_configs: bool,
has_deferred: bool,
}
impl Access {
pub const fn new() -> Self {
Self {
config_writes: FixedBitSet::new(),
config_read_and_writes: FixedBitSet::new(),
reads_all_configs: false,
writes_all_configs: false,
has_deferred: false,
}
}
}
impl Access {
pub fn add_config_write(&mut self, id: usize) {
self.config_writes.grow_and_insert(id);
self.config_read_and_writes.grow_and_insert(id);
}
pub fn add_config_read(&mut self, id: usize) {
self.config_read_and_writes.grow_and_insert(id);
}
pub fn has_config_write(&self, id: usize) -> bool {
self.writes_all_configs | self.config_writes.contains(id)
}
pub fn has_config_read(&self, id: usize) -> bool {
self.reads_all_configs | self.config_read_and_writes.contains(id)
}
pub fn has_any_config_write(&self) -> bool {
self.writes_all_configs | (self.config_writes.count_ones(..) > 0)
}
pub fn has_any_config_read(&self) -> bool {
self.reads_all_configs | (self.config_read_and_writes.count_ones(..) > 0)
}
pub fn has_writes_all_configs(&self) -> bool {
self.writes_all_configs
}
pub fn has_reads_all_configs(&self) -> bool {
self.reads_all_configs
}
pub fn has_deferred(&self) -> bool {
self.has_deferred
}
pub fn reads_all_configs(&mut self) {
self.reads_all_configs = true;
}
pub fn writes_all_configs(&mut self) {
self.writes_all_configs = true;
self.reads_all_configs = true;
}
pub fn deferred(&mut self) {
self.has_deferred = true;
}
}
impl fmt::Debug for Access {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Access")
.field("reads_all_configs", &self.reads_all_configs)
.field("writes_all_configs", &self.writes_all_configs)
.field("config_writes", &PrettyFixedBitSet(&self.config_writes))
.field(
"config_reads",
&PrettyFixedBitSet(&self.config_read_and_writes.difference(&self.config_writes).collect()),
)
.finish()
}
}
pub struct PrettyFixedBitSet<'a>(&'a FixedBitSet);
impl fmt::Debug for PrettyFixedBitSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.ones().map(|i| i as u32)).finish()
}
}
#[cfg(test)]
#[coverage(off)]
mod tests {
use super::*;
extern crate std;
#[test]
fn test_debug_view_calculates_config_reads_correctly() {
let mut access = Access::new();
access.add_config_write(0);
access.add_config_read(1);
access.reads_all_configs();
access.writes_all_configs();
assert_eq!(
std::format!("{access:?}"),
"Access { reads_all_configs: true, writes_all_configs: true, config_writes: [0], config_reads: [1] }"
);
}
#[test]
fn test_write_config_marks_as_read_also() {
let mut access = Access::new();
access.add_config_write(0);
assert!(access.has_config_read(0));
assert!(access.has_config_write(0));
assert!(!access.has_config_read(1));
assert!(!access.has_config_write(1));
assert!(access.has_any_config_read());
assert!(access.has_any_config_write());
}
#[test]
fn test_read_config_does_not_mark_config_write() {
let mut access = Access::new();
access.add_config_read(0);
assert!(access.has_any_config_read());
assert!(access.has_config_read(0));
assert!(!access.has_any_config_write());
assert!(!access.has_config_write(0));
assert!(!access.has_config_read(1));
assert!(!access.has_config_write(1));
}
#[test]
fn test_reads_all_configs_does_not_mark_config_write() {
let mut access = Access::new();
access.reads_all_configs();
for i in 0..10 {
assert!(access.has_config_read(i));
assert!(!access.has_config_write(i));
}
assert!(access.has_any_config_read());
assert!(!access.has_any_config_write());
}
#[test]
fn test_writes_all_configs_marks_config_read_also() {
let mut access = Access::new();
access.writes_all_configs();
for i in 0..10 {
assert!(access.has_config_read(i));
assert!(access.has_config_write(i));
}
assert!(access.has_any_config_read());
assert!(access.has_any_config_write());
}
}