rustywallet_address/
address.rs1use crate::bitcoin::BitcoinAddress;
4use crate::error::AddressError;
5use crate::ethereum::EthereumAddress;
6use crate::network::Network;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum Address {
11 Bitcoin(BitcoinAddress),
13 Ethereum(EthereumAddress),
15}
16
17impl Address {
18 pub fn parse(s: &str) -> Result<Self, AddressError> {
26 s.parse()
27 }
28
29 pub fn validate(s: &str) -> Result<(), AddressError> {
31 s.parse::<Self>().map(|_| ())
32 }
33
34 pub fn network(&self) -> Network {
36 match self {
37 Address::Bitcoin(addr) => addr.network(),
38 Address::Ethereum(_) => Network::Ethereum,
39 }
40 }
41
42 #[inline]
44 pub fn is_bitcoin(&self) -> bool {
45 matches!(self, Address::Bitcoin(_))
46 }
47
48 #[inline]
50 pub fn is_ethereum(&self) -> bool {
51 matches!(self, Address::Ethereum(_))
52 }
53}
54
55impl std::fmt::Display for Address {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Address::Bitcoin(addr) => write!(f, "{}", addr),
59 Address::Ethereum(addr) => write!(f, "{}", addr),
60 }
61 }
62}
63
64impl std::str::FromStr for Address {
65 type Err = AddressError;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 if s.starts_with("0x") || s.starts_with("0X") {
70 return s.parse::<EthereumAddress>().map(Address::Ethereum);
71 }
72
73 s.parse::<BitcoinAddress>().map(Address::Bitcoin)
75 }
76}
77
78impl From<BitcoinAddress> for Address {
79 fn from(addr: BitcoinAddress) -> Self {
80 Address::Bitcoin(addr)
81 }
82}
83
84impl From<EthereumAddress> for Address {
85 fn from(addr: EthereumAddress) -> Self {
86 Address::Ethereum(addr)
87 }
88}
89
90pub trait AddressFormat: Sized {
92 fn parse(s: &str) -> Result<Self, AddressError>;
94
95 fn validate(s: &str) -> Result<(), AddressError>;
97
98 fn to_string(&self) -> String;
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::bitcoin::{P2PKHAddress, P2TRAddress, P2WPKHAddress};
106 use rustywallet_keys::private_key::PrivateKey;
107
108 #[test]
109 fn test_address_detection_p2pkh() {
110 let pk = PrivateKey::random();
111 let pubkey = pk.public_key();
112 let addr = P2PKHAddress::from_public_key(&pubkey, Network::BitcoinMainnet).unwrap();
113 let parsed: Address = addr.to_string().parse().unwrap();
114 assert!(parsed.is_bitcoin());
115 }
116
117 #[test]
118 fn test_address_detection_p2wpkh() {
119 let pk = PrivateKey::random();
120 let pubkey = pk.public_key();
121 let addr = P2WPKHAddress::from_public_key(&pubkey, Network::BitcoinMainnet).unwrap();
122 let parsed: Address = addr.to_string().parse().unwrap();
123 assert!(parsed.is_bitcoin());
124 }
125
126 #[test]
127 fn test_address_detection_p2tr() {
128 let pk = PrivateKey::random();
129 let pubkey = pk.public_key();
130 let addr = P2TRAddress::from_public_key(&pubkey, Network::BitcoinMainnet).unwrap();
131 let parsed: Address = addr.to_string().parse().unwrap();
132 assert!(parsed.is_bitcoin());
133 }
134
135 #[test]
136 fn test_address_detection_ethereum() {
137 let pk = PrivateKey::random();
138 let pubkey = pk.public_key();
139 let addr = EthereumAddress::from_public_key(&pubkey).unwrap();
140 let parsed: Address = addr.to_string().parse().unwrap();
141 assert!(parsed.is_ethereum());
142 }
143}