1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
//!
//! String tools.
//!
// xxx : move to crate string_tools
/// Define a private namespace for all its items.
mod private
{
// use crate::*;
/// Returns the size of the text in `src` as a `[ width, height ]` array.
///
/// This function calculates the dimensions of the input text, where the width is defined
/// as the length of the longest line, and the height is the total number of lines. It
/// handles various edge cases, including empty strings and strings with trailing newlines,
/// to ensure accurate dimension calculation.
///
/// # Arguments
///
/// * `src` - A string slice or any type that can be referenced as a string. This allows
/// for flexibility in passing different string-like types.
///
/// # Returns
///
/// A `[usize; 2]` array representing the dimensions of the text:
/// - `width`: The length of the longest line in the text.
/// - `height`: The total number of lines in the text.
///
/// # Nuances
///
/// - **Empty Strings**: If the input string is empty, the function returns `[0, 1]`
/// because there is one line with a width of zero.
/// - **Trailing Newlines**: If the input string ends with a newline character, it is
/// treated as having an additional empty line at the end.
/// - **Empty Lines**: Empty lines within the text are counted as lines with a width of zero.
///
/// # Examples
///
/// ```
/// let text = "Hello\nWorld\nThis is a test";
/// let dimensions = format_tools::string::size( text );
/// assert_eq!( dimensions, [ 14, 3 ] );
/// ```
///
/// In this example, the function returns `[ 14, 3 ]` because the longest line ( "This is a test" )
/// has 14 characters, and there are 3 lines in total.
///
/// ```
/// let text = "";
/// let dimensions = format_tools::string::size( text );
/// assert_eq!( dimensions, [ 0, 1 ] );
/// ```
///
/// Here, the function returns `[0, 1]` because the input is an empty string, which is considered
/// as a single line with zero width.
///
/// ```
/// let text = "Line 1\n\nLine 3\n";
/// let dimensions = format_tools::string::size( text );
/// assert_eq!( dimensions, [ 6, 4 ] );
/// ```
///
/// In this example, the function returns `[ 6, 4 ]` because the longest line ( "Line 1" or "Line 3" )
/// has 6 characters, there are 4 lines in total, including the empty line and the trailing newline.
pub fn size< S : AsRef< str > >( src : S ) -> [ usize ; 2 ]
{
let text = src.as_ref();
let mut height = 0;
let mut width = 0;
for line in lines( text )
{
height += 1;
let line_length = line.as_bytes().len();
if line_length > width
{
width = line_length;
}
}
[ width, height ]
}
/// Returns an iterator over the lines of a string slice.
///
/// This function provides an iterator that yields each line of the input string slice.
/// It is an enhancement over the standard `str::lines()` method, as it handles trailing
/// newlines by returning an additional empty line if the input string ends with a newline.
///
/// # Arguments
///
/// * `src` - A reference to a type that can be converted to a string slice. This allows
/// for flexibility in passing various string-like types.
///
/// # Returns
///
/// An iterator of type `Lines` that yields each line as a `&str`.
///
/// # Examples
///
/// ```
/// let text = "Hello\nWorld\n";
/// let mut lines = format_tools::string::lines( text );
/// assert_eq!( lines.next(), Some( "Hello" ) );
/// assert_eq!( lines.next(), Some( "World" ) );
/// assert_eq!( lines.next(), Some( "" ) );
/// assert_eq!( lines.next(), None );
/// ```
pub fn lines< S : AsRef< str > + ?Sized >( src : & S ) -> Lines< '_ >
{
Lines::new( src.as_ref() )
}
/// Returns an iterator over the lines of a string slice with text wrapping.
///
/// This function provides an iterator that yields each line of the input string slice.
/// It is based on previous iterator `lines` but it also includes text wrapping that is
/// controlled via `limit_width` argument. If the string contains a trailing new line,
/// then an empty string will be yielded in this iterator.
///
/// # Arguments
///
/// * `src` - A reference to a type that can be converted to a string slice. This allows
/// for flexibility in passing various string-like types.
///
/// * `limit_width` - text wrapping limit. Lines that are longer than this parameter will
// be split into smaller lines.
///
/// # Returns
///
/// An iterator of type `LinesWithLimit` that yields each line as a `&str`.
///
/// # Examples
///
/// ```
/// let text = "Hello\nWorld\n";
/// let mut lines = format_tools::string::lines_with_limit( text, 3 );
/// assert_eq!( lines.next(), Some( "Hel" ) );
/// assert_eq!( lines.next(), Some( "lo" ) );
/// assert_eq!( lines.next(), Some( "Wor" ) );
/// assert_eq!( lines.next(), Some( "ld" ) );
/// assert_eq!( lines.next(), Some( "" ) );
/// assert_eq!( lines.next(), None );
/// ```
pub fn lines_with_limit< S : AsRef< str > + ?Sized >
(
src : & S,
limit_width : usize
)
-> LinesWithLimit< '_ >
{
LinesWithLimit::new( src.as_ref(), limit_width )
}
/// An iterator over the lines of a string slice.
///
/// This struct implements the `Iterator` trait, allowing you to iterate over the lines
/// of a string. It enhances the standard `str::Lines` iterator by handling trailing
/// newlines, ensuring that an additional empty line is returned if the input string
/// ends with a newline character.
/// ```
#[ derive( Debug ) ]
pub struct Lines< 'a >
{
lines : std::str::Lines< 'a >,
has_trailing_newline : bool,
finished : bool,
}
impl< 'a > Lines< 'a >
{
fn new( input : &'a str ) -> Self
{
let has_trailing_newline = input.len() == 0 || input.ends_with( '\n' );
Lines
{
lines : input.lines(),
has_trailing_newline,
finished : false,
}
}
}
impl< 'a > Iterator for Lines< 'a >
{
type Item = &'a str;
fn next( &mut self ) -> Option< Self::Item >
{
if self.finished
{
return None;
}
match self.lines.next()
{
Some( line ) => Some( line ),
None =>
{
if self.has_trailing_newline
{
self.finished = true;
Some( "" )
}
else
{
None
}
}
}
}
}
/// An iterator over the lines of a string slice with text wrapping.
///
/// This struct implements the `Iterator` trait, allowing you to iterate over the parts
/// of a string. It uses `Lines` iterator and splits lines if they are longer that the
/// `limit_width` parameter. If the string contains a trailing new line, then an empty
/// string will be yielded in this iterator.
///
/// If `limit_width` is equal to 0, then no wrapping is applied, and behaviour of this
/// iterator is equals to `Lines` iterator.
#[ derive( Debug ) ]
pub struct LinesWithLimit< 'a >
{
lines : Lines< 'a >,
limit_width : usize,
cur : Option< &'a str >,
}
impl< 'a > LinesWithLimit< 'a >
{
fn new( input : &'a str, limit_width : usize ) -> Self
{
LinesWithLimit
{
lines : lines( input ),
limit_width,
cur : None,
}
}
}
impl< 'a > Iterator for LinesWithLimit< 'a >
{
type Item = &'a str;
fn next( &mut self ) -> Option< Self::Item >
{
loop
{
let s = match self.cur
{
Some( st ) if !st.is_empty() => st,
_ =>
{
let next_line = self.lines.next()?;
if next_line.is_empty()
{
self.cur = None;
return Some( "" );
}
else
{
self.cur = Some( next_line );
continue;
}
}
};
if self.limit_width == 0
{
self.cur = None;
return Some( s );
}
let mut boundary_byte_index = s.len();
let mut char_count = 0;
for ( byte_i, _ch ) in s.char_indices()
{
if char_count == self.limit_width
{
boundary_byte_index = byte_i;
break;
}
char_count += 1;
}
let chunk = &s[ ..boundary_byte_index ];
let rest = &s[ boundary_byte_index.. ];
match rest.is_empty()
{
true => self.cur = None,
false => self.cur = Some( rest )
};
return Some( chunk );
}
}
}
}
#[ allow( unused_imports ) ]
pub use own::*;
/// Own namespace of the module.
#[ allow( unused_imports ) ]
pub mod own
{
use super::*;
#[ doc( inline ) ]
pub use orphan::*;
#[ doc( inline ) ]
pub use private::
{
size,
lines,
Lines,
lines_with_limit,
LinesWithLimit,
};
}
/// Orphan namespace of the module.
#[ allow( unused_imports ) ]
pub mod orphan
{
use super::*;
#[ doc( inline ) ]
pub use exposed::*;
}
/// Exposed namespace of the module.
#[ allow( unused_imports ) ]
pub mod exposed
{
use super::*;
pub use super::super::string;
}
/// Prelude to use essentials: `use my_module::prelude::*`.
#[ allow( unused_imports ) ]
pub mod prelude
{
use super::*;
}