Skip to main content

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