aviutl2_alias/
table.rs

1use crate::FromTableValue;
2
3/// テーブル構造を定義します。
4#[derive(Clone, Default, PartialEq, Eq)]
5pub struct Table {
6    items: indexmap::IndexMap<String, TableItem>,
7}
8
9#[derive(Clone, PartialEq, Eq)]
10struct TableItem {
11    value: Option<String>,
12    table: Option<Table>,
13}
14
15impl Table {
16    /// 空のテーブルを作成します。
17    pub fn new() -> Self {
18        Self {
19            items: indexmap::IndexMap::new(),
20        }
21    }
22
23    /// 指定したキーに値を挿入します。
24    pub fn insert_value<T: std::fmt::Display>(&mut self, key: &str, value: T) {
25        self.items
26            .entry(key.to_string())
27            .or_insert_with(|| TableItem {
28                value: None,
29                table: None,
30            })
31            .value = Some(value.to_string());
32    }
33    /// 指定したキーにサブテーブルを挿入します。
34    pub fn insert_table(&mut self, key: &str, table: Table) {
35        self.items
36            .entry(key.to_string())
37            .or_insert_with(|| TableItem {
38                value: None,
39                table: None,
40            })
41            .table = Some(table);
42    }
43    /// 指定したキーの値を削除します。
44    pub fn remove_value(&mut self, key: &str) {
45        if let Some(item) = self.items.get_mut(key) {
46            item.value = None;
47            if item.table.is_none() {
48                self.items.shift_remove(key);
49            }
50        }
51    }
52    /// 指定したキーのサブテーブルを削除します。
53    pub fn remove_table(&mut self, key: &str) {
54        if let Some(item) = self.items.get_mut(key) {
55            item.table = None;
56            if item.value.is_none() {
57                self.items.shift_remove(key);
58            }
59        }
60    }
61    /// 指定したキーの値を文字列として読み取ります。
62    pub fn get_value(&self, key: &str) -> Option<&String> {
63        self.items.get(key).and_then(|item| item.value.as_ref())
64    }
65
66    /// 指定したキーの値をパースして読み取ります。
67    pub fn parse_value<T: FromTableValue>(&self, key: &str) -> Option<Result<T, T::Err>> {
68        self.get_value(key)
69            .map(|value_str| T::from_table_value(value_str))
70    }
71    /// 指定したキーの値への可変参照を取得します。
72    pub fn get_value_mut(&mut self, key: &str) -> Option<&mut String> {
73        self.items.get_mut(key).and_then(|item| item.value.as_mut())
74    }
75    /// 指定したキーのサブテーブルを取得します。
76    pub fn get_table(&self, key: &str) -> Option<&Table> {
77        self.items.get(key).and_then(|item| item.table.as_ref())
78    }
79    /// 指定したキーのサブテーブルへの可変参照を取得します。
80    pub fn get_table_mut(&mut self, key: &str) -> Option<&mut Table> {
81        self.items.get_mut(key).and_then(|item| item.table.as_mut())
82    }
83
84    /// 別のテーブルをマージします。
85    pub fn merge(&mut self, other: &Table) {
86        for (key, other_item) in &other.items {
87            match self.items.get_mut(key) {
88                Some(item) => {
89                    if let Some(other_value) = &other_item.value {
90                        item.value = Some(other_value.clone());
91                    }
92                    if let Some(other_table) = &other_item.table {
93                        if let Some(item_table) = &mut item.table {
94                            item_table.merge(other_table);
95                        } else {
96                            item.table = Some(other_table.clone());
97                        }
98                    }
99                }
100                None => {
101                    self.items.insert(key.clone(), other_item.clone());
102                }
103            }
104        }
105    }
106
107    /// 値を列挙するイテレーターを返します。
108    pub fn values<'a>(&'a self) -> TableValuesIterator<'a> {
109        TableValuesIterator::new(self)
110    }
111
112    /// 可変参照で値を列挙します。
113    pub fn values_mut<'a>(&'a mut self) -> TableValuesIteratorMut<'a> {
114        TableValuesIteratorMut::new(self)
115    }
116
117    /// 値が空かどうかを返します。
118    pub fn is_values_empty(&self) -> bool {
119        self.items.values().all(|item| item.value.is_none())
120    }
121
122    /// 子テーブルを列挙するイテレーターを返します。
123    pub fn subtables<'a>(&'a self) -> SubTablesIterator<'a> {
124        SubTablesIterator::new(self)
125    }
126
127    /// 子テーブルを可変参照で列挙します。
128    pub fn subtables_mut<'a>(&'a mut self) -> SubTablesIteratorMut<'a> {
129        SubTablesIteratorMut::new(self)
130    }
131
132    /// 子テーブルが空かどうかを返します。
133    pub fn is_subtables_empty(&self) -> bool {
134        self.items.values().all(|item| item.table.is_none())
135    }
136
137    /// `0`、`1`、`2`...のキーを持つ子テーブルを配列として列挙するイテレーターを返します。
138    pub fn iter_subtables_as_array<'a>(&'a self) -> ArraySubTablesIterator<'a> {
139        ArraySubTablesIterator::new(self)
140    }
141
142    // pub fn iter_subtables_as_array_mut<'a>(&'a mut self) -> ArraySubTablesIteratorMut<'a> {
143    //     ArraySubTablesIteratorMut::new(self)
144    // }
145
146    /// テーブルを文字列として書き出します。
147    ///
148    /// `prefix`はサブテーブルの名前の接頭辞として使用されます。
149    /// 具体的には、`${prefix}.${key}`の形式でサブテーブルの名前が生成されます。
150    pub fn write_table(
151        &self,
152        f: &mut impl std::fmt::Write,
153        prefix: Option<&str>,
154    ) -> std::fmt::Result {
155        for (key, item) in self.values() {
156            write!(f, "{}={}\r\n", key, item)?;
157        }
158        let prefix = prefix.map_or("".to_string(), |p| format!("{}.", p));
159        for (key, sub_table) in self.subtables() {
160            let subtable_name = format!("{}{}", prefix, key);
161            if !sub_table.is_values_empty() {
162                write!(f, "[{}]\r\n", subtable_name)?;
163            }
164            sub_table.write_table(f, Some(&subtable_name))?;
165        }
166        Ok(())
167    }
168}
169
170/// [`Table::values`]で使われるイテレーター。
171#[derive(Debug)]
172pub struct TableValuesIterator<'a> {
173    table: &'a Table,
174    index: usize,
175}
176impl<'a> TableValuesIterator<'a> {
177    pub fn new(table: &'a Table) -> Self {
178        Self { table, index: 0 }
179    }
180}
181impl<'a> Iterator for TableValuesIterator<'a> {
182    type Item = (&'a String, &'a String);
183
184    fn next(&mut self) -> Option<Self::Item> {
185        while self.index < self.table.items.len() {
186            let item = &self.table.items.get_index(self.index).unwrap();
187            self.index += 1;
188            if let Some(value) = &item.1.value {
189                return Some((item.0, value));
190            }
191        }
192        None
193    }
194
195    fn size_hint(&self) -> (usize, Option<usize>) {
196        let remaining = self.table.items.len().saturating_sub(self.index);
197        (0, Some(remaining))
198    }
199}
200
201/// [`Table::values_mut`]で使われるイテレーター。
202pub struct TableValuesIteratorMut<'a> {
203    inner: indexmap::map::IterMut<'a, String, TableItem>,
204}
205impl<'a> TableValuesIteratorMut<'a> {
206    pub fn new(table: &'a mut Table) -> Self {
207        Self {
208            inner: table.items.iter_mut(),
209        }
210    }
211}
212impl<'a> Iterator for TableValuesIteratorMut<'a> {
213    type Item = (&'a String, &'a mut String);
214
215    fn next(&mut self) -> Option<Self::Item> {
216        for (key, item) in self.inner.by_ref() {
217            if let Some(value) = item.value.as_mut() {
218                return Some((key, value));
219            }
220        }
221        None
222    }
223    fn size_hint(&self) -> (usize, Option<usize>) {
224        let remaining = self.inner.len();
225        (0, Some(remaining))
226    }
227}
228
229/// [`Table::subtables`]で使われるイテレーター。
230pub struct SubTablesIterator<'a> {
231    table: &'a Table,
232    index: usize,
233}
234impl<'a> SubTablesIterator<'a> {
235    pub fn new(table: &'a Table) -> Self {
236        Self { table, index: 0 }
237    }
238}
239impl<'a> Iterator for SubTablesIterator<'a> {
240    type Item = (&'a String, &'a Table);
241    fn next(&mut self) -> Option<Self::Item> {
242        while self.index < self.table.items.len() {
243            let item = &self.table.items.get_index(self.index).unwrap();
244            self.index += 1;
245            if let Some(sub_table) = &item.1.table {
246                return Some((item.0, sub_table));
247            }
248        }
249        None
250    }
251    fn size_hint(&self) -> (usize, Option<usize>) {
252        let remaining = self.table.items.len().saturating_sub(self.index);
253        (0, Some(remaining))
254    }
255}
256
257/// [`Table::subtables_mut`]で使われるイテレーター。
258pub struct SubTablesIteratorMut<'a> {
259    inner: indexmap::map::IterMut<'a, String, TableItem>,
260}
261impl<'a> SubTablesIteratorMut<'a> {
262    pub fn new(table: &'a mut Table) -> Self {
263        Self {
264            inner: table.items.iter_mut(),
265        }
266    }
267}
268impl<'a> Iterator for SubTablesIteratorMut<'a> {
269    type Item = (&'a String, &'a mut Table);
270    fn next(&mut self) -> Option<Self::Item> {
271        for (key, item) in self.inner.by_ref() {
272            if let Some(sub_table) = item.table.as_mut() {
273                return Some((key, sub_table));
274            }
275        }
276        None
277    }
278    fn size_hint(&self) -> (usize, Option<usize>) {
279        let remaining = self.inner.len();
280        (0, Some(remaining))
281    }
282}
283
284/// [`Table::iter_subtables_as_array`]で使われるイテレーター。
285pub struct ArraySubTablesIterator<'a> {
286    table: &'a Table,
287    index: usize,
288}
289impl<'a> ArraySubTablesIterator<'a> {
290    pub fn new(table: &'a Table) -> Self {
291        Self { table, index: 0 }
292    }
293}
294impl<'a> Iterator for ArraySubTablesIterator<'a> {
295    type Item = &'a Table;
296    fn next(&mut self) -> Option<Self::Item> {
297        let key = self.index.to_string();
298        self.index += 1;
299        if let Some(sub_table) = self.table.get_table(&key) {
300            Some(sub_table)
301        } else {
302            None
303        }
304    }
305    fn size_hint(&self) -> (usize, Option<usize>) {
306        let remaining = self.table.items.len().saturating_sub(self.index);
307        (0, Some(remaining))
308    }
309}
310
311// TODO: コメントを解除する(有識者求む)
312// pub struct ArraySubTablesIteratorMut<'a> {
313//     index: usize,
314//     table: &'a mut Table,
315// }
316// impl<'a> ArraySubTablesIteratorMut<'a> {
317//     pub fn new(table: &'a mut Table) -> Self {
318//         Self {
319//             index: 0,
320//             table,
321//         }
322//     }
323// }
324// impl<'a> Iterator for ArraySubTablesIteratorMut<'a> {
325//     type Item = &'a mut Table;
326//     fn next(&mut self) -> Option<Self::Item> {
327//         let raw: *mut Table = self.table;
328//         let key = self.index.to_string();
329//         self.index += 1;
330//
331//         // Safety: &mut self.tableは他に存在しないはず
332//         unsafe {
333//             match (&mut *raw).get_table_mut(&key) {
334//                 Some(sub) => Some(sub),
335//                 None => None,
336//             }
337//         }
338//     }
339// }
340
341/// テーブルのパースエラー。
342#[derive(Debug, Clone, thiserror::Error)]
343pub enum TableParseError {
344    #[error("Invalid line: {0}")]
345    InvalidLine(String),
346}
347
348impl std::fmt::Debug for TableItem {
349    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350        f.debug_struct("TableItem")
351            .field("value", &self.value)
352            .field("table", &self.table)
353            .finish()
354    }
355}
356
357impl std::str::FromStr for Table {
358    type Err = TableParseError;
359
360    fn from_str(s: &str) -> Result<Self, Self::Err> {
361        let mut root = Table::new();
362        let mut current_path: Vec<String> = Vec::new();
363
364        for line in s.lines() {
365            if line.trim().is_empty() {
366                continue;
367            } else if line.starts_with('[') && line.ends_with(']') {
368                let section = &line[1..line.len() - 1];
369                current_path.clear();
370                if !section.is_empty() {
371                    current_path.extend(section.split('.').map(|part| part.to_string()));
372                }
373            } else if let Some((key, value)) = line.split_once('=') {
374                let target = ensure_path(&mut root, &current_path);
375                target.insert_value(key, value);
376            } else {
377                return Err(TableParseError::InvalidLine(line.to_string()));
378            }
379        }
380
381        Ok(root)
382    }
383}
384
385fn ensure_path<'a>(mut table: &'a mut Table, path: &[String]) -> &'a mut Table {
386    for segment in path {
387        let entry = table
388            .items
389            .entry(segment.clone())
390            .or_insert_with(|| TableItem {
391                value: None,
392                table: Some(Table::new()),
393            });
394        table = entry.table.get_or_insert_with(Table::new);
395    }
396    table
397}
398impl std::fmt::Debug for Table {
399    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400        f.debug_struct("Table")
401            .field(
402                "values",
403                &self.values().collect::<indexmap::IndexMap<_, _>>(),
404            )
405            .field(
406                "subtables",
407                &self.subtables().collect::<indexmap::IndexMap<_, _>>(),
408            )
409            .finish()
410    }
411}
412impl std::fmt::Display for Table {
413    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414        self.write_table(f, None)
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421    #[test]
422    fn test_table_insert_and_get() {
423        let mut table = Table::new();
424        table.insert_value("key1", "value1");
425        assert_eq!(table.get_value("key1"), Some(&"value1".to_string()));
426        let mut sub_table = Table::new();
427        sub_table.insert_value("sub_key1", "sub_value1");
428        table.insert_table("sub_table", sub_table.clone());
429        assert_eq!(table.get_table("sub_table"), Some(&sub_table));
430    }
431
432    #[test]
433    fn test_parse_table() {
434        let input = include_str!("../test_assets/tracks.aup2");
435        let table: Table = input.parse().unwrap();
436
437        let (project_table_name, project_table) = table.subtables().next().unwrap();
438        assert_eq!(project_table_name, "project");
439        assert_eq!(
440            project_table.get_value("version"),
441            Some(&"2001802".to_string())
442        );
443
444        assert_eq!(
445            table
446                .get_table("0")
447                .unwrap()
448                .get_table("0")
449                .unwrap()
450                .get_value("effect.name"),
451            Some(&"test_tracks".to_string())
452        );
453        assert_eq!(
454            table
455                .get_table("2")
456                .unwrap()
457                .get_table("1")
458                .unwrap()
459                .get_value("effect.name"),
460            Some(&"標準描画".to_string())
461        );
462
463        let layers = table
464            .iter_subtables_as_array()
465            .map(|t| t.parse_value::<usize>("layer").unwrap().unwrap())
466            .collect::<Vec<_>>();
467        assert_eq!(layers, vec![0, 1, 2]);
468
469        insta::assert_debug_snapshot!(table);
470        assert_eq!(table.to_string(), input);
471    }
472
473    #[test]
474    fn test_values_mut_iterator() {
475        let mut table = Table::new();
476        table.insert_value("key1", "value1");
477        table.insert_value("key2", "value2");
478
479        for (_key, value) in table.values_mut() {
480            value.push_str("_mutated");
481        }
482
483        assert_eq!(table.get_value("key1"), Some(&"value1_mutated".to_string()));
484        assert_eq!(table.get_value("key2"), Some(&"value2_mutated".to_string()));
485    }
486
487    #[test]
488    fn test_subtables_mut_iterator() {
489        let mut table = Table::new();
490        let mut sub = Table::new();
491        sub.insert_value("inner", "value");
492        table.insert_table("sub1", sub);
493
494        for (_key, sub_table) in table.subtables_mut() {
495            sub_table.insert_value("updated", "true");
496        }
497
498        assert_eq!(
499            table.get_table("sub1").unwrap().get_value("updated"),
500            Some(&"true".to_string())
501        );
502    }
503}