rpgm_common_types/
table.rs

1use ruby_marshal::FromValue;
2use ruby_marshal::FromValueContext;
3use ruby_marshal::FromValueError;
4use ruby_marshal::IntoValue;
5use ruby_marshal::IntoValueError;
6use ruby_marshal::SymbolValue;
7use ruby_marshal::UserDefinedValue;
8use ruby_marshal::Value;
9use ruby_marshal::ValueArena;
10use ruby_marshal::ValueHandle;
11
12#[derive(Debug)]
13pub enum TableFromValueError {
14    TooShort { len: usize },
15    OddSizedPayload { len: usize },
16    ItemSizeMismatch { expected: i32, actual: usize },
17}
18
19impl std::fmt::Display for TableFromValueError {
20    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21        match self {
22            Self::TooShort { len } => {
23                write!(f, "the table payload (len {len}) is too short")
24            }
25            Self::OddSizedPayload { len } => {
26                write!(f, "the table payload (len {len}) is not a multiple of 2")
27            }
28            Self::ItemSizeMismatch { expected, actual } => {
29                write!(f, "the item array length is mismatched, expected {expected} bytes but got {actual}")
30            }
31        }
32    }
33}
34
35impl std::error::Error for TableFromValueError {}
36
37#[derive(Debug)]
38pub enum TableIntoValueError {
39    TooManyItems {
40        len: usize,
41        error: std::num::TryFromIntError,
42    },
43}
44
45impl std::fmt::Display for TableIntoValueError {
46    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47        match self {
48            Self::TooManyItems { len, .. } => {
49                write!(f, "there are too many table items in table of len {len}")
50            }
51        }
52    }
53}
54
55impl std::error::Error for TableIntoValueError {
56    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
57        match self {
58            Self::TooManyItems { error, .. } => Some(error),
59            // _ => None,
60        }
61    }
62}
63
64const HEADER_SIZE: usize = 4 * 5;
65
66const USER_DEFINED_NAME: &[u8] = b"Table";
67
68#[derive(Debug, serde::Deserialize, serde::Serialize)]
69pub struct Table {
70    pub dimensions: i32,
71    pub x_size: i32,
72    pub y_size: i32,
73    pub z_size: i32,
74    pub items: Vec<i16>,
75}
76
77impl<'a> FromValue<'a> for Table {
78    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
79        let user_defined: &UserDefinedValue = FromValue::from_value(ctx, value)?;
80        let name = user_defined.name();
81        let name: &SymbolValue = ctx.from_value(name.into())?;
82        let name = name.value();
83
84        if name != USER_DEFINED_NAME {
85            return Err(FromValueError::UnexpectedObjectName { name: name.into() });
86        }
87
88        let value = user_defined.value();
89
90        let value_len = value.len();
91        if value_len < HEADER_SIZE {
92            return Err(FromValueError::new_other(TableFromValueError::TooShort {
93                len: value_len,
94            }));
95        }
96        if value_len % 2 != 0 {
97            return Err(FromValueError::new_other(
98                TableFromValueError::OddSizedPayload { len: value_len },
99            ));
100        }
101
102        let (dimensions, value) = value.split_at(4);
103        let dimensions = i32::from_le_bytes(dimensions.try_into().unwrap());
104
105        let (x_size, value) = value.split_at(4);
106        let x_size = i32::from_le_bytes(x_size.try_into().unwrap());
107
108        let (y_size, value) = value.split_at(4);
109        let y_size = i32::from_le_bytes(y_size.try_into().unwrap());
110
111        let (z_size, value) = value.split_at(4);
112        let z_size = i32::from_le_bytes(z_size.try_into().unwrap());
113
114        let (size, value) = value.split_at(4);
115        let size = i32::from_le_bytes(size.try_into().unwrap());
116
117        let value_len = value.len();
118        if i32::try_from(value_len)
119            .ok()
120            .is_none_or(|value_len| value_len != 2 * size)
121        {
122            return Err(FromValueError::new_other(
123                TableFromValueError::ItemSizeMismatch {
124                    expected: 2 * size,
125                    actual: value_len,
126                },
127            ));
128        }
129
130        let items_len = usize::try_from(size).unwrap();
131        let mut items = Vec::with_capacity(items_len);
132        for chunk in value.chunks(2) {
133            items.push(i16::from_le_bytes(chunk.try_into().unwrap()));
134        }
135
136        Ok(Self {
137            dimensions,
138            x_size,
139            y_size,
140            z_size,
141            items,
142        })
143    }
144}
145
146impl IntoValue for Table {
147    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
148        let name = arena.create_symbol(USER_DEFINED_NAME.into());
149        let mut value = Vec::with_capacity(HEADER_SIZE + self.items.len());
150
151        value.extend(self.dimensions.to_le_bytes());
152        value.extend(self.x_size.to_le_bytes());
153        value.extend(self.y_size.to_le_bytes());
154        value.extend(self.z_size.to_le_bytes());
155
156        let items_len = self.items.len();
157        let size = i32::try_from(items_len).map_err(|error| {
158            IntoValueError::new_other(TableIntoValueError::TooManyItems {
159                len: items_len,
160                error,
161            })
162        })?;
163        value.extend(size.to_le_bytes());
164
165        for item in self.items.iter() {
166            value.extend(item.to_le_bytes());
167        }
168
169        let user_defined = arena.create_user_defined(name, value);
170        Ok(user_defined.into())
171    }
172}