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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
use ::std::collections::HashMap; use ::strum::IntoEnumIterator; use ::strum_macros::EnumIter; use crate::styling::presets::ASCII_FULL; pub enum ContentArrangement { /// Don't do any automatic width calculation. /// Table with this mode might overflow and look ugly, if content gets too long. /// Constraints on columns are still respected. Disabled, /// Automatically determine the width of columns in regard to terminal width and content length. /// With this mode, the content in cells will wrap automatically and comfy-table tries to determine /// the best column layout for the given content. /// Constraints on columns are still respected. Automatic, // /// Same as Automatic, but the full width of the terminal will always be used. // /// Use this, if you want tables to use as much space as possible. // /// Constraints on columns are still respected. // Full, } /// All configurable table components. /// A character can be assigned to each component in the [TableStyle] struct. /// This is then used to draw character of the respective component to the commandline. /// Most components should be self-explanatory. /// /// BorderIntersections are Intersections, where rows/columns lines meet outer borders. /// E.g.: /// ```text /// -------- /// v | /// +--+---+---+ | /// | | | | | /// +----------+ <- These "+" chars are border intersection /// | | | | /// +--+---+---+ /// ``` #[derive(Debug, PartialEq, Eq, Hash, EnumIter)] pub enum Component { LeftBorder, RightBorder, TopBorder, BottomBorder, LeftHeaderIntersection, HeaderLines, MiddleHeaderIntersections, RightHeaderIntersection, VerticalLines, HorizontalLines, MiddleIntersections, LeftBorderIntersections, RightBorderIntersections, TopBorderIntersections, BottomBorderIntersections, TopLeftCorner, TopRightCorner, BottomLeftCorner, BottomRightCorner, } /// This struct wraps the various styling options for a table /// The default style preset when using `::new` is the [ASCII_FULL] pub struct TableStyle { pub(crate) has_header: bool, style: HashMap<Component, char>, } impl TableStyle { /// Create a new TableStyle. The default style is [ASCII_FULL], pub fn new() -> Self { let mut table_style = TableStyle { has_header: false, style: HashMap::new(), }; table_style.load_preset(ASCII_FULL); table_style } /// This function creates a TableStyle from a given preset string. /// Preset strings can be found in styling::presets::* /// /// Anyway, you can write your own preset strings and use them with this function. /// The function expects a characters for components to be in the same order as in the [Component] enum. /// /// If the string isn't long enough, the default [ASCII_FULL] style will be used for all remaining components. /// /// If the string is too long, remaining charaacters will be simply ignored. pub fn load_preset(&mut self, preset: &str) { let mut components = Component::iter(); for character in preset.chars() { if let Some(component) = components.next() { // White spaces mean "don't draw this" in presets // If we want to override the default preset, we need to remove // this component from the HashMap in case we find a whitespace. if character == ' ' { self.style.remove(&component); continue; } self.style.insert(component, character); } else { break; } } } /// Modify a preset with a modifier string from [modifiers](crate::styling::modifiers). /// For instance, the [UTF8_ROUND_CORNERS](crate::styling::modifiers::UTF8_ROUND_CORNERS) modifies all corners to be round UTF8 box corners. pub fn apply_modifier(&mut self, modifier: &str) -> &mut Self { let mut components = Component::iter(); for character in modifier.chars() { // Skip spaces while applying modifiers. if character == ' ' { continue; } if let Some(component) = components.next() { self.style.insert(component, character); } else { break; } } self } /// Define the char that will be used to draw a specific component /// Look at [Component] to see all stylable Components /// /// If `None` is supplied, the element won't be displayed. /// In case of a e.g. *BorderIntersection a whitespace will be used as placeholder, /// unless related borders and and corners are set to `None` as well. /// /// For example, if `TopBorderIntersections` is `None` the first row would look like this: /// ```text /// +------ ------+ /// | asdf | ghij | /// ``` /// /// If in addition `TopLeftCorner`,`TopBorder` and `TopRightCorner` would be `None` as well, /// the first line wouldn't be displayed at all. pub fn set_style(&mut self, component: Component, character: Option<char>) -> &mut Self { match character { Some(character) => { self.style.insert(component, character); } None => (), }; self } /// Get a copy of the char currently used for drawing a specific component pub fn get_style(&mut self, component: Component) -> Option<char> { match self.style.get(&component) { None => None, Some(character) => Some(*character), } } pub fn style_or_default(&self, component: Component) -> String { match self.style.get(&component) { None => " ".to_string(), Some(character) => character.to_string(), } } pub fn style_exists(&self, component: Component) -> bool { self.style.get(&component).is_some() } }