1pub struct StrIndexArgsConv<T> {
2 pub str: &'static str,
3 pub arg: T,
4}
5
6#[allow(non_snake_case)]
7pub const fn StrIndexArgsConv<T>(str: &'static str, arg: T) -> StrIndexArgsConv<T> {
8 StrIndexArgsConv { str, arg }
9}
10
11pub struct StrIndexArgs {
12 pub str: &'static str,
13 pub index_validity: IndexValidity,
14 pub used_rstart: usize,
15 pub used_rlen: usize,
16 pub used_rend: usize,
17}
18
19#[derive(Copy, Clone)]
20#[cfg_attr(test, derive(Debug, PartialEq))]
21pub enum IndexValidity {
22 Valid,
23 StartOob(usize),
24 StartInsideChar(usize),
25 EndOob(usize),
26 EndInsideChar(usize),
27}
28
29impl IndexValidity {
30 pub const fn is_valid(self) -> bool {
31 matches!(self, Self::Valid)
32 }
33
34 pub const fn assert_valid(self) {
35 match self {
36 Self::Valid => (),
37 Self::StartOob(index: usize) => [/*start index is out of bounds*/][index],
38 Self::StartInsideChar(index: usize) => [/*start index is not on a char boundary*/][index],
39 Self::EndOob(index: usize) => [/*end index is out of bounds*/][index],
40 Self::EndInsideChar(index: usize) => [/*end index is not on a char boundary*/][index],
41 }
42 }
43}
44
45macro_rules! pass_range_types {
46 ($macro:ident) => {
47 const _: () = {
48 use core::ops;
49
50 #[allow(unused_imports)]
51 use crate::__hidden_utils::{is_char_boundary_no_len_check, max_usize, saturating_add};
52
53 $macro! {
54 fn(self, usize) {
55 let mut end = saturating_add(self.arg, 1);
56 let bytes = self.str.as_bytes();
57
58 if end < self.str.len() {
59 while !is_char_boundary_no_len_check(bytes, end) {
60 end = saturating_add(end, 1);
61 }
62 }
63
64 self.arg .. end
65 }
66
67 fn(self, ops::Range<usize>) {
68 let ops::Range{start, end} = self.arg;
69 start .. max_usize(start, end)
70 }
71
72 fn(self, ops::RangeTo<usize>) {
73 0..self.arg.end
74 }
75
76 fn(self, ops::RangeFrom<usize>) {
77 self.arg.start..self.str.len()
78 }
79
80 fn(self, ops::RangeInclusive<usize>) {
81 let start = *self.arg.start();
82 start .. max_usize(saturating_add(*self.arg.end(), 1), start)
83 }
84
85 fn(self, ops::RangeToInclusive<usize>) {
86 0 .. saturating_add(self.arg.end, 1)
87 }
88
89 fn(self, ops::RangeFull) {
90 0 .. self.str.len()
91 }
92 }
93 };
94 };
95}
96pub(super) use pass_range_types;
97
98macro_rules! define_conversions {
99 (
100 $( fn($self:ident, $ty:ty) $block:block )*
101 ) => {
102
103 $(
104 impl StrIndexArgsConv<$ty> {
105 pub const fn conv($self) -> StrIndexArgs {
106 use crate::__hidden_utils::is_char_boundary_no_len_check;
107
108 let range = $block;
109
110 let str_len = $self.str.len();
111
112 let mut used_rstart = 0;
113 let mut used_rend = str_len;
114
115 let mut index_validity = IndexValidity::Valid;
116 let bytes = $self.str.as_bytes();
117
118 if range.end > str_len {
119 index_validity = IndexValidity::EndOob(range.end);
120 } else if is_char_boundary_no_len_check(bytes, range.end) {
121 used_rend = range.end;
122 } else {
123 index_validity = IndexValidity::EndInsideChar(range.end);
124 };
125
126 if range.start > str_len {
127 index_validity = IndexValidity::StartOob(range.start);
128 } else if is_char_boundary_no_len_check(bytes, range.start) {
129 used_rstart = range.start;
130 } else {
131 index_validity = IndexValidity::StartInsideChar(range.start);
132 };
133
134 StrIndexArgs {
135 str: $self.str,
136 index_validity,
137 used_rstart,
138 used_rend,
139 used_rlen: used_rend - used_rstart,
140 }
141 }
142 }
143 )*
144 };
145}
146
147pass_range_types! {define_conversions}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn index_validity_test() {
155 macro_rules! miv {
156 ($str:expr, $range:expr) => {
157 StrIndexArgsConv($str, $range).conv().index_validity
158 };
159 }
160
161 assert_eq!(miv!("効率的", 3), IndexValidity::Valid);
162 assert_eq!(miv!("効率的", 6), IndexValidity::Valid);
163 assert_eq!(miv!("効率的", 3..6), IndexValidity::Valid);
164
165 assert_eq!(miv!("効率的", 4..6), IndexValidity::StartInsideChar(4));
166 assert_eq!(miv!("効率的", 3..5), IndexValidity::EndInsideChar(5));
167 assert_eq!(miv!("効率的", 7..9), IndexValidity::StartInsideChar(7));
168
169 assert_eq!(miv!("効率的", 100..9), IndexValidity::StartOob(100));
170 assert_eq!(miv!("効率的", 3..10), IndexValidity::EndOob(10));
171 assert_eq!(miv!("効率的", 9), IndexValidity::EndOob(10));
172 assert_eq!(miv!("効率的", 10), IndexValidity::StartOob(10));
173 assert_eq!(miv!("効率的", 100..900), IndexValidity::StartOob(100));
174 }
175}
176