tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 497HTML 254C 19JavaScript 81329 Definitions

Improve error handling for index based API

09eca732fb671db2a93f4adfec52108104549b7b · by msiglreith · (7 years ago) · parent d76d61f4c · browse the tree at this commit

Semantic delta · 25

src/algo.rs

src/lib.rs

src/node.rs

src/result.rs

4 files · +108 −47

diff --git a/src/algo.rs b/src/algo.rs
index 7d7240314..741512642 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -3,11 +3,11 @@ use alloc::{vec, vec::Vec};
 #[cfg(not(feature = "std"))]
 use libm::F32Ext;
 
+use core::any::Any;
 use core::f32;
 
 use crate::node::{Node, Storage, Stretch};
 use crate::result;
-use crate::result::Result;
 use crate::style::*;
 
 use crate::number::Number::*;
@@ -58,7 +58,7 @@ struct FlexLine {
 }
 
 impl Stretch {
-    pub(crate) fn compute(&mut self, root: Node, size: Size<Number>) -> Result<()> {
+    pub(crate) fn compute(&mut self, root: Node, size: Size<Number>) -> Result<(), Box<Any>> {
         let style = self.style[&root];
         let has_root_min_max = style.min_size.width.is_defined()
             || style.min_size.height.is_defined()
@@ -101,7 +101,7 @@ impl Stretch {
             )?
         };
 
-        *self.layout.get_mut(&root).unwrap() = result::Layout {
+        *self.layout.get_mut(root).unwrap() = result::Layout {
             order: 0,
             size: Size { width: result.size.width, height: result.size.height },
             location: Point { x: 0.0, y: 0.0 },
@@ -118,7 +118,7 @@ impl Stretch {
         abs_x: f32,
         abs_y: f32,
     ) {
-        let layout = layouts.get_mut(&root).unwrap();
+        let layout = layouts.get_mut(root).unwrap();
         let abs_x = abs_x + layout.location.x;
         let abs_y = abs_y + layout.location.y;
 
@@ -137,8 +137,8 @@ impl Stretch {
         node_size: Size<Number>,
         parent_size: Size<Number>,
         perform_layout: bool,
-    ) -> Result<ComputeResult> {
-        *self.is_dirty.get_mut(&node).unwrap() = false;
+    ) -> Result<ComputeResult, Box<Any>> {
+        *self.is_dirty.get_mut(node).unwrap() = false;
 
         // First we check if we have a result for the given input
         if let Some(cache) = &self.layout_cache[&node] {
@@ -200,7 +200,7 @@ impl Stretch {
 
             if let Some(ref measure) = self.measure[&node] {
                 let result = ComputeResult { size: measure(node_size)? };
-                *self.layout_cache.get_mut(&node).unwrap() =
+                *self.layout_cache.get_mut(node).unwrap() =
                     Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
                 return Ok(result);
             }
@@ -281,7 +281,7 @@ impl Stretch {
 
         // TODO - this does not follow spec. See commented out code below
         // 3. Determine the flex base size and hypothetical main size of each item:
-        flex_items.iter_mut().try_for_each(|child| -> Result<()> {
+        flex_items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
             let child_style = self.style[&child.node];
 
             // A. If the item has a definite used flex basis, that’s the flex base size.
@@ -368,7 +368,7 @@ impl Stretch {
         // The hypothetical main size is the item’s flex base size clamped according to its
         // used min and max main sizes (and flooring the content box size at zero).
 
-        flex_items.iter_mut().try_for_each(|child| -> Result<()> {
+        flex_items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
             child.inner_flex_basis = child.flex_basis - child.padding.main(dir) - child.border.main(dir);
 
             // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
@@ -445,7 +445,7 @@ impl Stretch {
         //
         // 9.7. Resolving Flexible Lengths
 
-        flex_lines.iter_mut().try_for_each(|line| -> Result<()> {
+        flex_lines.iter_mut().try_for_each(|line| -> Result<(), Box<Any>> {
             // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all
             //    items on the line. If the sum is less than the flex container’s inner main size,
             //    use the flex grow factor for the rest of this algorithm; otherwise, use the
@@ -462,7 +462,7 @@ impl Stretch {
             //    - If using the flex shrink factor: any item that has a flex base size
             //      smaller than its hypothetical main size
 
-            line.items.iter_mut().try_for_each(|child| -> Result<()> {
+            line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
                 // TODO - This is not found by reading the spec. Maybe this can be done in some other place
                 // instead. This was found by trail and error fixing tests to align with webkit output.
                 if node_inner_size.main(dir).is_undefined() && is_row {
@@ -618,7 +618,7 @@ impl Stretch {
                 //    item’s target main size was made smaller by this, it’s a max violation.
                 //    If the item’s target main size was made larger by this, it’s a min violation.
 
-                let total_violation = unfrozen.iter_mut().try_fold(0.0, |acc, child| -> Result<f32> {
+                let total_violation = unfrozen.iter_mut().try_fold(0.0, |acc, child| -> Result<f32, Box<Any>> {
                     // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
                     // The following logic was developed not from the spec but by trail and error looking into how
                     // webkit handled various scenarios. Can probably be solved better by passing in
@@ -695,7 +695,7 @@ impl Stretch {
         //    used main size and the available space, treating auto as fit-content.
 
         flex_lines.iter_mut().try_for_each(|line| {
-            line.items.iter_mut().try_for_each(|child| -> Result<()> {
+            line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
                 let child_cross =
                     child.size.cross(dir).maybe_max(child.min_size.cross(dir)).maybe_min(child.max_size.cross(dir));
 
@@ -741,7 +741,7 @@ impl Stretch {
 
         if has_baseline_child {
             flex_lines.iter_mut().try_for_each(|line| {
-                line.items.iter_mut().try_for_each(|child| -> Result<()> {
+                line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
                     let result = self.compute_internal(
                         child.node,
                         Size {
@@ -1088,7 +1088,7 @@ impl Stretch {
         // layout we are done now.
         if !perform_layout {
             let result = ComputeResult { size: container_size };
-            *self.layout_cache.get_mut(&node).unwrap() =
+            *self.layout_cache.get_mut(node).unwrap() =
                 Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
             return Ok(result);
         }
@@ -1152,12 +1152,12 @@ impl Stretch {
             let mut lines: Vec<Vec<result::Layout>> = vec![];
             let mut total_offset_cross = padding_border.cross_start(dir);
 
-            let layout_line = |line: &mut FlexLine| -> Result<()> {
+            let layout_line = |line: &mut FlexLine| -> Result<(), Box<Any>> {
                 let mut children: Vec<result::Layout> = vec![];
                 let mut total_offset_main = padding_border.main_start(dir);
                 let line_offset_cross = line.offset_cross;
 
-                let layout_item = |child: &mut FlexItem| -> Result<()> {
+                let layout_item = |child: &mut FlexItem| -> Result<(), Box<Any>> {
                     let result = self.compute_internal(
                         child.node,
                         child.target_size.map(|s| s.to_number()),
@@ -1176,7 +1176,7 @@ impl Stretch {
                         + child.margin.cross_start(dir)
                         + (child.position.cross_start(dir).or_else(0.0) - child.position.cross_end(dir).or_else(0.0));
 
-                    *self.layout.get_mut(&child.node).unwrap() = result::Layout {
+                    *self.layout.get_mut(child.node).unwrap() = result::Layout {
                         order: self.children[&node].iter().position(|n| *n == child.node).unwrap() as u32,
                         size: result.size,
                         location: Point {
@@ -1334,7 +1334,7 @@ impl Stretch {
                     }
                 };
 
-                *self.layout.get_mut(&child).unwrap() = result::Layout {
+                *self.layout.get_mut(child).unwrap() = result::Layout {
                     order: order as u32,
                     size: result.size,
                     location: Point {
@@ -1346,7 +1346,7 @@ impl Stretch {
         }
 
         fn hidden_layout(layout: &mut Storage<result::Layout>, children: &Storage<Vec<Node>>, node: Node, order: u32) {
-            *layout.get_mut(&node).unwrap() =
+            *layout.get_mut(node).unwrap() =
                 result::Layout { order, size: Size { width: 0.0, height: 0.0 }, location: Point { x: 0.0, y: 0.0 } };
 
             for (order, child) in children[&node].iter().enumerate() {
@@ -1361,7 +1361,7 @@ impl Stretch {
         }
 
         let result = ComputeResult { size: container_size };
-        *self.layout_cache.get_mut(&node).unwrap() =
+        *self.layout_cache.get_mut(node).unwrap() =
             Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
         Ok(result)
     }
diff --git a/src/lib.rs b/src/lib.rs
index b9705ac80..97fef7abb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,3 +16,29 @@ mod algo;
 mod id;
 
 pub use crate::node::Stretch;
+
+use core::any::Any;
+
+#[derive(Debug)]
+pub enum Error {
+    InvalidNode(node::Node),
+    Measure(Box<Any>),
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        match *self {
+            Error::InvalidNode(ref node) => write!(f, "Invalid node {:?}", node),
+            Error::Measure(_) => write!(f, "Error during measurement"),
+        }
+    }
+}
+
+impl std::error::Error for Error {
+    fn description(&self) -> &str {
+        match *self {
+            Error::InvalidNode(_) => "The node is not part of the stretch instance",
+            Error::Measure(_) => "Error occurred inside a measurement function",
+        }
+    }
+}
diff --git a/src/node.rs b/src/node.rs
index 66c5e7967..d9e80f71f 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -1,6 +1,8 @@
 #[cfg(not(feature = "std"))]
 use alloc::{vec, vec::Vec};
 
+use core::any::Any;
+
 use std::collections::HashMap;
 use std::ops::Drop;
 use std::sync::Mutex;
@@ -8,10 +10,11 @@ use std::sync::Mutex;
 use crate::geometry::Size;
 use crate::id;
 use crate::number::Number;
-use crate::result::{Cache, Layout, Result};
+use crate::result::{Cache, Layout};
 use crate::style::*;
+use crate::Error;
 
-type MeasureFunc = Box<Fn(Size<Number>) -> Result<Size<f32>>>;
+type MeasureFunc = Box<Fn(Size<Number>) -> Result<Size<f32>, Box<Any>>>;
 
 lazy_static! {
     /// Global stretch instance id allocator.
@@ -24,7 +27,39 @@ pub struct Node {
     local: id::Id,
 }
 
-pub(crate) type Storage<T> = HashMap<Node, T>;
+pub(crate) struct Storage<T>(HashMap<Node, T>);
+
+impl<T> Storage<T> {
+    pub fn new() -> Self {
+        Storage(HashMap::new())
+    }
+
+    pub fn get(&self, node: Node) -> Result<&T, Error> {
+        match self.0.get(&node) {
+            Some(v) => Ok(v),
+            None => Err(Error::InvalidNode(node)),
+        }
+    }
+
+    pub fn get_mut(&mut self, node: Node) -> Result<&mut T, Error> {
+        match self.0.get_mut(&node) {
+            Some(v) => Ok(v),
+            None => Err(Error::InvalidNode(node)),
+        }
+    }
+
+    pub fn insert(&mut self, node: Node, value: T) -> Option<T> {
+        self.0.insert(node, value)
+    }
+}
+
+impl<T> std::ops::Index<&Node> for Storage<T> {
+    type Output = T;
+
+    fn index(&self, idx: &Node) -> &T {
+        &(self.0)[idx]
+    }
+}
 
 pub struct Stretch {
     id: id::Id,
@@ -72,11 +107,11 @@ impl Stretch {
         node
     }
 
-    pub fn new_node(&mut self, style: Style, children: Vec<Node>) -> Node {
+    pub fn new_node(&mut self, style: Style, children: Vec<Node>) -> Result<Node, Error> {
         let node = self.allocate_node();
 
         for child in &children {
-            self.parents.get_mut(&child).unwrap().push(node);
+            self.parents.get_mut(*child)?.push(node);
         }
 
         self.style.insert(node, style);
@@ -87,12 +122,13 @@ impl Stretch {
         self.layout_cache.insert(node, None);
         self.is_dirty.insert(node, true);
 
-        node
+        Ok(node)
     }
 
-    pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) {
-        *self.measure[&node].unwrap() = measure;
-        self.mark_dirty(node);
+    pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> Result<(), Error> {
+        *self.measure.get_mut(node)? = measure;
+        self.mark_dirty(node)?;
+        Ok(())
     }
 
     pub fn add_child(&mut self, node: Node, child: Node) {
@@ -162,12 +198,12 @@ impl Stretch {
         unimplemented!()
     }
 
-    pub fn children(&self, node: Node) -> Vec<Node> {
-        self.children[&node].clone()
+    pub fn children(&self, node: Node) -> Result<Vec<Node>, Error> {
+        self.children.get(node).map(Clone::clone)
     }
 
-    pub fn child_count(&self, node: Node) -> usize {
-        self.children[&node].len()
+    pub fn child_count(&self, node: Node) -> Result<usize, Error> {
+        self.children.get(node).map(Vec::len)
     }
 
     pub fn set_style(&mut self, node: Node, style: Style) {
@@ -177,15 +213,15 @@ impl Stretch {
         unimplemented!()
     }
 
-    pub fn style(&self, node: Node) -> &Style {
-        &self.style[&node]
+    pub fn style(&self, node: Node) -> Result<&Style, Error> {
+        self.style.get(node)
     }
 
-    pub fn layout(&self, node: Node) -> &Layout {
-        &self.layout[&node]
+    pub fn layout(&self, node: Node) -> Result<&Layout, Error> {
+        self.layout.get(node)
     }
 
-    pub fn mark_dirty(&mut self, node: Node) {
+    pub fn mark_dirty(&mut self, node: Node) -> Result<(), Error> {
         // let mut node = self.0.borrow_mut();
         // node.layout_cache = None;
         // node.is_dirty = true;
@@ -199,12 +235,15 @@ impl Stretch {
         unimplemented!()
     }
 
-    pub fn dirty(&self, node: Node) -> bool {
-        self.is_dirty[&node]
+    pub fn dirty(&self, node: Node) -> Result<bool, Error> {
+        self.is_dirty.get(node).map(|v| *v)
     }
 
-    pub fn compute_layout(&mut self, node: Node, size: Size<Number>) -> Result<()> {
-        self.compute(node, size)
+    pub fn compute_layout(&mut self, node: Node, size: Size<Number>) -> Result<(), Error> {
+        match self.layout.get(node) {
+            Ok(_) => self.compute(node, size).map_err(|err| Error::Measure(err)),
+            _ => Err(Error::InvalidNode(node)),
+        }
     }
 }
 
diff --git a/src/result.rs b/src/result.rs
index ef000ef5a..6b464ade9 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,14 +1,10 @@
 #[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
-use core::any::Any;
-
 use crate::algo::ComputeResult;
 use crate::geometry::{Point, Size};
 use crate::number::Number;
 
-pub type Result<T> = core::result::Result<T, Box<Any>>;
-
 #[derive(Copy, Debug, Clone)]
 pub struct Layout {
     pub(crate) order: u32,