mod.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm 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 mod bytes; 17 mod parse; 18 mod serialize; 19 mod to_address; 20 mod to_bits; 21 mod to_fields; 22 23 use crate::Identifier; 24 use alphavm_console_network::prelude::*; 25 use alphavm_console_types::{Address, Boolean, Field}; 26 27 /// Returns `true` if the string consists of lowercase alphanumeric characters. 28 fn is_lowercase_alphanumeric(s: &str) -> bool { 29 s.chars().all(|c| matches!(c, '0'..='9' | 'a'..='z' | '_')) 30 } 31 32 /// A program ID is of the form `{name}.{network}`. 33 #[derive(Copy, Clone, PartialEq, Eq, Hash)] 34 pub struct ProgramID<N: Network> { 35 /// The program name. 36 name: Identifier<N>, 37 /// The network-level domain (NLD). 38 network: Identifier<N>, 39 } 40 41 impl<N: Network> From<&ProgramID<N>> for ProgramID<N> { 42 /// Returns a copy of the program ID. 43 fn from(program_id: &ProgramID<N>) -> Self { 44 *program_id 45 } 46 } 47 48 impl<N: Network> TryFrom<(Identifier<N>, Identifier<N>)> for ProgramID<N> { 49 type Error = Error; 50 51 /// Initializes a program ID from a name and network-level domain identifier. 52 fn try_from((name, network): (Identifier<N>, Identifier<N>)) -> Result<Self> { 53 // Ensure the name is lowercase alphabets and numbers. 54 ensure!(is_lowercase_alphanumeric(&name.to_string()), "Program name is invalid: {name}"); 55 // Construct the program ID. 56 let id = Self { name, network }; 57 // Ensure the program network-level domain is `alpha`. 58 ensure!(id.is_alpha(), "Program network is invalid: {network}"); 59 // Return the program ID. 60 Ok(id) 61 } 62 } 63 64 impl<N: Network> TryFrom<String> for ProgramID<N> { 65 type Error = Error; 66 67 /// Initializes a program ID from a name and network-level domain identifier. 68 fn try_from(program_id: String) -> Result<Self> { 69 Self::from_str(&program_id) 70 } 71 } 72 73 impl<N: Network> TryFrom<&String> for ProgramID<N> { 74 type Error = Error; 75 76 /// Initializes a program ID from a name and network-level domain identifier. 77 fn try_from(program_id: &String) -> Result<Self> { 78 Self::from_str(program_id) 79 } 80 } 81 82 impl<N: Network> TryFrom<&str> for ProgramID<N> { 83 type Error = Error; 84 85 /// Initializes a program ID from a name and network-level domain identifier. 86 fn try_from(program_id: &str) -> Result<Self> { 87 // Split the program ID into a name and network-level domain. 88 let mut split = program_id.split('.'); 89 // Parse the name and network. 90 if let (Some(name), Some(network), None) = (split.next(), split.next(), split.next()) { 91 // Ensure the name is lowercase alphabets and numbers. 92 ensure!(is_lowercase_alphanumeric(name), "Program name is invalid: {name}"); 93 // Construct the program ID. 94 Self::try_from((Identifier::from_str(name)?, Identifier::from_str(network)?)) 95 } else { 96 bail!("Invalid program ID '{program_id}'") 97 } 98 } 99 } 100 101 impl<N: Network> ProgramID<N> { 102 /// Returns the program name. 103 #[inline] 104 pub const fn name(&self) -> &Identifier<N> { 105 &self.name 106 } 107 108 /// Returns the network-level domain (NLD). 109 #[inline] 110 pub const fn network(&self) -> &Identifier<N> { 111 &self.network 112 } 113 114 /// Returns `true` if the network-level domain is `alpha`. 115 #[inline] 116 pub fn is_alpha(&self) -> bool { 117 self.network() == &Identifier::from_str("alpha").expect("Failed to parse Alpha domain") 118 } 119 } 120 121 impl<N: Network> Ord for ProgramID<N> { 122 /// Ordering is determined by the network first, then the program name second. 123 fn cmp(&self, other: &Self) -> Ordering { 124 match self.network == other.network { 125 true => self.name.to_string().cmp(&other.name.to_string()), 126 false => self.network.to_string().cmp(&other.network.to_string()), 127 } 128 } 129 } 130 131 impl<N: Network> PartialOrd for ProgramID<N> { 132 /// Ordering is determined by the network first, then the program name second. 133 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 134 Some(self.cmp(other)) 135 } 136 } 137 138 impl<N: Network> Equal<Self> for ProgramID<N> { 139 type Output = Boolean<N>; 140 141 /// Returns `true` if `self` and `other` are equal. 142 fn is_equal(&self, other: &Self) -> Self::Output { 143 Boolean::new(self == other) 144 } 145 146 /// Returns `true` if `self` and `other` are **not** equal. 147 fn is_not_equal(&self, other: &Self) -> Self::Output { 148 Boolean::new(self != other) 149 } 150 } 151 152 #[cfg(test)] 153 mod tests { 154 use super::*; 155 use alphavm_console_network::MainnetV0; 156 157 type CurrentNetwork = MainnetV0; 158 159 #[test] 160 fn test_partial_ord() -> Result<()> { 161 let import1 = ProgramID::<CurrentNetwork>::from_str("bar.alpha")?; 162 let import2 = ProgramID::<CurrentNetwork>::from_str("foo.alpha")?; 163 164 let import3 = ProgramID::<CurrentNetwork>::from_str("bar.alpha")?; 165 let import4 = ProgramID::<CurrentNetwork>::from_str("foo.alpha")?; 166 167 assert_eq!(import1.partial_cmp(&import1), Some(Ordering::Equal)); 168 assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Less)); 169 assert_eq!(import1.partial_cmp(&import3), Some(Ordering::Equal)); 170 assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Less)); 171 172 assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Greater)); 173 assert_eq!(import2.partial_cmp(&import2), Some(Ordering::Equal)); 174 assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Greater)); 175 assert_eq!(import2.partial_cmp(&import4), Some(Ordering::Equal)); 176 177 assert_eq!(import3.partial_cmp(&import1), Some(Ordering::Equal)); 178 assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Less)); 179 assert_eq!(import3.partial_cmp(&import3), Some(Ordering::Equal)); 180 assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Less)); 181 182 assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Greater)); 183 assert_eq!(import4.partial_cmp(&import2), Some(Ordering::Equal)); 184 assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Greater)); 185 assert_eq!(import4.partial_cmp(&import4), Some(Ordering::Equal)); 186 187 Ok(()) 188 } 189 }