properties: rewrite properties (whoops)
This commit is contained in:
270
src/properties.rs
Normal file
270
src/properties.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use rgb::Rgb;
|
||||
pub use paste::paste;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Variant {
|
||||
SignedByte(i8),
|
||||
Byte(u8),
|
||||
UInt(u32),
|
||||
Int(i32),
|
||||
BigUInt(u64),
|
||||
BigInt(i64),
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
RGB(Rgb<u8>),
|
||||
Vec(Vec<Variant>)
|
||||
}
|
||||
|
||||
macro_rules! impl_variant_type {
|
||||
($type:ty, $var_type:tt) => {
|
||||
impl From<$type> for Variant {
|
||||
fn from(value: $type) -> Self {
|
||||
Variant::$var_type(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<$type> for Variant {
|
||||
fn into(self) -> $type {
|
||||
match self {
|
||||
Variant::$var_type(value) => value,
|
||||
_ => panic!(concat!("Expected Variant::", stringify!($var_type), "but got {:?}"), self)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_variant_type!(u8, Byte);
|
||||
impl_variant_type!(i8, SignedByte);
|
||||
impl_variant_type!(u32, UInt);
|
||||
impl_variant_type!(i32, Int);
|
||||
impl_variant_type!(i64, BigInt);
|
||||
impl_variant_type!(u64, BigUInt);
|
||||
impl_variant_type!(bool, Boolean);
|
||||
impl_variant_type!(String, String);
|
||||
impl_variant_type!(Rgb<u8>, RGB);
|
||||
impl_variant_type!(Vec<Variant>, Vec);
|
||||
|
||||
impl<'a> From<&'a str> for Variant {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Variant::String(value.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Property {
|
||||
key: PropertyKey,
|
||||
value: Variant
|
||||
}
|
||||
|
||||
impl Property {
|
||||
pub const fn new(key: PropertyKey, value: Variant) -> Self {
|
||||
Property {
|
||||
key,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct NamespaceKey(pub &'static str);
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct PropertyKey(pub &'static str);
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct PropertyID {
|
||||
pub namespace: NamespaceKey,
|
||||
pub key: PropertyKey
|
||||
}
|
||||
|
||||
impl Display for PropertyID {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}::{}", self.namespace.0, self.key.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Namespace {
|
||||
fn nskey() -> NamespaceKey;
|
||||
fn properties() -> Vec<Property>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! prop_id {
|
||||
($head:ident $(:: $tail:tt)+) => {
|
||||
prop_id!( $head :: ; $($tail),* )
|
||||
};
|
||||
($($namespace:ident ::)+ ; $key:ident ) => {
|
||||
$crate::properties::PropertyID {
|
||||
namespace: $($namespace ::)+ NAMESPACE_KEY,
|
||||
key: paste! { $($namespace ::)+ [<KEY_ $key>] }
|
||||
}
|
||||
};
|
||||
($($namespace:ident ::)+ ; $head:ident , $($tail:ident),+) => {
|
||||
prop_id!( $($namespace ::)* $head :: ; $($tail),* )
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! property_namespace {
|
||||
($name:ident, $($prop_name:ident => $value:expr),*) => {
|
||||
pub enum $name {
|
||||
$($prop_name),*
|
||||
}
|
||||
|
||||
use paste::paste;
|
||||
|
||||
impl crate::properties::Namespace for $name {
|
||||
fn nskey() -> $crate::properties::NamespaceKey {
|
||||
Self::NAMESPACE_KEY
|
||||
}
|
||||
|
||||
fn properties() -> Vec<$crate::properties::Property> {
|
||||
vec![
|
||||
$($crate::properties::Property::new(paste! {Self::[<KEY_ $prop_name>]}, $crate::properties::Variant::from($value)),)*
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub const NAMESPACE_KEY: $crate::properties::NamespaceKey = $crate::properties::NamespaceKey(stringify!($name));
|
||||
|
||||
$(paste! { pub const [<KEY_ $prop_name>]: crate::properties::PropertyKey = $crate::properties::PropertyKey(stringify!($prop_name)); })*
|
||||
|
||||
pub const fn key(&self) -> $crate::properties::PropertyKey {
|
||||
match self {
|
||||
$(Self::$prop_name => $crate::properties::PropertyKey(stringify!($prop_name))),*
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn id(&self) -> $crate::properties::PropertyID {
|
||||
$crate::properties::PropertyID { namespace: Self::NAMESPACE_KEY, key: self.key() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<$crate::properties::PropertyID> for $name {
|
||||
fn into(self) -> $crate::properties::PropertyID {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NamespaceProps {
|
||||
props: Vec<Property>,
|
||||
key: NamespaceKey
|
||||
}
|
||||
|
||||
impl NamespaceProps {
|
||||
fn new<NS: Namespace>() -> Self {
|
||||
NamespaceProps {
|
||||
props: NS::properties(),
|
||||
key: NS::nskey()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self, key: &PropertyKey) -> Option<&Property> {
|
||||
for next in self.props.iter() {
|
||||
if next.key == *key {
|
||||
return Some(next);
|
||||
}
|
||||
}
|
||||
|
||||
log::warn!("Unknown key {:?}", key);
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
fn get_key_mut(&mut self, key: &PropertyKey) -> Option<&mut Property> {
|
||||
for next in self.props.iter_mut() {
|
||||
if next.key == *key {
|
||||
return Some(next);
|
||||
}
|
||||
}
|
||||
|
||||
log::warn!("Unknown key {:?}", key);
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Properties {
|
||||
contents: Vec<NamespaceProps>
|
||||
}
|
||||
|
||||
impl Display for Properties {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for ns in self.contents.iter() {
|
||||
write!(f, "{}\n", ns.key.0).unwrap();
|
||||
for prop in ns.props.iter() {
|
||||
write!(f, "\t{} = {:?}\n", prop.key.0, prop.value).unwrap();
|
||||
}
|
||||
}
|
||||
Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Properties {
|
||||
pub fn new() -> Self {
|
||||
Properties {
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_namespace<T: Namespace>(&mut self) {
|
||||
self.contents.push(NamespaceProps::new::<T>());
|
||||
}
|
||||
|
||||
fn get_namespace(&self, ns: &NamespaceKey) -> Option<&NamespaceProps> {
|
||||
for nsprops in self.contents.iter() {
|
||||
if nsprops.key == *ns {
|
||||
return Some(nsprops)
|
||||
}
|
||||
}
|
||||
|
||||
log::warn!("Unknown namespace {:?}", ns);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn get_namespace_mut(&mut self, ns: &NamespaceKey) -> Option<&mut NamespaceProps> {
|
||||
for nsprops in self.contents.iter_mut() {
|
||||
if nsprops.key == *ns {
|
||||
return Some(nsprops)
|
||||
}
|
||||
}
|
||||
|
||||
log::warn!("Unknown namespace {:?}", ns);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get<T: Into<PropertyID>>(&self, key: T) -> Option<Variant> {
|
||||
let as_id = key.into();
|
||||
match self.get_namespace(&as_id.namespace).unwrap().get_key(&as_id.key) {
|
||||
None => None,
|
||||
Some(v) => Some(v.value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set<V, T: Into<PropertyID>>(&mut self, key: T, value: V) -> bool where Variant: From<V> {
|
||||
let as_id = key.into();
|
||||
let as_variant: Variant = value.into();
|
||||
match self.get_namespace_mut(&as_id.namespace).unwrap().get_key_mut(&as_id.key) {
|
||||
None => (),
|
||||
Some(found_key) => {
|
||||
if found_key.value != as_variant {
|
||||
found_key.value = as_variant;
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user