Merge rust-bitcoin/rust-bitcoin#1137: Impl string conversion traits for `CommandString`

405be52f5c Impl string conversion traits for `CommandString` (Martin Habovstiak)

Pull request description:

  After MSRV bump `try_from` usually refers to `TryFrom` method but
  `CommandString` used inherent one making it confusing and non-idiomatic.

  This implements `FromStr` and `TryFrom<{stringly type}>` for
  `CommandString` and deprecates the inherent method in favor of those.
  To keep the code using `&'static str` efficient it provides
  `try_from_static` inherent method that only converts from
  `&'static str`.

  Closes #1135

ACKs for top commit:
  sanket1729:
    utACK 405be52f5c
  tcharding:
    ACK 405be52f5c

Tree-SHA512: 754fc960a4bc5c096cccf47b85a620e33fcf863f3c57ea113eae91cd34006168113dd1efc47231e79e6e237e2fc412890cc9e8a72d4cfc633bfebbecdc4610e6
This commit is contained in:
Andrew Poelstra 2022-07-29 14:02:30 +00:00
commit f54fe69c8d
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 55 additions and 5 deletions

View File

@ -10,6 +10,7 @@
use crate::prelude::*; use crate::prelude::*;
use core::{fmt, iter}; use core::{fmt, iter};
use core::convert::TryFrom;
use crate::io; use crate::io;
use io::Read as _; use io::Read as _;
@ -46,8 +47,25 @@ impl CommandString {
/// ///
/// Returns an error if and only if the string is /// Returns an error if and only if the string is
/// larger than 12 characters in length. /// larger than 12 characters in length.
#[deprecated(note = "Use `TryFrom::try_from` or `CommandString::try_from_static`", since = "0.29.0")]
pub fn try_from<S: Into<Cow<'static, str>>>(s: S) -> Result<CommandString, CommandStringError> { pub fn try_from<S: Into<Cow<'static, str>>>(s: S) -> Result<CommandString, CommandStringError> {
let cow = s.into(); Self::try_from_static_cow(s.into())
}
/// Convert `&'static str` to `CommandString`
///
/// This is more efficient for string literals than non-static conversions because it avoids
/// allocation.
///
/// # Errors
///
/// Returns an error if and only if the string is
/// larger than 12 characters in length.
pub fn try_from_static(s: &'static str) -> Result<CommandString, CommandStringError> {
Self::try_from_static_cow(s.into())
}
fn try_from_static_cow(cow: Cow<'static, str>) -> Result<CommandString, CommandStringError> {
if cow.len() > 12 { if cow.len() > 12 {
Err(CommandStringError { cow }) Err(CommandStringError { cow })
} else { } else {
@ -56,6 +74,38 @@ impl CommandString {
} }
} }
impl TryFrom<String> for CommandString {
type Error = CommandStringError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from_static_cow(value.into())
}
}
impl TryFrom<Box<str>> for CommandString {
type Error = CommandStringError;
fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
Self::try_from_static_cow(String::from(value).into())
}
}
impl<'a> TryFrom<&'a str> for CommandString {
type Error = CommandStringError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Self::try_from_static_cow(value.to_owned().into())
}
}
impl core::str::FromStr for CommandString {
type Err = CommandStringError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_static_cow(s.to_owned().into())
}
}
impl fmt::Display for CommandString { impl fmt::Display for CommandString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.0.as_ref()) f.write_str(self.0.as_ref())
@ -264,7 +314,7 @@ impl NetworkMessage {
pub fn command(&self) -> CommandString { pub fn command(&self) -> CommandString {
match *self { match *self {
NetworkMessage::Unknown { command: ref c, .. } => c.clone(), NetworkMessage::Unknown { command: ref c, .. } => c.clone(),
_ => CommandString::try_from(self.cmd()).expect("cmd returns valid commands") _ => CommandString::try_from_static(self.cmd()).expect("cmd returns valid commands")
} }
} }
} }
@ -523,8 +573,8 @@ mod test {
#[test] #[test]
fn commandstring_test() { fn commandstring_test() {
// Test converting. // Test converting.
assert_eq!(CommandString::try_from("AndrewAndrew").unwrap().as_ref(), "AndrewAndrew"); assert_eq!(CommandString::try_from_static("AndrewAndrew").unwrap().as_ref(), "AndrewAndrew");
assert!(CommandString::try_from("AndrewAndrewA").is_err()); assert!(CommandString::try_from_static("AndrewAndrewA").is_err());
// Test serializing. // Test serializing.
let cs = CommandString("Andrew".into()); let cs = CommandString("Andrew".into());
@ -534,7 +584,7 @@ mod test {
let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]); let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
assert!(cs.is_ok()); assert!(cs.is_ok());
assert_eq!(cs.as_ref().unwrap().to_string(), "Andrew".to_owned()); assert_eq!(cs.as_ref().unwrap().to_string(), "Andrew".to_owned());
assert_eq!(cs.unwrap(), CommandString::try_from("Andrew").unwrap()); assert_eq!(cs.unwrap(), CommandString::try_from_static("Andrew").unwrap());
let short_cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]); let short_cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
assert!(short_cs.is_err()); assert!(short_cs.is_err());