lipgloss_tree/
enumerator.rs

1//! Enumerator functions for tree branch characters and indentation.
2//!
3//! This module provides the core functions for generating tree branch
4//! characters (├──, └──, etc.) and indentation strings for nested content.
5//! It includes both built-in enumerators with box-drawing characters and
6//! type definitions for custom enumerator functions.
7
8use crate::Children;
9use lipgloss::Style;
10
11/// Function type for generating tree branch characters.
12///
13/// An `Enumerator` takes a children collection and an index, then returns
14/// the appropriate branch character string for that position. Typically,
15/// the last child in a sequence gets a different character (└──) than
16/// intermediate children (├──).
17///
18/// # Arguments
19///
20/// * `children` - The children collection being enumerated
21/// * `index` - The zero-based index of the current child
22///
23/// # Returns
24///
25/// A string representing the branch character(s) for this position
26///
27/// # Examples
28///
29/// ```rust
30/// use lipgloss_tree::{Children, Enumerator};
31///
32/// let custom_enumerator: Enumerator = |children, index| {
33///     if index == children.length() - 1 {
34///         "└──".to_string()  // Last child
35///     } else {
36///         "├──".to_string()  // Intermediate child
37///     }
38/// };
39/// ```
40pub type Enumerator = fn(&dyn Children, usize) -> String;
41
42/// Function type for generating indentation strings for nested tree content.
43///
44/// An `Indenter` generates the indentation used for child nodes that appear
45/// below their parent. This creates the visual connection lines between
46/// parent and nested content. The indentation typically differs based on
47/// whether there are more siblings following (│ continues the line) or if
48/// this is the last child (spaces for clean termination).
49///
50/// # Arguments
51///
52/// * `children` - The children collection being indented
53/// * `index` - The zero-based index of the current child
54///
55/// # Returns
56///
57/// A string representing the indentation for nested content under this child
58///
59/// # Examples
60///
61/// ```rust
62/// use lipgloss_tree::{Children, Indenter};
63///
64/// let custom_indenter: Indenter = |children, index| {
65///     if index == children.length() - 1 {
66///         "    ".to_string()  // Spaces for last child
67///     } else {
68///         "│   ".to_string()  // Vertical line for continuing branches
69///     }
70/// };
71/// ```
72pub type Indenter = fn(&dyn Children, usize) -> String;
73
74/// Function type for generating styles based on child position.
75///
76/// A `StyleFunc` dynamically determines the styling to apply to tree elements
77/// based on the child's position within its parent's children collection.
78/// This allows for positional styling like alternating colors, highlighting
79/// specific indices, or applying different styles to first/last children.
80///
81/// # Arguments
82///
83/// * `children` - The children collection containing the styled element
84/// * `index` - The zero-based index of the current child being styled
85///
86/// # Returns
87///
88/// A `Style` object with the desired formatting for this position
89///
90/// # Examples
91///
92/// ```rust
93/// use lipgloss::{Style, Color};
94/// use lipgloss_tree::{Children, StyleFunc};
95///
96/// // Alternating colors based on index
97/// let alternating_style: StyleFunc = |_, index| {
98///     if index % 2 == 0 {
99///         Style::new().foreground(Color::from("blue"))
100///     } else {
101///         Style::new().foreground(Color::from("green"))
102///     }
103/// };
104///
105/// // Highlight first and last items
106/// let highlight_ends: StyleFunc = |children, index| {
107///     if index == 0 || index == children.length() - 1 {
108///         Style::new().bold(true).foreground(Color::from("red"))
109///     } else {
110///         Style::new()
111///     }
112/// };
113/// ```
114pub type StyleFunc = fn(&dyn Children, usize) -> Style;
115
116/// Default tree enumerator using standard box-drawing characters.
117///
118/// This enumerator generates the classic tree structure using Unicode
119/// box-drawing characters. Intermediate children get "├──" and the
120/// last child gets "└──" to properly terminate the branch.
121///
122/// # Arguments
123///
124/// * `children` - The children collection being enumerated
125/// * `index` - The zero-based index of the current child
126///
127/// # Returns
128///
129/// "├──" for intermediate children, "└──" for the last child
130///
131/// # Output Example
132///
133/// ```text
134/// ├── Foo
135/// ├── Bar
136/// ├── Baz
137/// └── Qux
138/// ```
139///
140/// # Examples
141///
142/// ```rust
143/// use lipgloss_tree::{default_enumerator, new_string_data};
144///
145/// let children = new_string_data(&["Foo", "Bar", "Qux"]);
146/// assert_eq!(default_enumerator(&children, 0), "├──");  // First child
147/// assert_eq!(default_enumerator(&children, 1), "├──");  // Middle child  
148/// assert_eq!(default_enumerator(&children, 2), "└──");  // Last child
149/// ```
150pub fn default_enumerator(children: &dyn Children, index: usize) -> String {
151    if children.length() > 0 && children.length() - 1 == index {
152        "└──".to_string()
153    } else {
154        "├──".to_string()
155    }
156}
157
158/// Tree enumerator using rounded box-drawing characters for the last child.
159///
160/// Similar to the default enumerator, but uses a rounded corner character
161/// (╰──) for the last child instead of the standard corner (└──). This
162/// provides a softer, more modern aesthetic while maintaining the same
163/// tree structure.
164///
165/// # Arguments
166///
167/// * `children` - The children collection being enumerated
168/// * `index` - The zero-based index of the current child
169///
170/// # Returns
171///
172/// "├──" for intermediate children, "╰──" for the last child
173///
174/// # Output Example
175///
176/// ```text
177/// ├── Foo
178/// ├── Bar
179/// ├── Baz
180/// ╰── Qux
181/// ```
182///
183/// # Examples
184///
185/// ```rust
186/// use lipgloss_tree::{rounded_enumerator, new_string_data};
187///
188/// let children = new_string_data(&["Foo", "Bar", "Qux"]);
189/// assert_eq!(rounded_enumerator(&children, 0), "├──");  // First child
190/// assert_eq!(rounded_enumerator(&children, 1), "├──");  // Middle child
191/// assert_eq!(rounded_enumerator(&children, 2), "╰──");  // Last child (rounded)
192/// ```
193pub fn rounded_enumerator(children: &dyn Children, index: usize) -> String {
194    if children.length() > 0 && children.length() - 1 == index {
195        "╰──".to_string()
196    } else {
197        "├──".to_string()
198    }
199}
200
201/// Default tree indenter for nested content and multiline text.
202///
203/// This indenter creates the visual connection lines for nested tree content.
204/// It uses a vertical line (│) with spaces for continuing branches and
205/// plain spaces for content under the last child. The spacing is carefully
206/// calculated to align with the default enumerator characters.
207///
208/// # Arguments
209///
210/// * `children` - The children collection being indented
211/// * `index` - The zero-based index of the current child
212///
213/// # Returns
214///
215/// "│   " (vertical line + 3 spaces) for continuing branches,
216/// "    " (4 spaces) for content under the last child
217///
218/// # Output Example
219///
220/// ```text
221/// ├── Foo
222/// ├── Bar
223/// │   ├── Qux
224/// │   ├── Quux
225/// │   │   ├── Foo
226/// │   │   └── Bar
227/// │   └── Quuux
228/// └── Baz
229/// ```
230///
231/// # Examples
232///
233/// ```rust
234/// use lipgloss_tree::{default_indenter, new_string_data};
235///
236/// let children = new_string_data(&["Foo", "Bar", "Baz"]);
237/// assert_eq!(default_indenter(&children, 0), "│   ");   // Continuing branch
238/// assert_eq!(default_indenter(&children, 1), "│   ");   // Continuing branch
239/// assert_eq!(default_indenter(&children, 2), "    ");   // Last child (spaces only)
240/// ```
241///
242/// # Design Notes
243///
244/// The spacing is designed to align with standard enumerator widths:
245/// - "├── " is 4 characters wide
246/// - "└── " is 4 characters wide  
247/// - "│   " provides continuing vertical line + 3 spaces = 4 total
248/// - "    " provides 4 spaces for clean termination
249pub fn default_indenter(children: &dyn Children, index: usize) -> String {
250    if children.length() > 0 && children.length() - 1 == index {
251        "    ".to_string() // 4 spaces for last item (to align with "└── " width)
252    } else {
253        "│   ".to_string() // 3 spaces after │ (to align with "├── " width)
254    }
255}