string.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use super::*; 17 18 pub static SOLUTION_ID_PREFIX: &str = "solution"; 19 20 impl<N: Network> FromStr for SolutionID<N> { 21 type Err = Error; 22 23 /// Reads in the solution ID string. 24 fn from_str(solution_id: &str) -> Result<Self, Self::Err> { 25 // Decode the solution ID string from bech32m. 26 let (hrp, data, variant) = bech32::decode(solution_id)?; 27 if hrp != SOLUTION_ID_PREFIX { 28 bail!("Failed to decode solution ID: '{hrp}' is an invalid prefix") 29 } else if data.is_empty() { 30 bail!("Failed to decode solution ID: data field is empty") 31 } else if variant != bech32::Variant::Bech32m { 32 bail!("Found a solution ID that is not bech32m encoded: {solution_id}"); 33 } 34 // Decode the solution ID data from u5 to u8, and into the solution ID. 35 Ok(Self::read_le(&Vec::from_base32(&data)?[..])?) 36 } 37 } 38 39 impl<N: Network> Debug for SolutionID<N> { 40 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 41 Display::fmt(self, f) 42 } 43 } 44 45 impl<N: Network> Display for SolutionID<N> { 46 /// Writes the solution ID as a bech32m string. 47 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 48 // Convert the solution ID to bytes. 49 let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?; 50 // Encode the bytes into bech32m. 51 let string = 52 bech32::encode(SOLUTION_ID_PREFIX, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?; 53 // Output the string. 54 Display::fmt(&string, f) 55 } 56 } 57 58 #[cfg(test)] 59 mod tests { 60 use super::*; 61 use console::network::MainnetV0; 62 63 type CurrentNetwork = MainnetV0; 64 65 const ITERATIONS: u64 = 1_000; 66 67 #[test] 68 fn test_string() -> Result<()> { 69 // Ensure type and empty value fails. 70 assert!(SolutionID::<CurrentNetwork>::from_str(&format!("{SOLUTION_ID_PREFIX}1")).is_err()); 71 assert!(SolutionID::<CurrentNetwork>::from_str("").is_err()); 72 73 let mut rng = TestRng::default(); 74 75 for _ in 0..ITERATIONS { 76 // Sample a new solution ID. 77 let expected = SolutionID::<CurrentNetwork>::from(rng.r#gen::<u64>()); 78 79 // Check the string representation. 80 let candidate = format!("{expected}"); 81 assert_eq!(expected, SolutionID::from_str(&candidate)?); 82 assert_eq!(SOLUTION_ID_PREFIX, candidate.split('1').next().unwrap()); 83 } 84 Ok(()) 85 } 86 87 #[test] 88 fn test_display() -> Result<()> { 89 let mut rng = TestRng::default(); 90 91 for _ in 0..ITERATIONS { 92 // Sample a new solution ID. 93 let expected = SolutionID::<CurrentNetwork>::from(rng.r#gen::<u64>()); 94 95 let candidate = expected.to_string(); 96 assert_eq!(format!("{expected}"), candidate); 97 assert_eq!(SOLUTION_ID_PREFIX, candidate.split('1').next().unwrap()); 98 99 let candidate_recovered = SolutionID::<CurrentNetwork>::from_str(&candidate.to_string())?; 100 assert_eq!(expected, candidate_recovered); 101 } 102 Ok(()) 103 } 104 }