Skip to content

Commit d0deb8c

Browse files
committed
feat: str ext
1 parent 2029850 commit d0deb8c

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

src/infra/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ pub mod iter;
44
pub mod json;
55
pub mod option;
66
pub mod result;
7+
pub mod str;
78
pub mod time;
89
pub mod vec;

src/infra/str.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use std::ops::ControlFlow;
2+
use unicode_width::UnicodeWidthChar;
3+
4+
pub trait StrExt {
5+
fn width_split_head(&self, head_width: usize) -> (&str, &str);
6+
fn width_split(&self, width: usize) -> Option<Vec<&str>>;
7+
}
8+
9+
impl StrExt for str {
10+
fn width_split_head(&self, head_width: usize) -> (&str, &str) {
11+
let mut left_take = head_width;
12+
let mut take_bytes = 0;
13+
self.chars().try_for_each(|c| {
14+
let current_width = c.width_cjk().unwrap_or(0);
15+
if left_take > 0 {
16+
if left_take >= current_width {
17+
left_take -= current_width;
18+
take_bytes += c.len_utf8();
19+
ControlFlow::Continue(())
20+
} else {
21+
left_take = 0;
22+
ControlFlow::Break(())
23+
}
24+
} else {
25+
ControlFlow::Break(())
26+
}
27+
});
28+
self.split_at(take_bytes)
29+
}
30+
31+
fn width_split(&self, width: usize) -> Option<Vec<&str>> {
32+
let mut vec = vec![];
33+
let mut str = self;
34+
loop {
35+
let (head, tail) = str.width_split_head(width);
36+
// No split strategy exist, return None
37+
if head == "" {
38+
return None;
39+
}
40+
vec.push(head);
41+
if tail == "" {
42+
break;
43+
}
44+
str = tail;
45+
}
46+
Some(vec)
47+
}
48+
}
49+
50+
#[test]
51+
fn test_width_split_head() {
52+
let text = "测试test⭐";
53+
assert_eq!(text.width_split_head(0), ("", "测试test⭐"));
54+
assert_eq!(text.width_split_head(1), ("", "测试test⭐"));
55+
assert_eq!(text.width_split_head(2), ("测", "试test⭐"));
56+
assert_eq!(text.width_split_head(3), ("测", "试test⭐"));
57+
assert_eq!(text.width_split_head(4), ("测试", "test⭐"));
58+
assert_eq!(text.width_split_head(5), ("测试t", "est⭐"));
59+
assert_eq!(text.width_split_head(9), ("测试test", "⭐"));
60+
assert_eq!(text.width_split_head(10), ("测试test⭐", ""));
61+
assert_eq!(text.width_split_head(11), ("测试test⭐", ""));
62+
}
63+
64+
#[test]
65+
fn test_width_split() {
66+
let text = "测试test⭐测试test⭐";
67+
assert_eq!(text.width_split(0), None);
68+
assert_eq!(text.width_split(1), None);
69+
assert_eq!(
70+
text.width_split(2),
71+
vec!["测", "试", "te", "st", "⭐", "测", "试", "te", "st", "⭐"].into_some()
72+
);
73+
assert_eq!(
74+
text.width_split(3),
75+
vec!["测", "试t", "est", "⭐", "测", "试t", "est", "⭐"].into_some()
76+
);
77+
assert_eq!(
78+
text.width_split(4),
79+
vec!["测试", "test", "⭐测", "试te", "st⭐"].into_some()
80+
);
81+
assert_eq!(
82+
text.width_split(19),
83+
vec!["测试test⭐测试test", "⭐"].into_some()
84+
);
85+
assert_eq!(
86+
text.width_split(20),
87+
vec!["测试test⭐测试test⭐"].into_some()
88+
);
89+
assert_eq!(
90+
text.width_split(21),
91+
vec!["测试test⭐测试test⭐"].into_some()
92+
);
93+
}

0 commit comments

Comments
 (0)