stylish_stringlike/text/
width_sliceable.rs1use crate::text::{RawText, Sliceable};
2use std::ops::RangeBounds;
3use unicode_segmentation::UnicodeSegmentation;
4use unicode_width::UnicodeWidthStr;
5
6pub trait WidthSliceable {
11 type Output: Sized;
12 fn slice_width<R>(&self, range: R) -> Option<Self::Output>
29 where
30 R: RangeBounds<usize>;
31}
32
33impl<T> WidthSliceable for T
34where
35 T: RawText + Sliceable + Sized,
36{
37 type Output = T;
38 fn slice_width<R>(&self, range: R) -> Option<Self::Output>
39 where
40 Self: Sized,
41 R: RangeBounds<usize>,
42 {
43 let mut start_byte = None;
44 let mut end_byte = None;
45 let mut current_width = 0;
46 let mut current_byte = 0;
47 for grapheme in self.raw().graphemes(true) {
48 let grapheme_width = grapheme.width();
49 let in_range = {
50 let mut in_range = true;
51 for w in current_width..current_width + grapheme_width {
52 if !range.contains(&w) {
53 in_range = false;
54 break;
55 }
56 }
57 in_range
58 };
59 current_width += grapheme_width;
60 match (in_range, start_byte) {
61 (true, None) => start_byte = Some(current_byte),
62 (false, Some(_)) => {
63 end_byte = Some(current_byte);
64 break;
65 }
66 _ => {}
67 }
68 current_byte += grapheme.len();
69 }
70 match (start_byte, end_byte) {
71 (Some(s), Some(e)) => self.slice(s..e),
72 (Some(s), None) => self.slice(s..),
73 (None, Some(e)) => self.slice(..e),
74 (None, None) => None,
75 }
76 }
77}
78
79impl<T> WidthSliceable for Option<T>
80where
81 T: WidthSliceable,
82{
83 type Output = T::Output;
84 fn slice_width<R>(&self, range: R) -> Option<Self::Output>
85 where
86 R: RangeBounds<usize>,
87 {
88 match self {
89 Some(t) => t.slice_width(range),
90 None => None,
91 }
92 }
93}