{"id":13651892,"url":"https://github.com/paritytech/scale-decode","last_synced_at":"2025-10-25T22:31:37.115Z","repository":{"id":50064409,"uuid":"518503445","full_name":"paritytech/scale-decode","owner":"paritytech","description":"Decode SCALE bytes into custom types using a scale-info type registry and a custom Visitor impl.","archived":false,"fork":false,"pushed_at":"2025-03-25T00:55:01.000Z","size":227,"stargazers_count":9,"open_issues_count":7,"forks_count":3,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-09T10:49:03.223Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paritytech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-27T15:00:18.000Z","updated_at":"2025-04-04T04:33:49.000Z","dependencies_parsed_at":"2023-02-17T21:45:42.912Z","dependency_job_id":"f4a653ba-086b-446f-a19d-a0d10452587b","html_url":"https://github.com/paritytech/scale-decode","commit_stats":{"total_commits":26,"total_committers":4,"mean_commits":6.5,"dds":0.1923076923076923,"last_synced_commit":"9164b12da2949f74eef5c6f02ed4fe82a9dc9907"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paritytech%2Fscale-decode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paritytech%2Fscale-decode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paritytech%2Fscale-decode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paritytech%2Fscale-decode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paritytech","download_url":"https://codeload.github.com/paritytech/scale-decode/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250357672,"owners_count":21417325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T02:00:53.243Z","updated_at":"2025-10-25T22:31:32.081Z","avatar_url":"https://github.com/paritytech.png","language":"Rust","funding_links":[],"categories":["Tools"],"sub_categories":[],"readme":"# scale-decode\n\nThis crate makes it easy to decode SCALE encoded bytes into a custom data structure with the help of a `TypeResolver` (one of which\nis a `scale_info::PortableRegistry`). By using this type information to guide decoding (instead of just trying to decode bytes based\non the shape of the target type), it's possible to be much more flexible in how data is decoded and mapped to some target type.\n\nThe main trait used to decode types is a `Visitor` trait (example below). By implementing this trait, you can describe how to\ntake SCALE decoded values and map them to some custom type of your choosing (whether it is a dynamically shaped type or some\nstatic type you'd like to decode into). Implementations of this `Visitor` trait exist for many existing Rust types in the standard\nlibrary.\n\nThere also exists an `IntoVisitor` trait, which is implemented on many existing rust types and maps a given type to some visitor\nimplementation capable of decoding into it.\n\nFinally, a wrapper trait, `DecodeAsType`, is auto-implemented for all types that have an `IntoVisitor` implementation,\nand whose visitor errors can be turned into a standard `crate::Error`.\n\nFor custom structs and enums, one can use the `DecodeAsType` derive macro to have a `DecodeAsType` implementation automatically\ngenerated.\n\nHere's an example of implementing `Visitor` to decode bytes into a custom `Value` type:\n\n```rust\nuse std::marker::PhantomData;\nuse scale_decode::TypeResolver;\nuse scale_decode::visitor::{\n    self,\n    types::{Array, BitSequence, Composite, Sequence, Str, Tuple, Variant},\n    TypeIdFor,\n};\n\n// A custom type we'd like to decode into:\n#[derive(Debug, PartialEq)]\nenum Value {\n    Bool(bool),\n    Char(char),\n    U8(u8),\n    U16(u16),\n    U32(u32),\n    U64(u64),\n    U128(u128),\n    U256([u8; 32]),\n    I8(i8),\n    I16(i16),\n    I32(i32),\n    I64(i64),\n    I128(i128),\n    I256([u8; 32]),\n    Sequence(Vec\u003cValue\u003e),\n    Composite(Vec\u003c(String, Value)\u003e),\n    Tuple(Vec\u003cValue\u003e),\n    Str(String),\n    Array(Vec\u003cValue\u003e),\n    Variant(String, Vec\u003c(String, Value)\u003e),\n    BitSequence(scale_bits::Bits),\n}\n\n// Implement the `Visitor` trait to define how to go from SCALE\n// values into this type. Here, we are choosing to be generic over\n// how types are resolved, which is a good default choice when creating\n// a visitor unless you rely on a specific type resolver/ID type.\nstruct ValueVisitor\u003cR\u003e(PhantomData\u003cR\u003e);\n\nimpl\u003cR\u003e ValueVisitor\u003cR\u003e {\n    fn new() -\u003e Self {\n        Self(PhantomData)\n    }\n}\n\nimpl\u003cR: TypeResolver\u003e visitor::Visitor for ValueVisitor\u003cR\u003e {\n    type Value\u003c'scale, 'resolver\u003e = Value;\n    type Error = visitor::DecodeError;\n    type TypeResolver = R;\n\n    fn visit_bool\u003c'scale, 'resolver\u003e(\n        self,\n        value: bool,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::Bool(value))\n    }\n    fn visit_char\u003c'scale, 'resolver\u003e(\n        self,\n        value: char,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::Char(value))\n    }\n    fn visit_u8\u003c'scale, 'resolver\u003e(\n        self,\n        value: u8,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U8(value))\n    }\n    fn visit_u16\u003c'scale, 'resolver\u003e(\n        self,\n        value: u16,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U16(value))\n    }\n    fn visit_u32\u003c'scale, 'resolver\u003e(\n        self,\n        value: u32,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U32(value))\n    }\n    fn visit_u64\u003c'scale, 'resolver\u003e(\n        self,\n        value: u64,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U64(value))\n    }\n    fn visit_u128\u003c'scale, 'resolver\u003e(\n        self,\n        value: u128,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U128(value))\n    }\n    fn visit_u256\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026'scale [u8; 32],\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::U256(*value))\n    }\n    fn visit_i8\u003c'scale, 'resolver\u003e(\n        self,\n        value: i8,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I8(value))\n    }\n    fn visit_i16\u003c'scale, 'resolver\u003e(\n        self,\n        value: i16,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I16(value))\n    }\n    fn visit_i32\u003c'scale, 'resolver\u003e(\n        self,\n        value: i32,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I32(value))\n    }\n    fn visit_i64\u003c'scale, 'resolver\u003e(\n        self,\n        value: i64,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I64(value))\n    }\n    fn visit_i128\u003c'scale, 'resolver\u003e(\n        self,\n        value: i128,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I128(value))\n    }\n    fn visit_i256\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026'scale [u8; 32],\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::I256(*value))\n    }\n    fn visit_sequence\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Sequence\u003c'scale, 'resolver, Self::TypeResolver\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let mut vals = vec![];\n        while let Some(val) = value.decode_item(ValueVisitor::new()) {\n            let val = val?;\n            vals.push(val);\n        }\n        Ok(Value::Sequence(vals))\n    }\n    fn visit_composite\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Composite\u003c'scale, 'resolver, Self::TypeResolver\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let mut vals = vec![];\n        for item in value.by_ref() {\n            let item = item?;\n            let val = item.decode_with_visitor(ValueVisitor::new())?;\n            let name = item.name().unwrap_or(\"\").to_owned();\n            vals.push((name, val));\n        }\n        Ok(Value::Composite(vals))\n    }\n    fn visit_tuple\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Tuple\u003c'scale, 'resolver, Self::TypeResolver\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let mut vals = vec![];\n        while let Some(val) = value.decode_item(ValueVisitor::new()) {\n            let val = val?;\n            vals.push(val);\n        }\n        Ok(Value::Tuple(vals))\n    }\n    fn visit_str\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Str\u003c'scale\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        Ok(Value::Str(value.as_str()?.to_owned()))\n    }\n    fn visit_variant\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Variant\u003c'scale, 'resolver, Self::TypeResolver\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let mut vals = vec![];\n        let fields = value.fields();\n        for item in fields.by_ref() {\n            let item = item?;\n            let val = item.decode_with_visitor(ValueVisitor::new())?;\n            let name = item.name().unwrap_or(\"\").to_owned();\n            vals.push((name, val));\n        }\n        Ok(Value::Variant(value.name().to_owned(), vals))\n    }\n    fn visit_array\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut Array\u003c'scale, 'resolver, Self::TypeResolver\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let mut vals = vec![];\n        while let Some(val) = value.decode_item(ValueVisitor::new()) {\n            let val = val?;\n            vals.push(val);\n        }\n        Ok(Value::Array(vals))\n    }\n    fn visit_bitsequence\u003c'scale, 'resolver\u003e(\n        self,\n        value: \u0026mut BitSequence\u003c'scale\u003e,\n        _type_id: \u0026TypeIdFor\u003cSelf\u003e,\n    ) -\u003e Result\u003cSelf::Value\u003c'scale, 'resolver\u003e, Self::Error\u003e {\n        let bools: Result\u003cscale_bits::Bits, _\u003e = value.decode()?.collect();\n        Ok(Value::BitSequence(bools?))\n    }\n}\n```\n\nThis can then be passed to a decode function like so:\n\n```rust\nlet value: Value = scale_decode::visitor::decode_with_visitor(scale_bytes, \u0026type_id, \u0026types, ValueVisitor::new())?;\n```\n\nWhere `scale_bytes` are the bytes you'd like to decode, `type_id` is the type stored in the `types` registry\nthat you'd like to try and decode the bytes into, and `types` is a `scale_type_resolver::TypeResolver` containing\ninformation about the various types in use (a concrete implementation of this is `scale_info::PortableRegistry`).\n\nIf we were to then write an `IntoVisitor` implementation like so:\n\n```rust\nimpl scale_decode::IntoVisitor for Value {\n    type AnyVisitor\u003cR: TypeResolver\u003e = ValueVisitor\u003cR\u003e;\n    fn into_visitor\u003cR: TypeResolver\u003e() -\u003e Self::AnyVisitor\u003cR\u003e {\n        ValueVisitor::new()\n    }\n}\n```\n\nWe can then also decode via tha automatic `DecodeAsType` impl like so:\n\n```rust\nuse scale_decode::DecodeAsType;\n\nlet value = Value::decode_as_type(scale_bytes, type_id, types)?;\n```\n\nWith an `IntoVisitor` impl, you'd also benefit from being able to decode things like `Vec\u003cValue\u003e`,\n`(Value, bool)`, `Arc\u003cValue\u003e` and so on in the same way.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparitytech%2Fscale-decode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fparitytech%2Fscale-decode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparitytech%2Fscale-decode/lists"}