Skip to content

Commit 32d1d60

Browse files
authored
Merge pull request #12 from jonhteper
Add additional traits
2 parents 47fe686 + 07fa59a commit 32d1d60

File tree

3 files changed

+89
-20
lines changed

3 files changed

+89
-20
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use non_empty_string::NonEmptyString;
1414
let s = "A string with a length".to_owned();
1515
assert!(NonEmptyString::new(s).is_ok());
1616

17-
// But constructing it from a non-zero-length String results in an `Err`, where we get the `String` back that we passed in.
17+
// But constructing it from a zero-length String results in an `Err`, where we get the `String` back that we passed in.
1818
let empty = "".to_owned();
1919
let result = NonEmptyString::new(empty);
2020
assert!(result.is_err());

src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use std::fmt::Display;
2+
3+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
4+
pub struct EmptyString;
5+
6+
impl Display for EmptyString {
7+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8+
write!(f, "zero-value string")
9+
}
10+
}
11+
12+
impl std::error::Error for EmptyString {}

src/lib.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ mod test_readme {
99
#[doc = include_str!("../README.md")]
1010
mod something {}
1111
}
12-
13-
use delegate::delegate;
14-
use std::fmt::Display;
15-
12+
mod error;
1613
#[cfg(feature = "serde")]
1714
mod serde_support;
1815

16+
use delegate::delegate;
17+
pub use error::EmptyString;
18+
use std::{fmt::Display, str::FromStr};
19+
1920
/// A simple String wrapper type, similar to NonZeroUsize and friends.
2021
/// Guarantees that the String contained inside is not of length 0.
21-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
22+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2223
#[repr(transparent)]
2324
pub struct NonEmptyString(String);
2425

@@ -48,34 +49,34 @@ impl NonEmptyString {
4849
delegate! {
4950
to self.0 {
5051
/// Is forwarded to the inner String.
51-
/// See [`std::string::String::into_bytes`]
52+
/// See [`String::into_bytes`]
5253
pub fn into_bytes(self) -> Vec<u8>;
5354

5455
/// Is forwarded to the inner String.
55-
/// See [`std::string::String::as_str`]
56+
/// See [`String::as_str`]
5657
pub fn as_str(&self) -> &str;
5758

5859
/// Is forwarded to the inner String.
59-
/// See [`std::string::String::push_str`]
60+
/// See [`String::push_str`]
6061
pub fn push_str(&mut self, string: &str);
6162

6263
/// Is forwarded to the inner String.
63-
/// See [`std::string::String::capacity`]
64+
/// See [`String::capacity`]
6465
pub fn capacity(&self) -> usize;
6566

6667
/// Is forwarded to the inner String.
67-
/// See [`std::string::String::reserve`]
68+
/// See [`String::reserve`]
6869
pub fn reserve(&mut self, additional: usize);
6970

7071
/// Is forwarded to the inner String.
71-
/// See [`std::string::String::reserve_exact`]
72+
/// See [`String::reserve_exact`]
7273
pub fn reserve_exact(&mut self, additional: usize);
7374

7475
// For some reason we cannot delegate the following:
7576
// pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
7677

7778
/// Is forwarded to the inner String.
78-
/// See [`std::string::String::try_reserve_exact`]
79+
/// See [`String::try_reserve_exact`]
7980
pub fn try_reserve_exact(
8081
&mut self,
8182
additional: usize
@@ -86,31 +87,31 @@ impl NonEmptyString {
8687
pub fn shrink_to_fit(&mut self);
8788

8889
/// Is forwarded to the inner String.
89-
/// See [`std::string::String::shrink_to`]
90+
/// See [`String::shrink_to`]
9091
pub fn shrink_to(&mut self, min_capacity: usize);
9192

9293
/// Is forwarded to the inner String.
93-
/// See [`std::string::String::push`]
94+
/// See [`String::push`]
9495
pub fn push(&mut self, ch: char);
9596

9697
/// Is forwarded to the inner String.
97-
/// See [`std::string::String::as_bytes`]
98+
/// See [`String::as_bytes`]
9899
pub fn as_bytes(&self) -> &[u8];
99100

100101
/// Is forwarded to the inner String.
101-
/// See [`std::string::String::insert`]
102+
/// See [`String::insert`]
102103
pub fn insert(&mut self, idx: usize, ch: char);
103104

104105
/// Is forwarded to the inner String.
105-
/// See [`std::string::String::insert_str`]
106+
/// See [`String::insert_str`]
106107
pub fn insert_str(&mut self, idx: usize, string: &str);
107108

108109
/// Is forwarded to the inner String.
109-
/// See [`std::string::String::len`]
110+
/// See [`String::len`]
110111
pub fn len(&self) -> usize;
111112

112113
/// Is forwarded to the inner String.
113-
/// See [`std::string::String::into_boxed_str`]
114+
/// See [`String::into_boxed_str`]
114115
pub fn into_boxed_str(self) -> Box<str>;
115116
}
116117
}
@@ -154,8 +155,29 @@ impl Display for NonEmptyString {
154155
}
155156
}
156157

158+
impl FromStr for NonEmptyString {
159+
type Err = EmptyString;
160+
161+
fn from_str(s: &str) -> Result<Self, Self::Err> {
162+
if s.is_empty() {
163+
return Err(EmptyString);
164+
}
165+
166+
Ok(Self(s.to_string()))
167+
}
168+
}
169+
170+
impl From<NonEmptyString> for String {
171+
fn from(value: NonEmptyString) -> Self {
172+
value.0
173+
}
174+
}
175+
157176
#[cfg(test)]
158177
mod tests {
178+
use std::collections::HashMap;
179+
use std::collections::HashSet;
180+
159181
use super::*;
160182

161183
#[test]
@@ -205,4 +227,39 @@ mod tests {
205227
println!("{}", &str);
206228
assert_eq!(String::from("string"), str.to_string())
207229
}
230+
231+
#[test]
232+
fn from_str_works() {
233+
let valid_str = "string";
234+
235+
let _non_empty_string = NonEmptyString::from_str("").expect_err("operation must be failed");
236+
237+
let non_empty_string = NonEmptyString::from_str(valid_str).unwrap();
238+
assert_eq!(non_empty_string.as_str(), valid_str);
239+
assert_eq!(non_empty_string, valid_str.parse().unwrap());
240+
}
241+
242+
#[test]
243+
fn into_works() {
244+
let non_empty_string = NonEmptyString::new("string".to_string()).unwrap();
245+
let _string: String = non_empty_string.into();
246+
247+
let non_empty_string = NonEmptyString::new("string".to_string()).unwrap();
248+
let _string = String::from(non_empty_string);
249+
}
250+
251+
#[test]
252+
fn hash_works() {
253+
let mut map = HashMap::new();
254+
map.insert(NonEmptyString::from_str("id.1").unwrap(), 1);
255+
map.insert(NonEmptyString::from_str("id.2").unwrap(), 2);
256+
257+
assert_eq!(map.len(), 2);
258+
259+
let mut set = HashSet::new();
260+
set.insert(NonEmptyString::from_str("1").unwrap());
261+
set.insert(NonEmptyString::from_str("2").unwrap());
262+
263+
assert_eq!(set.len(), 2);
264+
}
208265
}

0 commit comments

Comments
 (0)