1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Utility functions for unicode box characters
use base::GraphemeCluster;

/// Components of unicode box characters. A single character can contain up to 4 segments.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LineSegment {
    Up,
    Down,
    Right,
    Left,
}
impl LineSegment {
    /// c.f. CELL_TO_CHAR lookup table
    fn to_u8(self) -> u8 {
        match self {
            LineSegment::Up => 0b00000001,
            LineSegment::Down => 0b00000100,
            LineSegment::Right => 0b00010000,
            LineSegment::Left => 0b01000000,
        }
    }
}

/// The type of a segment of a unicode box character
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LineType {
    None,
    Thin,
    Thick,
}
impl LineType {
    /// c.f. CELL_TO_CHAR lookup table
    fn to_u8(self) -> u8 {
        match self {
            LineType::None => 0b00,
            LineType::Thin => 0b01,
            LineType::Thick => 0b10,
        }
    }
}

/// A single box character, initially empty.
#[derive(Copy, Clone, Debug)]
pub struct LineCell {
    components: u8,
}

impl LineCell {
    /// Create an empty box drawing cell (i.e., a space character)
    /// Add segments using `set`.
    pub fn empty() -> Self {
        LineCell { components: 0 }
    }

    /// Convert the cell to a grapheme cluster (always safe).
    pub fn to_grapheme_cluster(self) -> GraphemeCluster {
        GraphemeCluster::try_from(CELL_TO_CHAR[self.components as usize])
            .expect("CELL_TO_CHAR elements are single clusters")
    }

    /// Set one of the four segments of the cell to the specified type.
    pub fn set(&mut self, segment: LineSegment, ltype: LineType) -> &mut Self {
        let segment = segment.to_u8();
        let ltype = ltype.to_u8();
        let other_component_mask = !(segment * 0b11);
        self.components = (self.components & other_component_mask) | segment * ltype;
        self
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
const CELL_TO_CHAR: [char; 256] = [
    ' ', '╵', '╹', '╳',
    '╷', '│', '╿', '╳',
    '╻', '╽', '┃', '╳',
    '╳', '╳', '╳', '╳',
    '╶', '└', '┖', '╳',
    '┌', '├', '┞', '╳',
    '┎', '┟', '┠', '╳',
    '╳', '╳', '╳', '╳',
    '╺', '┕', '┗', '╳',
    '┍', '┝', '┡', '╳',
    '┏', '┢', '┣', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╴', '┘', '┚', '╳',
    '┐', '┤', '┦', '╳',
    '┒', '┧', '┨', '╳',
    '╳', '╳', '╳', '╳',
    '─', '┴', '┸', '╳',
    '┬', '┼', '╀', '╳',
    '┰', '╁', '╂', '╳',
    '╳', '╳', '╳', '╳',
    '╼', '┶', '┺', '╳',
    '┮', '┾', '╄', '╳',
    '┲', '╆', '╊', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╸', '┙', '┛', '╳',
    '┑', '┥', '┩', '╳',
    '┓', '┪', '┫', '╳',
    '╳', '╳', '╳', '╳',
    '╾', '┵', '┹', '╳',
    '┭', '┽', '╃', '╳',
    '┱', '╅', '╉', '╳',
    '╳', '╳', '╳', '╳',
    '━', '┷', '┻', '╳',
    '┯', '┿', '╇', '╳',
    '┳', '╈', '╋', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
    '╳', '╳', '╳', '╳',
];