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 crate::TrackItem {
219 type Err = crate::TrackItemParseError;
220
221 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
222 value.parse()
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use crate::Table;
230
231 #[test]
232 fn test_parse_table_value() {
233 let input = include_str!("../test_assets/everything.aup2");
234 let table: Table = input.parse().unwrap();
235
236 let project_table = table.get_table("project").unwrap();
237 assert_eq!(
238 project_table
239 .parse_value::<std::path::PathBuf>("file")
240 .unwrap()
241 .unwrap(),
242 std::path::PathBuf::from("Z:\\test.aup2")
243 );
244
245 let obj0 = table.get_table("0").unwrap();
246 assert_eq!(
247 obj0.parse_value::<Vec<u8>>("frame").unwrap().unwrap(),
248 &[0, 80]
249 );
250 let effect = obj0.get_table("0").unwrap();
251 assert_eq!(
252 effect.parse_value::<ColorItem>("主色").unwrap().unwrap(),
253 ColorItem::Color(255, 255, 255)
254 );
255 assert_eq!(
256 effect.parse_value::<String>("テキスト").unwrap().unwrap(),
257 "Hello\\\nWorld"
258 );
259 }
260
261 #[test]
262 fn test_parse_binaries() {
263 let input = include_str!("../test_assets/binary.aup2");
264 let table: Table = input.parse().unwrap();
265
266 let obj0 = table.get_table("0").unwrap();
267 let effect = obj0.get_table("0").unwrap();
268 assert_eq!(
269 effect.parse_value::<BinaryItem>("color").unwrap().unwrap(),
270 BinaryItem::from(&[0x01, 0x93, 0x4d, 0x5e])
271 );
272
273 let plugin = table.get_table("plugin").unwrap();
274 let plugin0 = plugin.get_table("0").unwrap();
275
276 assert_eq!(
277 plugin0
278 .parse_value::<BinaryItem>("--aviutl2-rs:serde-zstd-v1:chunk:alias_entries:0")
279 .unwrap()
280 .unwrap(),
281 BinaryItem::from(&[0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x58, 0x09, 0x00, 0x00, 0x90])
282 );
283 }
284}