1#![feature(
5 iter_advance_by,
6 decl_macro,
7 macro_metavar_expr_concat,
8 if_let_guard,
9 macro_derive,
10 thread_local,
11 negative_impls,
12 unsafe_cell_access
13)]
14
15pub mod arena;
17pub mod ast_pos;
18pub mod ast_range;
19pub mod ast_str;
20pub mod config;
21pub mod oob;
22pub mod whitespace;
23
24pub use self::{
26 arena::{Arena, ArenaData, ArenaIdx, decl_arena},
27 ast_pos::AstPos,
28 ast_range::AstRange,
29 ast_str::AstStr,
30 config::Config,
31 oob::Oob,
32 whitespace::Whitespace,
33};
34
35use core::iter;
37
38#[must_use]
40pub fn is_str_blank(s: &str) -> bool {
41 s.chars().all(|ch| ch.is_ascii_whitespace())
42}
43
44#[must_use]
46pub fn str_count_newlines(s: &str) -> usize {
47 s.chars().filter(|&ch| ch == '\n').count()
48}
49
50#[extend::ext(name = StrPopFirst)]
51pub impl &str {
52 fn pop_first(&mut self) -> Option<char> {
53 let mut chars = self.chars();
54 let ch = chars.next()?;
55 *self = chars.as_str();
56
57 Some(ch)
58 }
59}
60
61#[extend::ext(name = StrChunk)]
62pub impl str {
63 fn chunk(&self, len: usize) -> impl Iterator<Item = &str> {
64 let mut s = self;
65 iter::from_fn(move || match s.is_empty() {
66 true => None,
67 false => {
68 let (cur, next) = s
69 .split_at_checked(len)
70 .unwrap_or_else(|| (s, &s[s.len()..]));
71 s = next;
72 Some(cur)
73 }
74 })
75 }
76}