Skip to content

Commit f1d9876

Browse files
authored
Merge pull request #7 from MidasLamb/forward-to-string
Added delegates
2 parents 99a576d + f8ab5ac commit f1d9876

File tree

4 files changed

+151
-9
lines changed

4 files changed

+151
-9
lines changed

CHANGELOG.md

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
### Added
9+
* Add & delegate all non-length-reducing methods of `std::string::String` to the inner `String`.
910

1011
### Changed
12+
* README has some more examples and explanations. It is also no longer included in the doc (except for doctests).
1113

1214
### Removed
1315

1416
## [0.2.1]
15-
### Added
16-
1717
### Changed
1818
* The error message when using `serde` now indicates that the empty string could not be deserialized.
1919
* Bumped rust edition to `2021`
2020

21-
### Removed
2221
## [0.2.0]
2322
### Added
2423
* `serde` support behind the `serde` feature flag.
@@ -28,8 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2827
### Changed
2928
* `new` constructor now returns a `Result` rather than an `Option`, which contains the original string
3029

31-
### Removed
32-
3330
[Unreleased]: https://github.com/MidasLamb/non-empty-string/v0.2.1...HEAD
3431
[0.2.1]: https://github.com/MidasLamb/non-empty-string/compare/v0.2.0...v0.2.1
3532
[0.2.0]: https://github.com/MidasLamb/non-empty-string/compare/v0.1.0...v0.2.0

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ name = "non_empty_string"
1313

1414
[dependencies]
1515
serde = { version = "1", optional = true }
16+
delegate = { version = "0.8" }
1617

1718
[dev-dependencies]
1819
assert_matches = "1.5.0"
1920
serde_json = { version = "1" }
21+
serde = { version = "1", features = ["derive"] }
2022

2123
[features]
2224
default = []

README.md

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,55 @@
1-
# NonEmptyString
1+
# Non Empty String
2+
3+
[![Crates.io](https://img.shields.io/crates/v/non-empty-string.svg)](https://crates.io/crates/non-empty-string)
4+
[![Documentation](https://docs.rs/non-empty-string/badge.svg)](https://docs.rs/non-empty-string/)
5+
26
A simple wrapper type for `String`s that ensures that the string inside is not `.empty()`, meaning that the length > 0.
37

8+
## Example
9+
10+
```rust
11+
use non_empty_string::NonEmptyString;
12+
13+
// Constructing it from a normal (non-zero-length) String works fine.
14+
let s = "A string with a length".to_owned();
15+
assert!(NonEmptyString::new(s).is_ok());
16+
17+
// But constructing it from a non-zero-length String results in an `Err`, where we get the `String` back that we passed in.
18+
let empty = "".to_owned();
19+
let result = NonEmptyString::new(empty);
20+
assert!(result.is_err());
21+
assert_eq!(result.unwrap_err(), "".to_owned())
22+
23+
```
24+
25+
## Methods of std::string::String
26+
`NonEmptyString` implements a subset of the functions of `std::string::String`, only the ones which are guaranteed to leave the `NonEmptyString` in a valid state.
27+
This means i.e. `push()` is implemented, but `pop()` is not.
28+
29+
This allows you to mostly treat it as a String without having to constantly turn it into the inner `String` before you can do any sort of operation on it and then having to reconstruct a `NonEmptyString` afterwards.
30+
31+
32+
## Serde Support
33+
34+
[serde] support is available behind the `serde` feature flag:
35+
```toml
36+
[dependencies]
37+
serde = { version = "1", features = ["derive"] }
38+
non-empty-string = { version = "*", features = ["serde"]}
39+
```
40+
41+
Afterwards you can use it in a struct:
42+
```rust
43+
use serde::{Serialize, Deserialize};
44+
use non_empty_string::NonEmptyString;
45+
46+
#[derive(Serialize, Deserialize)]
47+
struct MyApiObject {
48+
username: NonEmptyString,
49+
}
50+
```
51+
52+
Deserialization will fail if the field is present as a String, but the length of the `String` is 0.
453

554
## License
655

@@ -17,4 +66,6 @@ at your option.
1766

1867
Unless you explicitly state otherwise, any contribution intentionally submitted
1968
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
20-
dual licensed as above, without any additional terms or conditions.
69+
dual licensed as above, without any additional terms or conditions.
70+
71+
[serde]: https://docs.rs/serde

src/lib.rs

+94-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
#![doc = include_str!("../README.md")]
1+
#![doc(issue_tracker_base_url = "https://github.com/MidasLamb/non-empty-string/issues/")]
2+
3+
//! # NonEmptyString
4+
//! A simple wrapper type for `String`s that ensures that the string inside is not `.empty()`, meaning that the length > 0.
5+
6+
// Test the items in the readme file.
7+
#[cfg(doctest)]
8+
mod test_readme {
9+
#[doc = include_str!("../README.md")]
10+
mod something {}
11+
}
12+
13+
use delegate::delegate;
214

315
#[cfg(feature = "serde")]
416
mod serde_support;
@@ -9,6 +21,7 @@ mod serde_support;
921
#[repr(transparent)]
1022
pub struct NonEmptyString(String);
1123

24+
#[allow(clippy::len_without_is_empty)] // is_empty would always returns false so it seems a bit silly to have it.
1225
impl NonEmptyString {
1326
/// Attempts to create a new NonEmptyString.
1427
/// If the given `string` is empty, `Err` is returned, containing the original `String`, `Ok` otherwise.
@@ -29,6 +42,77 @@ impl NonEmptyString {
2942
pub fn into_inner(self) -> String {
3043
self.0
3144
}
45+
46+
// These are safe methods that can simply be forwarded.
47+
delegate! {
48+
to self.0 {
49+
/// Is forwarded to the inner String.
50+
/// See [`std::string::String::into_bytes`]
51+
pub fn into_bytes(self) -> Vec<u8>;
52+
53+
/// Is forwarded to the inner String.
54+
/// See [`std::string::String::as_str`]
55+
pub fn as_str(&self) -> &str;
56+
57+
/// Is forwarded to the inner String.
58+
/// See [`std::string::String::push_str`]
59+
pub fn push_str(&mut self, string: &str);
60+
61+
/// Is forwarded to the inner String.
62+
/// See [`std::string::String::capacity`]
63+
pub fn capacity(&self) -> usize;
64+
65+
/// Is forwarded to the inner String.
66+
/// See [`std::string::String::reserve`]
67+
pub fn reserve(&mut self, additional: usize);
68+
69+
/// Is forwarded to the inner String.
70+
/// See [`std::string::String::reserve_exact`]
71+
pub fn reserve_exact(&mut self, additional: usize);
72+
73+
// For some reason we cannot delegate the following:
74+
// pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
75+
76+
/// Is forwarded to the inner String.
77+
/// See [`std::string::String::try_reserve_exact`]
78+
pub fn try_reserve_exact(
79+
&mut self,
80+
additional: usize
81+
) -> Result<(), std::collections::TryReserveError>;
82+
83+
/// Is forwarded to the inner String.
84+
/// See std::string::String::[`(&`]
85+
pub fn shrink_to_fit(&mut self);
86+
87+
/// Is forwarded to the inner String.
88+
/// See [`std::string::String::shrink_to`]
89+
pub fn shrink_to(&mut self, min_capacity: usize);
90+
91+
/// Is forwarded to the inner String.
92+
/// See [`std::string::String::push`]
93+
pub fn push(&mut self, ch: char);
94+
95+
/// Is forwarded to the inner String.
96+
/// See [`std::string::String::as_bytes`]
97+
pub fn as_bytes(&self) -> &[u8];
98+
99+
/// Is forwarded to the inner String.
100+
/// See [`std::string::String::insert`]
101+
pub fn insert(&mut self, idx: usize, ch: char);
102+
103+
/// Is forwarded to the inner String.
104+
/// See [`std::string::String::insert_str`]
105+
pub fn insert_str(&mut self, idx: usize, string: &str);
106+
107+
/// Is forwarded to the inner String.
108+
/// See [`std::string::String::len`]
109+
pub fn len(&self) -> usize;
110+
111+
/// Is forwarded to the inner String.
112+
/// See [`std::string::String::into_boxed_str`]
113+
pub fn into_boxed_str(self) -> Box<str>;
114+
}
115+
}
32116
}
33117

34118
impl std::convert::AsRef<str> for NonEmptyString {
@@ -55,6 +139,14 @@ impl<'s> std::convert::TryFrom<&'s str> for NonEmptyString {
55139
}
56140
}
57141

142+
impl std::convert::TryFrom<String> for NonEmptyString {
143+
type Error = String;
144+
145+
fn try_from(value: String) -> Result<Self, Self::Error> {
146+
NonEmptyString::new(value)
147+
}
148+
}
149+
58150
#[cfg(test)]
59151
mod tests {
60152
use super::*;
@@ -98,6 +190,6 @@ mod tests {
98190
fn calling_string_methods_works() {
99191
let nes = NonEmptyString::new("string".to_owned()).unwrap();
100192
// `len` is a `String` method.
101-
assert!(nes.get().len() > 0);
193+
assert!(nes.len() > 0);
102194
}
103195
}

0 commit comments

Comments
 (0)