aviutl2_alias/
value.rs

1/// `Table::parse_value`から呼び出される変換トレイト。
2///
3/// 任意の型に実装することで、`Table`から直接その型として値を取得できます。
4pub trait FromTableValue: Sized {
5    type Err;
6    fn from_table_value(value: &str) -> Result<Self, Self::Err>;
7}
8
9/// バイナリ。
10/// フィルタ効果のdata、汎用プラグインのデータなどで使われています。
11#[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/// 色項目。
82#[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/// 色のパースエラー。
98#[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}