Browse Source

Merge pull request #384 from DioxusLabs/jk/enum-values

Arbitrary Attribute Values
Jon Kelley 3 năm trước cách đây
mục cha
commit
f57234a635

+ 232 - 0
packages/core/src/arbitrary_value.rs

@@ -0,0 +1,232 @@
+use std::fmt::Formatter;
+
+// trying to keep values at 3 bytes
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq)]
+pub enum AttributeValue<'a> {
+    Text(&'a str),
+    Float32(f32),
+    Float64(f64),
+    Int32(i32),
+    Int64(i64),
+    Uint32(u32),
+    Uint64(u64),
+    Bool(bool),
+
+    Vec3Float(f32, f32, f32),
+    Vec3Int(i32, i32, i32),
+    Vec3Uint(u32, u32, u32),
+
+    Vec4Float(f32, f32, f32, f32),
+    Vec4Int(i32, i32, i32, i32),
+    Vec4Uint(u32, u32, u32, u32),
+
+    Bytes(&'a [u8]),
+    Any(ArbitraryAttributeValue<'a>),
+}
+
+impl<'a> AttributeValue<'a> {
+    pub fn is_truthy(&self) -> bool {
+        match self {
+            AttributeValue::Text(t) => *t == "true",
+            AttributeValue::Bool(t) => *t,
+            _ => false,
+        }
+    }
+
+    pub fn is_falsy(&self) -> bool {
+        match self {
+            AttributeValue::Text(t) => *t == "false",
+            AttributeValue::Bool(t) => !(*t),
+            _ => false,
+        }
+    }
+}
+
+impl<'a> std::fmt::Display for AttributeValue<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self {
+            AttributeValue::Text(a) => write!(f, "{}", a),
+            AttributeValue::Float32(a) => write!(f, "{}", a),
+            AttributeValue::Float64(a) => write!(f, "{}", a),
+            AttributeValue::Int32(a) => write!(f, "{}", a),
+            AttributeValue::Int64(a) => write!(f, "{}", a),
+            AttributeValue::Uint32(a) => write!(f, "{}", a),
+            AttributeValue::Uint64(a) => write!(f, "{}", a),
+            AttributeValue::Bool(a) => write!(f, "{}", a),
+            AttributeValue::Vec3Float(_, _, _) => todo!(),
+            AttributeValue::Vec3Int(_, _, _) => todo!(),
+            AttributeValue::Vec3Uint(_, _, _) => todo!(),
+            AttributeValue::Vec4Float(_, _, _, _) => todo!(),
+            AttributeValue::Vec4Int(_, _, _, _) => todo!(),
+            AttributeValue::Vec4Uint(_, _, _, _) => todo!(),
+            AttributeValue::Bytes(_) => todo!(),
+            AttributeValue::Any(_) => todo!(),
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct ArbitraryAttributeValue<'a> {
+    pub value: &'a dyn std::any::Any,
+    pub cmp: fn(&'a dyn std::any::Any, &'a dyn std::any::Any) -> bool,
+}
+
+impl PartialEq for ArbitraryAttributeValue<'_> {
+    fn eq(&self, other: &Self) -> bool {
+        (self.cmp)(self.value, other.value)
+    }
+}
+
+impl std::fmt::Debug for ArbitraryAttributeValue<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ArbitraryAttributeValue").finish()
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'a> serde::Serialize for ArbitraryAttributeValue<'a> {
+    fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        panic!("ArbitraryAttributeValue should not be serialized")
+    }
+}
+#[cfg(feature = "serialize")]
+impl<'de, 'a> serde::Deserialize<'de> for &'a ArbitraryAttributeValue<'a> {
+    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        panic!("ArbitraryAttributeValue is not deserializable!")
+    }
+}
+#[cfg(feature = "serialize")]
+impl<'de, 'a> serde::Deserialize<'de> for ArbitraryAttributeValue<'a> {
+    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        panic!("ArbitraryAttributeValue is not deserializable!")
+    }
+}
+
+impl<'a> AttributeValue<'a> {
+    pub fn as_text(&self) -> Option<&'a str> {
+        match self {
+            AttributeValue::Text(s) => Some(s),
+            _ => None,
+        }
+    }
+
+    pub fn as_float32(&self) -> Option<f32> {
+        match self {
+            AttributeValue::Float32(f) => Some(*f),
+            _ => None,
+        }
+    }
+
+    pub fn as_float64(&self) -> Option<f64> {
+        match self {
+            AttributeValue::Float64(f) => Some(*f),
+            _ => None,
+        }
+    }
+
+    pub fn as_int32(&self) -> Option<i32> {
+        match self {
+            AttributeValue::Int32(i) => Some(*i),
+            _ => None,
+        }
+    }
+
+    pub fn as_int64(&self) -> Option<i64> {
+        match self {
+            AttributeValue::Int64(i) => Some(*i),
+            _ => None,
+        }
+    }
+
+    pub fn as_uint32(&self) -> Option<u32> {
+        match self {
+            AttributeValue::Uint32(i) => Some(*i),
+            _ => None,
+        }
+    }
+
+    pub fn as_uint64(&self) -> Option<u64> {
+        match self {
+            AttributeValue::Uint64(i) => Some(*i),
+            _ => None,
+        }
+    }
+
+    pub fn as_bool(&self) -> Option<bool> {
+        match self {
+            AttributeValue::Bool(b) => Some(*b),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> {
+        match self {
+            AttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec3_int(&self) -> Option<(i32, i32, i32)> {
+        match self {
+            AttributeValue::Vec3Int(x, y, z) => Some((*x, *y, *z)),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec3_uint(&self) -> Option<(u32, u32, u32)> {
+        match self {
+            AttributeValue::Vec3Uint(x, y, z) => Some((*x, *y, *z)),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec4_float(&self) -> Option<(f32, f32, f32, f32)> {
+        match self {
+            AttributeValue::Vec4Float(x, y, z, w) => Some((*x, *y, *z, *w)),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec4_int(&self) -> Option<(i32, i32, i32, i32)> {
+        match self {
+            AttributeValue::Vec4Int(x, y, z, w) => Some((*x, *y, *z, *w)),
+            _ => None,
+        }
+    }
+
+    pub fn as_vec4_uint(&self) -> Option<(u32, u32, u32, u32)> {
+        match self {
+            AttributeValue::Vec4Uint(x, y, z, w) => Some((*x, *y, *z, *w)),
+            _ => None,
+        }
+    }
+
+    pub fn as_bytes(&self) -> Option<&[u8]> {
+        match self {
+            AttributeValue::Bytes(b) => Some(b),
+            _ => None,
+        }
+    }
+
+    pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
+        match self {
+            AttributeValue::Any(a) => Some(a),
+            _ => None,
+        }
+    }
+}
+
+// #[test]
+// fn test_attribute_value_size() {
+//     assert_eq!(std::mem::size_of::<AttributeValue<'_>>(), 24);
+// }

+ 2 - 0
packages/core/src/lib.rs

@@ -2,6 +2,7 @@
 #![doc = include_str!("../README.md")]
 #![deny(missing_docs)]
 
+pub(crate) mod arbitrary_value;
 pub(crate) mod diff;
 pub(crate) mod events;
 pub(crate) mod lazynodes;
@@ -13,6 +14,7 @@ pub(crate) mod util;
 pub(crate) mod virtual_dom;
 
 pub(crate) mod innerlude {
+    pub use crate::arbitrary_value::*;
     pub use crate::events::*;
     pub use crate::lazynodes::*;
     pub use crate::mutations::*;

+ 4 - 3
packages/core/src/mutations.rs

@@ -167,8 +167,9 @@ pub enum DomEdit<'bump> {
         field: &'static str,
 
         /// The value of the attribute.
-        value: &'bump str,
+        value: AttributeValue<'bump>,
 
+        // value: &'bump str,
         /// The (optional) namespace of the attribute.
         /// For instance, "style" is in the "style" namespace.
         ns: Option<&'bump str>,
@@ -286,7 +287,7 @@ impl<'a> Mutations<'a> {
         self.edits.push(SetText { text, root });
     }
 
-    pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
+    pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: u64) {
         let Attribute {
             name,
             value,
@@ -296,7 +297,7 @@ impl<'a> Mutations<'a> {
 
         self.edits.push(SetAttribute {
             field: name,
-            value,
+            value: value.clone(),
             ns: *namespace,
             root,
         });

+ 21 - 2
packages/core/src/nodes.rs

@@ -4,7 +4,7 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 use crate::{
-    innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
+    innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState, AttributeValue},
     lazynodes::LazyNodes,
     AnyEvent, Component,
 };
@@ -339,7 +339,7 @@ pub struct Attribute<'a> {
     pub name: &'static str,
 
     /// The value of the attribute.
-    pub value: &'a str,
+    pub value: AttributeValue<'a>,
 
     /// An indication if this attribute can be ignored during diffing
     ///
@@ -357,6 +357,7 @@ pub struct Attribute<'a> {
     pub namespace: Option<&'static str>,
 }
 
+
 /// An event listener.
 /// IE onclick, onkeydown, etc
 pub struct Listener<'bump> {
@@ -610,6 +611,24 @@ impl<'a> NodeFactory<'a> {
         is_volatile: bool,
     ) -> Attribute<'a> {
         let (value, is_static) = self.raw_text(val);
+        Attribute {
+            name,
+            value: AttributeValue::Text(value),
+            is_static,
+            namespace,
+            is_volatile,
+        }
+    }
+
+    /// Create a new [`Attribute`] using non-arguments
+    pub fn custom_attr(
+        &self,
+        name: &'static str,
+        value: AttributeValue<'a>,
+        namespace: Option<&'static str>,
+        is_volatile: bool,
+        is_static: bool,
+    ) -> Attribute<'a> {
         Attribute {
             name,
             value,

+ 4 - 2
packages/ssr/src/lib.rs

@@ -186,7 +186,9 @@ impl<'a> TextRenderer<'a, '_> {
                 while let Some(attr) = attr_iter.next() {
                     match attr.namespace {
                         None => match attr.name {
-                            "dangerous_inner_html" => inner_html = Some(attr.value),
+                            "dangerous_inner_html" => {
+                                inner_html = Some(attr.value.as_text().unwrap())
+                            }
                             "allowfullscreen"
                             | "allowpaymentrequest"
                             | "async"
@@ -213,7 +215,7 @@ impl<'a> TextRenderer<'a, '_> {
                             | "reversed"
                             | "selected"
                             | "truespeed" => {
-                                if attr.value != "false" {
+                                if attr.value.is_truthy() {
                                     write!(f, " {}=\"{}\"", attr.name, attr.value)?
                                 }
                             }

+ 1 - 1
packages/web/Cargo.toml

@@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 dioxus-core = { path = "../core", version = "^0.2.1" }
 dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
 dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
-    "web",
+    "web"
 ] }
 
 js-sys = "0.3.56"

+ 1 - 1
packages/web/src/dom.rs

@@ -146,7 +146,7 @@ impl WebsysDom {
                     value,
                     ns,
                 } => {
-                    let value = serde_wasm_bindgen::to_value(value).unwrap();
+                    let value = serde_wasm_bindgen::to_value(&value).unwrap();
                     self.interpreter.SetAttribute(root, field, value, ns)
                 }
             }