Skip to main content

rich/
constrain.rs

1//! Width constraint.
2//!
3//! Port of upstream `rich/constrain.py`. [`Constrain`] renders a child within a
4//! reduced maximum width.
5
6use crate::console::{Console, ConsoleOptions};
7use crate::measure::Measurement;
8use crate::protocol::Renderable;
9use crate::segment::Segment;
10
11/// Limits a child renderable to at most `width` cells. Mirrors `rich.constrain.Constrain`.
12pub struct Constrain {
13    child: Box<dyn Renderable>,
14    width: Option<usize>,
15}
16
17impl Constrain {
18    /// Constrain `child` to `width` cells (or leave unconstrained when `None`).
19    pub fn new(child: Box<dyn Renderable>, width: Option<usize>) -> Self {
20        Constrain { child, width }
21    }
22}
23
24impl Renderable for Constrain {
25    fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
26        let width = match self.width {
27            Some(width) => width.min(options.max_width),
28            None => options.max_width,
29        };
30        let child_options = options.update_width(width);
31        self.child.rich_render(console, &child_options)
32    }
33
34    /// Port of `Constrain.__rich_measure__`: the child measured within the
35    /// constraint (`Measurement.get` then caps it at the outer width).
36    fn measure(&self, console: &Console, options: &ConsoleOptions) -> Measurement {
37        let options = match self.width {
38            Some(width) => options.update_width(width),
39            None => options.clone(),
40        };
41        Measurement::get(console, &options, self.child.as_ref())
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::color::ColorSystem;
49    use crate::panel::Panel;
50    use crate::r#box::SQUARE;
51    use crate::text::Text;
52
53    #[test]
54    fn constrains_panel_width() {
55        let console = Console::builder()
56            .force_terminal(true)
57            .color_system(Some(ColorSystem::Truecolor))
58            .width(20)
59            .build();
60        let panel = Panel::new(Box::new(Text::new("hi"))).box_set(SQUARE);
61        let constrained = Constrain::new(Box::new(panel), Some(10));
62        let out = console.render_export(&constrained);
63        assert_eq!(out, "┌────────┐\n│ hi     │\n└────────┘\n");
64    }
65}