comfy_table/utils/arrangement/
constraint.rs

1use super::helper::*;
2use super::{ColumnDisplayInfo, DisplayInfos};
3use crate::style::{ColumnConstraint, ColumnConstraint::*, Width};
4use crate::{Column, Table};
5
6/// Look at given constraints of a column and check if some of them can be resolved at the very
7/// beginning.
8///
9/// For example:
10/// - We get an absolute width.
11/// - MinWidth constraints on columns, whose content is garantueed to be smaller than the specified
12///     minimal width.
13/// - The Column is supposed to be hidden.
14pub fn evaluate(
15    table: &Table,
16    visible_columns: usize,
17    infos: &mut DisplayInfos,
18    column: &Column,
19    max_content_width: u16,
20) {
21    match &column.constraint {
22        Some(ContentWidth) => {
23            let info = ColumnDisplayInfo::new(column, max_content_width);
24            infos.insert(column.index, info);
25        }
26        Some(Absolute(width)) => {
27            if let Some(width) = absolute_value_from_width(table, width, visible_columns) {
28                // The column should get always get a fixed width.
29                let width = absolute_width_with_padding(column, width);
30                let info = ColumnDisplayInfo::new(column, width);
31                infos.insert(column.index, info);
32            }
33        }
34        Some(Hidden) => {
35            let mut info = ColumnDisplayInfo::new(column, max_content_width);
36            info.is_hidden = true;
37            infos.insert(column.index, info);
38        }
39        _ => {}
40    }
41
42    if let Some(min_width) = min(table, &column.constraint, visible_columns) {
43        // In case a min_width is specified, we may already fix the size of the column.
44        // We do this, if we know that the content is smaller than the min size.
45        let max_width = max_content_width + column.padding_width();
46        if max_width <= min_width {
47            let width = absolute_width_with_padding(column, min_width);
48            let info = ColumnDisplayInfo::new(column, width);
49            infos.insert(column.index, info);
50        }
51    }
52}
53
54/// A little wrapper, which resolves possible lower boundary constraints to their actual value for
55/// the current table and terminal width.
56///
57/// This returns the value of absolute characters that are allowed to be in this column. \
58/// Lower boundaries with [Width::Fixed] just return their internal value. \
59/// Lower boundaries with [Width::Percentage] return the percental amount of the current table
60/// width.
61pub fn min(
62    table: &Table,
63    constraint: &Option<ColumnConstraint>,
64    visible_columns: usize,
65) -> Option<u16> {
66    let constraint = if let Some(constraint) = constraint {
67        constraint
68    } else {
69        return None;
70    };
71
72    match constraint {
73        LowerBoundary(width) | Boundaries { lower: width, .. } => {
74            absolute_value_from_width(table, width, visible_columns)
75        }
76        _ => None,
77    }
78}
79
80/// A little wrapper, which resolves possible upper boundary constraints to their actual value for
81/// the current table and terminal width.
82///
83/// This returns the value of absolute characters that are allowed to be in this column. \
84/// Upper boundaries with [Width::Fixed] just return their internal value. \
85/// Upper boundaries with [Width::Percentage] return the percental amount of the current table
86/// width.
87pub fn max(
88    table: &Table,
89    constraint: &Option<ColumnConstraint>,
90    visible_columns: usize,
91) -> Option<u16> {
92    let constraint = if let Some(constraint) = constraint {
93        constraint
94    } else {
95        return None;
96    };
97
98    match constraint {
99        UpperBoundary(width) | Boundaries { upper: width, .. } => {
100            absolute_value_from_width(table, width, visible_columns)
101        }
102        _ => None,
103    }
104}
105
106/// Resolve an absolute value from a given boundary
107pub fn absolute_value_from_width(
108    table: &Table,
109    width: &Width,
110    visible_columns: usize,
111) -> Option<u16> {
112    match width {
113        Width::Fixed(width) => Some(*width),
114        Width::Percentage(percent) => {
115            // Don't return a value, if we cannot determine the current table width.
116            let table_width = table.width().map(usize::from)?;
117
118            // Enforce at most 100%
119            let percent = std::cmp::min(*percent, 100u16);
120
121            // Subtract the borders from the table width.
122            let width = table_width.saturating_sub(count_border_columns(table, visible_columns));
123
124            // Calculate the absolute value in actual columns.
125            let width = (width * usize::from(percent) / 100)
126                .try_into()
127                .unwrap_or(u16::MAX);
128            Some(width)
129        }
130    }
131}