about summary refs log tree commit diff
path: root/src/display/draw_box.rs
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2019-07-06T02·45-0400
committerGriffin Smith <root@gws.fyi>2019-07-06T02·45-0400
commitde081d7b1d0b791b2e61f9cde7369ea11647e0ae (patch)
tree687236ac6ca2d094053fa43112d967554531de86 /src/display/draw_box.rs
an @-sign in a box
Diffstat (limited to 'src/display/draw_box.rs')
-rw-r--r--src/display/draw_box.rs205
1 files changed, 205 insertions, 0 deletions
diff --git a/src/display/draw_box.rs b/src/display/draw_box.rs
new file mode 100644
index 000000000000..986f09a49f7c
--- /dev/null
+++ b/src/display/draw_box.rs
@@ -0,0 +1,205 @@
+use crate::display::utils::clone_times;
+use crate::display::utils::times;
+use crate::types::Dimensions;
+use itertools::Itertools;
+use proptest::prelude::Arbitrary;
+use proptest::strategy;
+use proptest_derive::Arbitrary;
+
+// Box Drawing
+//  	    0 	1 	2 	3 	4 	5 	6 	7 	8 	9 	A 	B 	C 	D 	E 	F
+// U+250x 	─ 	━ 	│ 	┃ 	┄ 	┅ 	┆ 	┇ 	┈ 	┉ 	┊ 	┋ 	┌ 	┍ 	┎ 	┏
+// U+251x 	┐ 	┑ 	┒ 	┓ 	└ 	┕ 	┖ 	┗ 	┘ 	┙ 	┚ 	┛ 	├ 	┝ 	┞ 	┟
+// U+252x 	┠ 	┡ 	┢ 	┣ 	┤ 	┥ 	┦ 	┧ 	┨ 	┩ 	┪ 	┫ 	┬ 	┭ 	┮ 	┯
+// U+253x 	┰ 	┱ 	┲ 	┳ 	┴ 	┵ 	┶ 	┷ 	┸ 	┹ 	┺ 	┻ 	┼ 	┽ 	┾ 	┿
+// U+254x 	╀ 	╁ 	╂ 	╃ 	╄ 	╅ 	╆ 	╇ 	╈ 	╉ 	╊ 	╋ 	╌ 	╍ 	╎ 	╏
+// U+255x 	═ 	║ 	╒ 	╓ 	╔ 	╕ 	╖ 	╗ 	╘ 	╙ 	╚ 	╛ 	╜ 	╝ 	╞ 	╟
+// U+256x 	╠ 	╡ 	╢ 	╣ 	╤ 	╥ 	╦ 	╧ 	╨ 	╩ 	╪ 	╫ 	╬ 	╭ 	╮ 	╯
+// U+257x 	╰ 	╱ 	╲ 	╳ 	╴ 	╵ 	╶ 	╷ 	╸ 	╹ 	╺ 	╻ 	╼ 	╽ 	╾ 	╿
+
+static BOX: char = '☐';
+
+static BOX_CHARS: [[char; 16]; 8] = [
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '─', '━', '│', '┃', '┄', '┅', '┆', '┇', '┈', '┉',
+        // 10
+        '┊', '┋', '┌', '┍', '┎', '┏',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '┐', '┑', '┒', '┓', '└', '┕', '┖', '┗', '┘', '┙',
+        '┚', '┛', '├', '┝', '┞', '┟',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '┠', '┡', '┢', '┣', '┤', '┥', '┦', '┧', '┨', '┩',
+        '┪', '┫', '┬', '┭', '┮', '┯',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '┰', '┱', '┲', '┳', '┴', '┵', '┶', '┷', '┸', '┹',
+        '┺', '┻', '┼', '┽', '┾', '┿',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '╀', '╁', '╂', '╃', '╄', '╅', '╆', '╇', '╈', '╉',
+        '╊', '╋', '╌', '╍', '╎', '╏',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '═', '║', '╒', '╓', '╔', '╕', '╖', '╗', '╘', '╙',
+        '╚', '╛', '╜', '╝', '╞', '╟',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '╠', '╡', '╢', '╣', '╤', '╥', '╦', '╧', '╨', '╩',
+        '╪', '╫', '╬', '╭', '╮', '╯',
+    ],
+    [
+        // 0    1    2    3    4    5    6    7    8    9
+        '╰', '╱', '╲', '╳', '╴', '╵', '╶', '╷', '╸', '╹',
+        '╺', '╻', '╼', '╽', '╾', '╿',
+    ],
+];
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum BoxStyle {
+    Thin,
+    Thick,
+    Dotted,
+    ThickDotted,
+    Dashed,
+    ThickDashed,
+    Double,
+}
+
+impl Arbitrary for BoxStyle {
+    type Parameters = ();
+    type Strategy = strategy::Just<Self>;
+    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+        // TODO
+        strategy::Just(BoxStyle::Thin)
+    }
+}
+
+trait Stylable {
+    fn style(self, style: BoxStyle) -> char;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
+enum Corner {
+    TopRight,
+    TopLeft,
+    BottomRight,
+    BottomLeft,
+}
+
+impl Stylable for Corner {
+    fn style(self, style: BoxStyle) -> char {
+        use BoxStyle::*;
+        use Corner::*;
+
+        match (self, style) {
+            (TopRight, Thin) => BOX_CHARS[1][0],
+            (TopLeft, Thin) => BOX_CHARS[0][12],
+            (BottomRight, Thin) => BOX_CHARS[1][8],
+            (BottomLeft, Thin) => BOX_CHARS[1][4],
+            _ => unimplemented!(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
+enum Line {
+    H,
+    V,
+}
+
+impl Stylable for Line {
+    fn style(self, style: BoxStyle) -> char {
+        use BoxStyle::*;
+        use Line::*;
+        match (self, style) {
+            (H, Thin) => BOX_CHARS[0][0],
+            (V, Thin) => BOX_CHARS[0][2],
+            _ => unimplemented!(),
+        }
+    }
+}
+
+#[must_use]
+pub fn make_box(style: BoxStyle, dims: Dimensions) -> String {
+    if dims.h == 0 || dims.w == 0 {
+        "".to_string()
+    } else if dims.h == 1 && dims.w == 1 {
+        BOX.to_string()
+    } else if dims.h == 1 {
+        times(Line::H.style(style), dims.w)
+    } else if dims.w == 1 {
+        (0..dims.h).map(|_| Line::V.style(style)).join("\n\r")
+    } else {
+        let h_line: String = times(Line::H.style(style), dims.w - 2);
+        let v_line = Line::V.style(style);
+        let v_walls: String = clone_times(
+            format!(
+                "{}{}{}\n\r",
+                v_line,
+                times::<_, String>(' ', dims.w - 2),
+                v_line
+            ),
+            dims.h - 2,
+        );
+
+        format!(
+            "{}{}{}\n\r{}{}{}{}",
+            Corner::TopLeft.style(style),
+            h_line,
+            Corner::TopRight.style(style),
+            v_walls,
+            Corner::BottomLeft.style(style),
+            h_line,
+            Corner::BottomRight.style(style),
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    #[test]
+    fn make_thin_box() {
+        let res = make_box(BoxStyle::Thin, Dimensions { w: 10, h: 10 });
+        assert_eq!(
+            res,
+            "┌────────┐
+\r│        │
+\r│        │
+\r│        │
+\r│        │
+\r│        │
+\r│        │
+\r│        │
+\r│        │
+\r└────────┘"
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn box_has_height_lines(dims: Dimensions, style: BoxStyle) {
+            let res = make_box(style, dims);
+            prop_assume!((dims.w > 0 && dims.h > 0));
+            assert_eq!(res.split("\n\r").count(), dims.h as usize);
+        }
+
+        #[test]
+        fn box_lines_have_width_length(dims: Dimensions, style: BoxStyle) {
+            let res = make_box(style, dims);
+            prop_assume!(dims.w == 0 && dims.h == 0 || (dims.w > 0 && dims.h > 0));
+            assert!(res.split("\n\r").all(|l| l.chars().count() == dims.w as usize));
+        }
+    }
+}