1use crate::data::{to_decimal, to_non_fungible_local_id, to_precise_decimal};
2use crate::internal_prelude::*;
3use radix_common::types::NonFungibleGlobalId;
4use radix_engine_interface::types::ResourceAddress;
5use radix_rust::unicode::{CustomCharEscaper, EscapeBehaviour};
6use sbor::rust::fmt;
7use sbor::*;
8
9#[derive(Clone, Copy, Debug)]
10pub struct MultiLine {
11 margin: usize,
12 indent: usize,
13}
14
15#[derive(Clone, Copy, Debug, Default)]
16pub struct ManifestDecompilationDisplayContext<'a> {
17 pub address_bech32_encoder: Option<&'a AddressBech32Encoder>,
18 pub object_names: ManifestObjectNamesRef<'a>,
19 pub multi_line: Option<MultiLine>,
20}
21
22impl<'a> ManifestDecompilationDisplayContext<'a> {
23 pub fn no_context() -> Self {
24 Self::default()
25 }
26
27 pub fn with_optional_bech32(address_bech32_encoder: Option<&'a AddressBech32Encoder>) -> Self {
28 Self {
29 address_bech32_encoder,
30 ..Default::default()
31 }
32 }
33
34 pub fn with_bech32_and_names(
35 address_bech32_encoder: Option<&'a AddressBech32Encoder>,
36 object_names: ManifestObjectNamesRef<'a>,
37 ) -> Self {
38 Self {
39 address_bech32_encoder,
40 object_names,
41 ..Default::default()
42 }
43 }
44
45 pub fn with_multi_line(mut self, margin: usize, indent: usize) -> Self {
46 self.multi_line = Some(MultiLine { margin, indent });
47 self
48 }
49
50 pub fn get_object_names(&self) -> ManifestObjectNamesRef {
51 self.object_names
52 }
53
54 pub fn get_indent(&self, depth: usize) -> String {
55 if let Some(MultiLine { margin, indent }) = self.multi_line {
56 " ".repeat(margin + indent * depth)
57 } else {
58 String::new()
59 }
60 }
61
62 pub fn get_new_line(&self) -> &str {
63 if self.multi_line.is_some() {
64 "\n"
65 } else {
66 " "
67 }
68 }
69}
70
71impl<'a> Into<ManifestDecompilationDisplayContext<'a>> for &'a AddressBech32Encoder {
72 fn into(self) -> ManifestDecompilationDisplayContext<'a> {
73 ManifestDecompilationDisplayContext::with_optional_bech32(Some(self))
74 }
75}
76
77impl<'a> Into<ManifestDecompilationDisplayContext<'a>> for Option<&'a AddressBech32Encoder> {
78 fn into(self) -> ManifestDecompilationDisplayContext<'a> {
79 ManifestDecompilationDisplayContext::with_optional_bech32(self)
80 }
81}
82
83impl<'a> ContextualDisplay<ManifestDecompilationDisplayContext<'a>> for ManifestValue {
84 type Error = fmt::Error;
85
86 fn contextual_format<F: fmt::Write>(
87 &self,
88 f: &mut F,
89 context: &ManifestDecompilationDisplayContext<'a>,
90 ) -> Result<(), Self::Error> {
91 format_manifest_value(f, self, context, false, 0)
92 }
93}
94
95macro_rules! write_with_indent {
96 ($f:expr, $context:expr, $should_indent:expr, $depth:expr, $($args:expr),*) => {
97 if $should_indent {
98 write!($f,
99 "{}{}",
100 $context.get_indent($depth),
101 format!($($args),*)
102 )
103 } else {
104 write!($f, $($args),*)
105 }
106 };
107}
108
109pub fn format_manifest_value<F: fmt::Write>(
110 f: &mut F,
111 value: &ManifestValue,
112 context: &ManifestDecompilationDisplayContext,
113 indent_start: bool,
114 depth: usize,
115) -> fmt::Result {
116 match value {
117 Value::Bool { value } => write_with_indent!(f, context, indent_start, depth, "{}", value)?,
119 Value::I8 { value } => write_with_indent!(f, context, indent_start, depth, "{}i8", value)?,
120 Value::I16 { value } => {
121 write_with_indent!(f, context, indent_start, depth, "{}i16", value)?
122 }
123 Value::I32 { value } => {
124 write_with_indent!(f, context, indent_start, depth, "{}i32", value)?
125 }
126 Value::I64 { value } => {
127 write_with_indent!(f, context, indent_start, depth, "{}i64", value)?
128 }
129 Value::I128 { value } => {
130 write_with_indent!(f, context, indent_start, depth, "{}i128", value)?
131 }
132 Value::U8 { value } => write_with_indent!(f, context, indent_start, depth, "{}u8", value)?,
133 Value::U16 { value } => {
134 write_with_indent!(f, context, indent_start, depth, "{}u16", value)?
135 }
136 Value::U32 { value } => {
137 write_with_indent!(f, context, indent_start, depth, "{}u32", value)?
138 }
139 Value::U64 { value } => {
140 write_with_indent!(f, context, indent_start, depth, "{}u64", value)?
141 }
142 Value::U128 { value } => {
143 write_with_indent!(f, context, indent_start, depth, "{}u128", value)?
144 }
145 Value::String { value } => {
146 write_with_indent!(
147 f,
148 context,
149 indent_start,
150 depth,
151 "{}",
152 ManifestCustomCharEscaper::escaped(value.as_str())
153 )?;
154 }
155 Value::Tuple { fields } => {
156 if fields.len() == 2 {
157 if let (
158 ManifestValue::Custom {
159 value: ManifestCustomValue::Address(ManifestAddress::Static(address)),
160 },
161 ManifestValue::Custom {
162 value: ManifestCustomValue::NonFungibleLocalId(id),
163 },
164 ) = (&fields[0], &fields[1])
165 {
166 if let Ok(resource_address) = ResourceAddress::try_from(address.0.as_ref()) {
167 let global_id = NonFungibleGlobalId::new(
168 resource_address,
169 to_non_fungible_local_id(id.clone()),
170 );
171 return write_with_indent!(
172 f,
173 context,
174 indent_start,
175 depth,
176 "NonFungibleGlobalId(\"{}\")",
177 global_id.display(context.address_bech32_encoder)
178 );
179 }
180 }
181 }
182
183 if fields.is_empty() {
184 write_with_indent!(f, context, indent_start, depth, "Tuple()")?;
185 } else {
186 write_with_indent!(
187 f,
188 context,
189 indent_start,
190 depth,
191 "Tuple({}",
192 context.get_new_line()
193 )?;
194 format_elements(f, fields, context, depth + 1)?;
195 write_with_indent!(f, context, true, depth, ")")?;
196 }
197 }
198 Value::Enum {
199 discriminator,
200 fields,
201 } => {
202 if fields.is_empty() {
203 write_with_indent!(
204 f,
205 context,
206 indent_start,
207 depth,
208 "Enum<{}u8>()",
209 discriminator
210 )?;
211 } else {
212 write_with_indent!(
213 f,
214 context,
215 indent_start,
216 depth,
217 "Enum<{}u8>({}",
218 discriminator,
219 context.get_new_line()
220 )?;
221 format_elements(f, fields, context, depth + 1)?;
222 write_with_indent!(f, context, true, depth, ")")?;
223 }
224 }
225 Value::Array {
226 element_value_kind,
227 elements,
228 } => match element_value_kind {
229 ValueKind::U8 => {
230 let vec: Vec<u8> = elements
231 .iter()
232 .map(|e| match e {
233 Value::U8 { value } => Ok(*value),
234 _ => Err(fmt::Error),
235 })
236 .collect::<Result<_, _>>()?;
237
238 write_with_indent!(
239 f,
240 context,
241 indent_start,
242 depth,
243 "Bytes(\"{}\")",
244 hex::encode(vec)
245 )?;
246 }
247 _ => {
248 if elements.is_empty() {
249 write_with_indent!(
250 f,
251 context,
252 indent_start,
253 depth,
254 "Array<{}>()",
255 format_value_kind(element_value_kind)
256 )?;
257 } else {
258 write_with_indent!(
259 f,
260 context,
261 indent_start,
262 depth,
263 "Array<{}>({}",
264 format_value_kind(element_value_kind),
265 context.get_new_line()
266 )?;
267 format_elements(f, elements, context, depth + 1)?;
268 write_with_indent!(f, context, true, depth, ")")?;
269 }
270 }
271 },
272 Value::Map {
273 key_value_kind,
274 value_value_kind,
275 entries,
276 } => {
277 if entries.is_empty() {
278 write_with_indent!(
279 f,
280 context,
281 indent_start,
282 depth,
283 "Map<{}, {}>()",
284 format_value_kind(key_value_kind),
285 format_value_kind(value_value_kind)
286 )?;
287 } else {
288 write_with_indent!(
289 f,
290 context,
291 indent_start,
292 depth,
293 "Map<{}, {}>({}",
294 format_value_kind(key_value_kind),
295 format_value_kind(value_value_kind),
296 context.get_new_line()
297 )?;
298 format_kv_entries(f, entries, context, depth + 1)?;
299 write_with_indent!(f, context, true, depth, ")")?;
300 }
301 }
302 Value::Custom { value } => {
304 format_custom_value(f, value, context, indent_start, depth)?;
305 }
306 };
307 Ok(())
308}
309
310pub fn format_elements<F: fmt::Write>(
311 f: &mut F,
312 values: &[ManifestValue],
313 context: &ManifestDecompilationDisplayContext,
314 depth: usize,
315) -> fmt::Result {
316 for (i, x) in values.iter().enumerate() {
317 format_manifest_value(f, x, context, true, depth)?;
318 if i == values.len() - 1 {
319 write!(f, "{}", context.get_new_line())?;
320 } else {
321 write!(f, ",{}", context.get_new_line())?;
322 }
323 }
324 Ok(())
325}
326
327pub fn format_kv_entries<F: fmt::Write>(
328 f: &mut F,
329 entries: &[(ManifestValue, ManifestValue)],
330 context: &ManifestDecompilationDisplayContext,
331 depth: usize,
332) -> fmt::Result {
333 for (i, x) in entries.iter().enumerate() {
334 format_manifest_value(f, &x.0, context, true, depth)?;
335 write!(f, " => ")?;
336 format_manifest_value(f, &x.1, context, false, depth)?;
337 if i == entries.len() - 1 {
338 write!(f, "{}", context.get_new_line())?;
339 } else {
340 write!(f, ",{}", context.get_new_line())?;
341 }
342 }
343 Ok(())
344}
345
346pub fn format_custom_value<F: fmt::Write>(
347 f: &mut F,
348 value: &ManifestCustomValue,
349 context: &ManifestDecompilationDisplayContext,
350 indent_start: bool,
351 depth: usize,
352) -> fmt::Result {
353 match value {
354 ManifestCustomValue::Address(value) => match value {
355 ManifestAddress::Static(node_id) => {
356 write_with_indent!(
357 f,
358 context,
359 indent_start,
360 depth,
361 "Address(\"{}\")",
362 if let Some(encoder) = context.address_bech32_encoder {
363 if let Ok(bech32) = encoder.encode(node_id.as_ref()) {
364 bech32
365 } else {
366 hex::encode(node_id.as_bytes())
367 }
368 } else {
369 hex::encode(node_id.as_bytes())
370 }
371 )?;
372 }
373 ManifestAddress::Named(address_id) => {
374 write_with_indent!(
375 f,
376 context,
377 indent_start,
378 depth,
379 "NamedAddress(\"{}\")",
380 context.get_object_names().address_name(*address_id)
381 )?;
382 }
383 },
384 ManifestCustomValue::Bucket(value) => {
385 write_with_indent!(
386 f,
387 context,
388 indent_start,
389 depth,
390 "Bucket(\"{}\")",
391 context.get_object_names().bucket_name(*value)
392 )?;
393 }
394 ManifestCustomValue::Proof(value) => {
395 write_with_indent!(
396 f,
397 context,
398 indent_start,
399 depth,
400 "Proof(\"{}\")",
401 context.get_object_names().proof_name(*value)
402 )?;
403 }
404 ManifestCustomValue::AddressReservation(value) => {
405 write_with_indent!(
406 f,
407 context,
408 indent_start,
409 depth,
410 "AddressReservation(\"{}\")",
411 context.get_object_names().address_reservation_name(*value)
412 )?;
413 }
414 ManifestCustomValue::Expression(value) => {
415 write_with_indent!(
416 f,
417 context,
418 indent_start,
419 depth,
420 "Expression(\"{}\")",
421 match value {
422 ManifestExpression::EntireWorktop => "ENTIRE_WORKTOP",
423 ManifestExpression::EntireAuthZone => "ENTIRE_AUTH_ZONE",
424 }
425 )?;
426 }
427 ManifestCustomValue::Blob(value) => {
428 write_with_indent!(
429 f,
430 context,
431 indent_start,
432 depth,
433 "Blob(\"{}\")",
434 hex::encode(&value.0)
435 )?;
436 }
437 ManifestCustomValue::Decimal(value) => {
438 write_with_indent!(
439 f,
440 context,
441 indent_start,
442 depth,
443 "Decimal(\"{}\")",
444 to_decimal(value.clone())
445 )?;
446 }
447 ManifestCustomValue::PreciseDecimal(value) => {
448 write_with_indent!(
449 f,
450 context,
451 indent_start,
452 depth,
453 "PreciseDecimal(\"{}\")",
454 to_precise_decimal(value.clone())
455 )?;
456 }
457 ManifestCustomValue::NonFungibleLocalId(value) => {
458 write_with_indent!(
459 f,
460 context,
461 indent_start,
462 depth,
463 "NonFungibleLocalId(\"{}\")",
464 to_non_fungible_local_id(value.clone())
465 )?;
466 }
467 }
468 Ok(())
469}
470
471pub fn format_value_kind(value_kind: &ManifestValueKind) -> &str {
472 match value_kind {
473 ValueKind::Bool => "Bool",
474 ValueKind::I8 => "I8",
475 ValueKind::I16 => "I16",
476 ValueKind::I32 => "I32",
477 ValueKind::I64 => "I64",
478 ValueKind::I128 => "I128",
479 ValueKind::U8 => "U8",
480 ValueKind::U16 => "U16",
481 ValueKind::U32 => "U32",
482 ValueKind::U64 => "U64",
483 ValueKind::U128 => "U128",
484 ValueKind::String => "String",
485 ValueKind::Enum => "Enum",
486 ValueKind::Array => "Array",
487 ValueKind::Tuple => "Tuple",
488 ValueKind::Map => "Map",
489 ValueKind::Custom(value_kind) => match value_kind {
490 ManifestCustomValueKind::Address => "Address",
491 ManifestCustomValueKind::Bucket => "Bucket",
492 ManifestCustomValueKind::Proof => "Proof",
493 ManifestCustomValueKind::Expression => "Expression",
494 ManifestCustomValueKind::Blob => "Blob",
495 ManifestCustomValueKind::Decimal => "Decimal",
496 ManifestCustomValueKind::PreciseDecimal => "PreciseDecimal",
497 ManifestCustomValueKind::NonFungibleLocalId => "NonFungibleLocalId",
498 ManifestCustomValueKind::AddressReservation => "AddressReservation",
499 },
500 }
501}
502
503pub fn display_value_kind(value_kind: &ManifestValueKind) -> DisplayableManifestValueKind {
504 DisplayableManifestValueKind(value_kind)
505}
506
507pub struct DisplayableManifestValueKind<'a>(&'a ManifestValueKind);
508
509impl<'a> fmt::Display for DisplayableManifestValueKind<'a> {
510 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
511 write!(f, "{}", format_value_kind(&self.0))
512 }
513}
514
515impl<'a> ContextualDisplay<ManifestDecompilationDisplayContext<'a>> for ManifestCustomValue {
516 type Error = fmt::Error;
517
518 fn contextual_format<F: fmt::Write>(
519 &self,
520 f: &mut F,
521 context: &ManifestDecompilationDisplayContext<'a>,
522 ) -> Result<(), Self::Error> {
523 format_custom_value(f, self, context, false, 0)
524 }
525}
526
527pub struct ManifestCustomCharEscaper;
528
529impl CustomCharEscaper for ManifestCustomCharEscaper {
530 fn resolve_escape_behaviour(c: char) -> EscapeBehaviour {
531 match c {
532 '\\' => EscapeBehaviour::Replace(r#"\\"#),
533 '\n' => EscapeBehaviour::Replace(r#"\n"#),
534 '\r' => EscapeBehaviour::Replace(r#"\r"#),
535 '\t' => EscapeBehaviour::Replace(r#"\t"#),
536 '\x08' => EscapeBehaviour::Replace(r#"\b"#),
537 '\x0c' => EscapeBehaviour::Replace(r#"\f"#),
538 '"' => EscapeBehaviour::Replace(r#"\""#),
539 _ if should_escape_unicode_char(c) => EscapeBehaviour::UnicodeEscape,
540 _ => EscapeBehaviour::None,
541 }
542 }
543
544 fn format_unicode_escaped_char(f: &mut impl fmt::Write, c: char) -> fmt::Result {
545 radix_rust::unicode::format_json_utf16_escaped_char(f, c)
546 }
547}
548
549fn should_escape_unicode_char(c: char) -> bool {
550 radix_rust::unicode::rust_1_81_should_unicode_escape_in_debug_str(c)
572}