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()).map(Self).map_err(|_| Error::InvalidUtf8)
41 }
42 }
43
44 #[inline]
46 pub fn as_str(&self) -> &str {
47 self.0.as_str()
48 }
49
50 #[inline]
52 pub fn as_bytes(&self) -> &[u8] {
53 self.0.as_bytes()
54 }
55
56 #[inline]
58 pub fn len(&self) -> usize {
59 self.0.len()
60 }
61
62 #[inline]
64 pub fn is_empty(&self) -> bool {
65 self.0.is_empty()
66 }
67
68 #[inline]
72 pub fn push_str(&mut self, s: &str) -> Result<()> {
73 #[cfg(feature = "alloc")]
74 {
75 let new_len = self.0.len().saturating_add(s.len());
76 if new_len > N {
77 return Err(Error::CapacityExceeded);
78 }
79 self.0.push_str(s);
80 Ok(())
81 }
82 #[cfg(not(feature = "alloc"))]
83 {
84 self.0.push_str(s).map_err(|_| Error::CapacityExceeded)
85 }
86 }
87}
88
89impl<const N: usize> AsRef<str> for String<N> {
90 #[inline]
91 fn as_ref(&self) -> &str {
92 self.as_str()
93 }
94}
95
96impl<'a, const N: usize> TryFrom<&'a str> for String<N> {
97 type Error = Error;
98
99 #[inline]
100 fn try_from(s: &'a str) -> Result<Self> {
101 if s.len() > N {
102 return Err(Error::CapacityExceeded);
103 }
104 #[cfg(feature = "alloc")]
105 {
106 Ok(Self(alloc::string::String::from(s)))
107 }
108 #[cfg(not(feature = "alloc"))]
109 {
110 Inner::try_from(s).map(Self).map_err(|_| Error::CapacityExceeded)
111 }
112 }
113}
114
115impl<const N: usize> Default for String<N> {
116 #[inline]
117 fn default() -> Self {
118 #[cfg(feature = "alloc")]
119 {
120 Self(Inner::with_capacity(N))
121 }
122 #[cfg(not(feature = "alloc"))]
123 {
124 Self(Inner::new())
125 }
126 }
127}
128
129impl<const N: usize> From<Inner<N>> for String<N> {
130 #[inline]
131 fn from(inner: Inner<N>) -> Self {
132 Self(inner)
133 }
134}
135
136impl<const N: usize> fmt::Display for String<N> {
137 #[inline]
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 self.0.fmt(f)
140 }
141}
142
143impl<const N: usize> hash::Hash for String<N> {
144 #[inline]
145 fn hash<H: hash::Hasher>(&self, state: &mut H) {
146 self.0.hash(state);
147 }
148}
149
150impl<const N1: usize, const N2: usize> PartialEq<String<N2>> for String<N1> {
151 #[inline]
152 fn eq(&self, other: &String<N2>) -> bool {
153 self.as_str() == other.as_str()
154 }
155}
156
157impl<const N: usize> PartialEq<str> for String<N> {
158 #[inline]
159 fn eq(&self, other: &str) -> bool {
160 self.as_str() == other
161 }
162}
163
164impl<const N: usize> Eq for String<N> {}
165
166impl<const N: usize> PartialOrd for String<N> {
167 #[inline]
168 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
169 Some(self.cmp(other))
170 }
171}
172
173impl<const N: usize> Ord for String<N> {
174 #[inline]
175 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
176 self.as_str().cmp(other.as_str())
177 }
178}
179
180impl<const N: usize> fmt::Write for String<N> {
181 #[inline]
182 fn write_str(&mut self, s: &str) -> fmt::Result {
183 self.push_str(s).map_err(|_| fmt::Error)
184 }
185}