1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! # Helper trait for building a dynamic-language decode method

use base_decode::BaseDecode;
use converter::Converter;
use core::{Loc, RpInterfaceBody, RpType, WithPos};
use core::errors::*;
use dynamic_converter::DynamicConverter;
use genco::Tokens;

pub trait DynamicDecode<'el>
where
    Self: Converter<'el>,
    Self: DynamicConverter<'el>,
{
    fn assign_tag_var(
        &self,
        data: &'el str,
        tag_var: &'el str,
        tag: &Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    fn check_tag_var(
        &self,
        data: &'el str,
        tag_var: &'el str,
        name: &'el str,
        type_name: Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    fn raise_bad_type(&self, tag_var: &'el str) -> Tokens<'el, Self::Custom>;

    fn new_decode_method(
        &self,
        data: &'el str,
        body: Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    fn name_decode(
        &self,
        input: Tokens<'el, Self::Custom>,
        name: Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    fn array_decode(
        &self,
        input: Tokens<'el, Self::Custom>,
        inner: Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    fn map_decode(
        &self,
        input: Tokens<'el, Self::Custom>,
        key: Tokens<'el, Self::Custom>,
        value: Tokens<'el, Self::Custom>,
    ) -> Tokens<'el, Self::Custom>;

    /// Handle the decoding of a datetime object.
    fn datetime_decode(&self, input: Tokens<'el, Self::Custom>) -> Tokens<'el, Self::Custom> {
        input
    }

    fn dynamic_decode(
        &self,
        ty: &RpType,
        input: Tokens<'el, Self::Custom>,
    ) -> Result<Tokens<'el, Self::Custom>> {
        use self::RpType::*;

        if self.is_native(ty) {
            return Ok(input);
        }

        let input = match *ty {
            Signed { size: _ } | Unsigned { size: _ } => input,
            Float | Double => input,
            String => input,
            DateTime => self.datetime_decode(input),
            Boolean => input,
            Bytes => input,
            Any => input,
            Name { ref name } => {
                let name = self.convert_type(name)?;
                self.name_decode(input, name)
            }
            Array { ref inner } => {
                let inner_var = self.array_inner_var();
                let inner = self.dynamic_decode(inner, inner_var)?;
                self.array_decode(input, inner)
            }
            Map { ref key, ref value } => {
                let map_key = self.map_key_var();
                let key = self.dynamic_decode(key, map_key)?;
                let map_value = self.map_value_var();
                let value = self.dynamic_decode(value, map_value)?;
                self.map_decode(input, key, value)
            }
        };

        Ok(input)
    }

    fn interface_decode_method(
        &self,
        body: &'el RpInterfaceBody,
        tag: &Tokens<'el, Self::Custom>,
    ) -> Result<Tokens<'el, Self::Custom>> {
        let mut decode_body: Tokens<Self::Custom> = Tokens::new();

        let data = "data";
        let tag_var = "f_tag";
        decode_body.push(self.assign_tag_var(data, tag_var, tag));

        for sub_type in body.sub_types.iter() {
            let type_name = self.convert_type(&sub_type.name)
                .with_pos(Loc::pos(sub_type))?;
            decode_body.push(self.check_tag_var(data, tag_var, sub_type.name(), type_name));
        }

        decode_body.push(self.raise_bad_type(tag_var));

        Ok(self.new_decode_method(data, decode_body.join_line_spacing()))
    }
}

/// Dynamic decode is a valid decoding mechanism
impl<'el, T> BaseDecode<'el> for T
where
    T: DynamicDecode<'el>,
{
    fn base_decode(
        &self,
        ty: &RpType,
        input: Tokens<'el, Self::Custom>,
    ) -> Result<Tokens<'el, Self::Custom>> {
        self.dynamic_decode(ty, input)
    }
}