sol-chainsaw 0.0.2

Deserializing Solana accounts using their progam IDL
Documentation
use bson::{RawBson, RawDocumentBuf};
use solana_idl::{EnumFields, IdlEnumVariant, IdlType};

use crate::{ChainsawError, ChainsawResult};

use super::{
    bson_common::deserialize_fields_to_object,
    bson_idl_field_de::BsonIdlFieldDeserializer,
    bson_idl_type_de::BsonIdlTypeDeserializer,
    bson_serialization_opts::BsonSerializationOpts,
    BsonTypeDefinitionDeserializerMap,
};

#[derive(Clone)]
pub struct BsonIdlEnumVariantDeserializer<'opts> {
    pub name: String,
    pub named_fields: Option<Vec<BsonIdlFieldDeserializer<'opts>>>,
    pub tuple_types: Option<(BsonIdlTypeDeserializer<'opts>, IdlType)>,
    pub type_map: BsonTypeDefinitionDeserializerMap<'opts>,
}

impl<'opts> BsonIdlEnumVariantDeserializer<'opts> {
    pub fn new(
        variant: &IdlEnumVariant,
        type_map: BsonTypeDefinitionDeserializerMap<'opts>,
        opts: &'opts BsonSerializationOpts,
    ) -> Self {
        let name = variant.name.clone();
        use EnumFields::*;
        match &variant.fields {
            Some(Named(fields)) => {
                let named_fields = fields
                    .iter()
                    .map(|f| {
                        BsonIdlFieldDeserializer::new(f, type_map.clone(), opts)
                    })
                    .collect();
                Self {
                    name,
                    named_fields: Some(named_fields),
                    tuple_types: None,
                    type_map,
                }
            }
            Some(Tuple(types)) => {
                let tuple_ty_de =
                    BsonIdlTypeDeserializer::new(type_map.clone(), opts);
                Self {
                    name,
                    named_fields: None,
                    tuple_types: Some((
                        tuple_ty_de,
                        IdlType::Tuple(types.clone()),
                    )),
                    type_map,
                }
            }
            None => Self {
                name,
                named_fields: None,
                tuple_types: None,
                type_map,
            },
        }
    }

    /// Deserializes the enum variant into BSON.
    /// Non-scalar variants field values are wrapped in an object whose key is the variant name.
    /// Scalar variants are just a string of the variant name.
    pub fn deserialize(&self, buf: &mut &[u8]) -> ChainsawResult<RawBson> {
        if let Some(named_fields) = &self.named_fields {
            let mut doc = RawDocumentBuf::new();
            let object = deserialize_fields_to_object(buf, named_fields)
                .map_err(|e| {
                    ChainsawError::EnumVariantDeserializeError(
                        self.name.to_string(),
                        Box::new(e),
                    )
                })?;
            doc.append(&self.name, object);
            Ok(RawBson::Document(doc))
        } else if let Some((tuple_ty_de, ty)) = &self.tuple_types {
            let mut doc = RawDocumentBuf::new();
            let tuple = self
                .deserialize_tuple_fields(buf, tuple_ty_de, ty)
                .map_err(|e| {
                    ChainsawError::EnumVariantDeserializeError(
                        self.name.to_string(),
                        Box::new(e),
                    )
                })?;
            doc.append(&self.name, tuple);
            Ok(RawBson::Document(doc))
        } else {
            Ok(RawBson::String(self.name.clone()))
        }
    }

    fn deserialize_tuple_fields(
        &self,
        buf: &mut &[u8],
        tuple_el_de: &BsonIdlTypeDeserializer<'opts>,
        ty: &IdlType,
    ) -> ChainsawResult<RawBson> {
        tuple_el_de.deserialize(ty, buf)
    }
}