Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,20 @@ impl Multiaddr {
pub fn protocol_stack(&self) -> ProtoStackIter<'_> {
ProtoStackIter { parts: self.iter() }
}

// Returns the IP address of this multiaddress, if it has one.
///
/// Iterates the components of this multiaddress and returns the first
/// [`Protocol::Ip4`] or [`Protocol::Ip6`] found. Multiaddresses without an
/// IP component, hostname-based (`/dns4/...`) or non-IP transport
/// addresses (`/memory/`), return `None`. Hostnames are not resolved.
pub fn ip_addr(&self) -> Option<IpAddr> {
self.iter().find_map(|p| match p {
Protocol::Ip4(addr) => Some(IpAddr::V4(addr)),
Protocol::Ip6(addr) => Some(IpAddr::V6(addr)),
_ => None,
})
Comment on lines +232 to +237

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already implemented From<IpAddr> for Multiaddr. Would it maybe make sense to just analogously implement TryFrom<&Multiaddr> for IpAddr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh I like that! But we'd need to add a new Error variant and have Err instead of None for the return type. It would also be less visible the method

}
}

impl fmt::Debug for Multiaddr {
Expand Down
37 changes: 37 additions & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,8 @@ fn arbitrary_impl_for_all_proto_variants() {
}

mod multiaddr_with_p2p {
use std::net::{IpAddr, Ipv4Addr};

use multiaddr::{Multiaddr, PeerId};

fn test_multiaddr_with_p2p(
Expand Down Expand Up @@ -868,4 +870,39 @@ mod multiaddr_with_p2p {
Err("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"),
)
}
#[test]
fn ip_addr_of_ip_addresses() {
let v4: Multiaddr = "/ip4/127.0.0.1/udp/4001/quic-v1".parse().unwrap();
assert_eq!(v4.ip_addr(), Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));

let v6: Multiaddr = "/ip6/2001:db8::1/tcp/443".parse().unwrap();
assert_eq!(
v6.ip_addr(),
Some(IpAddr::V6("2001:db8::1".parse().unwrap()))
);
}

#[test]
fn ip_addr_absent_without_ip_component() {
// Hostname-based addresses are not resolved.
let hostname: Multiaddr = "/dns4/example.com/tcp/443".parse().unwrap();
assert_eq!(hostname.ip_addr(), None);

let memory: Multiaddr = "/memory/1234".parse().unwrap();
assert_eq!(memory.ip_addr(), None);

assert_eq!(Multiaddr::empty().ip_addr(), None);
}

#[test]
fn ip_addr_tracks_components() {
let mut address: Multiaddr = "/ip4/127.0.0.1/tcp/80".parse().unwrap();
assert_eq!(address.ip_addr(), Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));

address.pop(); // `tcp`
assert_eq!(address.ip_addr(), Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));

address.pop(); // `ip4` — nothing left
assert_eq!(address.ip_addr(), None);
}
}
Loading