scale_serialization/
compress.rs1use crate::registry::*;
2use crate::Error;
3use scale_info::{form::PortableForm, PortableRegistry};
4
5type SiType = scale_info::Type<PortableForm>;
6type SiTypeDef = scale_info::TypeDef<PortableForm>;
7type SiComposite = scale_info::TypeDefComposite<PortableForm>;
8
9pub fn compress(source: &PortableRegistry) -> Result<Registry, Error> {
13 let types = source
14 .types
15 .iter()
16 .map(|pt| convert_type(&pt.ty, source))
17 .collect::<Result<_, _>>()?;
18 Ok(Registry::new(types))
19}
20
21fn convert_type(ty: &SiType, source: &PortableRegistry) -> Result<TypeDef, Error> {
22 use scale_info::TypeDefPrimitive as P;
23
24 let name = || {
25 ty.path
26 .segments
27 .last()
28 .cloned()
29 .unwrap_or_else(|| "".into())
30 };
31
32 let is_map = ty
33 .path
34 .segments
35 .last()
36 .is_some_and(|s| s == "BTreeMap");
37
38 Ok(match &ty.type_def {
39 SiTypeDef::Primitive(p) => match p {
40 P::Bool => TypeDef::Bool,
41 P::U8 => TypeDef::U8,
42 P::U16 => TypeDef::U16,
43 P::U32 => TypeDef::U32,
44 P::U64 => TypeDef::U64,
45 P::U128 => TypeDef::U128,
46 P::I8 => TypeDef::I8,
47 P::I16 => TypeDef::I16,
48 P::I32 => TypeDef::I32,
49 P::I64 => TypeDef::I64,
50 P::I128 => TypeDef::I128,
51 P::Char => TypeDef::Char,
52 P::Str => TypeDef::Str,
53 P::U256 | P::I256 => return Err(Error::BadInput("256-bit integers not supported".into())),
54 },
55 SiTypeDef::Composite(c) => {
56 if c.fields.is_empty() {
57 TypeDef::StructUnit
58 } else if is_map {
59 let (k, v) = extract_map_types(c, source)?;
60 TypeDef::Map(k, v)
61 } else if c.fields.len() == 1 && c.fields[0].name.is_none() {
62 TypeDef::StructNewType(c.fields[0].ty.id)
63 } else if is_tuple(c) {
64 TypeDef::StructTuple(c.fields.iter().map(|f| f.ty.id).collect())
65 } else {
66 TypeDef::Struct(
67 c.fields
68 .iter()
69 .map(|f| Ok(Field {
70 name: f.name.as_ref().ok_or(Error::BadInput("expected named field".into()))?.to_string(),
71 ty: f.ty.id,
72 }))
73 .collect::<Result<_, Error>>()?,
74 )
75 }
76 }
77 SiTypeDef::Variant(v) => TypeDef::Variant(VariantDef {
78 name: name(),
79 variants: v
80 .variants
81 .iter()
82 .map(|var| {
83 let fields = if var.fields.is_empty() {
84 Fields::Unit
85 } else if var.fields.len() == 1 && var.fields[0].name.is_none() {
86 Fields::NewType(var.fields[0].ty.id)
87 } else if var.fields[0].name.is_none() {
88 Fields::Tuple(var.fields.iter().map(|f| f.ty.id).collect())
89 } else {
90 Fields::Struct(
91 var.fields
92 .iter()
93 .map(|f| Ok(Field {
94 name: f.name.as_ref().ok_or(Error::BadInput("expected named field".into()))?.to_string(),
95 ty: f.ty.id,
96 }))
97 .collect::<Result<_, Error>>()?,
98 )
99 };
100 Ok(Variant {
101 index: var.index,
102 name: var.name.to_string(),
103 fields,
104 })
105 })
106 .collect::<Result<_, Error>>()?,
107 }),
108 SiTypeDef::Sequence(s) => {
109 let inner = s.type_param.id;
110 if let Some(inner_ty) = source.resolve(inner) {
111 if matches!(inner_ty.type_def, SiTypeDef::Primitive(P::U8)) {
112 return Ok(TypeDef::Bytes);
113 }
114 }
115 TypeDef::Sequence(inner)
116 }
117 SiTypeDef::Array(a) => TypeDef::Array(a.type_param.id, a.len),
118 SiTypeDef::Tuple(t) => TypeDef::Tuple(t.fields.iter().map(|f| f.id).collect()),
119 SiTypeDef::Compact(c) => TypeDef::Compact(c.type_param.id),
120 SiTypeDef::BitSequence(b) => TypeDef::BitSequence(b.bit_store_type.id, b.bit_order_type.id),
121 })
122}
123
124fn is_tuple(c: &SiComposite) -> bool {
125 c.fields.first().and_then(|f| f.name.as_ref()).is_none()
126}
127
128fn extract_map_types(c: &SiComposite, source: &PortableRegistry) -> Result<(TypeId, TypeId), Error> {
129 let field = c.fields.first().ok_or(Error::BadInput("map has no fields".into()))?;
130 let resolved = source.resolve(field.ty.id).ok_or(Error::BadInput("unresolved map type".into()))?;
131 if let SiTypeDef::Sequence(s) = &resolved.type_def {
132 let inner = source.resolve(s.type_param.id).ok_or(Error::BadInput("unresolved map inner type".into()))?;
133 if let SiTypeDef::Tuple(t) = &inner.type_def {
134 if t.fields.len() == 2 {
135 return Ok((t.fields[0].id, t.fields[1].id));
136 }
137 }
138 }
139 Err(Error::BadInput("unexpected map structure".into()))
140}