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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use serde::ser::SerializeMap;
use serde_derive::{Deserialize, Serialize};
use std::ops::{Add, Index, IndexMut};

#[derive(
    Eq,
    PartialEq,
    Hash,
    Copy,
    Clone,
    Debug,
    Ord,
    PartialOrd,
    Serialize,
    Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum Space {
    Native,
    #[serde(rename(serialize = "evm", deserialize = "evm"))]
    Ethereum,
}

impl From<Space> for String {
    fn from(space: Space) -> Self {
        let str: &'static str = space.into();
        str.into()
    }
}

impl From<Space> for &'static str {
    fn from(space: Space) -> Self {
        match space {
            Space::Native => "native",
            Space::Ethereum => "evm",
        }
    }
}

impl Encodable for Space {
    fn rlp_append(&self, s: &mut RlpStream) {
        let type_int: u8 = match self {
            Space::Native => 1,
            Space::Ethereum => 2,
        };
        type_int.rlp_append(s)
    }
}

impl Decodable for Space {
    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
        match u8::decode(rlp)? {
            1u8 => Ok(Space::Native),
            2u8 => Ok(Space::Ethereum),
            _ => Err(DecoderError::Custom("Unrecognized space byte.")),
        }
    }
}

impl Default for Space {
    fn default() -> Self { Space::Native }
}

#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
pub struct SpaceMap<T> {
    native: T,
    evm: T,
}

impl<T> SpaceMap<T> {
    pub const fn new(native: T, evm: T) -> Self { SpaceMap { native, evm } }

    #[inline]
    pub const fn in_space(&self, space: Space) -> &T {
        match space {
            Space::Native => &self.native,
            Space::Ethereum => &self.evm,
        }
    }

    #[inline]
    pub fn in_space_mut(&mut self, space: Space) -> &mut T {
        match space {
            Space::Native => &mut self.native,
            Space::Ethereum => &mut self.evm,
        }
    }

    pub fn zip3<B, C>(
        a: SpaceMap<T>, b: SpaceMap<B>, c: SpaceMap<C>,
    ) -> SpaceMap<(T, B, C)> {
        SpaceMap {
            native: (a.native, b.native, c.native),
            evm: (a.evm, b.evm, c.evm),
        }
    }

    pub fn zip4<B, C, D>(
        a: SpaceMap<T>, b: SpaceMap<B>, c: SpaceMap<C>, d: SpaceMap<D>,
    ) -> SpaceMap<(T, B, C, D)> {
        SpaceMap {
            native: (a.native, b.native, c.native, d.native),
            evm: (a.evm, b.evm, c.evm, d.evm),
        }
    }

    pub fn map_sum<F: FnMut(&T) -> U, U: Add<U, Output = U>>(
        &self, mut f: F,
    ) -> U {
        f(&self.native) + f(&self.evm)
    }

    pub const fn size(&self) -> usize { 2 }

    pub fn map_all<U, F: Fn(T) -> U>(self, f: F) -> SpaceMap<U> {
        SpaceMap {
            native: f(self.native),
            evm: f(self.evm),
        }
    }

    pub fn apply_all<U, F: FnMut(&mut T) -> U>(
        &mut self, mut f: F,
    ) -> SpaceMap<U> {
        SpaceMap {
            native: f(&mut self.native),
            evm: f(&mut self.evm),
        }
    }
}

impl<T> Index<Space> for SpaceMap<T> {
    type Output = T;

    fn index(&self, space: Space) -> &Self::Output { self.in_space(space) }
}

impl<T> IndexMut<Space> for SpaceMap<T> {
    fn index_mut(&mut self, space: Space) -> &mut Self::Output {
        self.in_space_mut(space)
    }
}

impl<T: serde::Serialize> serde::Serialize for SpaceMap<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::Serializer {
        let mut map = serializer.serialize_map(Some(self.size()))?;
        map.serialize_entry("core", &self.native)?;
        map.serialize_entry("espace", &self.evm)?;
        map.end()
    }
}