ratatui_macros/
layout.rs

1/// Creates a single constraint.
2///
3/// If creating an array of constraints, you probably want to use
4/// [`constraints!`] instead.
5///
6/// # Examples
7///
8/// ```
9/// # use ratatui_core::layout::Constraint;
10/// use ratatui_macros::constraint;
11/// assert_eq!(constraint!(>= 3 + 4), Constraint::Min(7));
12/// assert_eq!(constraint!(<= 3 + 4), Constraint::Max(7));
13/// assert_eq!(constraint!(== 1 / 3), Constraint::Ratio(1, 3));
14/// assert_eq!(constraint!(== 3), Constraint::Length(3));
15/// assert_eq!(constraint!(== 10 %), Constraint::Percentage(10));
16/// assert_eq!(constraint!(*= 1), Constraint::Fill(1));
17/// ```
18///
19/// [`constraints!`]: crate::constraints
20#[macro_export]
21macro_rules! constraint {
22    (== $token:tt %) => {
23        $crate::ratatui_core::layout::Constraint::Percentage($token)
24    };
25    (>= $expr:expr) => {
26        $crate::ratatui_core::layout::Constraint::Min($expr)
27    };
28    (<= $expr:expr) => {
29        $crate::ratatui_core::layout::Constraint::Max($expr)
30    };
31    (== $num:tt / $denom:tt) => {
32        $crate::ratatui_core::layout::Constraint::Ratio($num as u32, $denom as u32)
33    };
34    (== $expr:expr) => {
35        $crate::ratatui_core::layout::Constraint::Length($expr)
36    };
37    (*= $expr:expr) => {
38        $crate::ratatui_core::layout::Constraint::Fill($expr)
39    };
40}
41
42/// Creates an array of constraints.
43///
44/// See [`constraint!`] for more information.
45///
46/// If you want to solve the constraints, see
47/// [`vertical!`] and [`horizontal!`] macros.
48///
49/// # Examples
50///
51/// ```rust
52/// use ratatui_macros::constraints;
53/// assert_eq!(constraints![==5, ==30%, >=3, <=1, ==1/2].len(), 5);
54/// assert_eq!(constraints![==5; 5].len(), 5);
55/// ```
56///
57/// ```rust
58/// # use ratatui_core::layout::Constraint;
59/// # use ratatui_macros::constraints;
60/// assert_eq!(
61///     constraints![==50, ==30%, >=3, <=1, ==1/2, *=1],
62///     [
63///         Constraint::Length(50),
64///         Constraint::Percentage(30),
65///         Constraint::Min(3),
66///         Constraint::Max(1),
67///         Constraint::Ratio(1, 2),
68///         Constraint::Fill(1),
69///     ]
70/// )
71/// ```
72///
73/// [`constraint!`]: crate::constraint
74/// [`vertical!`]: crate::vertical
75/// [`horizontal!`]: crate::horizontal
76#[macro_export]
77macro_rules! constraints {
78    // Note: this implementation forgoes speed for the sake of simplicity. Adding variations of the
79    // comma and semicolon rules for each constraint type would be faster, but would result in a lot
80    // of duplicated code.
81
82    // Cannot start the constraints macro with a ,
83    ([ , $($rest:tt)* ] -> () []) => {
84        compile_error!("No rules expected the token `,` while trying to match the end of the macro")
85    };
86
87    // Comma finishes a constraint element, so parse it and continue.
88    // When a comma is encountered, it marks the end of a constraint element, so this rule is responsible
89    // for parsing the constraint expression up to the comma and continuing the parsing process.
90    // It accumulated the $partial contains a Constraint and is parsed using a separate $crate::constraint! macro.
91    // The constraint is then appended to the list of parsed constraints.
92    //
93    // [ , $($rest:tt)* ]                     -> In the rule matcher, this pattern matches a comma followed
94    //                                              by the rest of the tokens. The comma signals the end of
95    //                                              the current constraint element.
96    // ($($partial:tt)*)                      -> In the rule matcher, this contains the partial tokens
97    //                                              accumulated so far for the current constraint element.
98    // [$($parsed:tt)* ]                      -> This contains the constraints that have been successfully
99    //                                              parsed so far.
100    // $crate::constraint!($($partial)*)      -> This macro call parses and expands the accumulated
101    //                                              partial tokens into a single Constraint expression.
102    // [$($parsed)* $crate::constraint!(...)] -> Appends the newly parsed constraint to the list of
103    //                                              already parsed constraints.
104    ([ , $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
105        $crate::constraints!([$($rest)*] -> () [$($parsed)* $crate::constraint!($($partial)*) ,])
106    };
107
108    // Semicolon indicates that there's repetition. The trailing comma is required because the 'entrypoint'
109    // rule adds a trailing comma.
110    // This rule is triggered when a semicolon is encountered, indicating that there is repetition of
111    // constraints. It handles the repetition logic by parsing the count and generating an array of
112    // constraints using the $crate::constraint! macro.
113    //
114    // [ ; $count:expr , ]                  -> In the rule matcher, this pattern matches a semicolon
115    //                                          followed by an expression representing the count, and a
116    //                                          trailing comma.
117    // ($($partial:tt)*)                    -> In the rule matcher, this contains the partial tokens
118    //                                          accumulated so far for the current constraint element.
119    //                                          This represents everything before the ;
120    // []                                   -> There will be no existed parsed constraints when using ;
121    // $crate::constraint!($($partial)*)    -> This macro call parses and expands the accumulated
122    //                                          partial tokens into a single Constraint expression.
123    // [$crate::constraint!(...) ; $count]  -> Generates an array of constraints by repeating the
124    //                                          parsed constraint count number of times.
125    ([ ; $count:expr , ] -> ($($partial:tt)*) []) => {
126        [$crate::constraint!($($partial)*); $count]
127    };
128
129    // Pull the first token (which can't be a comma or semicolon) onto the accumulator.
130    // if first token is a comma or semicolon, previous rules will match before this rule
131    //
132    // [ $head:tt $($rest:tt)* ]           -> In the rule matcher, this pulls a single `head` token
133    //                                          out of the previous rest, and puts
134    //                                          the remaining into `rest`
135    // [ $($rest)* ]                       -> This is what is fed back into the `constraints!` macro
136    //                                          as the first segment for the match rule
137    //
138    // ($($partial:tt)*)                   -> In the rule matcher, this contains previous partial
139    //                                          tokens that will make up a `Constraint` expression
140    // ($($partial)* $head)                -> This combines head with the previous partial tokens
141    //                                          i.e. this is the accumulated tokens
142    //
143    // [ $($parsed:tt)* ]                  -> In the rule matcher, this contains all parsed exprs
144    // [$($parsed)* ]                      -> These are passed on to the next match untouched.
145    ([ $head:tt $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
146        $crate::constraints!([$($rest)*] -> ($($partial)* $head) [$($parsed)* ])
147    };
148
149    // This rule is triggered when there are no more input tokens to process. It signals the end of the
150    // macro invocation and outputs the parsed constraints as a final array.
151    ([$(,)?]  -> () [ $( $parsed:tt )* ]) => {
152        [$($parsed)*]
153    };
154
155    // Entrypoint where there's no comma at the end.
156    // We add a comma to make sure there's always a trailing comma.
157    // Right-hand side will accumulate the actual `Constraint` literals.
158    ($( $constraint:tt )+) => {
159        $crate::constraints!([ $($constraint)+ , ] -> () [])
160    };
161}
162
163/// Creates a vertical layout with specified constraints.
164///
165/// It accepts a series of constraints and applies them to create a vertical layout. The constraints
166/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
167///
168/// See [`constraint!`]  or [`constraints!`] for more information.
169///
170/// # Examples
171///
172/// ```
173/// // Vertical layout with a fixed size and a percentage constraint
174/// use ratatui_macros::vertical;
175/// vertical![== 50, == 30%];
176/// ```
177#[macro_export]
178macro_rules! vertical {
179    ($( $constraint:tt )+) => {
180        $crate::ratatui_core::layout::Layout::vertical($crate::constraints!( $($constraint)+ ))
181    };
182}
183
184/// Creates a horizontal layout with specified constraints.
185///
186/// It takes a series of constraints and applies them to create a horizontal layout. The constraints
187/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
188///
189/// See [`constraint!`]  or [`constraints!`] for more information.
190///
191/// # Examples
192///
193/// ```
194/// // Horizontal layout with a ratio constraint and a minimum size constraint
195/// use ratatui_macros::horizontal;
196/// horizontal![== 1/3, >= 100];
197/// ```
198#[macro_export]
199macro_rules! horizontal {
200    ($( $constraint:tt )+) => {
201        $crate::ratatui_core::layout::Layout::horizontal($crate::constraints!( $($constraint)+ ))
202    };
203}