stylex_css_parser/
base_types.rs1use std::fmt::{self, Display};
9
10#[derive(Debug, Clone)]
12pub struct SubString {
13 pub string: String,
14 pub start_index: usize,
15 pub end_index: usize,
16}
17
18impl SubString {
19 pub fn new(s: &str) -> Self {
21 Self {
22 string: s.to_string(),
23 start_index: 0,
24 end_index: if s.is_empty() { 0 } else { s.len() - 1 },
25 }
26 }
27
28 pub fn starts_with(&self, s: &str) -> bool {
31 let chars: Vec<char> = self.string.chars().collect();
32 let search_chars: Vec<char> = s.chars().collect();
33
34 for i in 0..search_chars.len() {
35 if self.start_index + i > self.end_index
36 || self.start_index + i >= chars.len()
37 || chars[self.start_index + i] != search_chars[i]
38 {
39 return false;
40 }
41 }
42 true
43 }
44
45 pub fn first(&self) -> Option<char> {
47 if self.start_index > self.end_index {
48 return None;
49 }
50 self.string.chars().nth(self.start_index)
51 }
52
53 pub fn get(&self, relative_index: usize) -> Option<char> {
55 let absolute_index = self.start_index + relative_index;
56 if absolute_index > self.end_index {
57 return None;
58 }
59 self.string.chars().nth(absolute_index)
60 }
61
62 pub fn into_string(&self) -> String {
64 if self.start_index > self.end_index {
65 return String::new();
66 }
67
68 let chars: Vec<char> = self.string.chars().collect();
69 let start = self.start_index.min(chars.len());
70 let end = (self.end_index + 1).min(chars.len());
71
72 if start >= end {
73 String::new()
74 } else {
75 chars[start..end].iter().collect()
76 }
77 }
78
79 pub fn is_empty(&self) -> bool {
81 self.start_index > self.end_index
82 }
83}
84
85impl Display for SubString {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "{}", self.into_string())
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_new_with_string() {
97 let substr = SubString::new("hello");
98 assert_eq!(substr.string, "hello");
99 assert_eq!(substr.start_index, 0);
100 assert_eq!(substr.end_index, 4); }
102
103 #[test]
104 fn test_new_with_empty_string() {
105 let substr = SubString::new("");
106 assert_eq!(substr.string, "");
107 assert_eq!(substr.start_index, 0);
108 assert_eq!(substr.end_index, 0);
109 }
110
111 #[test]
112 fn test_starts_with() {
113 let substr = SubString::new("hello world");
114 assert!(substr.starts_with("hello"));
115 assert!(substr.starts_with("h"));
116 assert!(substr.starts_with(""));
117 assert!(!substr.starts_with("world"));
118 assert!(!substr.starts_with("hello world!")); }
120
121 #[test]
122 fn test_first() {
123 let substr = SubString::new("hello");
124 assert_eq!(substr.first(), Some('h'));
125
126 let empty_substr = SubString::new("");
127 assert_eq!(empty_substr.first(), None);
128 }
129
130 #[test]
131 fn test_get() {
132 let substr = SubString::new("hello");
133 assert_eq!(substr.get(0), Some('h'));
134 assert_eq!(substr.get(1), Some('e'));
135 assert_eq!(substr.get(4), Some('o'));
136 assert_eq!(substr.get(5), None); }
138
139 #[test]
140 fn test_to_string() {
141 let substr = SubString::new("hello");
142 assert_eq!(substr.to_string(), "hello");
143
144 let empty_substr = SubString::new("");
145 assert_eq!(empty_substr.to_string(), "");
146 }
147
148 #[test]
149 fn test_is_empty() {
150 let substr = SubString::new("hello");
151 assert!(!substr.is_empty());
152
153 let empty_substr = SubString::new("");
154 assert!(!empty_substr.is_empty()); let mut modified_substr = SubString::new("hello");
158 modified_substr.start_index = 3;
159 modified_substr.end_index = 2; assert!(modified_substr.is_empty());
161 }
162
163 #[test]
164 fn test_unicode_support() {
165 let substr = SubString::new("héllo 🌍");
166 assert_eq!(substr.first(), Some('h'));
167 assert_eq!(substr.get(1), Some('é'));
168 assert!(substr.starts_with("hé"));
169 assert_eq!(substr.to_string(), "héllo 🌍");
170 }
171}