1mod detectors;
7mod identify;
8mod input;
9mod loaders;
10mod models;
11mod pipelines;
12mod registry;
13mod shared;
14
15pub use identify::{identify as identify_all, IdentificationCandidate, InputType};
16
17pub fn identify(input: &str) -> Result<Vec<IdentificationCandidate>, Error> {
35 identify_all(input)
36}
37
38#[derive(Debug, Clone)]
40pub enum Error {
41 NotImplemented,
43 InvalidInput(String),
45}
46
47impl std::fmt::Display for Error {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Error::NotImplemented => write!(f, "Feature not yet implemented"),
51 Error::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
52 }
53 }
54}
55
56impl std::error::Error for Error {}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_identify_evm_address() {
64 let input = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
66 let result = identify(input);
67 if let Err(e) = &result {
68 eprintln!("Error: {}", e);
69 }
70 assert!(result.is_ok());
71 let candidates = result.unwrap();
72 assert!(!candidates.is_empty());
73 assert!(candidates.iter().any(|c| c.chain == "ethereum"));
75 assert!(candidates[0].confidence > 0.0);
77 assert_ne!(candidates[0].normalized, input);
79 assert!(candidates[0].normalized.starts_with("0x"));
80 assert_eq!(candidates[0].normalized.len(), 42);
81 }
82
83 #[test]
84 fn test_identify_evm_address_lowercase() {
85 let input = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
86 let result = identify(input);
87 assert!(result.is_ok());
88 let candidates = result.unwrap();
89 assert!(!candidates.is_empty());
90 assert_ne!(candidates[0].normalized, input);
92 assert!(candidates[0].normalized.starts_with("0x"));
93 assert_eq!(candidates[0].normalized.len(), 42);
94 }
95
96 #[test]
97 fn test_identify_evm_multiple_chains() {
98 let input = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
100 let result = identify(input);
101 assert!(result.is_ok());
102 let candidates = result.unwrap();
103 assert!(candidates.len() >= 1);
105 let evm_chains = [
107 "ethereum",
108 "polygon",
109 "bsc",
110 "avalanche",
111 "arbitrum",
112 "optimism",
113 "base",
114 "fantom",
115 "celo",
116 "gnosis",
117 ];
118 assert!(candidates
119 .iter()
120 .all(|c| evm_chains.contains(&c.chain.as_str())));
121 }
122
123 #[test]
124 fn test_identify_invalid_address() {
125 let result = identify("not-an-address");
126 assert!(result.is_err());
127 if let Err(Error::InvalidInput(msg)) = result {
129 assert!(msg.contains("not-an-address"));
130 } else {
131 panic!("Expected InvalidInput error");
132 }
133 }
134
135 #[test]
136 fn test_identify_unrecognized_format() {
137 let result = identify("xyz123abc");
140 assert!(result.is_err());
141 if let Err(Error::InvalidInput(msg)) = result {
142 assert!(
144 msg.contains("Unable to classify input format")
145 || msg.contains("Unable to identify address format")
146 );
147 assert!(msg.contains("xyz123abc"));
148 } else {
149 panic!("Expected InvalidInput error");
150 }
151 }
152
153 #[test]
154 fn test_identify_empty_string() {
155 let result = identify("");
157 assert!(result.is_err());
158 if let Err(Error::InvalidInput(msg)) = result {
159 assert!(
161 msg.contains("Unable to classify input format")
162 || msg.contains("Unable to identify address format")
163 );
164 } else {
165 panic!("Expected InvalidInput error");
166 }
167 }
168
169 #[test]
170 fn test_identify_tron() {
171 use base58::ToBase58;
174 use sha2::{Digest, Sha256};
175
176 let version = 0x41u8;
177 let address_bytes = vec![0u8; 20];
178 let payload = [&[version], address_bytes.as_slice()].concat();
179 let hash1 = Sha256::digest(&payload);
180 let hash2 = Sha256::digest(hash1);
181 let checksum = &hash2[..4];
182 let full_bytes = [payload, checksum.to_vec()].concat();
183 let tron_addr = full_bytes.to_base58();
184
185 let result = identify(&tron_addr);
186 if result.is_ok() {
188 let candidates = result.unwrap();
189 assert!(!candidates.is_empty());
190 assert!(candidates.iter().any(|c| c.chain == "tron"));
191 }
192 }
193
194 #[test]
195 fn test_identify_substrate() {
196 use base58::ToBase58;
198 let mut bytes = vec![0u8]; bytes.extend(vec![0u8; 32]); bytes.extend(vec![0u8; 2]); let substrate_addr = bytes.to_base58();
203
204 let result = identify(&substrate_addr);
205 if result.is_ok() {
207 let candidates = result.unwrap();
208 assert!(!candidates.is_empty());
210 }
211 }
212}