linera_wallet_json/
display.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Display/formatting for wallet contents.
5
6use linera_base::{
7    data_types::{ChainDescription, ChainOrigin},
8    identifiers::ChainId,
9};
10use linera_core::wallet;
11
12use crate::wallet::Data;
13
14struct ChainDetails {
15    is_default: bool,
16    is_admin: bool,
17    origin: Option<ChainOrigin>,
18    chain_id: ChainId,
19    user_chain: wallet::Chain,
20}
21
22impl ChainDetails {
23    fn new(chain_id: ChainId, data: &Data) -> Self {
24        let Some(user_chain) = data.chains.get(chain_id) else {
25            panic!("Chain {chain_id} not found.");
26        };
27        ChainDetails {
28            is_default: Some(chain_id) == *data.default.read().unwrap(),
29            is_admin: chain_id == data.genesis_config.admin_chain_id(),
30            chain_id,
31            origin: data
32                .genesis_config
33                .chains
34                .iter()
35                .find(|description| description.id() == chain_id)
36                .map(ChainDescription::origin),
37            user_chain,
38        }
39    }
40
41    fn print_paragraph(&self) {
42        println!("-----------------------");
43        println!("{:<20}  {}", "Chain ID:", self.chain_id);
44
45        let mut tags = Vec::new();
46        if self.is_default {
47            tags.push("DEFAULT");
48        }
49        if self.is_admin {
50            tags.push("ADMIN");
51        }
52        if self.user_chain.is_follow_only() {
53            tags.push("FOLLOW-ONLY");
54        }
55        if !tags.is_empty() {
56            println!("{:<20}  {}", "Tags:", tags.join(", "));
57        }
58
59        match self.origin {
60            Some(ChainOrigin::Root(_)) | None => {
61                println!("{:<20}  -", "Parent chain:");
62            }
63            Some(ChainOrigin::Child { parent, .. }) => {
64                println!("{:<20}  {parent}", "Parent chain:");
65            }
66        }
67
68        if let Some(owner) = &self.user_chain.owner {
69            println!("{:<20}  {owner}", "Default owner:");
70        } else {
71            println!("{:<20}  No owner key", "Default owner:");
72        }
73
74        println!("{:<20}  {}", "Timestamp:", self.user_chain.timestamp);
75        println!("{:<20}  {}", "Blocks:", self.user_chain.next_block_height);
76
77        if let Some(epoch) = self.user_chain.epoch {
78            println!("{:<20}  {epoch}", "Epoch:");
79        } else {
80            println!("{:<20}  -", "Epoch:");
81        }
82
83        if let Some(hash) = self.user_chain.block_hash {
84            println!("{:<20}  {hash}", "Latest block hash:");
85        }
86
87        if self.user_chain.pending_fast_proposal.is_some() {
88            println!("{:<20}  present", "Pending fast proposal:");
89        }
90    }
91}
92
93/// Prints wallet chain details to stdout.
94pub fn pretty_print(wallet: &crate::PersistentWallet, chain_ids: Vec<ChainId>) {
95    let total_chains = chain_ids.len();
96    let plural_s = if total_chains == 1 { "" } else { "s" };
97    tracing::info!("Found {total_chains} chain{plural_s}");
98
99    let mut chains = chain_ids
100        .into_iter()
101        .map(|chain_id| ChainDetails::new(chain_id, wallet.data()))
102        .collect::<Vec<_>>();
103    chains.sort_unstable_by_key(|chain| {
104        let root_id = chain
105            .origin
106            .and_then(|origin| origin.root())
107            .unwrap_or(u32::MAX);
108        let chain_id = chain.chain_id;
109        (!chain.is_default, !chain.is_admin, root_id, chain_id)
110    });
111    for chain in chains {
112        chain.print_paragraph();
113    }
114    println!("------------------------");
115}