Skip to content

Commit c7015f5

Browse files
authored
Merge pull request #421 from dusk-network/neotamandua/from_string
uplink: impl TryFrom<String> for ContractId
2 parents 38694d4 + 79e9d51 commit c7015f5

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

piecrust-uplink/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add `TryFrom<String>` trait impl for `ContractId` [#420]
13+
1014
## [0.17.3] - 2024-12-19
1115

1216
## [0.17.2] - 2024-12-17
@@ -226,6 +230,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
226230
- First `piecrust-uplink` release
227231

228232
<!-- ISSUES -->
233+
[#420]: https://github.com/dusk-network/piecrust/issues/420
229234
[#414]: https://github.com/dusk-network/piecrust/issues/414
230235
[#375]: https://github.com/dusk-network/piecrust/issues/375
231236
[#365]: https://github.com/dusk-network/piecrust/issues/365

piecrust-uplink/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ rkyv = { version = "0.7", default-features = false, features = ["size_32", "allo
1717
bytecheck = { version = "0.6", default-features = false }
1818
dlmalloc = { version = "0.2", optional = true, features = ["global"] }
1919
serde = { version = "1.0", optional = true }
20-
hex = { version = "0.4" , optional = true }
20+
hex = { version = "0.4", default-features = false, features = ["alloc"]}
2121
base64 = { version = "0.22", optional = true }
2222
serde_json = { version = "1.0", optional = true }
2323

@@ -27,7 +27,7 @@ rand = "0.8"
2727
[features]
2828
abi = []
2929
debug = []
30-
serde = ["dep:serde", "serde_json", "hex", "base64"]
30+
serde = ["dep:serde", "serde_json", "base64"]
3131

3232
[package.metadata.docs.rs]
3333
all-features = true

piecrust-uplink/src/types.rs

+80
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,20 @@ impl PartialEq<[u8; CONTRACT_ID_BYTES]> for ContractId {
110110
}
111111
}
112112

113+
/// Debug implementation for [`ContractId`]
114+
///
115+
/// This implementation uses the normal display implementation.
113116
impl core::fmt::Debug for ContractId {
114117
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
115118
core::fmt::Display::fmt(self, f)
116119
}
117120
}
118121

122+
/// Display implementation for [`ContractId`]
123+
///
124+
/// This implementation will display the hexadecimal representation of the bytes
125+
/// of the [`ContractId`]. If the alternate flag is set, it will also display
126+
/// the `0x` prefix.
119127
impl core::fmt::Display for ContractId {
120128
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
121129
if f.alternate() {
@@ -127,3 +135,75 @@ impl core::fmt::Display for ContractId {
127135
Ok(())
128136
}
129137
}
138+
139+
impl TryFrom<String> for ContractId {
140+
type Error = core::fmt::Error;
141+
142+
/// Tries to convert a hexadecimal string into a [`ContractId`]
143+
///
144+
/// The string can be prefixed with `0x` or not.
145+
fn try_from(value: String) -> core::result::Result<Self, Self::Error> {
146+
let value = value.trim_start_matches("0x");
147+
let decoded = hex::decode(value).map_err(|_| core::fmt::Error)?;
148+
let bytes: [u8; CONTRACT_ID_BYTES] =
149+
decoded.try_into().map_err(|_| core::fmt::Error)?;
150+
151+
Ok(ContractId::from_bytes(bytes))
152+
}
153+
}
154+
155+
#[cfg(test)]
156+
mod tests {
157+
use alloc::{format, string::ToString};
158+
159+
use rand::rngs::StdRng;
160+
use rand::{Rng, SeedableRng};
161+
162+
use super::*;
163+
164+
const CONTRACT_ID_STR: &str =
165+
"0000000000000000000000000000000000000000000000000000000000000000";
166+
const CONTRACT_ID_STR_PRETTY: &str =
167+
"0x0000000000000000000000000000000000000000000000000000000000000000";
168+
169+
#[test]
170+
fn contract_id_display() {
171+
let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
172+
assert_eq!(format!("{}", contract_id), CONTRACT_ID_STR);
173+
174+
let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
175+
assert_eq!(format!("{:#?}", contract_id), CONTRACT_ID_STR_PRETTY);
176+
}
177+
178+
#[test]
179+
fn contract_id_debug() {
180+
let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
181+
assert_eq!(format!("{}", contract_id), CONTRACT_ID_STR);
182+
}
183+
184+
#[test]
185+
fn contract_id_to_from_string() {
186+
let mut rng = StdRng::seed_from_u64(1618);
187+
let contract_id = ContractId::from_bytes(rng.gen());
188+
189+
let string = contract_id.to_string();
190+
191+
assert_eq!(string.starts_with("0x"), false);
192+
assert_eq!(string.len(), CONTRACT_ID_BYTES * 2);
193+
194+
let contract_id_from_string = ContractId::try_from(string).unwrap();
195+
196+
assert_eq!(contract_id, contract_id_from_string);
197+
}
198+
199+
#[test]
200+
fn contract_id_try_from_invalid_string() {
201+
let too_short = ContractId::try_from("0x".to_string()).is_err();
202+
203+
let too_long =
204+
ContractId::try_from(format!("{}0", CONTRACT_ID_STR)).is_err();
205+
206+
assert!(too_short);
207+
assert!(too_long);
208+
}
209+
}

0 commit comments

Comments
 (0)