1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6use core::{fmt, hash, str};
7
8use crate::error::{Error, Result};
9use crate::vec::Vec;
10
11#[cfg(feature = "alloc")]
12type Inner<const N: usize> = alloc::string::String;
13#[cfg(not(feature = "alloc"))]
14type Inner<const N: usize> = heapless::String<N>;
15
16#[derive(Clone, Debug)]
21pub struct String<const N: usize>(Inner<N>);
22
23impl<const N: usize> String<N> {
24 #[inline]
26 pub fn new() -> Self {
27 Self::default()
28 }
29
30 #[inline]
32 pub fn from_utf8(vec: Vec<u8, N>) -> Result<Self> {
33 #[cfg(feature = "alloc")]
34 {
35 let utf8_str = str::from_utf8(vec.as_slice()).map_err(|_| Error::InvalidUtf8)?;
36 Ok(Self(alloc::string::String::from(utf8_str)))
37 }
38 #[cfg(not(feature = "alloc"))]
39 {
40 Inner::from_utf8(vec.into_inner())
41 .map(Self)
42 .map_err(|_| Error::InvalidUtf8)
43 }
44 }
45
46 #[inline]
48 pub fn as_str(&self) -> &str {
49 self.0.as_str()
50 }
51
52 #[inline]
54 pub fn as_bytes(&self) -> &[u8] {
55 self.0.as_bytes()
56 }
57
58 #[inline]
60 pub fn len(&self) -> usize {
61 self.0.len()
62 }
63
64 #[inline]
66 pub fn is_empty(&self) -> bool {
67 self.0.is_empty()
68 }
69
70 #[inline]
74 pub fn push_str(&mut self, s: &str) -> Result<()> {
75 #[cfg(feature = "alloc")]
76 {
77 let new_len = self.0.len().saturating_add(s.len());
78 if new_len > N {
79 return Err(Error::CapacityExceeded);
80 }
81 self.0.push_str(s);
82 Ok(())
83 }
84 #[cfg(not(feature = "alloc"))]
85 {
86 self.0.push_str(s).map_err(|_| Error::CapacityExceeded)
87 }
88 }
89}
90
91impl<const N: usize> AsRef<str> for String<N> {
92 #[inline]
93 fn as_ref(&self) -> &str {
94 self.as_str()
95 }
96}
97
98impl<'a, const N: usize> TryFrom<&'a str> for String<N> {
99 type Error = Error;
100
101 #[inline]
102 fn try_from(s: &'a str) -> Result<Self> {
103 if s.len() > N {
104 return Err(Error::CapacityExceeded);
105 }
106 #[cfg(feature = "alloc")]
107 {
108 Ok(Self(alloc::string::String::from(s)))
109 }
110 #[cfg(not(feature = "alloc"))]
111 {
112 Inner::try_from(s)
113 .map(Self)
114 .map_err(|_| Error::CapacityExceeded)
115 }
116 }
117}
118
119impl<const N: usize> Default for String<N> {
120 #[inline]
121 fn default() -> Self {
122 #[cfg(feature = "alloc")]
123 {
124 Self(Inner::with_capacity(N))
125 }
126 #[cfg(not(feature = "alloc"))]
127 {
128 Self(Inner::new())
129 }
130 }
131}
132
133impl<const N: usize> From<Inner<N>> for String<N> {
134 #[inline]
135 fn from(inner: Inner<N>) -> Self {
136 Self(inner)
137 }
138}
139
140impl<const N: usize> fmt::Display for String<N> {
141 #[inline]
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 self.0.fmt(f)
144 }
145}
146
147impl<const N: usize> hash::Hash for String<N> {
148 #[inline]
149 fn hash<H: hash::Hasher>(&self, state: &mut H) {
150 self.0.hash(state);
151 }
152}
153
154impl<const N1: usize, const N2: usize> PartialEq<String<N2>> for String<N1> {
155 #[inline]
156 fn eq(&self, other: &String<N2>) -> bool {
157 self.as_str() == other.as_str()
158 }
159}
160
161impl<const N: usize> PartialEq<str> for String<N> {
162 #[inline]
163 fn eq(&self, other: &str) -> bool {
164 self.as_str() == other
165 }
166}
167
168impl<const N: usize> Eq for String<N> {}
169
170impl<const N: usize> PartialOrd for String<N> {
171 #[inline]
172 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
173 Some(self.cmp(other))
174 }
175}
176
177impl<const N: usize> Ord for String<N> {
178 #[inline]
179 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
180 self.as_str().cmp(other.as_str())
181 }
182}
183
184impl<const N: usize> fmt::Write for String<N> {
185 #[inline]
186 fn write_str(&mut self, s: &str) -> fmt::Result {
187 self.push_str(s).map_err(|_| fmt::Error)
188 }
189}