Skip to content

Commit 8c721b5

Browse files
authored
mcf: remove lifetime from PasswordHashRef (#2116)
NOTE: breaking change Changes the `PasswordHashRef` type into a `repr(transparent)` newtype for `str`, constructing it as `&PasswordHashRef` using an unsafe pointer cast which is only invoked after first validating it's well-formed. This makes it possible to impl `Borrow<PasswordHashRef>` on `PasswordHash` and `ToOwned` on `PasswordHashRef`. `AsRef` and `Deref` impls have been added to `PasswordHash` for obtaining a `&PasswordHashRef`, and the inherent methods which were duplicated on both `PasswordHashRef` and `PasswordHash` have been removed, following a similar pattern to `str`/`String`. The `PasswordHash::as_mcf_hash_ref` method has been removed in favor of using any of `AsRef`, `Borrow`, or `Deref` to do the conversion.
1 parent 9168a02 commit 8c721b5

File tree

1 file changed

+91
-55
lines changed

1 file changed

+91
-55
lines changed

mcf/src/lib.rs

Lines changed: 91 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
66
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
77
)]
8-
#![forbid(unsafe_code)]
8+
#![deny(unsafe_code)]
99
#![warn(
1010
clippy::mod_module_files,
1111
clippy::unwrap_used,
@@ -36,23 +36,34 @@ const INVARIANT_MSG: &str = "should be ensured valid by constructor";
3636
/// e.g. `$<id>$...`.
3737
///
3838
/// For more information, see [`PasswordHash`].
39-
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
40-
pub struct PasswordHashRef<'a>(&'a str);
39+
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
40+
#[repr(transparent)]
41+
pub struct PasswordHashRef(str);
4142

42-
impl<'a> PasswordHashRef<'a> {
43+
impl PasswordHashRef {
4344
/// Parse the given input string, returning an [`PasswordHashRef`] if valid.
44-
pub fn new(s: &'a str) -> Result<Self> {
45+
pub fn new(s: &str) -> Result<&PasswordHashRef> {
4546
validate(s)?;
46-
Ok(Self(s))
47+
Ok(Self::new_unchecked(s))
48+
}
49+
50+
/// Construct a new [`PasswordHashRef`] string from the given input `str` reference without
51+
/// first asserting its validity.
52+
fn new_unchecked(s: &str) -> &PasswordHashRef {
53+
// SAFETY: `Self` is a `repr(transparent)` newtype for `str`
54+
#[allow(unsafe_code)]
55+
unsafe {
56+
&*(s as *const str as *const Self)
57+
}
4758
}
4859

4960
/// Get the contained string as a `str`.
50-
pub fn as_str(self) -> &'a str {
51-
self.0
61+
pub fn as_str(&self) -> &str {
62+
&self.0
5263
}
5364

5465
/// Get the algorithm identifier for this MCF hash.
55-
pub fn id(self) -> &'a str {
66+
pub fn id(&self) -> &str {
5667
Fields::new(self.as_str())
5768
.next()
5869
.expect(INVARIANT_MSG)
@@ -61,7 +72,7 @@ impl<'a> PasswordHashRef<'a> {
6172

6273
/// Get an iterator over the parts of the password hash as delimited by `$`, excluding the
6374
/// initial identifier.
64-
pub fn fields(self) -> Fields<'a> {
75+
pub fn fields(&self) -> Fields<'_> {
6576
let mut fields = Fields::new(self.as_str());
6677

6778
// Remove the leading identifier
@@ -72,38 +83,40 @@ impl<'a> PasswordHashRef<'a> {
7283
}
7384
}
7485

75-
impl fmt::Display for PasswordHashRef<'_> {
76-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77-
f.write_str(self.as_str())
86+
impl AsRef<str> for &PasswordHashRef {
87+
fn as_ref(&self) -> &str {
88+
self.as_str()
7889
}
7990
}
8091

81-
impl<'a> From<PasswordHashRef<'a>> for &'a str {
82-
fn from(hash: PasswordHashRef<'a>) -> &'a str {
83-
hash.0
92+
impl fmt::Display for PasswordHashRef {
93+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94+
f.write_str(self.as_str())
8495
}
8596
}
8697

87-
#[cfg(feature = "alloc")]
88-
impl From<PasswordHashRef<'_>> for alloc::string::String {
89-
fn from(hash: PasswordHashRef<'_>) -> Self {
90-
hash.0.into()
98+
impl<'a> From<&'a PasswordHashRef> for &'a str {
99+
fn from(hash: &'a PasswordHashRef) -> &'a str {
100+
hash.as_str()
91101
}
92102
}
93103

94-
impl<'a> TryFrom<&'a str> for PasswordHashRef<'a> {
104+
impl<'a> TryFrom<&'a str> for &'a PasswordHashRef {
95105
type Error = Error;
96106

97107
fn try_from(s: &'a str) -> Result<Self> {
98-
Self::new(s)
108+
PasswordHashRef::new(s)
99109
}
100110
}
101111

102112
#[cfg(feature = "alloc")]
103113
mod allocating {
104-
use crate::{Error, Field, Fields, PasswordHashRef, Result, fields, validate, validate_id};
105-
use alloc::string::{String, ToString};
106-
use core::{fmt, str};
114+
use crate::{Error, Field, PasswordHashRef, Result, fields, validate, validate_id};
115+
use alloc::{
116+
borrow::ToOwned,
117+
string::{String, ToString},
118+
};
119+
use core::{borrow::Borrow, fmt, ops::Deref, str::FromStr};
107120

108121
#[cfg(feature = "base64")]
109122
use crate::Base64;
@@ -152,27 +165,6 @@ mod allocating {
152165
Ok(Self(hash))
153166
}
154167

155-
/// Get the contained string as a `str`.
156-
pub fn as_str(&self) -> &str {
157-
&self.0
158-
}
159-
160-
/// Get an [`PasswordHashRef`] which corresponds to this owned [`PasswordHash`].
161-
pub fn as_mcf_hash_ref(&self) -> PasswordHashRef<'_> {
162-
PasswordHashRef(self.as_str())
163-
}
164-
165-
/// Get the algorithm identifier for this MCF hash.
166-
pub fn id(&self) -> &str {
167-
self.as_mcf_hash_ref().id()
168-
}
169-
170-
/// Get an iterator over the parts of the password hash as delimited by `$`, excluding the
171-
/// initial identifier.
172-
pub fn fields(&self) -> Fields<'_> {
173-
self.as_mcf_hash_ref().fields()
174-
}
175-
176168
/// Encode the given data as the specified variant of Base64 and push it onto the password
177169
/// hash string, first adding a `$` delimiter.
178170
#[cfg(feature = "base64")]
@@ -207,15 +199,29 @@ mod allocating {
207199
}
208200
}
209201

210-
impl<'a> AsRef<str> for PasswordHashRef<'a> {
202+
impl AsRef<str> for PasswordHash {
211203
fn as_ref(&self) -> &str {
212-
self.as_str()
204+
self.0.as_str()
213205
}
214206
}
215207

216-
impl AsRef<str> for PasswordHash {
217-
fn as_ref(&self) -> &str {
218-
self.as_str()
208+
impl AsRef<PasswordHashRef> for PasswordHash {
209+
fn as_ref(&self) -> &PasswordHashRef {
210+
PasswordHashRef::new_unchecked(&self.0)
211+
}
212+
}
213+
214+
impl Borrow<PasswordHashRef> for PasswordHash {
215+
fn borrow(&self) -> &PasswordHashRef {
216+
self.as_ref()
217+
}
218+
}
219+
220+
impl Deref for PasswordHash {
221+
type Target = PasswordHashRef;
222+
223+
fn deref(&self) -> &PasswordHashRef {
224+
self.as_ref()
219225
}
220226
}
221227

@@ -225,6 +231,14 @@ mod allocating {
225231
}
226232
}
227233

234+
impl FromStr for PasswordHash {
235+
type Err = Error;
236+
237+
fn from_str(s: &str) -> Result<Self> {
238+
Self::new(s)
239+
}
240+
}
241+
228242
impl TryFrom<String> for PasswordHash {
229243
type Error = Error;
230244

@@ -247,11 +261,33 @@ mod allocating {
247261
}
248262
}
249263

250-
impl str::FromStr for PasswordHash {
251-
type Err = Error;
264+
//
265+
// PasswordHashRef extensions
266+
//
252267

253-
fn from_str(s: &str) -> Result<Self> {
254-
Self::new(s)
268+
impl<'a> From<&'a PasswordHash> for &'a PasswordHashRef {
269+
fn from(hash: &'a PasswordHash) -> &'a PasswordHashRef {
270+
hash.as_ref()
271+
}
272+
}
273+
274+
impl From<&PasswordHashRef> for PasswordHash {
275+
fn from(hash: &PasswordHashRef) -> Self {
276+
PasswordHash(hash.into())
277+
}
278+
}
279+
280+
impl From<&PasswordHashRef> for String {
281+
fn from(hash: &PasswordHashRef) -> Self {
282+
hash.0.into()
283+
}
284+
}
285+
286+
impl ToOwned for PasswordHashRef {
287+
type Owned = PasswordHash;
288+
289+
fn to_owned(&self) -> PasswordHash {
290+
self.into()
255291
}
256292
}
257293
}

0 commit comments

Comments
 (0)