1#![doc(issue_tracker_base_url = "https://github.com/MidasLamb/non-empty-string/issues/")]
2
3#[cfg(doctest)]
8mod test_readme {
9 #[doc = include_str!("../README.md")]
10 mod something {}
11}
12
13use std::str::FromStr;
14
15use delegate::delegate;
16
17#[cfg(feature = "serde")]
18mod serde_support;
19
20mod trait_impls;
21
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[repr(transparent)]
26pub struct NonEmptyString(String);
27
28#[allow(clippy::len_without_is_empty, reason = "is_empty would always returns false so it seems a bit silly to have it.")]
29impl NonEmptyString {
30 pub fn new(string: String) -> Result<Self, String> {
33 if string.is_empty() {
34 Err(string)
35 } else {
36 Ok(NonEmptyString(string))
37 }
38 }
39 pub unsafe fn new_unchecked(string: String) -> Self {
44 Self::new(string).unwrap_unchecked()
45 }
46
47 pub fn get(&self) -> &str {
49 &self.0
50 }
51
52 pub fn into_inner(self) -> String {
54 self.0
55 }
56
57 delegate! {
59 to self.0 {
60 pub fn into_bytes(self) -> Vec<u8>;
63
64 pub fn as_str(&self) -> &str;
67
68 pub fn push_str(&mut self, string: &str);
71
72 pub fn capacity(&self) -> usize;
75
76 pub fn reserve(&mut self, additional: usize);
79
80 pub fn reserve_exact(&mut self, additional: usize);
83
84 pub fn try_reserve_exact(
90 &mut self,
91 additional: usize
92 ) -> Result<(), std::collections::TryReserveError>;
93
94 pub fn shrink_to_fit(&mut self);
97
98 pub fn shrink_to(&mut self, min_capacity: usize);
101
102 pub fn push(&mut self, ch: char);
105
106 pub fn as_bytes(&self) -> &[u8];
109
110 pub fn insert(&mut self, idx: usize, ch: char);
113
114 pub fn insert_str(&mut self, idx: usize, string: &str);
117
118 pub fn len(&self) -> usize;
121
122 pub fn into_boxed_str(self) -> Box<str>;
125 }
126 }
127}
128
129impl AsRef<str> for NonEmptyString {
130 fn as_ref(&self) -> &str {
131 &self.0
132 }
133}
134
135impl AsRef<String> for NonEmptyString {
136 fn as_ref(&self) -> &String {
137 &self.0
138 }
139}
140
141impl<'s> TryFrom<&'s str> for NonEmptyString {
142 type Error = &'s str;
143
144 fn try_from(value: &'s str) -> Result<Self, Self::Error> {
145 if value.is_empty() {
146 return Err(value);
147 }
148
149 Ok(NonEmptyString(value.to_owned()))
150 }
151}
152
153impl TryFrom<String> for NonEmptyString {
154 type Error = String;
155
156 fn try_from(value: String) -> Result<Self, Self::Error> {
157 NonEmptyString::new(value)
158 }
159}
160
161impl FromStr for NonEmptyString {
162 type Err = <NonEmptyString as TryFrom<String>>::Error;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 <Self as TryFrom<String>>::try_from(s.to_string())
166 }
167}
168
169impl From<NonEmptyString> for String {
170 fn from(value: NonEmptyString) -> Self {
171 value.0
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use std::hash::Hash;
178
179 use super::*;
180
181 #[test]
182 fn empty_string_returns_err() {
183 assert_eq!(NonEmptyString::new("".to_owned()), Err("".to_owned()));
184 }
185
186 #[test]
187 fn non_empty_string_returns_ok() {
188 assert!(NonEmptyString::new("string".to_owned()).is_ok())
189 }
190
191 #[test]
192 fn what_goes_in_comes_out() {
193 assert_eq!(
194 NonEmptyString::new("string".to_owned())
195 .unwrap()
196 .into_inner(),
197 "string".to_owned()
198 );
199 }
200
201 #[test]
202 fn as_ref_str_works() {
203 let nes = NonEmptyString::new("string".to_owned()).unwrap();
204 let val: &str = nes.as_ref();
205 assert_eq!(val, "string");
206 }
207
208 #[test]
209 fn as_ref_string_works() {
210 let nes = NonEmptyString::new("string".to_owned()).unwrap();
211 let val: &String = nes.as_ref();
212 assert_eq!(val, "string");
213 }
214
215 #[test]
216 fn calling_string_methods_works() {
217 let nes = NonEmptyString::new("string".to_owned()).unwrap();
218 assert!(nes.len() > 0);
220 }
221
222 #[test]
223 fn format_test() {
224 let str = NonEmptyString::new("string".to_owned()).unwrap();
225 println!("{}", &str);
226 assert_eq!(String::from("string"), str.to_string())
227 }
228
229 #[test]
230 fn from_str_works() {
231 let valid_str = "string";
232 let non_empty_string = NonEmptyString::from_str(valid_str).unwrap();
233 let parsed: NonEmptyString = valid_str.parse().unwrap();
234 assert_eq!(non_empty_string, parsed);
235 }
236
237 #[test]
238 fn into_works() {
239 let non_empty_string = NonEmptyString::new("string".to_string()).unwrap();
240 let _string: String = non_empty_string.into();
241
242 let non_empty_string = NonEmptyString::new("string".to_string()).unwrap();
243 let _string = String::from(non_empty_string);
244 }
245
246 #[test]
247 fn hash_works() {
248 fn is_hash<T: Hash>() {}
249 is_hash::<NonEmptyString>();
250 }
251}