1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! Types and functions related to parsing/formating EOSIO names. use std::convert::TryFrom; use std::error::Error; use std::fmt; /// All possible characters that can be used in EOSIO names. pub const NAME_UTF8_CHARS: [u8; 32] = *b".12345abcdefghijklmnopqrstuvwxyz"; /// The maximum character length of an EOSIO name. pub const NAME_LEN_MAX: usize = 12; /// An error which can be returned when parsing an EOSIO name. #[derive(Debug, PartialEq, Clone, Copy)] pub enum ParseNameError { /// The name is over the maximum allowed length. TooLong, /// The name contains an unallowed character. BadChar(char), } impl Error for ParseNameError {} impl fmt::Display for ParseNameError { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ParseNameError::TooLong => write!( f, "name is too long, must be {} chars or less", NAME_LEN_MAX ), ParseNameError::BadChar(c) => write!( f, "name contains invalid character '{}'; must only contain the following characters: {}", c, String::from_utf8_lossy(&NAME_UTF8_CHARS) ), } } } /// Attempts to create an EOSIO name from a `&str`. /// /// # Examples /// /// ``` /// use eosio_numstr::{name_from_str, ParseNameError}; /// assert_eq!(name_from_str(""), Ok(0)); /// assert_eq!(name_from_str("a"), Ok(3458764513820540928)); /// assert_eq!(name_from_str("123456789012"), Err(ParseNameError::BadChar('6'))); /// assert_eq!(name_from_str("123451234512"), Ok(614251535012020768)); /// assert_eq!(name_from_str("1234512345123"), Err(ParseNameError::TooLong)); /// assert_eq!(name_from_str("eosio.token"), Ok(6138663591592764928)); /// assert_eq!(name_from_str("eosio.bpay"), Ok(6138663581940940800)); /// assert_eq!(name_from_str("A"), Err(ParseNameError::BadChar('A'))); /// assert_eq!(name_from_str("TEST"), Err(ParseNameError::BadChar('T'))); /// ``` #[inline] pub fn name_from_str(value: &str) -> Result<u64, ParseNameError> { name_from_chars(value.chars()) } /// Attempts to create an EOSIO name from an `Iterator`. /// /// # Examples /// /// ``` /// use eosio_numstr::{name_from_chars, ParseNameError}; /// assert_eq!(name_from_chars("".chars()), Ok(0)); /// assert_eq!(name_from_chars("a".chars()), Ok(3458764513820540928)); /// assert_eq!(name_from_chars("123456789012".chars()), Err(ParseNameError::BadChar('6'))); /// assert_eq!(name_from_chars("123451234512".chars()), Ok(614251535012020768)); /// assert_eq!(name_from_chars("1234512345123".chars()), Err(ParseNameError::TooLong)); /// assert_eq!(name_from_chars("eosio.token".chars()), Ok(6138663591592764928)); /// assert_eq!(name_from_chars("eosio.bpay".chars()), Ok(6138663581940940800)); /// assert_eq!(name_from_chars("A".chars()), Err(ParseNameError::BadChar('A'))); /// assert_eq!(name_from_chars("TEST".chars()), Err(ParseNameError::BadChar('T'))); /// ``` #[inline] pub fn name_from_chars<I>(chars: I) -> Result<u64, ParseNameError> where I: Iterator<Item = char>, { let mut value = 0; for (i, c) in chars.enumerate() { if i == NAME_LEN_MAX { return Err(ParseNameError::TooLong); } else if c == '.' { continue; } else if let Some(symbol) = char_to_symbol(c) { let mut n = symbol as u64; if i < NAME_LEN_MAX { n &= 31; n <<= 64 - 5 * (i + 1); } else { n &= 15; } value |= n; } else { return Err(ParseNameError::BadChar(c)); } } Ok(value) } /// Converts a character to a symbol. fn char_to_symbol(c: char) -> Option<char> { if c >= 'a' && c <= 'z' { ::std::char::from_u32((c as u32 - 'a' as u32) + 6) } else if c >= '1' && c <= '5' { ::std::char::from_u32((c as u32 - '1' as u32) + 1) } else { None } } /// Converts an EOSIO name value into a string. /// /// # Examples /// /// ``` /// use eosio_numstr::name_to_string; /// assert_eq!(name_to_string(6138663591592764928), "eosio.token"); /// assert_eq!(name_to_string(6138663581940940800), "eosio.bpay"); /// assert_eq!(name_to_string(0), ""); /// assert_eq!(name_to_string(614251535012020768), "123451234512"); /// ``` #[inline] pub fn name_to_string(name: u64) -> String { String::from_utf8_lossy(&name_to_utf8(name)) .trim_matches('.') .into() } /// Converts an EOSIO name into an array of UTF-8 characters. /// /// # Examples /// /// ``` /// use eosio_numstr::name_to_utf8; /// assert_eq!(name_to_utf8(6138663591592764928), *b"eosio.token.."); /// assert_eq!(name_to_utf8(6138663581940940800), *b"eosio.bpay..."); /// assert_eq!(name_to_utf8(0), *b"............."); /// assert_eq!(name_to_utf8(614251535012020768), *b"123451234512."); /// ``` #[inline] pub fn name_to_utf8(name: u64) -> [u8; 13] { let mut chars = [b'.'; 13]; // TODO: make this 12 instead of 13 let mut t = name; for (i, c) in chars.iter_mut().rev().enumerate() { let index = t & if i == 0 { 15 } else { 31 }; let index = usize::try_from(index).unwrap_or_default(); if let Some(v) = NAME_UTF8_CHARS.get(index) { *c = *v; } t >>= if i == 0 { 4 } else { 5 }; } chars }