Skip to main content

ion_rs/types/
string.rs

1use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
2use crate::text::text_formatter::FmtValueFormatter;
3use std::cmp::Ordering;
4use std::fmt::{Display, Formatter};
5use std::hash::{Hash, Hasher};
6
7/// An owned, immutable in-memory representation of an Ion `string`.
8///
9/// ```
10/// use ion_rs::Str;
11/// let s: Str = "hello!".into();
12/// assert_eq!(s, "hello!");
13/// ```
14#[derive(Eq, Ord, PartialOrd, Debug, Clone, Hash)]
15pub struct Str {
16    // For the time being, the `Str` type is an opaque wrapper around the standard Rust `String`
17    // type. Having this opaque wrapper means that we can swap out its implementation without a
18    // breaking change, allowing us to offer string interning, stack-allocated small strings, or
19    // other optimizations as needed.
20    text: String,
21}
22
23impl Str {
24    /// Returns the number of UTF-8 encoded bytes in this string.
25    ///
26    /// ```
27    /// use ion_rs::Str;
28    /// let s: Str = "hello!".into();
29    /// assert_eq!(s.len(), 6);
30    /// // Note that the length returned is a number of UTF-8 bytes, not codepoints or graphemes.
31    /// let s: Str = "🚀🚀🚀".into();
32    /// assert_eq!(s.len(), 12);
33    /// ```
34    pub fn len(&self) -> usize {
35        self.text.len()
36    }
37
38    /// Returns `true` if this is the empty string (`""`); otherwise, returns `false`.
39    ///
40    /// ```
41    /// use ion_rs::Str;
42    /// let s: Str = "".into();
43    /// assert!(s.is_empty());
44    /// let s: Str = "hello!".into();
45    /// assert!(!s.is_empty());
46    /// ```
47    // This method is largely here because clippy complains if you provide a `len()` method but not
48    // an accompanying `is_empty()` method.
49    pub fn is_empty(&self) -> bool {
50        self.text.is_empty()
51    }
52
53    /// Returns a `&str` representation of this string's text.
54    ///
55    /// ```
56    /// use ion_rs::Str;
57    /// let s: Str = "hello, world!".into();
58    /// assert!(s.text().contains("world"));
59    /// assert!(s.text().is_ascii());
60    /// ```
61    pub fn text(&self) -> &str {
62        self.text.as_str()
63    }
64}
65
66impl Display for Str {
67    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
68        let mut formatter = FmtValueFormatter { output: f };
69        formatter
70            .format_string(self.as_ref())
71            .map_err(|_| std::fmt::Error)
72    }
73}
74
75impl From<&str> for Str {
76    fn from(value: &str) -> Self {
77        Str {
78            text: value.to_string(),
79        }
80    }
81}
82
83impl From<String> for Str {
84    fn from(value: String) -> Self {
85        Str { text: value }
86    }
87}
88
89impl From<Str> for String {
90    fn from(value: Str) -> Self {
91        value.text
92    }
93}
94
95impl AsRef<str> for Str {
96    fn as_ref(&self) -> &str {
97        self.text()
98    }
99}
100
101impl<S> PartialEq<S> for Str
102where
103    S: AsRef<str>,
104{
105    fn eq(&self, other: &S) -> bool {
106        let other_text: &str = other.as_ref();
107        self.text() == other_text
108    }
109}
110
111impl PartialEq<Str> for &str {
112    fn eq(&self, other: &Str) -> bool {
113        self.eq(&other.text())
114    }
115}
116
117impl PartialEq<Str> for String {
118    fn eq(&self, other: &Str) -> bool {
119        let self_text: &str = self.as_str();
120        self_text.eq(other.text())
121    }
122}
123
124impl IonEq for Str {
125    fn ion_eq(&self, other: &Self) -> bool {
126        self == other
127    }
128}
129
130impl IonDataOrd for Str {
131    fn ion_cmp(&self, other: &Self) -> Ordering {
132        self.cmp(other)
133    }
134}
135
136impl IonDataHash for Str {
137    fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
138        self.hash(state)
139    }
140}