Skip to content

Commit 0ec6662

Browse files
committed
Added delegates
Implement and delegate a lot of std::string::String methods. Also clean up the README.
1 parent 99a576d commit 0ec6662

File tree

4 files changed

+150
-9
lines changed

4 files changed

+150
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 5 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 53 additions & 2 deletions
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

Lines changed: 93 additions & 2 deletions
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;
@@ -29,6 +41,77 @@ impl NonEmptyString {
2941
pub fn into_inner(self) -> String {
3042
self.0
3143
}
44+
45+
// These are safe methods that can simply be forwarded.
46+
delegate! {
47+
to self.0 {
48+
/// Is forwarded to the inner String.
49+
/// See [`std::string::String::into_bytes`]
50+
pub fn into_bytes(self) -> Vec<u8>;
51+
52+
/// Is forwarded to the inner String.
53+
/// See [`std::string::String::as_str`]
54+
pub fn as_str(&self) -> &str;
55+
56+
/// Is forwarded to the inner String.
57+
/// See [`std::string::String::push_str`]
58+
pub fn push_str(&mut self, string: &str);
59+
60+
/// Is forwarded to the inner String.
61+
/// See [`std::string::String::capacity`]
62+
pub fn capacity(&self) -> usize;
63+
64+
/// Is forwarded to the inner String.
65+
/// See [`std::string::String::reserve`]
66+
pub fn reserve(&mut self, additional: usize);
67+
68+
/// Is forwarded to the inner String.
69+
/// See [`std::string::String::reserve_exact`]
70+
pub fn reserve_exact(&mut self, additional: usize);
71+
72+
// For some reason we cannot delegate the following:
73+
// pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
74+
75+
/// Is forwarded to the inner String.
76+
/// See [`std::string::String::try_reserve_exact`]
77+
pub fn try_reserve_exact(
78+
&mut self,
79+
additional: usize
80+
) -> Result<(), std::collections::TryReserveError>;
81+
82+
/// Is forwarded to the inner String.
83+
/// See std::string::String::[`(&`]
84+
pub fn shrink_to_fit(&mut self);
85+
86+
/// Is forwarded to the inner String.
87+
/// See [`std::string::String::shrink_to`]
88+
pub fn shrink_to(&mut self, min_capacity: usize);
89+
90+
/// Is forwarded to the inner String.
91+
/// See [`std::string::String::push`]
92+
pub fn push(&mut self, ch: char);
93+
94+
/// Is forwarded to the inner String.
95+
/// See [`std::string::String::as_bytes`]
96+
pub fn as_bytes(&self) -> &[u8];
97+
98+
/// Is forwarded to the inner String.
99+
/// See [`std::string::String::insert`]
100+
pub fn insert(&mut self, idx: usize, ch: char);
101+
102+
/// Is forwarded to the inner String.
103+
/// See [`std::string::String::insert_str`]
104+
pub fn insert_str(&mut self, idx: usize, string: &str);
105+
106+
/// Is forwarded to the inner String.
107+
/// See [`std::string::String::len`]
108+
pub fn len(&self) -> usize;
109+
110+
/// Is forwarded to the inner String.
111+
/// See [`std::string::String::into_boxed_str`]
112+
pub fn into_boxed_str(self) -> Box<str>;
113+
}
114+
}
32115
}
33116

34117
impl std::convert::AsRef<str> for NonEmptyString {
@@ -55,6 +138,14 @@ impl<'s> std::convert::TryFrom<&'s str> for NonEmptyString {
55138
}
56139
}
57140

141+
impl std::convert::TryFrom<String> for NonEmptyString {
142+
type Error = String;
143+
144+
fn try_from(value: String) -> Result<Self, Self::Error> {
145+
NonEmptyString::new(value)
146+
}
147+
}
148+
58149
#[cfg(test)]
59150
mod tests {
60151
use super::*;
@@ -98,6 +189,6 @@ mod tests {
98189
fn calling_string_methods_works() {
99190
let nes = NonEmptyString::new("string".to_owned()).unwrap();
100191
// `len` is a `String` method.
101-
assert!(nes.get().len() > 0);
192+
assert!(nes.len() > 0);
102193
}
103194
}

0 commit comments

Comments
 (0)