| //! Functionality for wrapping text into columns. | 
 |  | 
 | use crate::core::display_width; | 
 | use crate::{wrap, Options}; | 
 |  | 
 | /// Wrap text into columns with a given total width. | 
 | /// | 
 | /// The `left_gap`, `middle_gap` and `right_gap` arguments specify the | 
 | /// strings to insert before, between, and after the columns. The | 
 | /// total width of all columns and all gaps is specified using the | 
 | /// `total_width_or_options` argument. This argument can simply be an | 
 | /// integer if you want to use default settings when wrapping, or it | 
 | /// can be a [`Options`] value if you want to customize the wrapping. | 
 | /// | 
 | /// If the columns are narrow, it is recommended to set | 
 | /// [`Options::break_words`] to `true` to prevent words from | 
 | /// protruding into the margins. | 
 | /// | 
 | /// The per-column width is computed like this: | 
 | /// | 
 | /// ``` | 
 | /// # let (left_gap, middle_gap, right_gap) = ("", "", ""); | 
 | /// # let columns = 2; | 
 | /// # let options = textwrap::Options::new(80); | 
 | /// let inner_width = options.width | 
 | ///     - textwrap::core::display_width(left_gap) | 
 | ///     - textwrap::core::display_width(right_gap) | 
 | ///     - textwrap::core::display_width(middle_gap) * (columns - 1); | 
 | /// let column_width = inner_width / columns; | 
 | /// ``` | 
 | /// | 
 | /// The `text` is wrapped using [`wrap()`] and the given `options` | 
 | /// argument, but the width is overwritten to the computed | 
 | /// `column_width`. | 
 | /// | 
 | /// # Panics | 
 | /// | 
 | /// Panics if `columns` is zero. | 
 | /// | 
 | /// # Examples | 
 | /// | 
 | /// ``` | 
 | /// use textwrap::wrap_columns; | 
 | /// | 
 | /// let text = "\ | 
 | /// This is an example text, which is wrapped into three columns. \ | 
 | /// Notice how the final column can be shorter than the others."; | 
 | /// | 
 | /// #[cfg(feature = "smawk")] | 
 | /// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"), | 
 | ///            vec!["| This is       | into three    | column can be  |", | 
 | ///                 "| an example    | columns.      | shorter than   |", | 
 | ///                 "| text, which   | Notice how    | the others.    |", | 
 | ///                 "| is wrapped    | the final     |                |"]); | 
 | /// | 
 | /// // Without the `smawk` feature, the middle column is a little more uneven: | 
 | /// #[cfg(not(feature = "smawk"))] | 
 | /// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"), | 
 | ///            vec!["| This is an    | three         | column can be  |", | 
 | ///                 "| example text, | columns.      | shorter than   |", | 
 | ///                 "| which is      | Notice how    | the others.    |", | 
 | ///                 "| wrapped into  | the final     |                |"]); | 
 | pub fn wrap_columns<'a, Opt>( | 
 |     text: &str, | 
 |     columns: usize, | 
 |     total_width_or_options: Opt, | 
 |     left_gap: &str, | 
 |     middle_gap: &str, | 
 |     right_gap: &str, | 
 | ) -> Vec<String> | 
 | where | 
 |     Opt: Into<Options<'a>>, | 
 | { | 
 |     assert!(columns > 0); | 
 |  | 
 |     let mut options: Options = total_width_or_options.into(); | 
 |  | 
 |     let inner_width = options | 
 |         .width | 
 |         .saturating_sub(display_width(left_gap)) | 
 |         .saturating_sub(display_width(right_gap)) | 
 |         .saturating_sub(display_width(middle_gap) * (columns - 1)); | 
 |  | 
 |     let column_width = std::cmp::max(inner_width / columns, 1); | 
 |     options.width = column_width; | 
 |     let last_column_padding = " ".repeat(inner_width % column_width); | 
 |     let wrapped_lines = wrap(text, options); | 
 |     let lines_per_column = | 
 |         wrapped_lines.len() / columns + usize::from(wrapped_lines.len() % columns > 0); | 
 |     let mut lines = Vec::new(); | 
 |     for line_no in 0..lines_per_column { | 
 |         let mut line = String::from(left_gap); | 
 |         for column_no in 0..columns { | 
 |             match wrapped_lines.get(line_no + column_no * lines_per_column) { | 
 |                 Some(column_line) => { | 
 |                     line.push_str(column_line); | 
 |                     line.push_str(&" ".repeat(column_width - display_width(column_line))); | 
 |                 } | 
 |                 None => { | 
 |                     line.push_str(&" ".repeat(column_width)); | 
 |                 } | 
 |             } | 
 |             if column_no == columns - 1 { | 
 |                 line.push_str(&last_column_padding); | 
 |             } else { | 
 |                 line.push_str(middle_gap); | 
 |             } | 
 |         } | 
 |         line.push_str(right_gap); | 
 |         lines.push(line); | 
 |     } | 
 |  | 
 |     lines | 
 | } | 
 |  | 
 | #[cfg(test)] | 
 | mod tests { | 
 |     use super::*; | 
 |  | 
 |     #[test] | 
 |     fn wrap_columns_empty_text() { | 
 |         assert_eq!(wrap_columns("", 1, 10, "| ", "", " |"), vec!["|        |"]); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn wrap_columns_single_column() { | 
 |         assert_eq!( | 
 |             wrap_columns("Foo", 3, 30, "| ", " | ", " |"), | 
 |             vec!["| Foo    |        |          |"] | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn wrap_columns_uneven_columns() { | 
 |         // The gaps take up a total of 5 columns, so the columns are | 
 |         // (21 - 5)/4 = 4 columns wide: | 
 |         assert_eq!( | 
 |             wrap_columns("Foo Bar Baz Quux", 4, 21, "|", "|", "|"), | 
 |             vec!["|Foo |Bar |Baz |Quux|"] | 
 |         ); | 
 |         // As the total width increases, the last column absorbs the | 
 |         // excess width: | 
 |         assert_eq!( | 
 |             wrap_columns("Foo Bar Baz Quux", 4, 24, "|", "|", "|"), | 
 |             vec!["|Foo |Bar |Baz |Quux   |"] | 
 |         ); | 
 |         // Finally, when the width is 25, the columns can be resized | 
 |         // to a width of (25 - 5)/4 = 5 columns: | 
 |         assert_eq!( | 
 |             wrap_columns("Foo Bar Baz Quux", 4, 25, "|", "|", "|"), | 
 |             vec!["|Foo  |Bar  |Baz  |Quux |"] | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     #[cfg(feature = "unicode-width")] | 
 |     fn wrap_columns_with_emojis() { | 
 |         assert_eq!( | 
 |             wrap_columns( | 
 |                 "Words and a few emojis 😍 wrapped in ⓶ columns", | 
 |                 2, | 
 |                 30, | 
 |                 "✨ ", | 
 |                 " ⚽ ", | 
 |                 " 👀" | 
 |             ), | 
 |             vec![ | 
 |                 "✨ Words      ⚽ wrapped in 👀", | 
 |                 "✨ and a few  ⚽ ⓶ columns  👀", | 
 |                 "✨ emojis 😍  ⚽            👀" | 
 |             ] | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn wrap_columns_big_gaps() { | 
 |         // The column width shrinks to 1 because the gaps take up all | 
 |         // the space. | 
 |         assert_eq!( | 
 |             wrap_columns("xyz", 2, 10, "----> ", " !!! ", " <----"), | 
 |             vec![ | 
 |                 "----> x !!! z <----", // | 
 |                 "----> y !!!   <----" | 
 |             ] | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     #[should_panic] | 
 |     fn wrap_columns_panic_with_zero_columns() { | 
 |         wrap_columns("", 0, 10, "", "", ""); | 
 |     } | 
 | } |