Skip to main content

scale_serialization/
compress.rs

1use 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
9/// Compress a `PortableRegistry` into a minimal `Registry`, stripping
10/// docs, paths, and type params while pre-classifying types into
11/// serde-compatible shapes.
12pub 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}