1use core::fmt;
13
14use crate::{
15 ArgDescriptor, CodamaProjection, CompatibilityPair, EventDescriptor, FieldDescriptor,
16 IdlAccountEntry, InstructionDescriptor, LayoutFingerprint, LayoutManifest, MigrationPolicy,
17 PdaSeedHint, PolicyDescriptor, ProgramIdl, ProgramManifest,
18};
19
20fn write_json_str(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
25 write!(f, "\"")?;
26 for c in s.chars() {
27 match c {
28 '"' => write!(f, "\\\"")?,
29 '\\' => write!(f, "\\\\")?,
30 '\n' => write!(f, "\\n")?,
31 '\r' => write!(f, "\\r")?,
32 '\t' => write!(f, "\\t")?,
33 _ => write!(f, "{}", c)?,
34 }
35 }
36 write!(f, "\"")
37}
38
39fn write_hex_json(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
40 write!(f, "\"")?;
41 for b in bytes {
42 write!(f, "{:02x}", b)?;
43 }
44 write!(f, "\"")
45}
46
47fn write_indent(f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
48 for _ in 0..level {
49 write!(f, " ")?;
50 }
51 Ok(())
52}
53
54fn write_str_array(f: &mut fmt::Formatter<'_>, items: &[&str], indent: usize) -> fmt::Result {
55 if items.is_empty() {
56 return write!(f, "[]");
57 }
58 writeln!(f, "[")?;
59 for (i, s) in items.iter().enumerate() {
60 write_indent(f, indent + 1)?;
61 write_json_str(f, s)?;
62 if i + 1 < items.len() {
63 writeln!(f, ",")?;
64 } else {
65 writeln!(f)?;
66 }
67 }
68 write_indent(f, indent)?;
69 write!(f, "]")
70}
71
72fn write_field_json(
77 f: &mut fmt::Formatter<'_>,
78 field: &FieldDescriptor,
79 indent: usize,
80) -> fmt::Result {
81 write_indent(f, indent)?;
82 write!(f, "{{ \"name\": ")?;
83 write_json_str(f, field.name)?;
84 write!(f, ", \"type\": ")?;
85 write_json_str(f, field.canonical_type)?;
86 write!(
87 f,
88 ", \"size\": {}, \"offset\": {}",
89 field.size, field.offset
90 )?;
91 write!(f, ", \"intent\": ")?;
92 write_json_str(f, field.intent.name())?;
93 write!(f, " }}")
94}
95
96fn write_fields_json(
97 f: &mut fmt::Formatter<'_>,
98 fields: &[FieldDescriptor],
99 indent: usize,
100) -> fmt::Result {
101 if fields.is_empty() {
102 return write!(f, "[]");
103 }
104 writeln!(f, "[")?;
105 for (i, field) in fields.iter().enumerate() {
106 write_field_json(f, field, indent + 1)?;
107 if i + 1 < fields.len() {
108 writeln!(f, ",")?;
109 } else {
110 writeln!(f)?;
111 }
112 }
113 write_indent(f, indent)?;
114 write!(f, "]")
115}
116
117fn write_args_json(
122 f: &mut fmt::Formatter<'_>,
123 args: &[ArgDescriptor],
124 indent: usize,
125) -> fmt::Result {
126 if args.is_empty() {
127 return write!(f, "[]");
128 }
129 writeln!(f, "[")?;
130 for (i, arg) in args.iter().enumerate() {
131 write_indent(f, indent + 1)?;
132 write!(f, "{{ \"name\": ")?;
133 write_json_str(f, arg.name)?;
134 write!(f, ", \"type\": ")?;
135 write_json_str(f, arg.canonical_type)?;
136 write!(f, ", \"size\": {} }}", arg.size)?;
137 if i + 1 < args.len() {
138 writeln!(f, ",")?;
139 } else {
140 writeln!(f)?;
141 }
142 }
143 write_indent(f, indent)?;
144 write!(f, "]")
145}
146
147fn write_idl_account_json(
152 f: &mut fmt::Formatter<'_>,
153 a: &IdlAccountEntry,
154 indent: usize,
155) -> fmt::Result {
156 write_indent(f, indent)?;
157 writeln!(f, "{{")?;
158 write_indent(f, indent + 1)?;
159 write!(f, "\"name\": ")?;
160 write_json_str(f, a.name)?;
161 writeln!(f, ",")?;
162 write_indent(f, indent + 1)?;
163 writeln!(f, "\"writable\": {},", a.writable)?;
164 write_indent(f, indent + 1)?;
165 write!(f, "\"signer\": {}", a.signer)?;
166 if !a.layout_ref.is_empty() {
167 writeln!(f, ",")?;
168 write_indent(f, indent + 1)?;
169 write!(f, "\"layoutRef\": ")?;
170 write_json_str(f, a.layout_ref)?;
171 }
172 if !a.pda_seeds.is_empty() {
173 writeln!(f, ",")?;
174 write_indent(f, indent + 1)?;
175 write!(f, "\"pdaSeeds\": ")?;
176 write_pda_seeds_json(f, a.pda_seeds, indent + 1)?;
177 }
178 writeln!(f)?;
179 write_indent(f, indent)?;
180 write!(f, "}}")
181}
182
183fn write_pda_seeds_json(
184 f: &mut fmt::Formatter<'_>,
185 seeds: &[PdaSeedHint],
186 indent: usize,
187) -> fmt::Result {
188 writeln!(f, "[")?;
189 for (i, seed) in seeds.iter().enumerate() {
190 write_indent(f, indent + 1)?;
191 write!(f, "{{ \"kind\": ")?;
192 write_json_str(f, seed.kind)?;
193 write!(f, ", \"value\": ")?;
194 write_json_str(f, seed.value)?;
195 write!(f, " }}")?;
196 if i + 1 < seeds.len() {
197 writeln!(f, ",")?;
198 } else {
199 writeln!(f)?;
200 }
201 }
202 write_indent(f, indent)?;
203 write!(f, "]")
204}
205
206pub struct CodamaJson<'a>(pub &'a CodamaProjection);
208
209impl<'a> fmt::Display for CodamaJson<'a> {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 let p = self.0;
212 writeln!(f, "{{")?;
213 write!(f, " \"name\": ")?;
214 write_json_str(f, p.name)?;
215 writeln!(f, ",")?;
216 write!(f, " \"version\": ")?;
217 write_json_str(f, p.version)?;
218 writeln!(f, ",")?;
219
220 write!(f, " \"instructions\": ")?;
222 if p.instructions.is_empty() {
223 writeln!(f, "[],")?;
224 } else {
225 writeln!(f, "[")?;
226 for (i, ix) in p.instructions.iter().enumerate() {
227 write_indent(f, 2)?;
228 writeln!(f, "{{")?;
229 write_indent(f, 3)?;
230 write!(f, "\"name\": ")?;
231 write_json_str(f, ix.name)?;
232 writeln!(f, ",")?;
233 write_indent(f, 3)?;
234 writeln!(f, "\"discriminator\": {},", ix.discriminator)?;
235 write_indent(f, 3)?;
236 write!(f, "\"args\": ")?;
237 write_args_json(f, ix.args, 3)?;
238 writeln!(f, ",")?;
239 write_indent(f, 3)?;
240 write!(f, "\"accounts\": ")?;
241 if ix.accounts.is_empty() {
242 write!(f, "[]")?;
243 } else {
244 writeln!(f, "[")?;
245 for (j, a) in ix.accounts.iter().enumerate() {
246 write_idl_account_json(f, a, 4)?;
247 if j + 1 < ix.accounts.len() {
248 writeln!(f, ",")?;
249 } else {
250 writeln!(f)?;
251 }
252 }
253 write_indent(f, 3)?;
254 write!(f, "]")?;
255 }
256 writeln!(f)?;
257 write_indent(f, 2)?;
258 write!(f, "}}")?;
259 if i + 1 < p.instructions.len() {
260 writeln!(f, ",")?;
261 } else {
262 writeln!(f)?;
263 }
264 }
265 writeln!(f, " ],")?;
266 }
267
268 write!(f, " \"accounts\": ")?;
270 if p.accounts.is_empty() {
271 writeln!(f, "[],")?;
272 } else {
273 writeln!(f, "[")?;
274 for (i, a) in p.accounts.iter().enumerate() {
275 write_indent(f, 2)?;
276 writeln!(f, "{{")?;
277 write_indent(f, 3)?;
278 write!(f, "\"name\": ")?;
279 write_json_str(f, a.name)?;
280 writeln!(f, ",")?;
281 write_indent(f, 3)?;
282 writeln!(f, "\"discriminator\": {},", a.discriminator)?;
283 write_indent(f, 3)?;
284 writeln!(f, "\"size\": {},", a.size)?;
285 write_indent(f, 3)?;
286 write!(f, "\"fields\": ")?;
287 write_fields_json(f, a.fields, 3)?;
288 writeln!(f)?;
289 write_indent(f, 2)?;
290 write!(f, "}}")?;
291 if i + 1 < p.accounts.len() {
292 writeln!(f, ",")?;
293 } else {
294 writeln!(f)?;
295 }
296 }
297 writeln!(f, " ],")?;
298 }
299
300 write!(f, " \"events\": ")?;
302 if p.events.is_empty() {
303 writeln!(f, "[]")?;
304 } else {
305 writeln!(f, "[")?;
306 for (i, e) in p.events.iter().enumerate() {
307 write_indent(f, 2)?;
308 writeln!(f, "{{")?;
309 write_indent(f, 3)?;
310 write!(f, "\"name\": ")?;
311 write_json_str(f, e.name)?;
312 writeln!(f, ",")?;
313 write_indent(f, 3)?;
314 writeln!(f, "\"discriminator\": {},", e.discriminator)?;
315 write_indent(f, 3)?;
316 write!(f, "\"fields\": ")?;
317 write_fields_json(f, e.fields, 3)?;
318 writeln!(f)?;
319 write_indent(f, 2)?;
320 write!(f, "}}")?;
321 if i + 1 < p.events.len() {
322 writeln!(f, ",")?;
323 } else {
324 writeln!(f)?;
325 }
326 }
327 writeln!(f, " ]")?;
328 }
329
330 write!(f, "}}")
331 }
332}
333
334pub struct IdlJson<'a>(pub &'a ProgramIdl);
340
341impl<'a> fmt::Display for IdlJson<'a> {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 let p = self.0;
344 writeln!(f, "{{")?;
345 write!(f, " \"name\": ")?;
346 write_json_str(f, p.name)?;
347 writeln!(f, ",")?;
348 write!(f, " \"version\": ")?;
349 write_json_str(f, p.version)?;
350 writeln!(f, ",")?;
351 write!(f, " \"description\": ")?;
352 write_json_str(f, p.description)?;
353 writeln!(f, ",")?;
354
355 write!(f, " \"instructions\": ")?;
357 if p.instructions.is_empty() {
358 writeln!(f, "[],")?;
359 } else {
360 writeln!(f, "[")?;
361 for (i, ix) in p.instructions.iter().enumerate() {
362 write_indent(f, 2)?;
363 writeln!(f, "{{")?;
364 write_indent(f, 3)?;
365 write!(f, "\"name\": ")?;
366 write_json_str(f, ix.name)?;
367 writeln!(f, ",")?;
368 write_indent(f, 3)?;
369 writeln!(f, "\"tag\": {},", ix.tag)?;
370 write_indent(f, 3)?;
371 write!(f, "\"args\": ")?;
372 write_args_json(f, ix.args, 3)?;
373 writeln!(f, ",")?;
374 write_indent(f, 3)?;
375 write!(f, "\"accounts\": ")?;
376 if ix.accounts.is_empty() {
377 write!(f, "[]")?;
378 } else {
379 writeln!(f, "[")?;
380 for (j, a) in ix.accounts.iter().enumerate() {
381 write_idl_account_json(f, a, 4)?;
382 if j + 1 < ix.accounts.len() {
383 writeln!(f, ",")?;
384 } else {
385 writeln!(f)?;
386 }
387 }
388 write_indent(f, 3)?;
389 write!(f, "]")?;
390 }
391 writeln!(f)?;
392 write_indent(f, 2)?;
393 write!(f, "}}")?;
394 if i + 1 < p.instructions.len() {
395 writeln!(f, ",")?;
396 } else {
397 writeln!(f)?;
398 }
399 }
400 writeln!(f, " ],")?;
401 }
402
403 write!(f, " \"accounts\": ")?;
405 if p.accounts.is_empty() {
406 writeln!(f, "[],")?;
407 } else {
408 writeln!(f, "[")?;
409 for (i, a) in p.accounts.iter().enumerate() {
410 write_indent(f, 2)?;
411 writeln!(f, "{{")?;
412 write_indent(f, 3)?;
413 write!(f, "\"name\": ")?;
414 write_json_str(f, a.name)?;
415 writeln!(f, ",")?;
416 write_indent(f, 3)?;
417 writeln!(f, "\"disc\": {},", a.disc)?;
418 write_indent(f, 3)?;
419 writeln!(f, "\"version\": {},", a.version)?;
420 write_indent(f, 3)?;
421 write!(f, "\"layoutId\": ")?;
422 write_hex_json(f, &a.layout_id)?;
423 writeln!(f, ",")?;
424 write_indent(f, 3)?;
425 writeln!(f, "\"totalSize\": {},", a.total_size)?;
426 write_indent(f, 3)?;
427 write!(f, "\"fields\": ")?;
428 write_fields_json(f, a.fields, 3)?;
429 writeln!(f)?;
430 write_indent(f, 2)?;
431 write!(f, "}}")?;
432 if i + 1 < p.accounts.len() {
433 writeln!(f, ",")?;
434 } else {
435 writeln!(f)?;
436 }
437 }
438 writeln!(f, " ],")?;
439 }
440
441 write!(f, " \"events\": ")?;
443 if p.events.is_empty() {
444 writeln!(f, "[],")?;
445 } else {
446 writeln!(f, "[")?;
447 for (i, e) in p.events.iter().enumerate() {
448 write_indent(f, 2)?;
449 writeln!(f, "{{")?;
450 write_indent(f, 3)?;
451 write!(f, "\"name\": ")?;
452 write_json_str(f, e.name)?;
453 writeln!(f, ",")?;
454 write_indent(f, 3)?;
455 writeln!(f, "\"tag\": {},", e.tag)?;
456 write_indent(f, 3)?;
457 write!(f, "\"fields\": ")?;
458 write_fields_json(f, e.fields, 3)?;
459 writeln!(f)?;
460 write_indent(f, 2)?;
461 write!(f, "}}")?;
462 if i + 1 < p.events.len() {
463 writeln!(f, ",")?;
464 } else {
465 writeln!(f)?;
466 }
467 }
468 writeln!(f, " ],")?;
469 }
470
471 write!(f, " \"fingerprints\": ")?;
473 if p.fingerprints.is_empty() {
474 writeln!(f, "[]")?;
475 } else {
476 writeln!(f, "[")?;
477 for (i, (fp, name)) in p.fingerprints.iter().enumerate() {
478 write_indent(f, 2)?;
479 write!(f, "{{ \"layoutId\": ")?;
480 write_hex_json(f, fp)?;
481 write!(f, ", \"name\": ")?;
482 write_json_str(f, name)?;
483 write!(f, " }}")?;
484 if i + 1 < p.fingerprints.len() {
485 writeln!(f, ",")?;
486 } else {
487 writeln!(f)?;
488 }
489 }
490 writeln!(f, " ]")?;
491 }
492
493 write!(f, "}}")
494 }
495}
496
497pub struct ManifestJson<'a>(pub &'a ProgramManifest);
503
504impl<'a> fmt::Display for ManifestJson<'a> {
505 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506 let p = self.0;
507 writeln!(f, "{{")?;
508 write!(f, " \"name\": ")?;
509 write_json_str(f, p.name)?;
510 writeln!(f, ",")?;
511 write!(f, " \"version\": ")?;
512 write_json_str(f, p.version)?;
513 writeln!(f, ",")?;
514 write!(f, " \"description\": ")?;
515 write_json_str(f, p.description)?;
516 writeln!(f, ",")?;
517
518 write!(f, " \"layouts\": ")?;
520 write_layout_array(f, p.layouts)?;
521 writeln!(f, ",")?;
522
523 write!(f, " \"instructions\": ")?;
525 write_instruction_array(f, p.instructions)?;
526 writeln!(f, ",")?;
527
528 write!(f, " \"events\": ")?;
530 write_event_array(f, p.events)?;
531 writeln!(f, ",")?;
532
533 write!(f, " \"policies\": ")?;
535 write_policy_array(f, p.policies)?;
536 writeln!(f, ",")?;
537
538 write!(f, " \"compatRules\": ")?;
540 write_compat_pair_array(f, p.compatibility_pairs)?;
541 writeln!(f, ",")?;
542
543 write!(f, " \"receiptSchema\": ")?;
545 write_receipt_schema(f)?;
546 writeln!(f, ",")?;
547
548 write!(f, " \"toolingHints\": ")?;
550 write_str_array(f, p.tooling_hints, 1)?;
551 writeln!(f)?;
552
553 write!(f, "}}")
554 }
555}
556
557fn write_layout_array(f: &mut fmt::Formatter<'_>, layouts: &[LayoutManifest]) -> fmt::Result {
558 if layouts.is_empty() {
559 return write!(f, "[]");
560 }
561 writeln!(f, "[")?;
562 for (i, l) in layouts.iter().enumerate() {
563 write_indent(f, 2)?;
564 writeln!(f, "{{")?;
565 write_indent(f, 3)?;
566 write!(f, "\"name\": ")?;
567 write_json_str(f, l.name)?;
568 writeln!(f, ",")?;
569 write_indent(f, 3)?;
570 writeln!(f, "\"disc\": {},", l.disc)?;
571 write_indent(f, 3)?;
572 writeln!(f, "\"version\": {},", l.version)?;
573 write_indent(f, 3)?;
574 write!(f, "\"layoutId\": ")?;
575 write_hex_json(f, &l.layout_id)?;
576 writeln!(f, ",")?;
577 write_indent(f, 3)?;
578 writeln!(f, "\"totalSize\": {},", l.total_size)?;
579 write_indent(f, 3)?;
580 writeln!(f, "\"fieldCount\": {},", l.field_count)?;
581 write_indent(f, 3)?;
582 write!(f, "\"fields\": ")?;
583 write_fields_json(f, l.fields, 3)?;
584 writeln!(f, ",")?;
585 let fp = LayoutFingerprint::from_manifest(l);
587 write_indent(f, 3)?;
588 write!(f, "\"semanticFingerprint\": ")?;
589 write_hex_json(f, &fp.semantic_hash)?;
590 writeln!(f)?;
591 write_indent(f, 2)?;
592 write!(f, "}}")?;
593 if i + 1 < layouts.len() {
594 writeln!(f, ",")?;
595 } else {
596 writeln!(f)?;
597 }
598 }
599 write_indent(f, 1)?;
600 write!(f, "]")
601}
602
603fn write_instruction_array(
604 f: &mut fmt::Formatter<'_>,
605 instrs: &[InstructionDescriptor],
606) -> fmt::Result {
607 if instrs.is_empty() {
608 return write!(f, "[]");
609 }
610 writeln!(f, "[")?;
611 for (i, ix) in instrs.iter().enumerate() {
612 write_indent(f, 2)?;
613 writeln!(f, "{{")?;
614 write_indent(f, 3)?;
615 write!(f, "\"name\": ")?;
616 write_json_str(f, ix.name)?;
617 writeln!(f, ",")?;
618 write_indent(f, 3)?;
619 writeln!(f, "\"tag\": {},", ix.tag)?;
620 write_indent(f, 3)?;
621 write!(f, "\"args\": ")?;
622 write_args_json(f, ix.args, 3)?;
623 writeln!(f, ",")?;
624 write_indent(f, 3)?;
625 write!(f, "\"accounts\": ")?;
626 write_account_entry_array(f, ix.accounts, 3)?;
627 writeln!(f, ",")?;
628 write_indent(f, 3)?;
629 write!(f, "\"capabilities\": ")?;
630 write_str_array(f, ix.capabilities, 3)?;
631 writeln!(f, ",")?;
632 write_indent(f, 3)?;
633 write!(f, "\"policyPack\": ")?;
634 write_json_str(f, ix.policy_pack)?;
635 writeln!(f, ",")?;
636 write_indent(f, 3)?;
637 writeln!(f, "\"receiptExpected\": {}", ix.receipt_expected)?;
638 write_indent(f, 2)?;
639 write!(f, "}}")?;
640 if i + 1 < instrs.len() {
641 writeln!(f, ",")?;
642 } else {
643 writeln!(f)?;
644 }
645 }
646 write_indent(f, 1)?;
647 write!(f, "]")
648}
649
650fn write_account_entry_array(
651 f: &mut fmt::Formatter<'_>,
652 accounts: &[crate::AccountEntry],
653 indent: usize,
654) -> fmt::Result {
655 if accounts.is_empty() {
656 return write!(f, "[]");
657 }
658 writeln!(f, "[")?;
659 for (i, a) in accounts.iter().enumerate() {
660 write_indent(f, indent + 1)?;
661 write!(f, "{{ \"name\": ")?;
662 write_json_str(f, a.name)?;
663 write!(
664 f,
665 ", \"writable\": {}, \"signer\": {}",
666 a.writable, a.signer
667 )?;
668 if !a.layout_ref.is_empty() {
669 write!(f, ", \"layoutRef\": ")?;
670 write_json_str(f, a.layout_ref)?;
671 }
672 write!(f, " }}")?;
673 if i + 1 < accounts.len() {
674 writeln!(f, ",")?;
675 } else {
676 writeln!(f)?;
677 }
678 }
679 write_indent(f, indent)?;
680 write!(f, "]")
681}
682
683fn write_event_array(f: &mut fmt::Formatter<'_>, events: &[EventDescriptor]) -> fmt::Result {
684 if events.is_empty() {
685 return write!(f, "[]");
686 }
687 writeln!(f, "[")?;
688 for (i, e) in events.iter().enumerate() {
689 write_indent(f, 2)?;
690 writeln!(f, "{{")?;
691 write_indent(f, 3)?;
692 write!(f, "\"name\": ")?;
693 write_json_str(f, e.name)?;
694 writeln!(f, ",")?;
695 write_indent(f, 3)?;
696 writeln!(f, "\"tag\": {},", e.tag)?;
697 write_indent(f, 3)?;
698 write!(f, "\"fields\": ")?;
699 write_fields_json(f, e.fields, 3)?;
700 writeln!(f)?;
701 write_indent(f, 2)?;
702 write!(f, "}}")?;
703 if i + 1 < events.len() {
704 writeln!(f, ",")?;
705 } else {
706 writeln!(f)?;
707 }
708 }
709 write_indent(f, 1)?;
710 write!(f, "]")
711}
712
713fn write_policy_array(f: &mut fmt::Formatter<'_>, policies: &[PolicyDescriptor]) -> fmt::Result {
714 if policies.is_empty() {
715 return write!(f, "[]");
716 }
717 writeln!(f, "[")?;
718 for (i, p) in policies.iter().enumerate() {
719 write_indent(f, 2)?;
720 writeln!(f, "{{")?;
721 write_indent(f, 3)?;
722 write!(f, "\"name\": ")?;
723 write_json_str(f, p.name)?;
724 writeln!(f, ",")?;
725 write_indent(f, 3)?;
726 write!(f, "\"capabilities\": ")?;
727 write_str_array(f, p.capabilities, 3)?;
728 writeln!(f, ",")?;
729 write_indent(f, 3)?;
730 write!(f, "\"requirements\": ")?;
731 write_str_array(f, p.requirements, 3)?;
732 writeln!(f, ",")?;
733 write_indent(f, 3)?;
734 write!(f, "\"invariants\": ")?;
735 write_str_array(f, p.invariants, 3)?;
736 writeln!(f, ",")?;
737 write_indent(f, 3)?;
738 write!(f, "\"receiptProfile\": ")?;
739 write_json_str(f, p.receipt_profile)?;
740 writeln!(f)?;
741 write_indent(f, 2)?;
742 write!(f, "}}")?;
743 if i + 1 < policies.len() {
744 writeln!(f, ",")?;
745 } else {
746 writeln!(f)?;
747 }
748 }
749 write_indent(f, 1)?;
750 write!(f, "]")
751}
752
753fn write_compat_pair_array(f: &mut fmt::Formatter<'_>, pairs: &[CompatibilityPair]) -> fmt::Result {
754 if pairs.is_empty() {
755 return write!(f, "[]");
756 }
757 writeln!(f, "[")?;
758 for (i, cp) in pairs.iter().enumerate() {
759 write_indent(f, 2)?;
760 writeln!(f, "{{")?;
761 write_indent(f, 3)?;
762 write!(f, "\"from\": ")?;
763 write_json_str(f, cp.from_layout)?;
764 writeln!(f, ",")?;
765 write_indent(f, 3)?;
766 writeln!(f, "\"fromVersion\": {},", cp.from_version)?;
767 write_indent(f, 3)?;
768 write!(f, "\"to\": ")?;
769 write_json_str(f, cp.to_layout)?;
770 writeln!(f, ",")?;
771 write_indent(f, 3)?;
772 writeln!(f, "\"toVersion\": {},", cp.to_version)?;
773 write_indent(f, 3)?;
774 let policy_name = match cp.policy {
775 MigrationPolicy::NoOp => "noop",
776 MigrationPolicy::AppendOnly => "append-only",
777 MigrationPolicy::RequiresMigration => "requires-migration",
778 MigrationPolicy::Incompatible => "incompatible",
779 };
780 write!(f, "\"policy\": ")?;
781 write_json_str(f, policy_name)?;
782 writeln!(f, ",")?;
783 write_indent(f, 3)?;
784 writeln!(f, "\"backwardReadable\": {}", cp.backward_readable)?;
785 write_indent(f, 2)?;
786 write!(f, "}}")?;
787 if i + 1 < pairs.len() {
788 writeln!(f, ",")?;
789 } else {
790 writeln!(f)?;
791 }
792 }
793 write_indent(f, 1)?;
794 write!(f, "]")
795}
796
797fn write_receipt_schema(f: &mut fmt::Formatter<'_>) -> fmt::Result {
801 writeln!(f, "{{")?;
802 write_indent(f, 2)?;
803 writeln!(f, "\"size\": 64,")?;
804 write_indent(f, 2)?;
805 writeln!(f, "\"fields\": [")?;
806 let fields: &[(&str, &str, u8, u8)] = &[
807 ("layout_id", "bytes", 0, 8),
808 ("phase", "u8", 8, 1),
809 ("committed", "bool", 9, 1),
810 ("changed_fields", "u64", 10, 8),
811 ("changed_bytes", "u16", 18, 2),
812 ("changed_regions", "u8", 20, 1),
813 ("was_resized", "bool", 21, 1),
814 ("old_size", "u16", 22, 2),
815 ("new_size", "u16", 24, 2),
816 ("before_fingerprint", "bytes", 26, 4),
817 ("after_fingerprint", "bytes", 30, 4),
818 ("invariants_passed", "bool", 34, 1),
819 ("invariants_checked", "u8", 35, 1),
820 ("cpi_invoked", "bool", 36, 1),
821 ("cpi_count", "u8", 37, 1),
822 ("journal_appends", "u8", 38, 1),
823 ("segment_changed_mask", "u16", 39, 2),
824 ("policy_flags", "u32", 41, 4),
825 ("compat_impact", "u8", 45, 1),
826 ("validation_bundle_id", "u8", 46, 1),
827 ("migration_flags", "u8", 47, 1),
828 ];
829 for (i, (name, ty, offset, size)) in fields.iter().enumerate() {
830 write_indent(f, 3)?;
831 write!(f, "{{ \"name\": ")?;
832 write_json_str(f, name)?;
833 write!(f, ", \"type\": ")?;
834 write_json_str(f, ty)?;
835 write!(f, ", \"offset\": {}, \"size\": {} }}", offset, size)?;
836 if i + 1 < fields.len() {
837 writeln!(f, ",")?;
838 } else {
839 writeln!(f)?;
840 }
841 }
842 write_indent(f, 2)?;
843 writeln!(f, "]")?;
844 write_indent(f, 1)?;
845 write!(f, "}}")
846}
847
848pub struct IdlJsonFromManifest<'a>(pub &'a ProgramManifest);
858
859impl<'a> fmt::Display for IdlJsonFromManifest<'a> {
860 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 let p = self.0;
862 writeln!(f, "{{")?;
863 write!(f, " \"name\": ")?;
864 write_json_str(f, p.name)?;
865 writeln!(f, ",")?;
866 write!(f, " \"version\": ")?;
867 write_json_str(f, p.version)?;
868 writeln!(f, ",")?;
869 write!(f, " \"description\": ")?;
870 write_json_str(f, p.description)?;
871 writeln!(f, ",")?;
872
873 write!(f, " \"instructions\": ")?;
875 if p.instructions.is_empty() {
876 writeln!(f, "[],")?;
877 } else {
878 writeln!(f, "[")?;
879 for (i, ix) in p.instructions.iter().enumerate() {
880 write_indent(f, 2)?;
881 writeln!(f, "{{")?;
882 write_indent(f, 3)?;
883 write!(f, "\"name\": ")?;
884 write_json_str(f, ix.name)?;
885 writeln!(f, ",")?;
886 write_indent(f, 3)?;
887 writeln!(f, "\"tag\": {},", ix.tag)?;
888 write_indent(f, 3)?;
889 write!(f, "\"args\": ")?;
890 write_args_json(f, ix.args, 3)?;
891 writeln!(f, ",")?;
892 write_indent(f, 3)?;
893 write!(f, "\"accounts\": ")?;
894 write_account_entry_array(f, ix.accounts, 3)?;
895 writeln!(f)?;
896 write_indent(f, 2)?;
897 write!(f, "}}")?;
898 if i + 1 < p.instructions.len() {
899 writeln!(f, ",")?;
900 } else {
901 writeln!(f)?;
902 }
903 }
904 writeln!(f, " ],")?;
905 }
906
907 write!(f, " \"accounts\": ")?;
909 write_layout_array(f, p.layouts)?;
910 writeln!(f, ",")?;
911
912 write!(f, " \"events\": ")?;
914 write_event_array(f, p.events)?;
915 writeln!(f, ",")?;
916
917 write!(f, " \"fingerprints\": ")?;
919 if p.layouts.is_empty() {
920 writeln!(f, "[]")?;
921 } else {
922 writeln!(f, "[")?;
923 for (i, l) in p.layouts.iter().enumerate() {
924 write_indent(f, 2)?;
925 write!(f, "{{ \"layoutId\": ")?;
926 write_hex_json(f, &l.layout_id)?;
927 write!(f, ", \"name\": ")?;
928 write_json_str(f, l.name)?;
929 write!(f, " }}")?;
930 if i + 1 < p.layouts.len() {
931 writeln!(f, ",")?;
932 } else {
933 writeln!(f)?;
934 }
935 }
936 writeln!(f, " ]")?;
937 }
938
939 write!(f, "}}")
940 }
941}
942
943pub struct CodamaJsonFromManifest<'a>(pub &'a ProgramManifest);
949
950impl<'a> fmt::Display for CodamaJsonFromManifest<'a> {
951 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
952 let p = self.0;
953 writeln!(f, "{{")?;
954 write!(f, " \"name\": ")?;
955 write_json_str(f, p.name)?;
956 writeln!(f, ",")?;
957 write!(f, " \"version\": ")?;
958 write_json_str(f, p.version)?;
959 writeln!(f, ",")?;
960
961 write!(f, " \"instructions\": ")?;
963 if p.instructions.is_empty() {
964 writeln!(f, "[],")?;
965 } else {
966 writeln!(f, "[")?;
967 for (i, ix) in p.instructions.iter().enumerate() {
968 write_indent(f, 2)?;
969 writeln!(f, "{{")?;
970 write_indent(f, 3)?;
971 write!(f, "\"name\": ")?;
972 write_json_str(f, ix.name)?;
973 writeln!(f, ",")?;
974 write_indent(f, 3)?;
975 writeln!(f, "\"discriminator\": {},", ix.tag)?;
976 write_indent(f, 3)?;
977 write!(f, "\"args\": ")?;
978 write_args_json(f, ix.args, 3)?;
979 writeln!(f, ",")?;
980 write_indent(f, 3)?;
981 write!(f, "\"accounts\": ")?;
982 write_account_entry_array(f, ix.accounts, 3)?;
983 writeln!(f)?;
984 write_indent(f, 2)?;
985 write!(f, "}}")?;
986 if i + 1 < p.instructions.len() {
987 writeln!(f, ",")?;
988 } else {
989 writeln!(f)?;
990 }
991 }
992 writeln!(f, " ],")?;
993 }
994
995 write!(f, " \"accounts\": ")?;
997 if p.layouts.is_empty() {
998 writeln!(f, "[],")?;
999 } else {
1000 writeln!(f, "[")?;
1001 for (i, l) in p.layouts.iter().enumerate() {
1002 write_indent(f, 2)?;
1003 writeln!(f, "{{")?;
1004 write_indent(f, 3)?;
1005 write!(f, "\"name\": ")?;
1006 write_json_str(f, l.name)?;
1007 writeln!(f, ",")?;
1008 write_indent(f, 3)?;
1009 writeln!(f, "\"discriminator\": {},", l.disc)?;
1010 write_indent(f, 3)?;
1011 writeln!(f, "\"size\": {},", l.total_size)?;
1012 write_indent(f, 3)?;
1013 write!(f, "\"fields\": ")?;
1014 write_fields_json(f, l.fields, 3)?;
1015 writeln!(f)?;
1016 write_indent(f, 2)?;
1017 write!(f, "}}")?;
1018 if i + 1 < p.layouts.len() {
1019 writeln!(f, ",")?;
1020 } else {
1021 writeln!(f)?;
1022 }
1023 }
1024 writeln!(f, " ],")?;
1025 }
1026
1027 write!(f, " \"events\": ")?;
1029 if p.events.is_empty() {
1030 writeln!(f, "[]")?;
1031 } else {
1032 writeln!(f, "[")?;
1033 for (i, e) in p.events.iter().enumerate() {
1034 write_indent(f, 2)?;
1035 writeln!(f, "{{")?;
1036 write_indent(f, 3)?;
1037 write!(f, "\"name\": ")?;
1038 write_json_str(f, e.name)?;
1039 writeln!(f, ",")?;
1040 write_indent(f, 3)?;
1041 writeln!(f, "\"discriminator\": {},", e.tag)?;
1042 write_indent(f, 3)?;
1043 write!(f, "\"fields\": ")?;
1044 write_fields_json(f, e.fields, 3)?;
1045 writeln!(f)?;
1046 write_indent(f, 2)?;
1047 write!(f, "}}")?;
1048 if i + 1 < p.events.len() {
1049 writeln!(f, ",")?;
1050 } else {
1051 writeln!(f)?;
1052 }
1053 }
1054 writeln!(f, " ]")?;
1055 }
1056
1057 write!(f, "}}")
1058 }
1059}
1060
1061#[cfg(test)]
1066mod tests {
1067 extern crate alloc;
1068 use alloc::format;
1069
1070 use super::*;
1071 use crate::{
1072 CodamaAccount, CodamaInstruction, CodamaProjection, FieldIntent, ProgramIdl,
1073 ProgramManifest,
1074 };
1075
1076 #[test]
1077 fn codama_json_empty() {
1078 let c = CodamaProjection::empty();
1079 let json = format!("{}", CodamaJson(&c));
1080 assert!(json.contains("\"name\": \"\""));
1081 assert!(json.contains("\"instructions\": []"));
1082 assert!(json.contains("\"accounts\": []"));
1083 assert!(json.contains("\"events\": []"));
1084 }
1085
1086 #[test]
1087 fn idl_json_empty() {
1088 let idl = ProgramIdl::empty();
1089 let json = format!("{}", IdlJson(&idl));
1090 assert!(json.contains("\"name\": \"\""));
1091 assert!(json.contains("\"instructions\": []"));
1092 assert!(json.contains("\"accounts\": []"));
1093 assert!(json.contains("\"events\": []"));
1094 assert!(json.contains("\"fingerprints\": []"));
1095 }
1096
1097 #[test]
1098 fn manifest_json_empty() {
1099 let m = ProgramManifest::empty();
1100 let json = format!("{}", ManifestJson(&m));
1101 assert!(json.contains("\"name\": \"\""));
1102 assert!(json.contains("\"layouts\": []"));
1103 assert!(json.contains("\"instructions\": []"));
1104 assert!(json.contains("\"events\": []"));
1105 assert!(json.contains("\"policies\": []"));
1106 }
1107
1108 #[test]
1109 fn codama_json_with_instruction() {
1110 static ARGS: &[ArgDescriptor] = &[ArgDescriptor {
1111 name: "amount",
1112 canonical_type: "WireU64",
1113 size: 8,
1114 }];
1115 static ACCOUNTS: &[IdlAccountEntry] = &[IdlAccountEntry {
1116 name: "vault",
1117 writable: true,
1118 signer: false,
1119 layout_ref: "VaultState",
1120 pda_seeds: &[],
1121 }];
1122 static IX: &[CodamaInstruction] = &[CodamaInstruction {
1123 name: "deposit",
1124 discriminator: 1,
1125 args: ARGS,
1126 accounts: ACCOUNTS,
1127 }];
1128 let c = CodamaProjection {
1129 name: "test_program",
1130 version: "0.1.0",
1131 instructions: IX,
1132 accounts: &[],
1133 events: &[],
1134 };
1135 let json = format!("{}", CodamaJson(&c));
1136 assert!(json.contains("\"test_program\""));
1137 assert!(json.contains("\"deposit\""));
1138 assert!(json.contains("\"discriminator\": 1"));
1139 assert!(json.contains("\"amount\""));
1140 assert!(json.contains("\"vault\""));
1141 assert!(json.contains("\"writable\": true"));
1142 assert!(json.contains("\"layoutRef\": \"VaultState\""));
1143 }
1144
1145 #[test]
1146 fn codama_json_with_account() {
1147 static FIELDS: &[FieldDescriptor] = &[FieldDescriptor {
1148 name: "balance",
1149 canonical_type: "WireU64",
1150 size: 8,
1151 offset: 16,
1152 intent: FieldIntent::Custom,
1153 }];
1154 static ACCTS: &[CodamaAccount] = &[CodamaAccount {
1155 name: "VaultState",
1156 discriminator: 1,
1157 size: 24,
1158 fields: FIELDS,
1159 }];
1160 let c = CodamaProjection {
1161 name: "test",
1162 version: "0.1.0",
1163 instructions: &[],
1164 accounts: ACCTS,
1165 events: &[],
1166 };
1167 let json = format!("{}", CodamaJson(&c));
1168 assert!(json.contains("\"VaultState\""));
1169 assert!(json.contains("\"discriminator\": 1"));
1170 assert!(json.contains("\"size\": 24"));
1171 assert!(json.contains("\"balance\""));
1172 }
1173
1174 #[test]
1175 fn manifest_json_with_policy() {
1176 static POLICIES: &[PolicyDescriptor] = &[PolicyDescriptor {
1177 name: "TREASURY_WRITE",
1178 capabilities: &["MutatesState", "MutatesTreasury"],
1179 requirements: &["SignerAuthority"],
1180 invariants: &[],
1181 receipt_profile: "full",
1182 }];
1183 let m = ProgramManifest {
1184 name: "test",
1185 version: "0.1.0",
1186 description: "A test program",
1187 layouts: &[],
1188 layout_metadata: &[],
1189 instructions: &[],
1190 events: &[],
1191 policies: POLICIES,
1192 compatibility_pairs: &[],
1193 tooling_hints: &["show_receipt"],
1194 contexts: &[],
1195 };
1196 let json = format!("{}", ManifestJson(&m));
1197 assert!(json.contains("\"TREASURY_WRITE\""));
1198 assert!(json.contains("\"MutatesState\""));
1199 assert!(json.contains("\"SignerAuthority\""));
1200 assert!(json.contains("\"full\""));
1201 assert!(json.contains("\"show_receipt\""));
1202 }
1203
1204 #[test]
1205 fn json_str_escapes_special_chars() {
1206 static FIELDS: &[FieldDescriptor] = &[];
1207 static ACCTS: &[CodamaAccount] = &[CodamaAccount {
1208 name: "has\"quotes",
1209 discriminator: 1,
1210 size: 16,
1211 fields: FIELDS,
1212 }];
1213 let c = CodamaProjection {
1214 name: "test\\prog",
1215 version: "1.0",
1216 instructions: &[],
1217 accounts: ACCTS,
1218 events: &[],
1219 };
1220 let json = format!("{}", CodamaJson(&c));
1221 assert!(json.contains("\"test\\\\prog\""));
1222 assert!(json.contains("\"has\\\"quotes\""));
1223 }
1224
1225 #[test]
1226 fn idl_from_manifest_projection() {
1227 static FIELDS: &[FieldDescriptor] = &[FieldDescriptor {
1228 name: "balance",
1229 canonical_type: "WireU64",
1230 size: 8,
1231 offset: 16,
1232 intent: FieldIntent::Custom,
1233 }];
1234 static LAYOUTS: &[LayoutManifest] = &[LayoutManifest {
1235 name: "Vault",
1236 disc: 1,
1237 version: 1,
1238 layout_id: [1, 2, 3, 4, 5, 6, 7, 8],
1239 total_size: 24,
1240 field_count: 1,
1241 fields: FIELDS,
1242 }];
1243 static ARGS: &[ArgDescriptor] = &[ArgDescriptor {
1244 name: "amount",
1245 canonical_type: "WireU64",
1246 size: 8,
1247 }];
1248 static ACCTS: &[crate::AccountEntry] = &[crate::AccountEntry {
1249 name: "vault",
1250 writable: true,
1251 signer: false,
1252 layout_ref: "Vault",
1253 }];
1254 static IX: &[InstructionDescriptor] = &[InstructionDescriptor {
1255 name: "deposit",
1256 tag: 1,
1257 args: ARGS,
1258 accounts: ACCTS,
1259 capabilities: &["MutatesState"],
1260 policy_pack: "TREASURY_WRITE",
1261 receipt_expected: true,
1262 }];
1263 let m = ProgramManifest {
1264 name: "vault_prog",
1265 version: "1.0.0",
1266 description: "A vault",
1267 layouts: LAYOUTS,
1268 layout_metadata: &[],
1269 instructions: IX,
1270 events: &[],
1271 policies: &[],
1272 compatibility_pairs: &[],
1273 tooling_hints: &[],
1274 contexts: &[],
1275 };
1276 let json = format!("{}", IdlJsonFromManifest(&m));
1277 assert!(json.contains("\"deposit\""));
1279 assert!(json.contains("\"tag\": 1"));
1280 assert!(json.contains("\"amount\""));
1281 assert!(json.contains("\"vault\""));
1282 assert!(!json.contains("\"capabilities\""));
1283 assert!(!json.contains("\"policyPack\""));
1284 assert!(!json.contains("\"receiptExpected\""));
1285 assert!(json.contains("\"fingerprints\""));
1287 assert!(json.contains("\"Vault\""));
1288 }
1289
1290 #[test]
1291 fn codama_from_manifest_projection() {
1292 static ARGS: &[ArgDescriptor] = &[ArgDescriptor {
1293 name: "amount",
1294 canonical_type: "WireU64",
1295 size: 8,
1296 }];
1297 static ACCTS: &[crate::AccountEntry] = &[crate::AccountEntry {
1298 name: "vault",
1299 writable: true,
1300 signer: false,
1301 layout_ref: "Vault",
1302 }];
1303 static IX: &[InstructionDescriptor] = &[InstructionDescriptor {
1304 name: "deposit",
1305 tag: 1,
1306 args: ARGS,
1307 accounts: ACCTS,
1308 capabilities: &["MutatesState"],
1309 policy_pack: "TREASURY_WRITE",
1310 receipt_expected: true,
1311 }];
1312 static FIELDS: &[FieldDescriptor] = &[FieldDescriptor {
1313 name: "balance",
1314 canonical_type: "WireU64",
1315 size: 8,
1316 offset: 16,
1317 intent: FieldIntent::Custom,
1318 }];
1319 static LAYOUTS: &[LayoutManifest] = &[LayoutManifest {
1320 name: "Vault",
1321 disc: 1,
1322 version: 1,
1323 layout_id: [1, 2, 3, 4, 5, 6, 7, 8],
1324 total_size: 24,
1325 field_count: 1,
1326 fields: FIELDS,
1327 }];
1328 let m = ProgramManifest {
1329 name: "vault_prog",
1330 version: "1.0.0",
1331 description: "A vault",
1332 layouts: LAYOUTS,
1333 layout_metadata: &[],
1334 instructions: IX,
1335 events: &[],
1336 policies: &[],
1337 compatibility_pairs: &[],
1338 tooling_hints: &[],
1339 contexts: &[],
1340 };
1341 let json = format!("{}", CodamaJsonFromManifest(&m));
1342 assert!(json.contains("\"discriminator\": 1"));
1344 assert!(json.contains("\"deposit\""));
1345 assert!(json.contains("\"Vault\""));
1346 assert!(json.contains("\"size\": 24"));
1347 assert!(!json.contains("\"capabilities\""));
1349 assert!(!json.contains("\"policyPack\""));
1350 assert!(!json.contains("\"receiptExpected\""));
1351 assert!(!json.contains("\"fingerprints\""));
1352 assert!(!json.contains("\"toolingHints\""));
1353 }
1354}