1pub trait FromTableValue: Sized {
5 type Err;
6 fn from_table_value(value: &str) -> Result<Self, Self::Err>;
7}
8
9#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct BinaryItem(Vec<u8>);
13
14impl std::ops::Deref for BinaryItem {
15 type Target = Vec<u8>;
16 fn deref(&self) -> &Self::Target {
17 &self.0
18 }
19}
20impl From<Vec<u8>> for BinaryItem {
21 fn from(value: Vec<u8>) -> Self {
22 BinaryItem(value)
23 }
24}
25impl From<&[u8]> for BinaryItem {
26 fn from(value: &[u8]) -> Self {
27 BinaryItem(value.to_vec())
28 }
29}
30impl<const N: usize> From<&[u8; N]> for BinaryItem {
31 fn from(value: &[u8; N]) -> Self {
32 BinaryItem(value.to_vec())
33 }
34}
35impl std::fmt::Display for BinaryItem {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 for byte in &self.0 {
38 write!(f, "{:02x}", byte)?;
39 }
40 Ok(())
41 }
42}
43impl std::str::FromStr for BinaryItem {
44 type Err = BinaryParseError;
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 if !s.len().is_multiple_of(2) {
47 return Err(BinaryParseError::InvalidLength);
48 }
49 let mut result = Vec::with_capacity(s.len() / 2);
50 for i in (0..s.len()).step_by(2) {
51 let byte = u8::from_str_radix(&s[i..i + 2], 16)?;
52 result.push(byte);
53 }
54 Ok(result.into())
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
59pub enum BinaryParseError {
60 #[error("invalid length")]
61 InvalidLength,
62 #[error("invalid hex value")]
63 InvalidHex(#[from] std::num::ParseIntError),
64}
65
66impl FromTableValue for BinaryItem {
67 type Err = BinaryParseError;
68 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
69 if !value.len().is_multiple_of(2) {
70 return Err(BinaryParseError::InvalidLength);
71 }
72 let mut result = Vec::with_capacity(value.len() / 2);
73 for i in (0..value.len()).step_by(2) {
74 let byte = u8::from_str_radix(&value[i..i + 2], 16)?;
75 result.push(byte);
76 }
77 Ok(result.into())
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum ColorItem {
84 Transparent,
85 Color(u8, u8, u8),
86}
87
88impl std::fmt::Display for ColorItem {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 ColorItem::Transparent => write!(f, ""),
92 ColorItem::Color(r, g, b) => write!(f, "{:02x}{:02x}{:02x}", r, g, b),
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
99pub enum ColorParseError {
100 #[error("invalid length")]
101 InvalidLength,
102 #[error("invalid hex value")]
103 InvalidHex(#[from] std::num::ParseIntError),
104}
105
106impl std::str::FromStr for ColorItem {
107 type Err = ColorParseError;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 if s.is_empty() {
111 return Ok(ColorItem::Transparent);
112 }
113 if s.len() != 6 {
114 return Err(ColorParseError::InvalidLength);
115 }
116 let r = u8::from_str_radix(&s[0..2], 16)?;
117 let g = u8::from_str_radix(&s[2..4], 16)?;
118 let b = u8::from_str_radix(&s[4..6], 16)?;
119 Ok(ColorItem::Color(r, g, b))
120 }
121}
122
123impl FromTableValue for ColorItem {
124 type Err = ColorParseError;
125
126 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
127 value.parse()
128 }
129}
130
131impl FromTableValue for std::path::PathBuf {
132 type Err = std::convert::Infallible;
133
134 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
135 Ok(std::path::PathBuf::from(value))
136 }
137}
138
139impl FromTableValue for String {
140 type Err = std::convert::Infallible;
141
142 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
143 let mut str = String::with_capacity(value.len());
144 let mut iter = value.chars();
145 while let Some(c) = iter.next() {
146 match c {
147 '\\' => match iter.next() {
148 Some('n') => str.push('\n'),
149 Some('\\') => str.push('\\'),
150 Some(other) => {
151 str.push('\\');
152 str.push(other);
153 }
154 None => str.push('\\'),
155 },
156 _ => str.push(c),
157 }
158 }
159 Ok(str)
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
164pub enum BoolParseError {
165 #[error("invalid boolean value")]
166 InvalidValue,
167}
168
169impl FromTableValue for bool {
170 type Err = BoolParseError;
171
172 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
173 Ok(match value {
174 "1" => true,
175 "0" => false,
176 _ => return Err(BoolParseError::InvalidValue),
177 })
178 }
179}
180
181#[duplicate::duplicate_item(
182 Int;
183 [i8];
184 [i16];
185 [i32];
186 [i64];
187 [i128];
188 [isize];
189 [u8];
190 [u16];
191 [u32];
192 [u64];
193 [u128];
194 [usize];
195)]
196const _: () = {
197 use std::str::FromStr;
198
199 impl FromTableValue for Int {
200 type Err = std::num::ParseIntError;
201
202 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
203 value.parse()
204 }
205 }
206 impl FromTableValue for Vec<Int> {
207 type Err = std::num::ParseIntError;
208
209 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
210 value
211 .split(',')
212 .map(Int::from_str)
213 .collect::<Result<Vec<_>, _>>()
214 }
215 }
216};
217
218impl FromTableValue for f32 {
219 type Err = std::num::ParseFloatError;
220
221 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
222 value.parse()
223 }
224}
225
226impl FromTableValue for f64 {
227 type Err = std::num::ParseFloatError;
228
229 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
230 value.parse()
231 }
232}
233
234impl FromTableValue for crate::TrackItem {
235 type Err = crate::TrackItemParseError;
236
237 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
238 value.parse()
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::Table;
246
247 #[test]
248 fn test_parse_table_value() {
249 let input = include_str!("../test_assets/everything.aup2");
250 let table: Table = input.parse().unwrap();
251
252 let project_table = table.get_table("project").unwrap();
253 assert_eq!(
254 project_table
255 .parse_value::<std::path::PathBuf>("file")
256 .unwrap()
257 .unwrap(),
258 std::path::PathBuf::from("Z:\\test.aup2")
259 );
260
261 let obj0 = table.get_table("0").unwrap();
262 assert_eq!(
263 obj0.parse_value::<Vec<u8>>("frame").unwrap().unwrap(),
264 &[0, 80]
265 );
266 let effect = obj0.get_table("0").unwrap();
267 assert_eq!(
268 effect.parse_value::<ColorItem>("主色").unwrap().unwrap(),
269 ColorItem::Color(255, 255, 255)
270 );
271 assert_eq!(
272 effect.parse_value::<String>("テキスト").unwrap().unwrap(),
273 "Hello\\\nWorld"
274 );
275 }
276
277 #[test]
278 fn test_parse_binaries() {
279 let input = include_str!("../test_assets/binary.aup2");
280 let table: Table = input.parse().unwrap();
281
282 let obj0 = table.get_table("0").unwrap();
283 let effect = obj0.get_table("0").unwrap();
284 assert_eq!(
285 effect.parse_value::<BinaryItem>("color").unwrap().unwrap(),
286 BinaryItem::from(&[0x01, 0x93, 0x4d, 0x5e])
287 );
288
289 let plugin = table.get_table("plugin").unwrap();
290 let plugin0 = plugin.get_table("0").unwrap();
291
292 assert_eq!(
293 plugin0
294 .parse_value::<BinaryItem>("--aviutl2-rs:serde-zstd-v1:chunk:alias_entries:0")
295 .unwrap()
296 .unwrap(),
297 BinaryItem::from(&[0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x58, 0x09, 0x00, 0x00, 0x90])
298 );
299 }
300}