diff --git a/CHANGELOG.md b/CHANGELOG.md index c32f275..01855bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ - Add `WebTransport` instance for `Multiaddr`. See [PR 70]. - Disable all features of `multihash`. See [PR 77]. +- Make `Error` an opaque struct. See [PR 81]. [PR 70]: https://github.com/multiformats/rust-multiaddr/pull/70 [PR 77]: https://github.com/multiformats/rust-multiaddr/pull/77 +[PR 81]: https://github.com/multiformats/rust-multiaddr/pull/81 # 0.17.0 diff --git a/src/errors.rs b/src/errors.rs index d3ded90..1c41d45 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,12 +1,60 @@ use std::{error, fmt, io, net, num, str, string}; use unsigned_varint::decode; +#[deprecated(note = "Use `Result` instead.")] pub type Result = ::std::result::Result; +#[derive(Debug)] +pub struct Error { + kind: Kind, +} + +impl Error { + pub fn is_unknown_protocol(&self) -> bool { + matches!(self.kind, Kind::UnknownProtocolString(_)) + } + + pub(crate) fn data_less_than_len() -> Self { + Self { + kind: Kind::DataLessThanLen, + } + } + + pub(crate) fn invalid_multiaddr() -> Self { + Self { + kind: Kind::InvalidMultiaddr, + } + } + + pub(crate) fn invalid_protocol_string() -> Self { + Self { + kind: Kind::InvalidProtocolString, + } + } + + pub(crate) fn unknown_protocol_id(id: u32) -> Self { + Self { + kind: Kind::UnknownProtocolId(id), + } + } + + pub(crate) fn unknown_protocol_string(id: String) -> Self { + Self { + kind: Kind::UnknownProtocolString(id), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + /// Error types #[derive(Debug)] #[non_exhaustive] -pub enum Error { +enum Kind { DataLessThanLen, InvalidMultiaddr, InvalidProtocolString, @@ -16,16 +64,16 @@ pub enum Error { UnknownProtocolString(String), } -impl fmt::Display for Error { +impl fmt::Display for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::DataLessThanLen => f.write_str("we have less data than indicated by length"), - Error::InvalidMultiaddr => f.write_str("invalid multiaddr"), - Error::InvalidProtocolString => f.write_str("invalid protocol string"), - Error::InvalidUvar(e) => write!(f, "failed to decode unsigned varint: {e}"), - Error::ParsingError(e) => write!(f, "failed to parse: {e}"), - Error::UnknownProtocolId(id) => write!(f, "unknown protocol id: {id}"), - Error::UnknownProtocolString(string) => { + Kind::DataLessThanLen => f.write_str("we have less data than indicated by length"), + Kind::InvalidMultiaddr => f.write_str("invalid multiaddr"), + Kind::InvalidProtocolString => f.write_str("invalid protocol string"), + Kind::InvalidUvar(e) => write!(f, "failed to decode unsigned varint: {e}"), + Kind::ParsingError(e) => write!(f, "failed to parse: {e}"), + Kind::UnknownProtocolId(id) => write!(f, "unknown protocol id: {id}"), + Kind::UnknownProtocolString(string) => { write!(f, "unknown protocol string: {string}") } } @@ -35,58 +83,78 @@ impl fmt::Display for Error { impl error::Error for Error { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { - if let Error::ParsingError(e) = self { - Some(&**e) - } else { - None + match &self.kind { + Kind::DataLessThanLen => None, + Kind::InvalidMultiaddr => None, + Kind::InvalidProtocolString => None, + Kind::InvalidUvar(inner) => Some(inner), + Kind::ParsingError(inner) => Some(inner.as_ref()), + Kind::UnknownProtocolId(_) => None, + Kind::UnknownProtocolString(_) => None, } } } impl From for Error { fn from(err: io::Error) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: multihash::Error) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: multibase::Error) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: net::AddrParseError) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: num::ParseIntError) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: string::FromUtf8Error) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(err: str::Utf8Error) -> Error { - Error::ParsingError(err.into()) + Error { + kind: Kind::ParsingError(err.into()), + } } } impl From for Error { fn from(e: decode::Error) -> Error { - Error::InvalidUvar(e) + Error { + kind: Kind::InvalidUvar(e), + } } } diff --git a/src/lib.rs b/src/lib.rs index e79e385..83af38e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ mod protocol; #[cfg(feature = "url")] mod from_url; +#[allow(deprecated)] pub use self::errors::{Error, Result}; pub use self::onion_addr::Onion3Addr; pub use self::protocol::Protocol; @@ -260,13 +261,13 @@ impl<'a> FromIterator> for Multiaddr { impl FromStr for Multiaddr { type Err = Error; - fn from_str(input: &str) -> Result { + fn from_str(input: &str) -> std::result::Result { let mut writer = Vec::new(); let mut parts = input.split('/').peekable(); if Some("") != parts.next() { // A multiaddr must start with `/` - return Err(Error::InvalidMultiaddr); + return Err(Error::invalid_multiaddr()); } while parts.peek().is_some() { @@ -345,7 +346,7 @@ impl From for Multiaddr { impl TryFrom> for Multiaddr { type Error = Error; - fn try_from(v: Vec) -> Result { + fn try_from(v: Vec) -> std::result::Result { // Check if the argument is a valid `Multiaddr` by reading its protocols. let mut slice = &v[..]; while !slice.is_empty() { @@ -359,7 +360,7 @@ impl TryFrom> for Multiaddr { impl TryFrom for Multiaddr { type Error = Error; - fn try_from(s: String) -> Result { + fn try_from(s: String) -> std::result::Result { s.parse() } } @@ -367,7 +368,7 @@ impl TryFrom for Multiaddr { impl<'a> TryFrom<&'a str> for Multiaddr { type Error = Error; - fn try_from(s: &'a str) -> Result { + fn try_from(s: &'a str) -> std::result::Result { s.parse() } } diff --git a/src/protocol.rs b/src/protocol.rs index a70a40d..b0d6094 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,5 +1,5 @@ use crate::onion_addr::Onion3Addr; -use crate::{Error, Result}; +use crate::Error; use arrayref::array_ref; use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; use data_encoding::BASE32; @@ -122,61 +122,61 @@ impl<'a> Protocol<'a> { /// produce a well-formed protocol. The same iterator can thus be used to parse /// a sequence of protocols in succession. It is up to client code to check /// that iteration has finished whenever appropriate. - pub fn from_str_parts(mut iter: I) -> Result + pub fn from_str_parts(mut iter: I) -> Result where I: Iterator, { - match iter.next().ok_or(Error::InvalidProtocolString)? { + match iter.next().ok_or(Error::invalid_protocol_string())? { "ip4" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Ip4(Ipv4Addr::from_str(s)?)) } "tcp" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Tcp(s.parse()?)) } "tls" => Ok(Protocol::Tls), "noise" => Ok(Protocol::Noise), "udp" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Udp(s.parse()?)) } "dccp" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Dccp(s.parse()?)) } "ip6" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?)) } "dns" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Dns(Cow::Borrowed(s))) } "dns4" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Dns4(Cow::Borrowed(s))) } "dns6" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Dns6(Cow::Borrowed(s))) } "dnsaddr" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Dnsaddr(Cow::Borrowed(s))) } "sctp" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Sctp(s.parse()?)) } "udt" => Ok(Protocol::Udt), "utp" => Ok(Protocol::Utp), "unix" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Unix(Cow::Borrowed(s))) } "p2p" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; let decoded = multibase::Base::Base58Btc.decode(s)?; Ok(Protocol::P2p(Multihash::from_bytes(&decoded)?)) } @@ -184,12 +184,12 @@ impl<'a> Protocol<'a> { "https" => Ok(Protocol::Https), "onion" => iter .next() - .ok_or(Error::InvalidProtocolString) + .ok_or(Error::invalid_protocol_string()) .and_then(|s| read_onion(&s.to_uppercase())) .map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)), "onion3" => iter .next() - .ok_or(Error::InvalidProtocolString) + .ok_or(Error::invalid_protocol_string()) .and_then(|s| read_onion3(&s.to_uppercase())) .map(|(a, p)| Protocol::Onion3((a, p).into())), "quic" => Ok(Protocol::Quic), @@ -198,12 +198,12 @@ impl<'a> Protocol<'a> { "ws" => Ok(Protocol::Ws(Cow::Borrowed("/"))), "wss" => Ok(Protocol::Wss(Cow::Borrowed("/"))), "x-parity-ws" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?; Ok(Protocol::Ws(decoded)) } "x-parity-wss" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?; Ok(Protocol::Wss(decoded)) } @@ -211,26 +211,26 @@ impl<'a> Protocol<'a> { "p2p-webrtc-star" => Ok(Protocol::P2pWebRtcStar), "webrtc" => Ok(Protocol::WebRTC), "certhash" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; let (_base, decoded) = multibase::decode(s)?; Ok(Protocol::Certhash(Multihash::from_bytes(&decoded)?)) } "p2p-webrtc-direct" => Ok(Protocol::P2pWebRtcDirect), "p2p-circuit" => Ok(Protocol::P2pCircuit), "memory" => { - let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let s = iter.next().ok_or(Error::invalid_protocol_string())?; Ok(Protocol::Memory(s.parse()?)) } - unknown => Err(Error::UnknownProtocolString(unknown.to_string())), + unknown => Err(Error::unknown_protocol_string(unknown.to_string())), } } /// Parse a single `Protocol` value from its byte slice representation, /// returning the protocol as well as the remaining byte slice. - pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8])> { - fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8])> { + pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8]), Error> { + fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8]), Error> { if input.len() < n { - return Err(Error::DataLessThanLen); + return Err(Error::data_less_than_len()); } Ok(input.split_at(n)) } @@ -368,13 +368,13 @@ impl<'a> Protocol<'a> { let (data, rest) = split_at(n, input)?; Ok((Protocol::Wss(Cow::Borrowed(str::from_utf8(data)?)), rest)) } - _ => Err(Error::UnknownProtocolId(id)), + _ => Err(Error::unknown_protocol_id(id)), } } /// Encode this protocol by writing its binary representation into /// the given `Write` impl. - pub fn write_bytes(&self, w: &mut W) -> Result<()> { + pub fn write_bytes(&self, w: &mut W) -> Result<(), Error> { let mut buf = encode::u32_buffer(); match self { Protocol::Ip4(addr) => { @@ -643,43 +643,43 @@ impl<'a> From for Protocol<'a> { macro_rules! read_onion_impl { ($name:ident, $len:expr, $encoded_len:expr) => { - fn $name(s: &str) -> Result<([u8; $len], u16)> { + fn $name(s: &str) -> Result<([u8; $len], u16), Error> { let mut parts = s.split(':'); // address part (without ".onion") - let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?; + let b32 = parts.next().ok_or(Error::invalid_multiaddr())?; if b32.len() != $encoded_len { - return Err(Error::InvalidMultiaddr); + return Err(Error::invalid_multiaddr()); } // port number let port = parts .next() - .ok_or(Error::InvalidMultiaddr) + .ok_or(Error::invalid_multiaddr()) .and_then(|p| str::parse(p).map_err(From::from))?; // port == 0 is not valid for onion if port == 0 { - return Err(Error::InvalidMultiaddr); + return Err(Error::invalid_multiaddr()); } // nothing else expected if parts.next().is_some() { - return Err(Error::InvalidMultiaddr); + return Err(Error::invalid_multiaddr()); } if $len != BASE32 .decode_len(b32.len()) - .map_err(|_| Error::InvalidMultiaddr)? + .map_err(|_| Error::invalid_multiaddr())? { - return Err(Error::InvalidMultiaddr); + return Err(Error::invalid_multiaddr()); } let mut buf = [0u8; $len]; BASE32 .decode_mut(b32.as_bytes(), &mut buf) - .map_err(|_| Error::InvalidMultiaddr)?; + .map_err(|_| Error::invalid_multiaddr())?; Ok((buf, port)) } diff --git a/tests/lib.rs b/tests/lib.rs index fbea090..e4b044f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -555,12 +555,9 @@ fn replace_ip4_with_ip6() { fn unknown_protocol_string() { match "/unknown/1.2.3.4".parse::() { Ok(_) => panic!("The UnknownProtocolString error should be caused"), - Err(e) => match e { - crate::Error::UnknownProtocolString(protocol) => { - assert_eq!(protocol, "unknown") - } - _ => panic!("The UnknownProtocolString error should be caused"), - }, + Err(e) => { + assert!(e.is_unknown_protocol()) + } } }