Implement a customizable borsh_serialize in derive macros
ailisp opened this issue · comments
Currently we can't do much to affect how derive(BorshSerialize, Deserialize)
works besides borsh_skip
, however one case is very useful. Consider this big structure in wasmer:
pub struct ModuleInfo {
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
pub globals: Map<LocalGlobalIndex, GlobalInit>,
pub tables: Map<LocalTableIndex, TableDescriptor>,
pub imported_functions: Map<ImportedFuncIndex, ImportName>,
pub imported_memories: Map<ImportedMemoryIndex, (ImportName, MemoryDescriptor)>,
pub imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
pub exports: IndexMap<String, ExportIndex>,
pub data_initializers: Vec<DataInitializer>,
pub elem_initializers: Vec<TableInitializer>,
pub start_func: Option<FuncIndex>,
pub func_assoc: Map<FuncIndex, SigIndex>,
pub signatures: Map<SigIndex, FuncSig>,
pub backend: String,
pub namespace_table: StringTable<NamespaceIndex>,
pub name_table: StringTable<NameIndex>,
pub em_symbol_map: Option<HashMap<u32, String>>,
pub custom_sections: HashMap<String, Vec<Vec<u8>>>,
pub generate_debug_info: bool,
#[borsh_skip]
pub(crate) debug_info_manager: jit_debug::JitCodeDebugInfoManager,
}
Every fields in this giant struct can derive BorshSerialize and BorshDeserialize, except one: IndexMap<String, ExportIndex>
, because IndexMap isn't a type defined in this crate, nor it's a type defined in std or borsh, so you cannot
impl BorshSerialize for IndexMap
, but due to this one field, you cannot derive BorshSerialize of the giant struct. There's two workaround of this:
- make
pub exports: IndexMap<String, ExportIndex>,
topub exports: ExportsMap
and define a structExportsMap
enclosing IndexMap, so you can impl BorshSerialize/Deserialize on ExportsMap and makeModuleInfo
Borsh-derivable. But this cause any reference to exports become exports.inner or exports.0 - implement BorshSerialize/Deserialize on ModuleInfo manually.
Either one is a big inconvenience or cause structural change hacks. So i propose aborsh_serializer/borsh_deserializer
macro:
fn borsh_serialize_index_map<K:BorshSerialize, V:BorshSerialize, W: Write>(index_map: &IndexMap<K,V>, writer: &mut W) -> std::io::Result<()> {
...
}
#[borsh_serializer(borsh_serialize_index_map)]
#[borsh_deserializer(borsh_deserialize_index_map)]
pub exports: IndexMap<String, ExportIndex>,
With help of these macros, user can specify a customize borsh serializer/deserializer to a field of struct, making the whole struct borsh-derivable
I think it would be nice to have the same API as serde for familiarity. They have an option for choosing a module for the functions to avoid having to specify both individually, as well as options for each specifically.