mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 04:07:57 +00:00
CLI subxt explore commands (#950)
* add cli command to explore metadata * fmt and clippy * Bump serde from 1.0.160 to 1.0.162 (#948) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.160 to 1.0.162. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.160...1.0.162) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * extrinsics: Decode extrinsics from blocks (#929) * Update polkadot.scale Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics: Add extrinsics client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics: Decode extrinsics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Add extrinsic error Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Expose extrinsics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Fetch and decode block extrinsics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics: Fetch pallet and variant index Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Move extrinsics on the subxt::blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * example: Adjust example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Collect ExtrinsicMetadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Implement StaticExtrinsic for the calls Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust examples Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Add root level Call enum Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Add new decode interface Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Merge ExtrinsicError with BlockError Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Find first extrinsic Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Move code to extrinsic_types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Add Extrinsic struct Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust examples Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * test: Decode extinsics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/test: Add fake metadata for static decoding Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/test: Decode from insufficient bytes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/test: Check unsupported versions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/test: Statically decode to root and pallet enums Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/tests: Remove clones Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Fetch block body inline Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Rename ExtrinsicIds to ExtrinsicPartTypeIds Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics/test: Check decode as_extrinsic Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Remove InsufficientData error Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Return error from extrinsic_metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * extrinsics: Postpone decoding of call bytes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata_type: Rename variables Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust calls path for example and tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Remove traces Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * book: Add extrinsics documentation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * book: Improve extrinsics docs Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: James Wilson <james@jsdw.me> * change doc comments * add constants exploration * add storage access interface but not done yet * add storage exploration * formatting * remove dbg * some small tweaks * fix formatting and scale value for storage * split up files, sort entries, change formatting * fmt and clippy fix * fix minor formatting issue * implement suggestions * implement other suggestion, fix bug --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use scale_info::{
|
||||
form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefArray, TypeDefBitSequence,
|
||||
TypeDefCompact, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, TypeDefVariant, Variant,
|
||||
};
|
||||
|
||||
/// pretty formatted type description
|
||||
pub fn print_type_description<T>(ty: &T, registry: &PortableRegistry) -> color_eyre::Result<String>
|
||||
where
|
||||
T: TypeDescription,
|
||||
{
|
||||
let type_description = ty.type_description(registry)?;
|
||||
let type_description = format_type_description(&type_description);
|
||||
Ok(type_description)
|
||||
}
|
||||
|
||||
/// a trait for producing human readable type descriptions with a rust-like syntax.
|
||||
pub trait TypeDescription {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String>;
|
||||
}
|
||||
|
||||
impl TypeDescription for u32 {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let ty = registry
|
||||
.resolve(*self)
|
||||
.ok_or(eyre!("Type with id {} not found in registry", *self))?;
|
||||
let ident = ty.path.ident();
|
||||
let prefix = type_def_prefix(&ty.type_def);
|
||||
let mut type_def_description = ty.type_def.type_description(registry)?;
|
||||
if let Some(ident) = ident {
|
||||
type_def_description = format!("{} {}", ident, type_def_description)
|
||||
}
|
||||
if let Some(prefix) = prefix {
|
||||
type_def_description = format!("{} {}", prefix, type_def_description)
|
||||
}
|
||||
Ok(type_def_description)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_def_prefix(type_def: &TypeDef<PortableForm>) -> Option<&str> {
|
||||
match type_def {
|
||||
TypeDef::Composite(_) => Some("struct"),
|
||||
TypeDef::Variant(_) => Some("enum"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDef<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
match self {
|
||||
TypeDef::Composite(composite) => composite.fields.type_description(registry),
|
||||
TypeDef::Variant(variant) => variant.type_description(registry),
|
||||
TypeDef::Sequence(sequence) => sequence.type_description(registry),
|
||||
TypeDef::Array(array) => array.type_description(registry),
|
||||
TypeDef::Tuple(tuple) => tuple.type_description(registry),
|
||||
TypeDef::Primitive(primitive) => primitive.type_description(registry),
|
||||
TypeDef::Compact(compact) => compact.type_description(registry),
|
||||
TypeDef::BitSequence(bit_sequence) => bit_sequence.type_description(registry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefTuple<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let mut output = "(".to_string();
|
||||
let mut iter = self.fields.iter().peekable();
|
||||
while let Some(ty) = iter.next() {
|
||||
let type_description = ty.id.type_description(registry)?;
|
||||
output.push_str(&type_description);
|
||||
if iter.peek().is_some() {
|
||||
output.push(',')
|
||||
}
|
||||
}
|
||||
output.push(')');
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefBitSequence<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let bit_order_type = self.bit_order_type.id.type_description(registry)?;
|
||||
let bit_store_type = self.bit_store_type.id.type_description(registry)?;
|
||||
Ok(format!("BitSequence({bit_order_type}, {bit_store_type})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefSequence<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("Sequence({type_description})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefCompact<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("Compact({type_description})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefArray<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("[{type_description}; {}]", self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefPrimitive {
|
||||
fn type_description(&self, _registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
Ok(match &self {
|
||||
TypeDefPrimitive::Bool => "bool",
|
||||
TypeDefPrimitive::Char => "char",
|
||||
TypeDefPrimitive::Str => "String",
|
||||
TypeDefPrimitive::U8 => "u8",
|
||||
TypeDefPrimitive::U16 => "u16",
|
||||
TypeDefPrimitive::U32 => "u32",
|
||||
TypeDefPrimitive::U64 => "u64",
|
||||
TypeDefPrimitive::U128 => "u128",
|
||||
TypeDefPrimitive::U256 => "u256",
|
||||
TypeDefPrimitive::I8 => "i8",
|
||||
TypeDefPrimitive::I16 => "i16",
|
||||
TypeDefPrimitive::I32 => "i32",
|
||||
TypeDefPrimitive::I64 => "i64",
|
||||
TypeDefPrimitive::I128 => "i128",
|
||||
TypeDefPrimitive::I256 => "i256",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefVariant<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
const MIN_VARIANT_COUNT_FOR_TRAILING_COMMA: usize = 100;
|
||||
let add_trailing_comma = self.variants.len() >= MIN_VARIANT_COUNT_FOR_TRAILING_COMMA;
|
||||
|
||||
let mut variants_string = String::new();
|
||||
variants_string.push('{');
|
||||
let mut iter = self.variants.iter().peekable();
|
||||
while let Some(variant) = iter.next() {
|
||||
let variant_string = variant.type_description(registry)?;
|
||||
variants_string.push_str(&variant_string);
|
||||
|
||||
if iter.peek().is_some() || add_trailing_comma {
|
||||
variants_string.push(',');
|
||||
}
|
||||
}
|
||||
variants_string.push('}');
|
||||
Ok(variants_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Variant<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let fields_string = self.fields.type_description(registry)?;
|
||||
let output = if fields_string.is_empty() {
|
||||
self.name.to_string()
|
||||
} else if fields_string.starts_with('(') {
|
||||
format!("{}{}", &self.name, fields_string)
|
||||
} else {
|
||||
format!("{} {}", &self.name, fields_string)
|
||||
};
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Vec<Field<PortableForm>> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
if self.is_empty() {
|
||||
return Ok("()".to_string());
|
||||
}
|
||||
|
||||
const MIN_FIELD_COUNT_FOR_TRAILING_COMMA: usize = 100;
|
||||
let add_trailing_comma = self.len() >= MIN_FIELD_COUNT_FOR_TRAILING_COMMA;
|
||||
|
||||
let all_fields_named = self.iter().all(|f| f.name.is_some());
|
||||
let all_fields_unnamed = self.iter().all(|f| f.name.is_none());
|
||||
let brackets = match (all_fields_named, all_fields_unnamed) {
|
||||
(true, false) => ('{', '}'),
|
||||
(false, true) => ('(', ')'),
|
||||
_ => {
|
||||
return Err(eyre!(
|
||||
"combination of named and unnamed fields in compound type"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut fields_string = String::new();
|
||||
fields_string.push(brackets.0);
|
||||
let mut iter = self.iter().peekable();
|
||||
while let Some(field) = iter.next() {
|
||||
let field_description = field.type_description(registry)?;
|
||||
fields_string.push_str(&field_description);
|
||||
|
||||
if iter.peek().is_some() || add_trailing_comma {
|
||||
fields_string.push(',')
|
||||
}
|
||||
}
|
||||
fields_string.push(brackets.1);
|
||||
Ok(fields_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Field<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.ty.id.type_description(registry)?;
|
||||
let type_description_maybe_named = if let Some(name) = &self.name {
|
||||
format!("{}: {}", name, type_description)
|
||||
} else {
|
||||
type_description
|
||||
};
|
||||
Ok(type_description_maybe_named)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_type_description(input: &str) -> String {
|
||||
fn add_indentation(output: &mut String, indent_level: i32) {
|
||||
for _ in 0..indent_level {
|
||||
output.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = String::new();
|
||||
let mut indent_level = 0;
|
||||
// in a tuple we will not set line breaks on comma, so we keep track of it here:
|
||||
let mut in_tuple = 0;
|
||||
let mut tokens_since_last_bracket_or_comma: usize = 0;
|
||||
for ch in input.chars() {
|
||||
let mut token_is_bracket_or_comma = true;
|
||||
match ch {
|
||||
'{' => {
|
||||
indent_level += 1;
|
||||
output.push(ch);
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
}
|
||||
'}' => {
|
||||
indent_level -= 1;
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
output.push(ch);
|
||||
}
|
||||
',' => {
|
||||
output.push(ch);
|
||||
// makes small tuples e.g. (u8, u16, u8, u8) not cause line breaks.
|
||||
if in_tuple > 0 && tokens_since_last_bracket_or_comma < 5 {
|
||||
output.push(' ');
|
||||
} else {
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
}
|
||||
}
|
||||
'(' => {
|
||||
output.push(ch);
|
||||
in_tuple += 1;
|
||||
}
|
||||
')' => {
|
||||
output.push(ch);
|
||||
in_tuple -= 1;
|
||||
}
|
||||
_ => {
|
||||
token_is_bracket_or_comma = false;
|
||||
output.push(ch);
|
||||
}
|
||||
}
|
||||
if token_is_bracket_or_comma {
|
||||
tokens_since_last_bracket_or_comma = 0;
|
||||
} else {
|
||||
tokens_since_last_bracket_or_comma += 1;
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use scale_info::{
|
||||
form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefArray, TypeDefPrimitive,
|
||||
TypeDefTuple, TypeDefVariant,
|
||||
};
|
||||
use scale_value::{Value, ValueDef};
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
|
||||
pub fn print_type_examples<T>(
|
||||
ty: &T,
|
||||
registry: &PortableRegistry,
|
||||
type_placeholder: &str,
|
||||
) -> color_eyre::Result<String>
|
||||
where
|
||||
T: TypeExample,
|
||||
{
|
||||
let type_examples = ty.type_example(registry)?;
|
||||
let mut output = String::new();
|
||||
match type_examples.len() {
|
||||
0 => {
|
||||
write!(
|
||||
output,
|
||||
"There are no examples available for a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
1 => {
|
||||
write!(
|
||||
output,
|
||||
"Here is an example of a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
i => {
|
||||
write!(
|
||||
output,
|
||||
"Here are {i} examples of a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
};
|
||||
for self_value in type_examples {
|
||||
let value = <T as TypeExample>::upcast(self_value);
|
||||
let example_str = scale_value::stringify::to_string(&value);
|
||||
write!(output, "\n{}", example_str)?;
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// a trait for producing scale value examples for a type.
|
||||
pub trait TypeExample {
|
||||
type Value;
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>>;
|
||||
fn upcast(self_value: Self::Value) -> scale_value::Value;
|
||||
}
|
||||
|
||||
impl TypeExample for u32 {
|
||||
type Value = scale_value::Value;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let ty = registry
|
||||
.resolve(*self)
|
||||
.ok_or(eyre!("Type with id {} not found in registry", *self))?;
|
||||
|
||||
let examples = match &ty.type_def {
|
||||
TypeDef::Composite(composite) => composite
|
||||
.fields
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Variant(variant) => variant
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Variant(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Array(array) => array
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Tuple(tuple) => tuple
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Primitive(primitive) => primitive
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(scale_value::Value::primitive)
|
||||
.collect(),
|
||||
TypeDef::Compact(compact) => compact.type_param.id.type_example(registry)?,
|
||||
TypeDef::BitSequence(_) => {
|
||||
return Err(eyre!("no examples for BitSequence available"));
|
||||
}
|
||||
TypeDef::Sequence(sequence) => {
|
||||
// for sequences we just give an example of an array with 3 elements:
|
||||
TypeDefArray {
|
||||
len: 3,
|
||||
type_param: sequence.type_param,
|
||||
}
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
Ok(examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
self_value
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefVariant<PortableForm> {
|
||||
type Value = scale_value::Variant<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let mut examples: Vec<scale_value::Variant<()>> = Vec::new();
|
||||
|
||||
// returns one example for each variant
|
||||
for variant in &self.variants {
|
||||
// get the first example for the variant's data and use it
|
||||
let mut variant_value_examples = variant.fields.type_example(registry)?;
|
||||
let Some(values) = variant_value_examples.pop() else {
|
||||
return Err(eyre!("no example element for variant {}", variant.name));
|
||||
};
|
||||
|
||||
examples.push(scale_value::Variant {
|
||||
name: variant.name.clone(),
|
||||
values,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Variant(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefArray<PortableForm> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
// take the first example value and set it to each element of the array
|
||||
let mut value_examples = self.type_param.id.type_example(registry)?;
|
||||
let Some(first_value_example) = value_examples.pop() else {
|
||||
return Err(eyre!("no example element for array"));
|
||||
};
|
||||
|
||||
let one_example = {
|
||||
let mut values = Vec::with_capacity(self.len as usize);
|
||||
for _ in 0..self.len as usize {
|
||||
values.push(first_value_example.clone());
|
||||
}
|
||||
scale_value::Composite::<()>::Unnamed(values)
|
||||
};
|
||||
Ok(vec![one_example])
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefTuple<PortableForm> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
// create unnamed fields to use the same logic already used for struct example generation
|
||||
let fields_vector: Vec<Field<PortableForm>> = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|ty| Field {
|
||||
name: None,
|
||||
ty: *ty,
|
||||
type_name: None,
|
||||
docs: Vec::new(),
|
||||
})
|
||||
.collect();
|
||||
fields_vector.type_example(registry)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for Vec<Field<PortableForm>> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let all_fields_named = self.iter().all(|f| f.name.is_some());
|
||||
let all_fields_unnamed = self.iter().all(|f| f.name.is_none());
|
||||
// composite apparently has no fields:
|
||||
if all_fields_named && all_fields_unnamed {
|
||||
let one_empty_example = scale_value::Composite::Unnamed(Vec::new());
|
||||
return Ok(vec![one_empty_example]);
|
||||
}
|
||||
|
||||
// composite apparently has mix of named and unnamed fields:
|
||||
if !all_fields_named && !all_fields_unnamed {
|
||||
return Err(eyre!(
|
||||
"combination of named and unnamed fields in compound type"
|
||||
));
|
||||
}
|
||||
|
||||
// for each field get all the examples the type of that field can offer:
|
||||
let mut field_examples: Vec<(&Field<PortableForm>, Vec<scale_value::Value>)> = Vec::new();
|
||||
for field in self.iter() {
|
||||
let examples = field.ty.id.type_example(registry)?;
|
||||
field_examples.push((field, examples));
|
||||
}
|
||||
|
||||
// Let N be the mininum number of examples any field has.
|
||||
// Return N examples for the Compound type, by choosing the ith example for each of the 0..N examples for that field.
|
||||
let n = field_examples
|
||||
.iter()
|
||||
.map(|(_, examples)| examples.len())
|
||||
.min()
|
||||
.expect("Iterator is not non-empty checked above; qed");
|
||||
let mut composite_examples: Vec<Vec<(&Field<PortableForm>, scale_value::Value)>> =
|
||||
Vec::new();
|
||||
for _ in 0..n {
|
||||
let composite_example: Vec<(&Field<PortableForm>, scale_value::Value)> = field_examples
|
||||
.iter_mut()
|
||||
.map(|(field, examples)| (*field, examples.pop().unwrap()))
|
||||
.collect(); // the pop() is safe to unwrap because of the minimum we checked before
|
||||
composite_examples.push(composite_example);
|
||||
}
|
||||
|
||||
// create the vector of composite scale values. Distingiush between named and unnamed here.
|
||||
let composite_examples = composite_examples
|
||||
.into_iter()
|
||||
.map(|composite_example| {
|
||||
if all_fields_named {
|
||||
let composite_example = composite_example
|
||||
.into_iter()
|
||||
.map(|(field, value)| (field.name.as_ref().unwrap().clone(), value))
|
||||
.collect();
|
||||
scale_value::Composite::Named(composite_example)
|
||||
} else {
|
||||
let composite_example = composite_example
|
||||
.into_iter()
|
||||
.map(|(_, value)| (value))
|
||||
.collect();
|
||||
scale_value::Composite::Unnamed(composite_example)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(composite_examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 3-4 example values for each primitive
|
||||
impl TypeExample for TypeDefPrimitive {
|
||||
type Value = scale_value::Primitive;
|
||||
|
||||
fn type_example(&self, _registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let value = match &self {
|
||||
TypeDefPrimitive::Bool => vec![
|
||||
scale_value::Primitive::Bool(true),
|
||||
scale_value::Primitive::Bool(false),
|
||||
],
|
||||
TypeDefPrimitive::Char => vec![
|
||||
scale_value::Primitive::Char('r'),
|
||||
scale_value::Primitive::Char('u'),
|
||||
scale_value::Primitive::Char('s'),
|
||||
scale_value::Primitive::Char('t'),
|
||||
],
|
||||
TypeDefPrimitive::Str => vec![
|
||||
scale_value::Primitive::String("Alice".into()),
|
||||
scale_value::Primitive::String("Bob".into()),
|
||||
scale_value::Primitive::String("Foo".into()),
|
||||
scale_value::Primitive::String("Bar".into()),
|
||||
],
|
||||
TypeDefPrimitive::U8 => vec![
|
||||
scale_value::Primitive::U128(u8::MIN as u128),
|
||||
scale_value::Primitive::U128(69),
|
||||
scale_value::Primitive::U128(u8::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U16 => vec![
|
||||
scale_value::Primitive::U128(u16::MIN as u128),
|
||||
scale_value::Primitive::U128(420),
|
||||
scale_value::Primitive::U128(u16::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U32 => vec![
|
||||
scale_value::Primitive::U128(u32::MIN as u128),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u32::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U64 => vec![
|
||||
scale_value::Primitive::U128(u64::MIN as u128),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u64::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U128 => vec![
|
||||
scale_value::Primitive::U128(u128::MIN),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u128::MAX),
|
||||
],
|
||||
TypeDefPrimitive::U256 => vec![
|
||||
scale_value::Primitive::U256([u8::MIN; 32]),
|
||||
scale_value::Primitive::U256([3; 32]),
|
||||
scale_value::Primitive::U256([u8::MAX; 32]),
|
||||
],
|
||||
TypeDefPrimitive::I8 => vec![
|
||||
scale_value::Primitive::I128(i8::MIN as i128),
|
||||
scale_value::Primitive::I128(69),
|
||||
scale_value::Primitive::I128(i8::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I16 => vec![
|
||||
scale_value::Primitive::I128(i16::MIN as i128),
|
||||
scale_value::Primitive::I128(420),
|
||||
scale_value::Primitive::I128(i16::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I32 => vec![
|
||||
scale_value::Primitive::I128(i32::MIN as i128),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i32::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I64 => vec![
|
||||
scale_value::Primitive::I128(i64::MIN as i128),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i64::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I128 => vec![
|
||||
scale_value::Primitive::I128(i128::MIN),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i128::MAX),
|
||||
],
|
||||
TypeDefPrimitive::I256 => vec![
|
||||
scale_value::Primitive::I256([u8::MIN; 32]),
|
||||
scale_value::Primitive::I256([3; 32]),
|
||||
scale_value::Primitive::I256([u8::MAX; 32]),
|
||||
],
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Primitive(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user