aviutl2_alias/
value.rs

1/// 色項目。
2#[derive(Debug, Clone, PartialEq, Eq)]
3pub enum ColorItem {
4    Transparent,
5    Color(u8, u8, u8),
6}
7
8impl std::fmt::Display for ColorItem {
9    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        match self {
11            ColorItem::Transparent => write!(f, ""),
12            ColorItem::Color(r, g, b) => write!(f, "{:02x}{:02x}{:02x}", r, g, b),
13        }
14    }
15}
16
17/// 色のパースエラー。
18#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
19pub enum ColorParseError {
20    #[error("invalid length")]
21    InvalidLength,
22    #[error("invalid hex value")]
23    InvalidHex(#[from] std::num::ParseIntError),
24}
25
26impl std::str::FromStr for ColorItem {
27    type Err = ColorParseError;
28
29    fn from_str(s: &str) -> Result<Self, Self::Err> {
30        if s.is_empty() {
31            return Ok(ColorItem::Transparent);
32        }
33        if s.len() != 6 {
34            return Err(ColorParseError::InvalidLength);
35        }
36        let r = u8::from_str_radix(&s[0..2], 16)?;
37        let g = u8::from_str_radix(&s[2..4], 16)?;
38        let b = u8::from_str_radix(&s[4..6], 16)?;
39        Ok(ColorItem::Color(r, g, b))
40    }
41}
42
43/// `Table::parse_value`から呼び出される変換トレイト。
44///
45/// 任意の型に実装することで、`Table`から直接その型として値を取得できます。
46pub trait FromTableValue: Sized {
47    type Err;
48    fn from_table_value(value: &str) -> Result<Self, Self::Err>;
49}
50
51impl FromTableValue for ColorItem {
52    type Err = ColorParseError;
53
54    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
55        value.parse()
56    }
57}
58
59impl FromTableValue for std::path::PathBuf {
60    type Err = std::convert::Infallible;
61
62    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
63        Ok(std::path::PathBuf::from(value))
64    }
65}
66
67impl FromTableValue for String {
68    type Err = std::convert::Infallible;
69
70    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
71        let mut str = String::with_capacity(value.len());
72        let mut iter = value.chars();
73        while let Some(c) = iter.next() {
74            match c {
75                '\\' => match iter.next() {
76                    Some('n') => str.push('\n'),
77                    Some('\\') => str.push('\\'),
78                    Some(other) => {
79                        str.push('\\');
80                        str.push(other);
81                    }
82                    None => str.push('\\'),
83                },
84                _ => str.push(c),
85            }
86        }
87        Ok(str)
88    }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
92pub enum BoolParseError {
93    #[error("invalid boolean value")]
94    InvalidValue,
95}
96
97impl FromTableValue for bool {
98    type Err = BoolParseError;
99
100    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
101        Ok(match value {
102            "1" => true,
103            "0" => false,
104            _ => return Err(BoolParseError::InvalidValue),
105        })
106    }
107}
108
109#[duplicate::duplicate_item(
110    Int;
111    [i8];
112    [i16];
113    [i32];
114    [i64];
115    [i128];
116    [isize];
117    [u8];
118    [u16];
119    [u32];
120    [u64];
121    [u128];
122    [usize];
123)]
124const _: () = {
125    use std::str::FromStr;
126
127    impl FromTableValue for Int {
128        type Err = std::num::ParseIntError;
129
130        fn from_table_value(value: &str) -> Result<Self, Self::Err> {
131            value.parse()
132        }
133    }
134    impl FromTableValue for Vec<Int> {
135        type Err = std::num::ParseIntError;
136
137        fn from_table_value(value: &str) -> Result<Self, Self::Err> {
138            value
139                .split(',')
140                .map(Int::from_str)
141                .collect::<Result<Vec<_>, _>>()
142        }
143    }
144};
145
146impl FromTableValue for crate::TrackItem {
147    type Err = crate::TrackItemParseError;
148
149    fn from_table_value(value: &str) -> Result<Self, Self::Err> {
150        value.parse()
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::Table;
158
159    #[test]
160    fn test_parse_table_value() {
161        let input = include_str!("../test_assets/everything.aup2");
162        let table: Table = input.parse().unwrap();
163
164        let project_table = table.get_table("project").unwrap();
165        assert_eq!(
166            project_table
167                .parse_value::<std::path::PathBuf>("file")
168                .unwrap()
169                .unwrap(),
170            std::path::PathBuf::from("Z:\\test.aup2")
171        );
172
173        let obj0 = table.get_table("0").unwrap();
174        assert_eq!(
175            obj0.parse_value::<Vec<u8>>("frame").unwrap().unwrap(),
176            &[0, 80]
177        );
178        let effect = obj0.get_table("0").unwrap();
179        assert_eq!(
180            effect.parse_value::<ColorItem>("主色").unwrap().unwrap(),
181            ColorItem::Color(255, 255, 255)
182        );
183        assert_eq!(
184            effect.parse_value::<String>("テキスト").unwrap().unwrap(),
185            "Hello\\\nWorld"
186        );
187    }
188}