smt_str/lib.rs
1//! This crate provides types and utilities for working with SMT-LIB strings.
2//! An SMT-LIB string is a sequence of characters with unicode code points in the range 0x0000 to 0x2FFFF (first two planes of Unicode).
3//! The underlying semantics differ from those of Rust`s native `char` and `String` types:
4//!
5//! - **Character model**: Rust's `char` type represents any Unicode scalar value which is not a surrogate code point.
6//! In SMT-LIB, a character is a unicode code point in the range 0x0000 to 0x2FFFF, including surrogate code points (which are not valid Rust `char`s).
7//! - **String length**: In Rust, the length of a string is counted in bytes whereas in In SMT-LIB, the length of a string is counted in characters.
8//! For example, if a character takes more than one byte to encode (such as 🦀), Rust's `String.len()` will return the number of bytes.
9//! In order to obtain the number of characters, one must count the number of `char`s in the string instead, which can easily lead to errors.
10//! - **Escaping**: In SMT-LIB, the only escape sequences are of the form `\uXXXX` and `\u{X...}`.
11//! Especially, there are no escape sequences for control characters, such as `\n` or `\t`, that are present in Rust.
12//!
13//! This crate provides a convenient way to work with SMT-LIB strings through the [`SmtChar`] and [`SmtString`] types.
14//! - [`SmtChar`] represents a Unicode code point in the range 0x0000 to 0x2FFFF (including surrogates).
15//! - [`SmtString`] represents a sequence of `SmtChar` values and offers parsing, manipulation, and search utilities that conform to the SMT-LIB specification.
16
17pub mod alphabet;
18#[cfg(feature = "automata")]
19pub mod automata;
20
21#[cfg(feature = "regex")]
22pub mod re;
23#[cfg(feature = "sampling")]
24pub mod sampling;
25
26use std::{
27 fmt::Display,
28 ops::{self, Index},
29};
30
31use num_traits::{SaturatingAdd, SaturatingSub};
32use quickcheck::Arbitrary;
33
34/// The maximum unicode character.
35pub const SMT_MAX_CODEPOINT: u32 = 0x2FFFF;
36
37/// The minimum unicode character.
38pub const SMT_MIN_CODEPOINT: u32 = 0x0000;
39
40/// A Unicode character used in SMT-LIB strings.
41///
42/// In the SMT-LIB string theory, a character is any Unicode code point in the inclusive range
43/// `0x0000` to `0x2FFFF`.
44///
45/// `SmtChar` is a wrapper around a `u32` and provides convenient methods to construct, inspect,
46/// and manipulate SMT-LIB characters safely.
47///
48/// ## Examples
49///
50/// ```
51/// use smt_str::SmtChar;
52///
53/// // Create a new `SmtChar` from a `u32` representing a Unicode code point.
54/// let a = SmtChar::new(0x61); // 'a'
55/// // Get the `u32` representation of the `SmtChar`.
56/// assert_eq!(a.as_u32(), 0x61);
57/// // Get the `char` representation of the `SmtChar`.
58/// assert_eq!(a.as_char(), Some('a'));
59///
60/// // It is also possible to create an `SmtChar` from a `char`.
61/// let b = SmtChar::from('b');
62/// assert_eq!(b.as_u32(), 0x62);
63/// assert_eq!(b.as_char(), Some('b'));
64///
65/// let surrogate = SmtChar::new(0xD800); // valid in SMT-LIB, invalid as Rust `char`
66/// assert_eq!(surrogate.as_char(), None);
67///
68/// // Non-printable characters are escaped when displayed.
69/// let newline = SmtChar::new(0x0A); // '\n'
70/// assert_eq!(newline.to_string(), r#"\u{A}"#);
71/// ```
72#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct SmtChar(u32);
74
75impl SmtChar {
76 /// The maximum `SmtChar`.
77 /// This is the unicode code point 0x2FFFF.
78 pub const MAX: Self = Self(SMT_MAX_CODEPOINT);
79
80 /// The minimum `SmtChar`.
81 /// This is the unicode code point 0x0000.
82 pub const MIN: Self = Self(SMT_MIN_CODEPOINT);
83
84 /// Creates a new `SmtChar` from a `u32` code point.
85 /// Panics if `c as u32 > 0x2FFFF`.
86 pub fn new(c: u32) -> Self {
87 assert!(c <= 0x2FFFF, "character out of range: {}", c);
88 SmtChar(c)
89 }
90
91 /// Creates the `SmtChar` with the code point `1`.
92 fn one() -> Self {
93 SmtChar::new(1)
94 }
95
96 /// Get the `char` representation of this `SmtChar`, if it can be represented as a `char`.
97 /// Returns `None` if this `SmtChar` is a surrogate code point.
98 ///
99 ///
100 /// # Examples
101 /// ```
102 /// use smt_str::SmtChar;
103 /// let c = SmtChar::from('a');
104 /// assert_eq!(c.as_char(), Some('a'));
105 /// // This is a surrogate code point and cannot be represented as a `char`.
106 /// assert_eq!(SmtChar::new(55296).as_char(), None);
107 ///```
108 pub fn as_char(self) -> Option<char> {
109 char::from_u32(self.0)
110 }
111
112 /// Get the `u32` representation of this `SmtChar`.
113 /// The `u32` is the unicode code point of this `SmtChar`.
114 ///
115 /// # Examples
116 /// ```
117 /// use smt_str::SmtChar;
118 /// assert_eq!(SmtChar::from('a').as_u32(), 97);
119 /// assert_eq!(SmtChar::from('🦀').as_u32(), 129408);
120 /// ```
121 pub fn as_u32(self) -> u32 {
122 self.0
123 }
124
125 /// Returns the next `SmtChar` in the range 0x0000 to 0x2FFFF.
126 /// Panics if this `SmtChar` is the maximum `SmtChar`.
127 ///
128 /// # Examples
129 /// ```
130 /// use smt_str::SmtChar;
131 /// let c = SmtChar::from('a');
132 /// assert_eq!(c.next(), SmtChar::from('b'));
133 /// ```
134 ///
135 /// Cannot get the next character after the maximum `SmtChar`:
136 /// ```should_panic
137 /// use smt_str::SmtChar;
138 /// let c = SmtChar::MAX;
139 /// let _ = c.next(); // panics
140 /// ```
141 pub fn next(self) -> Self {
142 self + Self::one()
143 }
144
145 /// Returns the next `SmtChar` in the range 0x0000 to 0x2FFFF, if it exists.
146 /// Returns `None` if this `SmtChar` is the maximum `SmtChar`.
147 ///
148 /// # Examples
149 /// ```
150 /// use smt_str::SmtChar;
151 /// let c = SmtChar::from('a');
152 /// assert_eq!(c.try_next(), Some(SmtChar::from('b')));
153 /// assert_eq!(SmtChar::MAX.try_next(), None);
154 /// ```
155 pub fn try_next(self) -> Option<Self> {
156 if self == Self::MAX {
157 None
158 } else {
159 Some(self.next())
160 }
161 }
162
163 /// Like `next`, but instead of panicking when this `SmtChar` is the maximum `SmtChar`, it returns the maximum `SmtChar`.
164 ///
165 /// # Examples
166 /// ```
167 /// use smt_str::SmtChar;
168 /// let c = SmtChar::from('a');
169 /// assert_eq!(c.saturating_next(), SmtChar::from('b'));
170 /// assert_eq!(SmtChar::MAX.saturating_next(), SmtChar::MAX);
171 /// ```
172 pub fn saturating_next(self) -> Self {
173 self.try_next().unwrap_or(Self::MAX)
174 }
175
176 /// Returns the previous `SmtChar` in the range 0x0000 to 0x2FFFF.
177 /// Panics if this `SmtChar` is the minimum `SmtChar`.
178 ///
179 /// # Examples
180 /// ```
181 /// use smt_str::SmtChar;
182 /// let c = SmtChar::from('b');
183 /// assert_eq!(c.prev(), SmtChar::from('a'));
184 /// ```
185 ///
186 /// Cannot get the previous character before the minimum `SmtChar`:
187 ///
188 /// ```should_panic
189 /// use smt_str::SmtChar;
190 /// let c = SmtChar::MIN;
191 /// let _ = c.prev(); // panics
192 /// ```
193 pub fn prev(self) -> Self {
194 self - Self::one()
195 }
196
197 /// Returns the previous `SmtChar` in the range 0x0000 to 0x2FFFF, if it exists.
198 /// Returns `None` if this `SmtChar` is the minimum `SmtChar`.
199 ///
200 /// # Examples
201 /// ```
202 /// use smt_str::SmtChar;
203 /// let c = SmtChar::from('b');
204 /// assert_eq!(c.try_prev(), Some(SmtChar::from('a')));
205 /// assert_eq!(SmtChar::MIN.try_prev(), None);
206 /// ```
207 pub fn try_prev(self) -> Option<Self> {
208 if self == Self::MIN {
209 None
210 } else {
211 Some(self.prev())
212 }
213 }
214
215 /// Like `prev`, but instead of panicking when this `SmtChar` is the minimum `SmtChar`, it returns the minimum `SmtChar`.
216 ///
217 /// # Examples
218 /// ```
219 /// use smt_str::SmtChar;
220 /// let c = SmtChar::from('b');
221 /// assert_eq!(c.saturating_prev(), SmtChar::from('a'));
222 /// assert_eq!(SmtChar::MIN.saturating_prev(), SmtChar::MIN);
223 /// ```
224 pub fn saturating_prev(self) -> Self {
225 self.try_prev().unwrap_or(Self::MIN)
226 }
227
228 /// Returns `true` if this `SmtChar` is a printable ASCII character.
229 /// Printable ASCII characters are in the range 0x00020 to 0x0007E.
230 ///
231 /// # Examples
232 /// ```
233 /// use smt_str::SmtChar;
234 /// assert!(SmtChar::from('a').printable());
235 /// assert!(!SmtChar::from('\n').printable());
236 /// ```
237 pub fn printable(self) -> bool {
238 0x00020 <= self.0 && self.0 < 0x0007E
239 }
240
241 /// Escape this `SmtChar` as a Unicode escape sequence.
242 /// The escape sequence is of the form `\u{X}` where `X` is the hexadecimal representation of the unicode code point.
243 /// The function always chooses the shortest escape sequence, i.e., it uses the smallest number of digits and does not pad with zeros.
244 ///
245 /// # Examples
246 /// ```
247 /// use smt_str::SmtChar;
248 /// assert_eq!(SmtChar::from('a').escape(), r#"\u{61}"#);
249 /// assert_eq!(SmtChar::from('\n').escape(), r#"\u{A}"#);
250 /// assert_eq!(SmtChar::from('🦀').escape(), r#"\u{1F980}"#);
251 /// assert_eq!(SmtChar::MAX.escape(), r#"\u{2FFFF}"#);
252 /// assert_eq!(SmtChar::MIN.escape(), r#"\u{0}"#);
253 ///
254 /// ```
255 pub fn escape(self) -> String {
256 let mut escaped = String::with_capacity(6);
257 escaped.push('\\');
258 escaped.push('u');
259 escaped.push('{');
260 escaped.push_str(&format!("{:X}", self.0));
261 escaped.push('}');
262 escaped
263 }
264
265 /// Unescape a string that contains escaped characters.
266 /// Escaped characters are of the following form:
267 ///
268 /// - `\uDDDD`
269 /// - `\u{D}`,
270 /// - `\u{DD}`,
271 /// - `\u{DDD}`,
272 /// - `\u{DDDD}`,
273 /// - `\u{DDDDD}`
274 ///
275 /// where `D` is a hexadecimal digit. In the case `\u{DDDDD}`, the first digit must be in the range 0 to 2.
276 /// The function returns `None` if the input string is not a valid escaped character.
277 ///
278 /// # Examples
279 /// ```
280 /// use smt_str::SmtChar;
281 /// assert_eq!(SmtChar::unescape(r#"\u{61}"#), Some(SmtChar::from('a')));
282 /// assert_eq!(SmtChar::unescape(r#"\u{A}"#), Some(SmtChar::from('\n')));
283 /// assert_eq!(SmtChar::unescape(r#"\u{1F980}"#), Some(SmtChar::from('🦀')));
284 /// assert_eq!(SmtChar::unescape(r#"\u{2FFFF}"#), Some(SmtChar::MAX));
285 /// assert_eq!(SmtChar::unescape(r#"\u{0}"#), Some(SmtChar::MIN));
286 ///
287 /// // Invalid escape sequences
288 ///
289 /// assert_eq!(SmtChar::unescape(r#"\u{3000A}"#), None); // out of range
290 /// assert_eq!(SmtChar::unescape(r#"\u{61"#), None); // missing closing brace
291 /// assert_eq!(SmtChar::unescape(r#"\u{}"#), None); // empty digits
292 /// ```
293 pub fn unescape(escaped: &str) -> Option<Self> {
294 let mut chars = escaped.chars();
295 if chars.next()? != '\\' {
296 return None;
297 }
298 if chars.next()? != 'u' {
299 return None;
300 }
301 let mut digits = Vec::with_capacity(5);
302 let mut lparen = false;
303 let mut rparen = false;
304 for c in chars {
305 match c {
306 '{' if !lparen => {
307 lparen = true;
308 }
309 '}' if lparen => {
310 rparen = true;
311 }
312 c if !rparen && c.is_ascii_hexdigit() => {
313 digits.push(c);
314 }
315 _ => {
316 return None;
317 }
318 }
319 }
320 if lparen && !rparen {
321 return None;
322 }
323 if digits.is_empty() {
324 return None;
325 }
326 // Convert the digits to a u32
327 let mut code = 0;
328 for c in digits {
329 let digit = c.to_digit(16)?;
330 code = code * 16 + digit;
331 }
332 if code > SMT_MAX_CODEPOINT {
333 return None;
334 }
335 Some(SmtChar(code))
336 }
337}
338
339/* Conversion from primitives */
340
341impl From<u8> for SmtChar {
342 fn from(c: u8) -> Self {
343 SmtChar::new(c as u32)
344 }
345}
346
347impl From<u16> for SmtChar {
348 fn from(c: u16) -> Self {
349 SmtChar::new(c as u32)
350 }
351}
352
353impl From<u32> for SmtChar {
354 fn from(c: u32) -> Self {
355 SmtChar::new(c)
356 }
357}
358
359impl From<i32> for SmtChar {
360 fn from(c: i32) -> Self {
361 if c < 0 {
362 panic!("negative character: {}", c);
363 }
364 SmtChar::new(c as u32)
365 }
366}
367
368impl From<char> for SmtChar {
369 fn from(c: char) -> Self {
370 SmtChar::new(c as u32)
371 }
372}
373
374/* Operations */
375
376impl ops::Add<SmtChar> for SmtChar {
377 type Output = SmtChar;
378 /// Adds another `SmtChar` to this `SmtChar`, shifting the unicode code point.
379 /// The sum is the sum of the unicode code points of the two `SmtChar`s.
380 /// Panics if the resulting code point is greater than [SMT_MAX_CODEPOINT] (= `0x2FFFF`).
381 ///
382 /// # Examples
383 /// ```
384 /// use smt_str::SmtChar;
385 /// let c = SmtChar::from('a');
386 /// assert_eq!(c + SmtChar::new(1), SmtChar::from('b'));
387 /// assert_eq!(c + SmtChar::new(25), SmtChar::from('z'));
388 /// ```
389 ///
390 /// Overflowing the maximum code point panics:
391 ///
392 /// ```should_panic
393 /// use smt_str::SmtChar;
394 /// let c = SmtChar::MAX;
395 /// let _ = c + SmtChar::new(1); // panics
396 /// ```
397 fn add(self, rhs: SmtChar) -> Self::Output {
398 SmtChar::new(self.0 + rhs.0)
399 }
400}
401
402impl SaturatingAdd for SmtChar {
403 /// Adds another `SmtChar` to this `SmtChar`, saturating at the maximum unicode code point.
404 ///
405 /// # Examples
406 /// ```
407 /// use smt_str::SmtChar;
408 /// use num_traits::ops::saturating::SaturatingAdd;
409 /// let c = SmtChar::from('a');
410 /// assert_eq!(c.saturating_add(&SmtChar::new(1)), SmtChar::from('b'));
411 /// assert_eq!(c.saturating_add(&SmtChar::new(25)), SmtChar::from('z'));
412 /// let c = SmtChar::MAX;
413 /// assert_eq!(c.saturating_add(&SmtChar::new(1)), SmtChar::MAX);
414 /// ```
415 fn saturating_add(&self, v: &Self) -> Self {
416 let sum = (self.0 + v.0).min(SMT_MAX_CODEPOINT);
417 SmtChar::new(sum)
418 }
419}
420
421impl ops::Sub<SmtChar> for SmtChar {
422 type Output = SmtChar;
423 /// Subtracts another `SmtChar` from this `SmtChar`, shifting the unicode code point.
424 /// The difference is the difference of the unicode code points of the two `SmtChar`s.
425 /// Panics if the resulting code point is less than [SMT_MIN_CODEPOINT] (= `0`).
426 ///
427 /// # Examples
428 /// ```
429 /// use smt_str::SmtChar;
430 /// let c = SmtChar::from('z');
431 /// assert_eq!(c - SmtChar::new(1), SmtChar::from('y'));
432 /// assert_eq!(c - SmtChar::new(25), SmtChar::from('a'));
433 /// ```
434 ///
435 /// Underflowing the minimum code point panics:
436 ///
437 /// ```should_panic
438 /// use smt_str::SmtChar;
439 /// let c = SmtChar::MIN;
440 /// let _ = c - SmtChar::new(1); // panics
441 /// ```
442 fn sub(self, rhs: SmtChar) -> Self::Output {
443 SmtChar::new(self.0 - rhs.0)
444 }
445}
446
447impl SaturatingSub for SmtChar {
448 /// Subtracts another `SmtChar` from this `SmtChar`, saturating at the minimum unicode code point.
449 ///
450 /// # Examples
451 /// ```
452 /// use smt_str::SmtChar;
453 /// let c = SmtChar::from('z');
454 /// use num_traits::ops::saturating::SaturatingSub;
455 /// assert_eq!(c.saturating_sub(&SmtChar::new(1)), SmtChar::from('y'));
456 /// assert_eq!(c.saturating_sub(&SmtChar::new(25)), SmtChar::from('a'));
457 /// let c = SmtChar::MIN;
458 /// assert_eq!(c.saturating_sub(&SmtChar::new(1)), SmtChar::MIN);
459 /// ```
460 fn saturating_sub(&self, v: &Self) -> Self {
461 let diff = self.0.saturating_sub(v.0);
462 SmtChar::new(diff)
463 }
464}
465
466impl Display for SmtChar {
467 /// Display the `SmtChar` as a Unicode character if it is printable.
468 /// Otherwise, display the character as a Unicode escape sequence (see [SmtChar::escape]).
469 /// Additionally, backslashes and quotes are escaped.
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 if self.printable() {
472 // printable ASCII character are always safe to unwrap
473 let c = self.as_char().unwrap();
474 // Although the character is printable, we still escape it if it is a backslash or a quote
475 if c == '\\' || c == '"' {
476 write!(f, "{}", self.escape())
477 } else {
478 write!(f, "{}", c)
479 }
480 } else {
481 write!(f, "{}", self.escape())
482 }
483 }
484}
485
486/// An iterator over the characters in the range `start` to `end`.
487#[derive(Debug, Clone)]
488pub struct CharIterator {
489 current: SmtChar,
490 end: SmtChar,
491}
492
493impl CharIterator {
494 /// Create a new iterator over the characters in the range `start` to `end` (both inclusively).
495 pub fn new(start: SmtChar, end: SmtChar) -> Self {
496 CharIterator {
497 current: start,
498 end,
499 }
500 }
501}
502impl Iterator for CharIterator {
503 type Item = SmtChar;
504
505 fn next(&mut self) -> Option<Self::Item> {
506 if self.current <= self.end {
507 let c = self.current;
508 self.current = self.current.try_next()?;
509 Some(c)
510 } else {
511 None
512 }
513 }
514}
515
516/// An SMT-LIB string is a sequence of characters with unicode code points in the range 0x0000 to 0x2FFFF (first two planes of Unicode).
517/// The characters are represented by the [`SmtChar`] type.
518///
519/// # Examples
520/// ```
521/// use smt_str::{SmtString, SmtChar};
522///
523/// // Create a new SmtString from a string literal
524/// let s: SmtString = "foo".into();
525///
526/// // Obtain the length of the string
527/// assert_eq!(s.len(), 3);
528///
529/// // SmtStrings have the correct length even for multi-byte characters
530/// let s: SmtString = "🦀".into();
531/// assert_eq!(s.len(), 1);
532///
533/// // In Rust, the length of a string is counted in bytes, not characters
534/// assert_eq!("🦀".len(), 4);
535/// assert_eq!("🦀".chars().count(), 1);
536///
537/// // SmtString can be parsed from a string with escape sequences
538/// let s: SmtString = SmtString::parse(r#"foo\u{61}bar"#);
539/// assert_eq!(s, SmtString::from("fooabar"));
540///
541/// // Printing the string escapes non-printable characters
542/// let s: SmtString = SmtString::from("foo\nbar");
543/// assert_eq!(s.to_string(), r#"foo\u{A}bar"#);
544///
545/// ```
546#[derive(Debug, Clone, PartialEq, Eq, Hash)]
547pub struct SmtString(Vec<SmtChar>);
548
549impl SmtString {
550 /// The empty string.
551 pub fn empty() -> Self {
552 SmtString(Vec::new())
553 }
554
555 /// Create a new SmtString from a vector of SmtChar.
556 pub fn new(chars: Vec<SmtChar>) -> Self {
557 SmtString(chars)
558 }
559
560 /// Parse a string into an SmtString.
561 ///
562 /// The input string can contain escaped characters. The only valid escape sequences are:
563 ///
564 /// - `\uDDDD`
565 /// - `\u{D}`,
566 /// - `\u{DD}`,
567 /// - `\u{DDD}`,
568 /// - `\u{DDDD}`,
569 /// - `\u{DDDDD}`
570 ///
571 /// where `D` is a hexadecimal digit such that the resulting code point is in the range 0x0000 to 0x2FFFF.
572 /// If the string contains valid escape sequences, they are replaced with the corresponding `SmtChar`.
573 /// If the string contains invalid escape sequences, they are treated as literals.
574 /// For example, the string `"foo\u{61}bar"` is parsed as the string `"fooabar"`.
575 /// But the string `"foo\u{61bar"` is parsed as the string `"foo\u{61bar"`.
576 /// The holds for syntactically valid escape sequences that result in code points outside the valid range.
577 /// For example, the string `"foo\u{3000A}bar"` is parsed as the string `"foo\u{3000A}bar"`, since the code point `0x3000A` is outside the valid range.
578 ///
579 /// # Examples
580 /// ```
581 /// use smt_str::{SmtString};
582 /// let s: SmtString = SmtString::parse(r#"foo\u{61}bar"#);
583 /// assert_eq!(s, SmtString::from("fooabar"));
584 ///
585 /// // Invalid escape sequence, treated as literal
586 ///
587 /// let s: SmtString = SmtString::parse(r#"foo\u{61bar"#);
588 /// assert_eq!(s, SmtString::from(r#"foo\u{61bar"#));
589 /// ```
590 pub fn parse(input: &str) -> Self {
591 let mut chars = input.chars().peekable();
592 let mut result = Vec::new();
593 let mut buffer = String::new();
594
595 while let Some(&c) = chars.peek() {
596 if c == '\\' {
597 // Process escaped sequence
598 buffer.clear();
599 while let Some(&c) = chars.peek() {
600 buffer.push(c);
601 chars.next(); // Consume character
602 if c == '}' || (buffer.len() == 6 && !buffer.contains('{')) {
603 break;
604 }
605 }
606 if let Some(smt_char) = SmtChar::unescape(&buffer) {
607 result.push(smt_char);
608 } else {
609 // Invalid escape sequence, treat as literal
610 for c in buffer.chars() {
611 result.push(c.into());
612 }
613 }
614 } else {
615 // Process regular character
616 result.push(SmtChar(c as u32));
617 chars.next(); // Consume character
618 }
619 }
620
621 SmtString(result)
622 }
623
624 /// Returns whether this string is empty.
625 ///
626 /// # Examples
627 /// ```
628 /// use smt_str::{SmtString};
629 /// assert!(SmtString::empty().is_empty());
630 /// let s: SmtString = "foo".into();
631 /// assert!(!s.is_empty());
632 /// ```
633 pub fn is_empty(&self) -> bool {
634 self.0.is_empty()
635 }
636
637 /// Returns the length of this string.
638 ///
639 /// # Examples
640 /// ```
641 /// use smt_str::{SmtString};
642 /// assert_eq!(SmtString::empty().len(), 0);
643 /// let s: SmtString = "foo".into();
644 /// assert_eq!(s.len(), 3);
645 /// ```
646 ///
647 pub fn len(&self) -> usize {
648 self.0.len()
649 }
650
651 /// Empties this string, removing all characters.
652 /// After calling this method, the string will be empty.
653 ///
654 /// # Examples
655 /// ```
656 /// use smt_str::{SmtString};
657 /// let mut s: SmtString = "foo".into();
658 /// s.clear();
659 /// assert!(s.is_empty());
660 /// ```
661 pub fn clear(&mut self) {
662 self.0.clear();
663 }
664
665 /// Appends the characters of `other` to this string.
666 /// The characters are appended in order.
667 ///
668 /// # Examples
669 /// ```
670 /// use smt_str::{SmtString, SmtChar};
671 /// let mut s: SmtString = "foo".into();
672 /// let other: SmtString = "bar".into();
673 /// s.append(&other);
674 /// let mut iter = s.iter();
675 /// assert_eq!(iter.next(), Some(&SmtChar::from('f')));
676 /// assert_eq!(iter.next(), Some(&SmtChar::from('o')));
677 /// assert_eq!(iter.next(), Some(&SmtChar::from('o')));
678 /// assert_eq!(iter.next(), Some(&SmtChar::from('b')));
679 /// assert_eq!(iter.next(), Some(&SmtChar::from('a')));
680 /// assert_eq!(iter.next(), Some(&SmtChar::from('r')));
681 /// assert_eq!(iter.next(), None);
682 /// ```
683 pub fn append(&mut self, other: &SmtString) {
684 self.0.extend(other.0.iter().copied());
685 }
686
687 /// Pushes a character to the end of this string.
688 ///
689 /// # Examples
690 /// ```
691 /// use smt_str::{SmtString, SmtChar};
692 /// let mut s = SmtString::empty();
693 /// s.push(SmtChar::from('f'));
694 /// s.push(SmtChar::from('o'));
695 /// s.push(SmtChar::from('o'));
696 /// assert_eq!(s, SmtString::from("foo"));
697 /// ```
698 pub fn push(&mut self, c: impl Into<SmtChar>) {
699 self.0.push(c.into());
700 }
701
702 /// Concatenates this string with `other` and returns the result.
703 /// This is a convenience method that does not modify this string.
704 /// The characters of `other` are appended to the characters of this string, see [append].
705 ///
706 /// # Examples
707 /// ```
708 /// use smt_str::{SmtString};
709 /// let s1 = SmtString::from("foo");
710 /// let s2 = SmtString::from("bar");
711 /// let s3 = s1.concat(&s2);
712 /// assert_eq!(s3, SmtString::from("foobar"));
713 /// ```
714 pub fn concat(&self, other: &SmtString) -> SmtString {
715 let mut s = self.clone();
716 s.append(other);
717 s
718 }
719
720 /// Checks if this string contains a character.
721 ///
722 /// # Examples
723 /// ```
724 /// use smt_str::{SmtString, SmtChar};
725 /// let s: SmtString = "foobar".into();
726 /// assert!(s.contains_char('f'));
727 /// assert!(s.contains_char('o'));
728 /// assert!(s.contains_char('b'));
729 /// assert!(!s.contains_char('z'));
730 /// ```
731 pub fn contains_char(&self, c: impl Into<SmtChar>) -> bool {
732 self.0.contains(&c.into())
733 }
734
735 /// Return whether this string contains another string as a factor.
736 /// This is a naive implementation that checks all possible factors of this string, leading to O(n^2) complexity.
737 ///
738 /// # Examples
739 /// ```
740 /// use smt_str::{SmtString};
741 /// let s: SmtString = "foobar".into();
742 /// assert!(s.contains(&SmtString::empty()));
743 /// assert!(s.contains(&SmtString::from("foo")));
744 /// assert!(s.contains(&SmtString::from("bar")));
745 /// assert!(s.contains(&SmtString::from("oba")));
746 /// assert!(!s.contains(&SmtString::from("baz")));
747 ///
748 /// // The empty string contains only has the empty string as a factor
749 /// let empty: SmtString = SmtString::empty();
750 /// assert!(empty.contains(&SmtString::empty()));
751 /// assert!(!empty.contains(&SmtString::from("foo")));
752 /// ```
753 pub fn contains(&self, factor: &SmtString) -> bool {
754 self.index_of(factor, 0).is_some()
755 }
756
757 /// Find the index of the first occurrence of a factor in the suffix of this string starting at `start`.
758 /// Returns `None` if the factor is not found.
759 /// The empty string is a factor of every string and will always return `Some(0)`.
760 ///
761 /// # Examples
762 /// ```
763 /// use smt_str::{SmtString};
764 /// let s: SmtString = "foobar".into();
765 /// assert_eq!(s.index_of(&SmtString::empty(),0), Some(0));
766 /// assert_eq!(s.index_of(&SmtString::from("foo"),0), Some(0));
767 /// assert_eq!(s.index_of(&SmtString::from("foo"),1), None);
768 /// assert_eq!(s.index_of(&SmtString::from("bar"),0), Some(3));
769 /// assert_eq!(s.index_of(&SmtString::from("oba"),0), Some(2));
770 /// assert_eq!(s.index_of(&SmtString::from("baz"),0), None);
771 ///
772 /// // If the string is empty, the only factor is the empty string
773 /// let empty: SmtString = SmtString::empty();
774 /// assert_eq!(empty.index_of(&SmtString::empty(),0), Some(0));
775 /// assert_eq!(empty.index_of(&SmtString::from("foo"),0), None);
776 /// ```
777 pub fn index_of(&self, factor: &SmtString, start: usize) -> Option<usize> {
778 if self.is_empty() {
779 return if factor.is_empty() { Some(0) } else { None };
780 }
781
782 (start..self.len()).find(|&i| self.drop(i).starts_with(factor))
783 }
784
785 /// Returns whether this string starts with a prefix.
786 /// The empty string is a prefix of every string.
787 ///
788 /// # Examples
789 /// ```
790 /// use smt_str::{SmtString};
791 /// let s: SmtString = "foobar".into();
792 /// assert!(s.starts_with(&SmtString::empty()));
793 /// assert!(s.starts_with(&SmtString::from("foo")));
794 /// assert!(!s.starts_with(&SmtString::from("bar")));
795 /// ```
796 pub fn starts_with(&self, prefix: &SmtString) -> bool {
797 self.0.starts_with(&prefix.0)
798 }
799
800 /// Returns whether this string ends with a suffix.
801 /// The empty string is a suffix of every string.
802 ///
803 /// # Examples
804 /// ```
805 /// use smt_str::{SmtString};
806 /// let s: SmtString = "foobar".into();
807 /// assert!(s.ends_with(&SmtString::empty()));
808 /// assert!(s.ends_with(&SmtString::from("bar")));
809 /// assert!(!s.ends_with(&SmtString::from("foo")));
810 /// ```
811 pub fn ends_with(&self, suffix: &SmtString) -> bool {
812 self.0.ends_with(&suffix.0)
813 }
814
815 /// Returns the first character of this string, if it is not empty.
816 /// Returns `None` if this string is empty.
817 ///
818 /// # Examples
819 /// ```
820 /// use smt_str::{SmtString, SmtChar};
821 /// let s: SmtString = "foo".into();
822 /// assert_eq!(s.first(), Some('f'.into()));
823 /// assert_eq!(SmtString::empty().first(), None);
824 /// ```
825 pub fn first(&self) -> Option<SmtChar> {
826 self.0.first().copied()
827 }
828
829 /// Returns the last character of this string, if it is not empty.
830 /// Returns `None` if this string is empty.
831 ///
832 /// # Examples
833 /// ```
834 /// use smt_str::{SmtString, SmtChar};
835 /// let s: SmtString = "foo".into();
836 /// assert_eq!(s.last(), Some('o'.into()));
837 /// assert_eq!(SmtString::empty().last(), None);
838 /// ```
839 pub fn last(&self) -> Option<SmtChar> {
840 self.0.last().copied()
841 }
842
843 /// Returns the prefix of length `n` of this string.
844 /// If `n` is greater than the length of this string, the entire string is returned.
845 /// If `n` is zero, the empty string is returned.
846 ///
847 /// # Examples
848 /// ```
849 /// use smt_str::{SmtString, SmtChar};
850 /// let s: SmtString = "foo".into();
851 ///
852 /// assert_eq!( s.take(2), SmtString::from("fo"));
853 /// assert!(s.take(10) == s);
854 /// assert!(s.take(0) == SmtString::empty());
855 /// ```
856 ///
857 pub fn take(&self, n: usize) -> SmtString {
858 SmtString(self.0.iter().copied().take(n).collect())
859 }
860
861 /// Returns the suffix of this string after removing the first `n` characters.
862 /// If `n` is greater than the length of this string, the empty string is returned.
863 /// If `n` is zero, the entire string is returned.
864 ///
865 /// # Examples
866 /// ```
867 /// use smt_str::{SmtString, SmtChar};
868 /// let s = SmtString::from("foo");
869 /// assert_eq!(s.drop(2), SmtString::from("o"));
870 /// assert_eq!(s.drop(10), SmtString::empty());
871 /// assert_eq!(s.drop(0), s);
872 /// ```
873 pub fn drop(&self, n: usize) -> SmtString {
874 SmtString(self.0.iter().copied().skip(n).collect())
875 }
876
877 /// Returns the `n`-th character of this string.
878 /// Returns `None` if `n` is greater than or equal to the length of this string.
879 ///
880 /// # Examples
881 /// ```
882 /// use smt_str::{SmtString, SmtChar};
883 /// let s: SmtString = "foo".into();
884 /// assert_eq!(s.nth(0), Some(SmtChar::from('f')));
885 /// assert_eq!(s.nth(1), Some(SmtChar::from('o')));
886 /// assert_eq!(s.nth(2), Some(SmtChar::from('o')));
887 /// assert_eq!(s.nth(3), None);
888 /// ```
889 pub fn nth(&self, n: usize) -> Option<SmtChar> {
890 self.0.get(n).copied()
891 }
892
893 /// Returns the reverse of this string.
894 ///
895 /// # Examples
896 /// ```
897 /// use smt_str::{SmtString, SmtChar};
898 /// let s: SmtString = "foo".into();
899 /// let rev = s.reversed();
900 /// let mut iter = rev.iter();
901 /// assert_eq!(iter.next(), Some(&SmtChar::from('o')));
902 /// assert_eq!(iter.next(), Some(&SmtChar::from('o')));
903 /// assert_eq!(iter.next(), Some(&SmtChar::from('f')));
904 /// assert_eq!(iter.next(), None);
905 /// ```
906 pub fn reversed(&self) -> Self {
907 SmtString(self.0.iter().rev().copied().collect())
908 }
909
910 /// Repeat this string `n` times.
911 /// If `n` is zero, the empty string is returned.
912 /// If this string is empty, the empty string is returned.
913 /// If `n` is one, this string is returned.
914 /// Otherwise, the string is repeated `n` times.
915 ///
916 /// # Examples
917 /// ```
918 /// use smt_str::{SmtString, SmtChar};
919 /// let s = SmtString::from("foo");
920 /// assert_eq!(s.repeat(0), SmtString::empty());
921 /// assert_eq!(s.repeat(1), s);
922 /// assert_eq!(s.repeat(2), SmtString::from("foofoo"));
923 /// ```
924 pub fn repeat(&self, n: usize) -> Self {
925 let mut result = Vec::with_capacity(self.len() * n);
926 for _ in 0..n {
927 result.extend(self.0.iter().copied());
928 }
929 SmtString(result)
930 }
931
932 /// Replaces the first occurrence of `from` in this string with `to`.
933 /// If `from` is not found in this string, the string is returned unchanged.
934 ///
935 /// # Examples
936 /// ```
937 /// use smt_str::{SmtString};
938 /// let s: SmtString = "barbar".into();
939 /// let from: SmtString = "bar".into();
940 /// let to: SmtString = "foo".into();
941 /// assert_eq!(s.replace(&from, &to), SmtString::from("foobar"));
942 /// ```
943 pub fn replace(&self, from: &SmtString, to: &SmtString) -> SmtString {
944 let mut result = SmtString::empty();
945 if let Some(j) = self.index_of(from, 0) {
946 result.append(&self.take(j));
947 result.append(to);
948 let i = j + from.len();
949 result.append(&self.drop(i));
950 } else {
951 result = self.clone();
952 }
953 result
954 }
955
956 /// Replaces all occurrences of `from` in this string with `to`.
957 /// If `from` is not found in this string, the string is returned unchanged.
958 /// If `from` is the empty string, the string is returned unchanged.
959 ///
960 /// # Examples
961 /// ```
962 /// use smt_str::{SmtString};
963 /// let s: SmtString = "barbar".into();
964 /// let from: SmtString = "bar".into();
965 /// let to: SmtString = "foo".into();
966 /// assert_eq!(s.replace_all(&from, &to), SmtString::from("foofoo"));
967 /// ```
968 pub fn replace_all(&self, from: &SmtString, to: &SmtString) -> SmtString {
969 if from.is_empty() || self.is_empty() {
970 return self.clone(); // No changes needed if `from` is empty or `self` is empty
971 }
972
973 let mut result = SmtString::empty();
974 let mut current = self.clone();
975
976 while let Some(j) = current.index_of(from, 0) {
977 result.append(¤t.take(j));
978 result.append(to);
979 let i = j + from.len();
980 current = current.drop(i);
981 }
982
983 result.append(¤t);
984 result
985 }
986
987 /// Returns an iterator over the characters of this string.
988 pub fn iter(&self) -> std::slice::Iter<SmtChar> {
989 self.0.iter()
990 }
991}
992
993/* Conversions */
994
995impl FromIterator<SmtChar> for SmtString {
996 fn from_iter<I: IntoIterator<Item = SmtChar>>(iter: I) -> Self {
997 SmtString(iter.into_iter().collect())
998 }
999}
1000
1001impl FromIterator<SmtString> for SmtString {
1002 fn from_iter<I: IntoIterator<Item = SmtString>>(iter: I) -> Self {
1003 iter.into_iter()
1004 .fold(SmtString::empty(), |acc, s| acc.concat(&s))
1005 }
1006}
1007
1008impl From<&str> for SmtString {
1009 fn from(s: &str) -> Self {
1010 SmtString(s.chars().map(SmtChar::from).collect())
1011 }
1012}
1013
1014impl From<String> for SmtString {
1015 fn from(s: String) -> Self {
1016 SmtString::from(s.as_str())
1017 }
1018}
1019
1020impl From<SmtChar> for SmtString {
1021 fn from(c: SmtChar) -> Self {
1022 SmtString(vec![c])
1023 }
1024}
1025
1026impl Index<usize> for SmtString {
1027 type Output = SmtChar;
1028
1029 fn index(&self, index: usize) -> &Self::Output {
1030 &self.0[index]
1031 }
1032}
1033
1034impl Display for SmtString {
1035 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1036 for c in &self.0 {
1037 write!(f, "{}", c)?;
1038 }
1039 Ok(())
1040 }
1041}
1042
1043impl Arbitrary for SmtChar {
1044 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
1045 let code = u32::arbitrary(g) % (SMT_MAX_CODEPOINT + 1);
1046 SmtChar(code)
1047 }
1048}
1049
1050impl Arbitrary for SmtString {
1051 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
1052 let len = usize::arbitrary(g) % 100;
1053 let chars = std::iter::repeat_with(|| SmtChar::arbitrary(g))
1054 .take(len)
1055 .collect();
1056 SmtString(chars)
1057 }
1058}
1059
1060#[cfg(test)]
1061mod tests {
1062
1063 use quickcheck::TestResult;
1064 use quickcheck_macros::quickcheck;
1065
1066 use super::*;
1067
1068 #[quickcheck]
1069 fn next_prev_inverse(s: SmtChar) -> TestResult {
1070 if s == SmtChar::MAX {
1071 return TestResult::discard();
1072 }
1073 let next = s.next();
1074 assert_eq!(next.prev(), s);
1075 TestResult::passed()
1076 }
1077
1078 #[quickcheck]
1079 fn prev_next_inverse(s: SmtChar) -> TestResult {
1080 if s == SmtChar::MIN {
1081 return TestResult::discard();
1082 }
1083 let prev = s.prev();
1084 assert_eq!(prev.next(), s);
1085 TestResult::passed()
1086 }
1087
1088 #[test]
1089 #[should_panic]
1090 fn next_past_max() {
1091 SmtChar::MAX.next();
1092 }
1093
1094 #[test]
1095 #[should_panic]
1096 fn prev_past_min() {
1097 SmtChar::MIN.prev();
1098 }
1099
1100 #[test]
1101 fn test_unescape_valid() {
1102 assert_eq!(SmtChar::unescape(r#"\u000A"#), Some(SmtChar(0x000A)));
1103 assert_eq!(SmtChar::unescape(r#"\u{A}"#), Some(SmtChar(0x000A)));
1104 assert_eq!(SmtChar::unescape(r#"\u{0A}"#), Some(SmtChar(0x000A)));
1105 assert_eq!(SmtChar::unescape(r#"\u{00A}"#), Some(SmtChar(0x000A)));
1106 assert_eq!(SmtChar::unescape(r#"\u{000A}"#), Some(SmtChar(0x000A)));
1107 assert_eq!(SmtChar::unescape(r#"\u{000A}"#), Some(SmtChar(0x000A)));
1108 assert_eq!(SmtChar::unescape(r#"\u{0000A}"#), Some(SmtChar(0x000A)));
1109 }
1110
1111 #[test]
1112 fn test_unescape_empty() {
1113 assert_eq!(SmtChar::unescape(r#"\u{}"#), None);
1114 }
1115
1116 #[test]
1117 fn test_unescape_too_large() {
1118 assert_eq!(SmtChar::unescape(r#"\u{3000A}"#), None);
1119 assert_eq!(SmtChar::unescape(r#"\u{4000A}"#), None);
1120 assert_eq!(SmtChar::unescape(r#"\u{5000A}"#), None);
1121 assert_eq!(SmtChar::unescape(r#"\u{F000A}"#), None);
1122 }
1123
1124 #[test]
1125 fn test_unescape_not_hex() {
1126 assert_eq!(SmtChar::unescape(r#"\u{G}"#), None);
1127 assert_eq!(SmtChar::unescape(r#"\u000H"#), None);
1128 assert_eq!(SmtChar::unescape(r#"\u{39v}"#), None);
1129 assert_eq!(SmtChar::unescape(r#"\u{J}"#), None);
1130 }
1131
1132 #[test]
1133 fn test_unescape_invalid_braces() {
1134 assert_eq!(SmtChar::unescape(r#"\u{000A"#), None);
1135 assert_eq!(SmtChar::unescape(r#"\u000A}"#), None);
1136 assert_eq!(SmtChar::unescape(r#"\u{0{00A}"#), None);
1137 assert_eq!(SmtChar::unescape(r#"\u{0}00A}"#), None);
1138 }
1139
1140 #[test]
1141 fn test_unescape_invalid_prefix() {
1142 assert_eq!(SmtChar::unescape(r#"u{000A}"#), None);
1143 assert_eq!(SmtChar::unescape(r#"\{000A}"#), None);
1144 assert_eq!(SmtChar::unescape(r#"\u000A}"#), None);
1145 }
1146
1147 #[quickcheck]
1148 fn test_escape_unescape_inverse(c: u32) -> TestResult {
1149 if c > SMT_MAX_CODEPOINT {
1150 return TestResult::discard();
1151 }
1152 let smt_char = SmtChar::new(c);
1153 assert_eq!(smt_char, SmtChar::unescape(&smt_char.escape()).unwrap());
1154 TestResult::passed()
1155 }
1156
1157 #[quickcheck]
1158 fn append_empty_right_neutral(s: SmtString) {
1159 let mut s1 = s.clone();
1160 s1.append(&SmtString::empty());
1161 assert_eq!(s1, s);
1162 }
1163
1164 #[quickcheck]
1165 fn append_empty_left_neutral(s: SmtString) {
1166 let mut s1 = SmtString::empty();
1167 s1.append(&s);
1168 assert_eq!(s1, s);
1169 }
1170
1171 #[quickcheck]
1172 fn reverse_reverse_inverse(s: SmtString) {
1173 let rev = s.reversed();
1174 assert_eq!(rev.reversed(), s);
1175 }
1176
1177 #[test]
1178 fn test_parse_valid_strings_without_escaped() {
1179 assert_eq!(
1180 SmtString::parse("foo"),
1181 SmtString(vec![
1182 SmtChar('f' as u32),
1183 SmtChar('o' as u32),
1184 SmtChar('o' as u32),
1185 ])
1186 );
1187 assert_eq!(
1188 SmtString::parse("123!@#"),
1189 SmtString(vec![
1190 SmtChar('1' as u32),
1191 SmtChar('2' as u32),
1192 SmtChar('3' as u32),
1193 SmtChar('!' as u32),
1194 SmtChar('@' as u32),
1195 SmtChar('#' as u32)
1196 ])
1197 );
1198 }
1199
1200 #[test]
1201 fn test_parse_valid_string_with_one_escape() {
1202 assert_eq!(
1203 SmtString::parse(r#"a\u0042c"#),
1204 SmtString(vec![
1205 SmtChar('a' as u32),
1206 SmtChar('B' as u32), // Unicode for 'B'
1207 SmtChar('c' as u32)
1208 ])
1209 );
1210
1211 assert_eq!(
1212 SmtString::parse(r#"x\u{41}y"#),
1213 SmtString(vec![
1214 SmtChar('x' as u32),
1215 SmtChar('A' as u32), // Unicode for 'A'
1216 SmtChar('y' as u32)
1217 ])
1218 );
1219
1220 assert_eq!(
1221 SmtString::parse(r#"\u{1F600}"#), // Unicode for 😀
1222 SmtString(vec![SmtChar(0x1F600)])
1223 );
1224 }
1225
1226 #[test]
1227 fn test_parse_valid_string_with_multiple_escape() {
1228 assert_eq!(
1229 SmtString::parse(r#"abc\u0044\u{45}f"#),
1230 SmtString(vec![
1231 SmtChar('a' as u32),
1232 SmtChar('b' as u32),
1233 SmtChar('c' as u32),
1234 SmtChar('D' as u32), // Unicode for 'D'
1235 SmtChar('E' as u32), // Unicode for 'E'
1236 SmtChar('f' as u32),
1237 ])
1238 );
1239
1240 assert_eq!(
1241 SmtString::parse(r#"\u{1F604} smile \u{1F60A}"#), // 😄 smile 😊
1242 SmtString(vec![
1243 SmtChar(0x1F604), // 😄
1244 SmtChar(' ' as u32),
1245 SmtChar('s' as u32),
1246 SmtChar('m' as u32),
1247 SmtChar('i' as u32),
1248 SmtChar('l' as u32),
1249 SmtChar('e' as u32),
1250 SmtChar(' ' as u32),
1251 SmtChar(0x1F60A), // 😊
1252 ])
1253 );
1254 }
1255
1256 #[test]
1257 fn test_parse_invalid_escape_sequences() {
1258 // Missing closing brace
1259 let s = r#"\u{123"#;
1260 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1261 assert_eq!(SmtString::parse(s), expected);
1262
1263 // Non-hex character in escape sequence
1264 let s = r#"\u{12G3}"#;
1265 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1266 assert_eq!(SmtString::parse(s), expected);
1267
1268 // Escape sequence too long
1269 let s = r#"\u{123456}"#;
1270 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1271 assert_eq!(SmtString::parse(s), expected);
1272
1273 // Escape sequence without digits
1274 let s = r#"\u{}"#;
1275 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1276 assert_eq!(SmtString::parse(s), expected);
1277
1278 // Invalid escape sequence (SMT 2.5 style)
1279 let s = r#"\x1234"#;
1280 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1281 assert_eq!(SmtString::parse(s), expected);
1282
1283 // Unicode above allowed SMT max
1284 let s = r#"\u{110000}"#;
1285 let expected = SmtString::new(s.chars().map(SmtChar::from).collect());
1286 assert_eq!(SmtString::parse(s), expected);
1287 }
1288
1289 #[quickcheck]
1290 fn test_print_parse_inverse(s: SmtString) {
1291 let s1 = s.to_string();
1292 let s2 = SmtString::parse(&s1);
1293 assert_eq!(s, s2);
1294 }
1295
1296 #[quickcheck]
1297 fn index_of_empty_is_always_zero(s: SmtString) {
1298 assert_eq!(s.index_of(&SmtString::empty(), 0), Some(0));
1299 }
1300
1301 #[quickcheck]
1302 fn contains_empty(s: SmtString) {
1303 assert!(s.contains(&SmtString::empty()))
1304 }
1305
1306 #[test]
1307 fn test_replace_at_start() {
1308 let s: SmtString = "foobar".into();
1309 let from: SmtString = "foo".into();
1310 let to: SmtString = "bar".into();
1311 assert_eq!(s.replace(&from, &to), "barbar".into());
1312 }
1313
1314 #[test]
1315 fn test_replace_at_end() {
1316 let s: SmtString = "foobar".into();
1317 let from: SmtString = "bar".into();
1318 let to: SmtString = "foo".into();
1319 assert_eq!(s.replace(&from, &to), "foofoo".into());
1320 }
1321
1322 #[test]
1323 fn test_replace_no_match() {
1324 let s: SmtString = "abcdef".into();
1325 let from: SmtString = "xyz".into();
1326 let to: SmtString = "123".into();
1327 assert_eq!(s.replace(&from, &to), s);
1328 }
1329
1330 #[test]
1331 fn test_replace_empty_from() {
1332 let s: SmtString = "abcdef".into();
1333 let from: SmtString = "".into();
1334 let to: SmtString = "XYZ".into();
1335 assert_eq!(s.replace(&from, &to), "XYZabcdef".into()); // Empty string is inserted at the beginning
1336 }
1337
1338 #[test]
1339 fn test_replace_empty_to() {
1340 let s: SmtString = "abcdef".into();
1341 let from: SmtString = "cd".into();
1342 let to: SmtString = "".into();
1343 assert_eq!(s.replace(&from, &to), "abef".into()); // `cd` is removed
1344 }
1345
1346 #[test]
1347 fn test_replace_full_string() {
1348 let s: SmtString = "abcdef".into();
1349 let from: SmtString = "abcdef".into();
1350 let to: SmtString = "xyz".into();
1351 assert_eq!(s.replace(&from, &to), "xyz".into());
1352 }
1353
1354 #[test]
1355 fn test_replace_repeated_pattern() {
1356 let s: SmtString = "abcabcabc".into();
1357 let from: SmtString = "abc".into();
1358 let to: SmtString = "x".into();
1359 assert_eq!(s.replace(&from, &to), "xabcabc".into()); // Only first occurrence is replaced
1360 }
1361
1362 #[test]
1363 fn test_replace_single_character() {
1364 let s: SmtString = "banana".into();
1365 let from: SmtString = "a".into();
1366 let to: SmtString = "o".into();
1367 assert_eq!(s.replace(&from, &to), "bonana".into()); // Only first 'a' is replaced
1368 }
1369
1370 #[test]
1371 fn test_replace_all_basic() {
1372 let s: SmtString = "foobarbar".into();
1373 let from: SmtString = "bar".into();
1374 let to: SmtString = "foo".into();
1375 assert_eq!(s.replace_all(&from, &to), "foofoofoo".into());
1376 }
1377
1378 #[test]
1379 fn test_replace_all_complete() {
1380 let s: SmtString = "abcabcabc".into();
1381 let from: SmtString = "abc".into();
1382 let to: SmtString = "xyz".into();
1383 assert_eq!(s.replace_all(&from, &to), "xyzxyzxyz".into());
1384 }
1385
1386 #[test]
1387 fn test_replace_all_no_match() {
1388 let s: SmtString = "abcdef".into();
1389 let from: SmtString = "xyz".into();
1390 let to: SmtString = "123".into();
1391 assert_eq!(s.replace_all(&from, &to), "abcdef".into());
1392 }
1393
1394 #[test]
1395 fn test_replace_all_empty_from() {
1396 let s: SmtString = "abcdef".into();
1397 let from: SmtString = "".into();
1398 let to: SmtString = "XYZ".into();
1399 assert_eq!(s.replace_all(&from, &to), s); // No-op
1400 }
1401
1402 #[test]
1403 fn test_replace_all_empty_to() {
1404 let s: SmtString = "banana".into();
1405 let from: SmtString = "a".into();
1406 let to: SmtString = "".into();
1407 assert_eq!(s.replace_all(&from, &to), "bnn".into()); // All 'a's are removed
1408 }
1409
1410 #[test]
1411 fn test_replace_all_full_string() {
1412 let s: SmtString = "abcdef".into();
1413 let from: SmtString = "abcdef".into();
1414 let to: SmtString = "xyz".into();
1415 assert_eq!(s.replace_all(&from, &to), "xyz".into());
1416 }
1417
1418 #[test]
1419 fn test_replace_all_overlapping_occurrences() {
1420 let s: SmtString = "aaaa".into();
1421 let from: SmtString = "aa".into();
1422 let to: SmtString = "b".into();
1423 assert_eq!(s.replace_all(&from, &to), "bb".into()); // "aa" -> "b", then another "aa" -> "b"
1424 }
1425
1426 #[test]
1427 fn test_replace_all_overlapping_occurrences_2() {
1428 let s: SmtString = "aaa".into();
1429 let from: SmtString = "aa".into();
1430 let to: SmtString = "b".into();
1431 assert_eq!(s.replace_all(&from, &to), "ba".into()); // "aa" -> "b", then another "aa" -> "b"
1432 }
1433
1434 #[test]
1435 fn test_replace_all_overlapping_occurrences_3() {
1436 let s: SmtString = "aaa".into();
1437 let from: SmtString = "aa".into();
1438 let to: SmtString = "aaa".into();
1439 assert_eq!(s.replace_all(&from, &to), "aaaa".into());
1440 }
1441}