radix_common/data/scrypto/
custom_formatting.rs1use crate::internal_prelude::*;
2
3#[derive(Clone, Copy, Debug, Default)]
4pub struct ScryptoValueDisplayContext<'a> {
5 pub address_bech32_encoder: Option<&'a AddressBech32Encoder>,
6}
7
8impl<'a> ScryptoValueDisplayContext<'a> {
9 pub fn no_context() -> Self {
10 Self {
11 address_bech32_encoder: None,
12 }
13 }
14
15 pub fn with_optional_bech32(address_bech32_encoder: Option<&'a AddressBech32Encoder>) -> Self {
16 Self {
17 address_bech32_encoder,
18 }
19 }
20}
21
22impl<'a> From<AddressDisplayContext<'a>> for ScryptoValueDisplayContext<'a> {
23 fn from(val: AddressDisplayContext<'a>) -> Self {
24 ScryptoValueDisplayContext::with_optional_bech32(val.encoder)
25 }
26}
27
28impl<'a> From<&'a AddressBech32Encoder> for ScryptoValueDisplayContext<'a> {
29 fn from(val: &'a AddressBech32Encoder) -> Self {
30 ScryptoValueDisplayContext::with_optional_bech32(Some(val))
31 }
32}
33
34impl<'a> From<Option<&'a AddressBech32Encoder>> for ScryptoValueDisplayContext<'a> {
35 fn from(val: Option<&'a AddressBech32Encoder>) -> Self {
36 ScryptoValueDisplayContext::with_optional_bech32(val)
37 }
38}
39
40impl<'a> CustomDisplayContext<'a> for ScryptoValueDisplayContext<'a> {
41 type CustomExtension = ScryptoCustomExtension;
42}
43
44impl FormattableCustomExtension for ScryptoCustomExtension {
45 type CustomDisplayContext<'a> = ScryptoValueDisplayContext<'a>;
46
47 fn display_string_content<'s, 'de, 'a, 't, 's1, 's2, F: fmt::Write>(
48 f: &mut F,
49 context: &Self::CustomDisplayContext<'a>,
50 value: &<Self::CustomTraversal as CustomTraversal>::CustomTerminalValueRef<'de>,
51 ) -> Result<(), fmt::Error> {
52 match &value.0 {
53 ScryptoCustomValue::Reference(value) => {
54 write!(f, "\"{}\"", value.0.display(context.address_bech32_encoder))?;
55 }
56 ScryptoCustomValue::Own(value) => {
57 write!(f, "\"{}\"", value.0.display(context.address_bech32_encoder))?;
58 }
59 ScryptoCustomValue::Decimal(value) => {
60 write!(f, "\"{}\"", value)?;
61 }
62 ScryptoCustomValue::PreciseDecimal(value) => {
63 write!(f, "\"{}\"", value)?;
64 }
65 ScryptoCustomValue::NonFungibleLocalId(value) => {
66 write!(f, "\"{}\"", value)?;
67 }
68 }
69 Ok(())
70 }
71
72 fn debug_string_content<'s, 'de, 'a, 't, 's1, 's2, F: fmt::Write>(
73 f: &mut F,
74 context: &Self::CustomDisplayContext<'a>,
75 value: &<Self::CustomTraversal as CustomTraversal>::CustomTerminalValueRef<'de>,
76 ) -> Result<(), fmt::Error> {
77 match &value.0 {
78 ScryptoCustomValue::Reference(value) => {
79 write!(f, "\"{}\"", value.0.display(context.address_bech32_encoder))?;
80 }
81 ScryptoCustomValue::Own(value) => {
82 write!(f, "\"{}\"", value.0.display(context.address_bech32_encoder))?;
83 }
84 ScryptoCustomValue::Decimal(value) => {
85 write!(f, "{value:?}")?;
86 }
87 ScryptoCustomValue::PreciseDecimal(value) => {
88 write!(f, "{value:?}")?;
89 }
90 ScryptoCustomValue::NonFungibleLocalId(value) => {
91 write!(f, "{value:?}")?;
92 }
93 }
94 Ok(())
95 }
96
97 fn code_generation_string_content<'s, 'de, 'a, 't, 's1, 's2, F: fmt::Write>(
98 f: &mut F,
99 context: &Self::CustomDisplayContext<'a>,
100 value: &<Self::CustomTraversal as CustomTraversal>::CustomTerminalValueRef<'de>,
101 ) -> Result<(), fmt::Error> {
102 match &value.0 {
103 ScryptoCustomValue::Reference(value) => {
104 let address_type = match value
105 .as_node_id()
106 .entity_type()
107 .unwrap_or(EntityType::GlobalGenericComponent)
108 {
109 entity_type if entity_type.is_internal() => "InternalAddress",
110 entity_type if entity_type.is_global_package() => "PackageAddress",
111 entity_type if entity_type.is_global_resource_manager() => "ResourceAddress",
112 _ => "ComponentAddress",
113 };
114 write!(
115 f,
116 "{}::from_str(\"{}\").unwrap()",
117 address_type,
118 value.0.display(context.address_bech32_encoder)
119 )?;
120 }
121 ScryptoCustomValue::Own(value) => {
122 write!(
123 f,
124 "Own::from_str(\"{}\").unwrap()",
125 value.0.display(context.address_bech32_encoder)
126 )?;
127 }
128 ScryptoCustomValue::Decimal(value) => {
129 write!(f, "dec!(\"{}\")", value)?;
130 }
131 ScryptoCustomValue::PreciseDecimal(value) => {
132 write!(f, "pdec!(\"{}\")", value)?;
133 }
134 ScryptoCustomValue::NonFungibleLocalId(value) => {
135 write!(f, "NonFungibleLocalId::from_str(\"{}\").unwrap()", value)?;
136 }
137 }
138 Ok(())
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::address::test_addresses::*;
146 use crate::address::AddressBech32Encoder;
147 use crate::data::scrypto::model::*;
148
149 #[test]
150 fn test_rustlike_string_format_with_network() {
151 use crate::math::{Decimal, PreciseDecimal};
152 let encoder = AddressBech32Encoder::for_simulator();
153 let value = ScryptoValue::Tuple {
154 fields: vec![
155 Value::Custom {
156 value: ScryptoCustomValue::Reference(Reference(FUNGIBLE_RESOURCE_NODE_ID)),
157 },
158 Value::Custom {
159 value: ScryptoCustomValue::Own(Own(FUNGIBLE_RESOURCE_NODE_ID)),
160 },
161 Value::Custom {
162 value: ScryptoCustomValue::Decimal(Decimal::ONE),
163 },
164 Value::Custom {
165 value: ScryptoCustomValue::Decimal(Decimal::ONE.checked_div(100).unwrap()),
166 },
167 Value::Custom {
168 value: ScryptoCustomValue::PreciseDecimal(PreciseDecimal::ZERO),
169 },
170 Value::Custom {
171 value: ScryptoCustomValue::NonFungibleLocalId(
172 NonFungibleLocalId::string("hello").unwrap(),
173 ),
174 },
175 Value::Custom {
176 value: ScryptoCustomValue::NonFungibleLocalId(NonFungibleLocalId::integer(123)),
177 },
178 Value::Custom {
179 value: ScryptoCustomValue::NonFungibleLocalId(
180 NonFungibleLocalId::bytes(vec![0x23, 0x45]).unwrap(),
181 ),
182 },
183 Value::Custom {
184 value: ScryptoCustomValue::NonFungibleLocalId(NonFungibleLocalId::ruid(
185 [0x11; 32],
186 )),
187 },
188 ],
189 };
190
191 let expected = format!("Tuple(Reference(\"{FUNGIBLE_RESOURCE_SIM_ADDRESS}\"), Own(\"{FUNGIBLE_RESOURCE_SIM_ADDRESS}\"), Decimal(\"1\"), Decimal(\"0.01\"), PreciseDecimal(\"0\"), NonFungibleLocalId(\"<hello>\"), NonFungibleLocalId(\"#123#\"), NonFungibleLocalId(\"[2345]\"), NonFungibleLocalId(\"{{1111111111111111-1111111111111111-1111111111111111-1111111111111111}}\"))");
192
193 let context = ScryptoValueDisplayContext::with_optional_bech32(Some(&encoder));
194
195 let payload = ScryptoRawPayload::new_from_valid_owned(scrypto_encode(&value).unwrap());
196
197 let actual_rustlike = payload.to_string(ValueDisplayParameters::Schemaless {
198 display_mode: DisplayMode::RustLike(RustLikeOptions::full()),
199 print_mode: PrintMode::SingleLine,
200 custom_context: context,
201 depth_limit: SCRYPTO_SBOR_V1_MAX_DEPTH,
202 });
203 let actual_nested = payload.to_string(ValueDisplayParameters::Schemaless {
204 display_mode: DisplayMode::RustLike(RustLikeOptions::full()),
205 print_mode: PrintMode::SingleLine,
206 custom_context: context,
207 depth_limit: SCRYPTO_SBOR_V1_MAX_DEPTH,
208 });
209
210 assert_eq!(actual_rustlike, expected);
212 assert_eq!(actual_nested, expected);
213 }
214}