use std::fmt::Display;
use corevm_tooling::CoreVmCodeInfo;
use crossterm::style::Stylize;
use jam_program_blob_common::CrateInfo;
use jam_std_common::{Service, ServiceActivityRecord};
use jam_tooling::{
format::{amount, bytes, gas, percent},
CodeInfo,
};
use jam_types::{core_count, hex, max_accumulate_gas, max_refine_gas, ServiceId, UnsignedGas};
use crate::{ACC_COL, PROV_COL, REF_COL};
#[allow(dead_code)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Column {
Id,
Version,
Name,
Flags,
Items,
RefPercent,
RefPercentCore,
RefGas,
RefItems,
AccPercent,
AccPercentCore,
AccGas,
AccItems,
PreimageSize,
PreimageCount,
ExportsCount,
ImportsCount,
ExtrinsicsCount,
ExtrinsicsSize,
Balance,
MinBalance,
FreeBalance,
StorageSize,
StorageItems,
}
impl Display for Column {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Column::*;
match self {
Id => write!(f, "ID"),
Version => write!(f, "VERSION"),
Name => write!(f, "NAME"),
Flags => write!(f, "FLGS"),
Items => write!(f, "ITMS"),
RefPercent => write!(f, "%RG"),
RefPercentCore => write!(f, "%RGC"),
RefGas => write!(f, "RGAS"),
RefItems => write!(f, "REFS"),
AccPercent => write!(f, "%AG"),
AccPercentCore => write!(f, "%AGC"),
AccGas => write!(f, "AGAS"),
AccItems => write!(f, "ACCS"),
PreimageSize => write!(f, "PRVS"),
PreimageCount => write!(f, "PRVZ"),
ImportsCount => write!(f, "IMPS"),
ExportsCount => write!(f, "EXPS"),
ExtrinsicsCount => write!(f, "XTS"),
ExtrinsicsSize => write!(f, "XTZ"),
Balance => write!(f, "BAL"),
MinBalance => write!(f, "MBAL"),
FreeBalance => write!(f, "FBAL"),
StorageItems => write!(f, "STRS"),
StorageSize => write!(f, "STRZ"),
}
}
}
impl Column {
pub fn extract(
&self,
id: ServiceId,
info: &Service,
maybe_meta: &CodeInfo<CrateInfo>,
maybe_inner_meta: &Option<CoreVmCodeInfo>,
activity: &ServiceActivityRecord,
) -> String {
use CodeInfo::*;
use Column::*;
let max_ref_gas = max_refine_gas() * core_count() as UnsignedGas;
let max_acc_gas = max_accumulate_gas() * core_count() as UnsignedGas;
match (self, maybe_meta.as_ref(), maybe_inner_meta.as_ref()) {
(Id, _, _) => format!("{id:08x}"),
(Items, _, _) => amount(activity.refinement_count + activity.accumulate_count),
(Flags, _, _) => format!(
"{}{}{}",
if activity.refinement_count > 0 {
"R".bold().with(REF_COL)
} else {
"-".dark_grey()
},
if activity.accumulate_count > 0 {
"A".bold().with(ACC_COL)
} else {
"-".dark_grey()
},
if activity.provided_count > 0 {
"P".bold().with(PROV_COL)
} else {
"-".dark_grey()
},
),
(RefPercent, _, _) => percent(activity.refinement_gas_used, max_ref_gas).to_string(),
(RefPercentCore, _, _) =>
percent(activity.refinement_gas_used, max_refine_gas()).to_string(),
(RefGas, _, _) => gas(activity.refinement_gas_used),
(RefItems, _, _) => amount(activity.refinement_count),
(AccPercent, _, _) => percent(activity.accumulate_gas_used, max_acc_gas).to_string(),
(AccPercentCore, _, _) =>
percent(activity.accumulate_gas_used, max_accumulate_gas()).to_string(),
(AccGas, _, _) => gas(activity.accumulate_gas_used),
(AccItems, _, _) => amount(activity.accumulate_count),
(Name, CodeNotProvided(h), _) => format!("{}…???", hex::to_hex(&h[..4])),
(Name, _, Some(CoreVmCodeInfo::NotProvided(r))) =>
format!("{} {}…???", "vm:".dark_grey(), hex::to_hex(&r.hash.0[..4])),
(Version, CodeNotProvided(_), _) |
(Version, _, Some(CoreVmCodeInfo::NotProvided(_))) => String::new(),
(Name, Undefined(h), _) => format!("{}…", hex::to_hex(&h[..4])),
(Name, _, Some(CoreVmCodeInfo::Undefined(r))) =>
format!("{} {}…", "vm:".dark_grey(), hex::to_hex(&r.hash.0[..4])),
(Version, Undefined(..), _) | (Version, _, Some(CoreVmCodeInfo::Undefined(..))) =>
String::new(),
(Name, Known(meta), Some(CoreVmCodeInfo::Known(inner_meta))) =>
if meta.name == "corevm" {
format!("{} {}", "vm:".dark_grey(), self.fit_minus(&inner_meta.name, 4))
} else {
self.fit(&format!("{}: {}", meta.name, inner_meta.name))
},
(Name, Known(meta), _) => self.fit(&meta.name),
(Version, Known(meta), _) => self.fit(&meta.version),
(PreimageSize, _, _) => bytes(activity.provided_size),
(PreimageCount, _, _) => amount(activity.provided_count),
(ImportsCount, _, _) => amount(activity.imports),
(ExportsCount, _, _) => amount(activity.exports),
(ExtrinsicsSize, _, _) => bytes(activity.extrinsic_size),
(ExtrinsicsCount, _, _) => amount(activity.extrinsic_count),
(Balance, _, _) => amount(info.balance),
(MinBalance, _, _) => amount(info.threshold()),
(FreeBalance, _, _) => amount(info.free()),
(StorageSize, _, _) => bytes(info.bytes),
(StorageItems, _, _) => amount(info.items),
}
}
pub fn width(&self) -> usize {
use Column::*;
format!("{self}").len().max(match self {
Id => 8,
Name => 20,
Version => 7,
Flags => 4,
Items => 4,
RefPercent => 5,
RefPercentCore => 5,
RefGas => 5,
RefItems => 4,
AccPercent => 5,
AccPercentCore => 5,
AccGas => 5,
AccItems => 4,
PreimageSize => 5,
PreimageCount => 4,
ImportsCount => 5,
ExportsCount => 5,
ExtrinsicsSize => 5,
ExtrinsicsCount => 4,
Balance => 5,
MinBalance => 5,
FreeBalance => 5,
StorageSize => 5,
StorageItems => 4,
})
}
pub fn fit(&self, s: &str) -> String {
if s.len() <= self.width() || matches!(self, Column::Flags) {
s.to_string()
} else {
format!("{}…", s.split_at(self.width() - 1).0)
}
}
pub fn fit_minus(&self, s: &str, m: usize) -> String {
if s.len() + m <= self.width() || matches!(self, Column::Flags) {
s.to_string()
} else {
format!("{}…", s.split_at(self.width().max(m + 1) - m - 1).0)
}
}
pub fn sort_services(
&self,
mut services: Vec<(
ServiceId,
(Service, CodeInfo<CrateInfo>, Option<CoreVmCodeInfo>, ServiceActivityRecord),
)>,
) -> Vec<(
ServiceId,
(Service, CodeInfo<CrateInfo>, Option<CoreVmCodeInfo>, ServiceActivityRecord),
)> {
use Column::*;
match self {
Id => services.sort_by_key(|&(id, ..)| id),
Name =>
services.sort_by_key(|&(_, (_, ref meta, _, _))| meta.clone().map(|x| x.name)),
Version =>
services.sort_by_key(|&(_, (_, ref meta, _, _))| meta.clone().map(|x| x.version)),
Flags => services.sort_by_key(|&(_, (_, _, _, ref stats))|
if stats.refinement_count > 0 { -4 } else { 0 }
+ if stats.accumulate_count > 0 { -2 } else { 0 }
+ if stats.provided_count > 0 { -1 } else { 0 }
),
Items =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -((stats.refinement_count + stats.accumulate_count) as i128)),
RefGas | RefPercent | RefPercentCore =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.refinement_gas_used as i128)),
RefItems =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.refinement_count as i128)),
AccGas | AccPercent | AccPercentCore =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.accumulate_gas_used as i128)),
AccItems =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.accumulate_count as i128)),
PreimageSize =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.provided_size as i128)),
PreimageCount =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.provided_count as i128)),
ImportsCount =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.imports as i128)),
ExportsCount =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.exports as i128)),
ExtrinsicsSize =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.extrinsic_size as i128)),
ExtrinsicsCount =>
services.sort_by_key(|&(_, (_, _, _, ref stats))| -(stats.extrinsic_count as i128)),
Balance =>
services.sort_by_key(|&(_, (ref info, _, _, _))| -(info.balance as i128)),
MinBalance =>
services.sort_by_key(|&(_, (ref info, _, _, _))| -(info.threshold() as i128)),
FreeBalance =>
services.sort_by_key(|&(_, (ref info, _, _, _))| -(info.free() as i128)),
StorageSize =>
services.sort_by_key(|&(_, (ref info, _, _, _))| -(info.bytes as i128)),
StorageItems =>
services.sort_by_key(|&(_, (ref info, _, _, _))| -(info.items as i128)),
}
services
}
}