1#[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#[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
43pub 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}