You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I wrote a new declarative macro that uses the diagnostic::on_unimplemented that checks the validity of a const &str and converts it to an ObjectID under const context.
pubstructIsValidLength<constLENGTH:usize>;#[diagnostic::on_unimplemented( message = "literal passed into has the wrong length, its length should exactly be 64",)]pubtraitValidLength{}implValidLengthforIsValidLength<64>{}pubconstfnis_valid_length<T:ValidLength>(){}#[diagnostic::on_unimplemented( message = "the literal passed in has some invalid characters",)]pubtraitValidCharacters{}pubstructHasValidLength<constVALID:bool>;implValidCharactersforHasValidLength<true>{}pubconstfnhas_valid_characters<T:ValidCharacters>(){}pubconstfnparse_sui_address(address:&str,start:usize) -> ([u8;32],bool){letmut trimmed_address = [0u8;64];letmut i = 0;// Copy the remaining characters to trimmed_addresswhile i < 64 && start + i < address.len(){
trimmed_address[i] = address.as_bytes()[start + i];
i += 1;}// Initialize the result arrayletmut bytes = [0u8;32];letmut j = 0;while j < 32{// Convert each hex character to a bytelet byte = match(hex_char_to_nibble(trimmed_address[j *2]),hex_char_to_nibble(trimmed_address[j *2 + 1]),){(Some(high),Some(low)) => (high << 4) | low,
_ => return([0;32],false),};
bytes[j] = byte;
j += 1;}(bytes,true)}constfnhex_char_to_nibble(c:u8) -> Option<u8>{match c {b'0'..=b'9' => Some(c - b'0'),b'a'..=b'f' => Some(c - b'a' + 10),b'A'..=b'F' => Some(c - b'A' + 10),
_ => None,}}pubstructObjectID([u8;32]);implObjectID{pubconstfnnew(bytes:[u8;32]) -> Self{Self(bytes)}}#[macro_export]/// Validate and convert a hex literal string into an `ObjectID`.macro_rules! object_id {($address:expr) => {{constADDRESS_AND_START:(&str,usize) = check_length!($address);constBYTES_AND_ERR:([u8;32],bool) = $crate::parse_sui_address(ADDRESS_AND_START.0,ADDRESS_AND_START.1);constERR:bool = BYTES_AND_ERR.1;
$crate::has_valid_characters::<HasValidLength<ERR>>();
$crate::ObjectID::new(BYTES_AND_ERR.0)}};}#[macro_export]macro_rules! check_length {($address:expr) => {{constSTART:usize = 0;constADDRESS_BYTES_LENGTH_AND_START:(usize,usize) = if $address.len() >= 2 && $address.as_bytes()[0] == b'0' && $address.as_bytes()[1] == b'x'{constSTART:usize = 2;($address.as_bytes().len() - START,START)} else {($address.as_bytes().len() - START,START)};constADDRESS_BYTES_LENGTH:usize = ADDRESS_BYTES_LENGTH_AND_START.0;
is_valid_length::<IsValidLength<ADDRESS_BYTES_LENGTH>>();($address,ADDRESS_BYTES_LENGTH_AND_START.1)}}}fnmain(){// valid:object_id!("0xd1ec56d5d92d3e3d74ce6e50e1b2a6b505bedd7d7305ead61d5093619bedb2a7");// invalid character 'G'object_id!("0xd1ec56d5d92d3e3d74ce6e50e1b2a6b505beGG7d7305ead61d5093619bedb2a7");// ^^ here bro// invalid lengthobject_id!("0xd1ec56d5d92d3e3d74ce6e50e1b2a6b505beGG7d7305ead61d5093619bedb2a7abcabcabc");// ^^^^^^ 6 characters more}
Advantage here is that it does not rely on compile time panics and if relying on Rust's version < 1.80, we can always remove the #[diagnostic::on_unimplemented] - it's less beautiful but it works.
I wrote a new declarative macro that uses the
diagnostic::on_unimplemented
that checks the validity of aconst &str
and converts it to anObjectID
under const context.Playground.
Have a look @patrickkuo and @sblackshear. Thank you.
The text was updated successfully, but these errors were encountered: