Skip to main content

aviutl2\filter/
config.rs

1use crate::common::LeakManager;
2use aviutl2_sys::plugin2::EDIT_SECTION;
3use std::mem::MaybeUninit;
4use std::{ffi::c_void, ptr::NonNull};
5
6/// [`Vec<FilterConfigItem>`] と相互変換するためのトレイト。
7/// 基本的にはこのトレイトを手動で実装する必要はありません。
8/// [`macro@filter_config_items`] マクロを使用してください。
9///
10/// <div class="warning">
11///
12/// このcrateは[`Vec<FilterConfigItem>`]との相互変換が可能であれば十分に機能します。
13/// このトレイトを手動で実装する必要はありません。
14///
15/// </div>
16///
17/// # See Also
18///
19/// [`macro@filter_config_items`]
20pub trait FilterConfigItems: Sized {
21    /// [`Vec<FilterConfigItem>`] に変換します。
22    fn to_config_items() -> Vec<crate::filter::FilterConfigItem>;
23
24    /// [`Vec<FilterConfigItem>`] から変換します。
25    ///
26    /// # Panics
27    ///
28    /// `items` の内容が不正な場合、パニックします。
29    fn from_config_items(items: &[crate::filter::FilterConfigItem]) -> Self;
30}
31#[doc(inline)]
32pub use aviutl2_macros::filter_config_items;
33
34/// `&[FilterConfigItem]` に対する拡張トレイト。
35pub trait FilterConfigItemSliceExt {
36    /// `&[FilterConfigItem]` から指定した構造体に変換します。
37    fn to_struct<T: crate::filter::FilterConfigItems>(&self) -> T;
38}
39
40impl FilterConfigItemSliceExt for &[FilterConfigItem] {
41    fn to_struct<T: FilterConfigItems>(&self) -> T {
42        T::from_config_items(self)
43    }
44}
45
46/// フィルタの設定。
47#[allow(clippy::large_enum_variant)]
48#[derive(Debug, Clone)]
49pub enum FilterConfigItem {
50    /// トラックバー。
51    Track(FilterConfigTrack),
52    /// チェックボックス。
53    Checkbox(FilterConfigCheckbox),
54    /// 色選択。
55    Color(FilterConfigColor),
56    /// 選択リスト。
57    Select(FilterConfigSelect),
58    /// ファイル選択。
59    File(FilterConfigFile),
60    /// 文字列。
61    String(FilterConfigString),
62    /// テキスト。
63    Text(FilterConfigText),
64    /// フォルダ選択。
65    Folder(FilterConfigFolder),
66    /// 汎用データ。
67    Data(ErasedFilterConfigData),
68    /// グループ。
69    Group(FilterConfigGroup),
70    /// セパレーター。
71    Separator(FilterConfigSeparator),
72    /// ボタン。
73    Button(FilterConfigButton),
74}
75
76#[derive(Debug, Clone, PartialEq)]
77pub(crate) enum FilterConfigItemValue {
78    Track(f64),
79    Checkbox(bool),
80    Color(FilterConfigColorValue),
81    Select(i32),
82    File(String),
83    String(String),
84    Text(String),
85    Folder(String),
86    Data {
87        value: *mut std::ffi::c_void,
88        size: usize,
89    },
90    Group,
91    Button,
92}
93
94impl FilterConfigItem {
95    /// 設定名を取得します。
96    ///
97    /// # Note
98    ///
99    /// `FilterConfigItem::Group` の場合、`name` が `None` のときは空文字列を返します。
100    pub fn name(&self) -> &str {
101        match self {
102            FilterConfigItem::Track(item) => &item.name,
103            FilterConfigItem::Checkbox(item) => &item.name,
104            FilterConfigItem::Color(item) => &item.name,
105            FilterConfigItem::Select(item) => &item.name,
106            FilterConfigItem::File(item) => &item.name,
107            FilterConfigItem::String(item) => &item.name,
108            FilterConfigItem::Text(item) => &item.name,
109            FilterConfigItem::Folder(item) => &item.name,
110            FilterConfigItem::Data(item) => &item.name,
111            FilterConfigItem::Group(item) => item.name.as_deref().unwrap_or(""),
112            FilterConfigItem::Separator(item) => &item.name,
113            FilterConfigItem::Button(item) => &item.name,
114        }
115    }
116
117    pub(crate) fn to_raw(&self, leak_manager: &LeakManager) -> aviutl2_sys::filter2::FILTER_ITEM {
118        match self {
119            FilterConfigItem::Track(item) => aviutl2_sys::filter2::FILTER_ITEM {
120                track: aviutl2_sys::filter2::FILTER_ITEM_TRACK {
121                    r#type: leak_manager.leak_as_wide_string("track"),
122                    name: leak_manager.leak_as_wide_string(&item.name),
123                    value: item.value,
124                    s: *item.range.start(),
125                    e: *item.range.end(),
126                    step: item.step,
127                },
128            },
129            FilterConfigItem::Checkbox(item) => aviutl2_sys::filter2::FILTER_ITEM {
130                checkbox: aviutl2_sys::filter2::FILTER_ITEM_CHECKBOX {
131                    r#type: leak_manager.leak_as_wide_string("check"),
132                    name: leak_manager.leak_as_wide_string(&item.name),
133                    value: item.value,
134                },
135            },
136            FilterConfigItem::Color(item) => aviutl2_sys::filter2::FILTER_ITEM {
137                color: aviutl2_sys::filter2::FILTER_ITEM_COLOR {
138                    r#type: leak_manager.leak_as_wide_string("color"),
139                    name: leak_manager.leak_as_wide_string(&item.name),
140                    value: item.value.into(),
141                },
142            },
143            FilterConfigItem::Select(item) => {
144                let mut raw_items: Vec<aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM> = item
145                    .items
146                    .iter()
147                    .map(|i| aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
148                        name: leak_manager.leak_as_wide_string(&i.name),
149                        value: i.value,
150                    })
151                    .collect();
152                raw_items.push(aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
153                    name: std::ptr::null(),
154                    value: 0,
155                }); // 終端用
156                let raw_items_ptrs = leak_manager.leak_value_vec(raw_items);
157                aviutl2_sys::filter2::FILTER_ITEM {
158                    select: aviutl2_sys::filter2::FILTER_ITEM_SELECT {
159                        r#type: leak_manager.leak_as_wide_string("select"),
160                        name: leak_manager.leak_as_wide_string(&item.name),
161                        value: item.value,
162                        items: raw_items_ptrs,
163                    },
164                }
165            }
166            FilterConfigItem::File(item) => {
167                let raw_filters = crate::common::format_file_filters(&item.filters);
168                aviutl2_sys::filter2::FILTER_ITEM {
169                    file: aviutl2_sys::filter2::FILTER_ITEM_FILE {
170                        r#type: leak_manager.leak_as_wide_string("file"),
171                        name: leak_manager.leak_as_wide_string(&item.name),
172                        value: leak_manager.leak_as_wide_string(&item.value),
173                        filefilter: leak_manager.leak_as_wide_string(&raw_filters),
174                    },
175                }
176            }
177            FilterConfigItem::String(item) => aviutl2_sys::filter2::FILTER_ITEM {
178                string: aviutl2_sys::filter2::FILTER_ITEM_STRING {
179                    r#type: leak_manager.leak_as_wide_string("string"),
180                    name: leak_manager.leak_as_wide_string(&item.name),
181                    value: leak_manager.leak_as_wide_string(&item.value),
182                },
183            },
184            FilterConfigItem::Text(item) => aviutl2_sys::filter2::FILTER_ITEM {
185                text: aviutl2_sys::filter2::FILTER_ITEM_TEXT {
186                    r#type: leak_manager.leak_as_wide_string("text"),
187                    name: leak_manager.leak_as_wide_string(&item.name),
188                    value: leak_manager.leak_as_wide_string(&item.value),
189                },
190            },
191            FilterConfigItem::Folder(item) => aviutl2_sys::filter2::FILTER_ITEM {
192                folder: aviutl2_sys::filter2::FILTER_ITEM_FOLDER {
193                    r#type: leak_manager.leak_as_wide_string("folder"),
194                    name: leak_manager.leak_as_wide_string(&item.name),
195                    value: leak_manager.leak_as_wide_string(&item.value),
196                },
197            },
198            FilterConfigItem::Data(item) => {
199                let mut data = aviutl2_sys::filter2::FILTER_ITEM_DATA {
200                    r#type: leak_manager.leak_as_wide_string("data"),
201                    name: leak_manager.leak_as_wide_string(&item.name),
202                    value: std::ptr::null_mut(),
203                    size: item.size as i32,
204                    default_value: [MaybeUninit::new(0); 1024],
205                };
206                assert!(item.size <= 1024, "FilterConfigData size must be <= 1024");
207                unsafe {
208                    // SAFETY:
209                    // - item.size <= 1024かつ、
210                    // - item.default_value()はitem.size分のデータを持っている
211                    std::ptr::copy_nonoverlapping(
212                        item.default_value().as_ptr(),
213                        data.default_value.as_mut_ptr() as *mut u8,
214                        item.size,
215                    );
216                }
217
218                aviutl2_sys::filter2::FILTER_ITEM { data }
219            }
220            FilterConfigItem::Group(item) => aviutl2_sys::filter2::FILTER_ITEM {
221                group: aviutl2_sys::filter2::FILTER_ITEM_GROUP {
222                    r#type: leak_manager.leak_as_wide_string("group"),
223                    name: leak_manager.leak_as_wide_string(item.name.as_deref().unwrap_or("")),
224                    default_visible: item.opened,
225                },
226            },
227            FilterConfigItem::Separator(item) => aviutl2_sys::filter2::FILTER_ITEM {
228                separator: aviutl2_sys::filter2::FILTER_ITEM_SEPARATOR {
229                    r#type: leak_manager.leak_as_wide_string("separator"),
230                    name: leak_manager.leak_as_wide_string(&item.name),
231                },
232            },
233            FilterConfigItem::Button(item) => aviutl2_sys::filter2::FILTER_ITEM {
234                button: aviutl2_sys::filter2::FILTER_ITEM_BUTTON {
235                    r#type: leak_manager.leak_as_wide_string("button"),
236                    name: leak_manager.leak_as_wide_string(&item.name),
237                    callback: item.callback,
238                },
239            },
240        }
241    }
242
243    /// # Safety
244    ///
245    /// `raw` は有効なポインタである必要があります。
246    pub(crate) unsafe fn get_value(
247        raw: *const aviutl2_sys::filter2::FILTER_ITEM,
248    ) -> FilterConfigItemValue {
249        let item_type = unsafe {
250            crate::common::load_wide_string(
251                // SAFETY: aviutl2_sys::filter2::FILTER_ITEM の最初のメンバーはLPCWSTRなので問題ないはず
252                *(raw.cast::<aviutl2_sys::common::LPCWSTR>()),
253            )
254        };
255        match item_type.as_str() {
256            "track" => {
257                let raw_track = unsafe { &(*raw).track };
258                FilterConfigItemValue::Track(raw_track.value)
259            }
260            "check" => {
261                let raw_checkbox = unsafe { &(*raw).checkbox };
262                FilterConfigItemValue::Checkbox(raw_checkbox.value)
263            }
264            "color" => {
265                let raw_color = unsafe { &(*raw).color };
266                FilterConfigItemValue::Color(raw_color.value.into())
267            }
268            "select" => {
269                let raw_select = unsafe { &(*raw).select };
270                FilterConfigItemValue::Select(raw_select.value)
271            }
272            "file" => {
273                let raw_file = unsafe { &(*raw).file };
274                let value = unsafe { crate::common::load_wide_string(raw_file.value) };
275                FilterConfigItemValue::File(value)
276            }
277            "string" => {
278                let raw_string = unsafe { &(*raw).string };
279                let value = unsafe { crate::common::load_wide_string(raw_string.value) };
280                FilterConfigItemValue::String(value)
281            }
282            "text" => {
283                let raw_text = unsafe { &(*raw).text };
284                let value = unsafe { crate::common::load_wide_string(raw_text.value) };
285                FilterConfigItemValue::Text(value)
286            }
287            "folder" => {
288                let raw_folder = unsafe { &(*raw).folder };
289                let value = unsafe { crate::common::load_wide_string(raw_folder.value) };
290                FilterConfigItemValue::Folder(value)
291            }
292            "data" => {
293                // NOTE:
294                // `&(*raw).data`だと最後の方がアクセス違反になりかねないメモリを指す可能性があるのでしない
295                let raw_size = unsafe { (*raw).data.size };
296                let raw_data = unsafe { (*raw).data.value };
297                let size =
298                    usize::try_from(raw_size).expect("FILTER_ITEM_DATA size must not be negative");
299                assert!(
300                    size <= 1024,
301                    "FILTER_ITEM_DATA size must be 1024 bytes or less"
302                );
303                FilterConfigItemValue::Data {
304                    value: raw_data,
305                    size,
306                }
307            }
308            "group" => FilterConfigItemValue::Group,
309            "button" => FilterConfigItemValue::Button,
310            _ => panic!("Unknown filter config item type: {}", item_type),
311        }
312    }
313
314    /// # Safety
315    ///
316    /// `raw` は有効なポインタである必要があります。
317    pub(crate) unsafe fn should_apply_from_raw(
318        &self,
319        raw: *const aviutl2_sys::filter2::FILTER_ITEM,
320    ) -> bool {
321        let value = unsafe { Self::get_value(raw) };
322        match (self, value) {
323            (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => item.value != v,
324            (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
325                item.value != v
326            }
327            (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => item.value != v,
328            (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => item.value != v,
329            (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => item.value != v,
330            (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => item.value != v,
331            (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => item.value != v,
332            (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => item.value != v,
333            (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
334                let size_changed = item.size != size;
335                let ptr_changed = match (item.value, NonNull::new(value)) {
336                    (Some(old), Some(new)) => old != new,
337                    (None, None) => false,
338                    _ => true,
339                };
340
341                size_changed || ptr_changed
342            }
343            (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => false,
344            (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => false,
345            _ => {
346                panic!("Mismatched filter config item type");
347            }
348        }
349    }
350
351    /// # Safety
352    ///
353    /// `raw` は有効なポインタである必要があります。
354    pub(crate) unsafe fn apply_from_raw(&mut self, raw: *const aviutl2_sys::filter2::FILTER_ITEM) {
355        let value = unsafe { Self::get_value(raw) };
356        match (self, value) {
357            (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => {
358                item.value = v;
359            }
360            (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
361                item.value = v;
362            }
363            (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => {
364                item.value = v;
365            }
366            (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => {
367                item.value = v;
368            }
369            (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => {
370                item.value = v;
371            }
372            (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => {
373                item.value = v;
374            }
375            (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => {
376                item.value = v;
377            }
378            (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => {
379                item.value = v;
380            }
381            (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
382                item.size = size;
383                item.value = NonNull::new(value);
384            }
385            (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => {
386                // グループは値を持たないので何もしない
387            }
388            (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => {
389                // ボタンは値を持たないので何もしない
390            }
391            _ => {
392                panic!("Mismatched filter config item type");
393            }
394        }
395    }
396}
397
398/// トラックバー。
399#[derive(Debug, Clone)]
400pub struct FilterConfigTrack {
401    /// 設定名。
402    pub name: String,
403
404    /// 設定値。
405    pub value: f64,
406
407    /// 設定値の範囲。
408    pub range: std::ops::RangeInclusive<f64>,
409
410    /// 設定値の単位。
411    pub step: f64,
412}
413
414/// チェックボックス。
415#[derive(Debug, Clone)]
416pub struct FilterConfigCheckbox {
417    /// 設定名。
418    pub name: String,
419
420    /// 設定値。
421    pub value: bool,
422}
423
424/// 色選択。
425#[derive(Debug, Clone)]
426pub struct FilterConfigColor {
427    /// 設定名。
428    pub name: String,
429    /// 設定値。
430    pub value: FilterConfigColorValue,
431}
432
433/// 色選択の設定値の色。
434#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
435pub struct FilterConfigColorValue(pub u32);
436impl FilterConfigColorValue {
437    /// 色をRGB形式の各成分に分解して取得します。
438    pub fn to_rgb(&self) -> (u8, u8, u8) {
439        let r = ((self.0 >> 16) & 0xFF) as u8;
440        let g = ((self.0 >> 8) & 0xFF) as u8;
441        let b = (self.0 & 0xFF) as u8;
442        (r, g, b)
443    }
444
445    /// RGB形式の各成分から色を作成します。
446    pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
447        let value = (r as u32) << 16 | (g as u32) << 8 | (b as u32);
448        FilterConfigColorValue(value)
449    }
450}
451impl From<u32> for FilterConfigColorValue {
452    fn from(value: u32) -> Self {
453        FilterConfigColorValue(value)
454    }
455}
456impl From<FilterConfigColorValue> for u32 {
457    fn from(value: FilterConfigColorValue) -> Self {
458        value.0
459    }
460}
461impl From<aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE> for FilterConfigColorValue {
462    fn from(value: aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE) -> Self {
463        unsafe { FilterConfigColorValue(value.code) }
464    }
465}
466impl From<FilterConfigColorValue> for aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE {
467    fn from(value: FilterConfigColorValue) -> Self {
468        aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE { code: value.0 }
469    }
470}
471impl std::fmt::Display for FilterConfigColorValue {
472    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473        let (r, g, b) = self.to_rgb();
474        write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
475    }
476}
477impl std::fmt::LowerHex for FilterConfigColorValue {
478    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479        write!(f, "{:06x}", self.0 & 0xFFFFFF)
480    }
481}
482impl std::fmt::UpperHex for FilterConfigColorValue {
483    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484        write!(f, "{:06X}", self.0 & 0xFFFFFF)
485    }
486}
487
488/// 選択リスト。
489#[derive(Debug, Clone)]
490pub struct FilterConfigSelect {
491    /// 設定名。
492    pub name: String,
493    /// 設定値。
494    pub value: i32,
495    /// 選択肢リスト。
496    pub items: Vec<FilterConfigSelectItem>,
497}
498
499/// 選択リストの選択肢。
500#[derive(Debug, Clone)]
501pub struct FilterConfigSelectItem {
502    /// 選択肢の名前。
503    pub name: String,
504    /// 選択肢の値。
505    pub value: i32,
506}
507
508/// `[Vec<FilterConfigSelectItem>]`に変換したり、AviUtl2側の値から変換するためのトレイト。
509///
510/// 基本的にはこのトレイトを手動で実装する必要はありません。
511/// [`derive@FilterConfigSelectItems`] マクロを使用してください。
512///
513/// <div class="warning">
514///
515/// [`FilterConfigSelect`]は[`Vec<FilterConfigSelectItems>`]との相互変換が可能であれば十分に機能します。
516/// このトレイトを手動で実装する必要はありません。
517///
518/// </div>
519///
520/// # See Also
521///
522/// [derive@FilterConfigSelectItems]
523pub trait FilterConfigSelectItems {
524    /// [`Vec<FilterConfigSelectItem>`] に変換します。
525    fn to_select_items() -> Vec<crate::filter::FilterConfigSelectItem>;
526
527    /// [`i32`] から変換します。
528    ///
529    /// # Panics
530    ///
531    /// `item` の内容が不正な場合、パニックします。
532    fn from_select_item_value(item: i32) -> Self;
533
534    /// [`i32`] へ変換します。
535    fn to_select_item_value(&self) -> i32;
536}
537
538#[doc(inline)]
539pub use aviutl2_macros::FilterConfigSelectItems;
540
541/// ファイル選択。
542#[derive(Debug, Clone)]
543pub struct FilterConfigFile {
544    /// 設定名。
545    pub name: String,
546    /// 設定値。
547    pub value: String,
548    /// ファイルフィルタ。
549    pub filters: Vec<crate::common::FileFilter>,
550}
551
552/// ボタン。
553#[derive(Debug, Clone)]
554pub struct FilterConfigButton {
555    /// 設定名。
556    pub name: String,
557    /// コールバック関数。
558    pub callback: extern "C" fn(*mut EDIT_SECTION),
559}
560
561/// 文字列。
562#[derive(Debug, Clone)]
563pub struct FilterConfigString {
564    /// 設定名。
565    pub name: String,
566    /// 設定値。
567    pub value: String,
568}
569
570/// テキスト。
571#[derive(Debug, Clone)]
572pub struct FilterConfigText {
573    /// 設定名。
574    pub name: String,
575    /// 設定値。
576    pub value: String,
577}
578
579/// フォルダ選択。
580#[derive(Debug, Clone)]
581pub struct FilterConfigFolder {
582    /// 設定名。
583    pub name: String,
584    /// 設定値。
585    pub value: String,
586}
587
588/// 型を消去した汎用データ。
589///
590/// # Warning
591///
592/// この型は型が全くついていません。
593/// 基本的には[`FilterConfigData`]を使用してください。
594#[derive(Debug, Clone)]
595pub struct ErasedFilterConfigData {
596    /// 設定名。
597    pub name: String,
598    /// データのサイズ。
599    ///
600    /// # Note
601    ///
602    /// 1024バイトを超えることはできません。
603    pub size: usize,
604    /// 現在の値を指すポインタ。
605    pub value: Option<NonNull<std::ffi::c_void>>,
606    default_value: [u8; 1024],
607}
608
609impl ErasedFilterConfigData {
610    /// 新しく作成します。
611    /// `value` は `None` になります。
612    ///
613    /// # Panics
614    ///
615    /// Tが1024バイトを超える場合、パニックします。
616    pub fn new<T: Copy + Default + 'static>(name: String) -> Self {
617        Self::with_default_value(name, T::default())
618    }
619
620    /// デフォルト値を指定して新しく作成します。
621    /// `value` は `None` になります。
622    ///
623    /// # Panics
624    ///
625    /// Tが1024バイトを超える場合、パニックします。
626    pub fn with_default_value<T: Copy + 'static>(name: String, default_value: T) -> Self {
627        assert!(
628            std::mem::size_of::<T>() <= 1024,
629            "FilterConfigData<T> size must be <= 1024 bytes"
630        );
631        let size = std::mem::size_of::<T>();
632        let mut default_value_bytes = [0u8; 1024];
633        let default_value_ptr = (&raw const default_value).cast::<u8>();
634        default_value_bytes[..size]
635            .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
636
637        ErasedFilterConfigData {
638            name,
639            size,
640            value: None,
641            default_value: default_value_bytes,
642        }
643    }
644
645    /// デフォルト値のスライスを取得します。
646    pub fn default_value(&self) -> &[u8] {
647        &self.default_value[..self.size]
648    }
649
650    /// 型付きの汎用データに変換します。
651    ///
652    /// # Safety
653    ///
654    /// - `self` を消去する前の型Tと同じ型で呼び出す必要があります。
655    /// - Tのサイズが`self.size`と一致している必要があります。
656    /// - `self.value`が指すポインタが有効である必要があります。
657    /// - `self.default_value`はTとして有効なデータである必要があります。
658    pub unsafe fn into_typed<T: Copy + 'static>(self) -> FilterConfigData<T> {
659        let expected_size = std::mem::size_of::<T>();
660        assert_eq!(
661            self.size, expected_size,
662            "Size mismatch when converting ErasedFilterConfigData to FilterConfigData<T>"
663        );
664        let value = self
665            .value
666            .map(|v| NonNull::new(v.as_ptr().cast::<T>()).unwrap());
667        let default_value_ptr = self.default_value.as_ptr().cast::<T>();
668        let default_value = unsafe { *default_value_ptr };
669        FilterConfigData {
670            name: self.name,
671            value,
672            default_value,
673        }
674    }
675}
676
677/// 汎用データ。
678///
679/// # Note
680///
681/// Tのサイズが変わったとき、値はデフォルト値にリセットされます。
682#[derive(Debug, Clone)]
683pub struct FilterConfigData<T: Copy + 'static> {
684    /// 設定名。
685    pub name: String,
686    /// 設定値。
687    pub value: Option<NonNull<T>>,
688    /// デフォルト値。
689    pub default_value: T,
690}
691
692impl<T: Copy + 'static> FilterConfigData<T> {
693    /// 型を消去した汎用データに変換します。
694    ///
695    /// # Panics
696    ///
697    /// Tが1024バイトを超える場合、パニックします。
698    pub fn erase_type(&self) -> ErasedFilterConfigData {
699        assert!(
700            std::mem::size_of::<T>() <= 1024,
701            "FilterConfigData<T> size must be <= 1024 bytes"
702        );
703        let size = std::mem::size_of::<T>();
704        let mut default_value = [0u8; 1024];
705        let default_value_ptr = (&raw const self.default_value).cast::<u8>();
706        default_value[..size]
707            .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
708
709        ErasedFilterConfigData {
710            name: self.name.clone(),
711            size,
712            value: self
713                .value
714                .map(|v| NonNull::new(v.as_ptr().cast::<c_void>()).unwrap()),
715            default_value,
716        }
717    }
718}
719
720impl<T: Copy + 'static> From<FilterConfigData<T>> for ErasedFilterConfigData {
721    fn from(value: FilterConfigData<T>) -> Self {
722        value.erase_type()
723    }
724}
725
726/// グループ。
727#[derive(Debug, Clone)]
728pub struct FilterConfigGroup {
729    /// 設定名。
730    /// Noneの場合、グループの終端として扱われます。
731    pub name: Option<String>,
732
733    /// デフォルトで開いているかどうか。
734    pub opened: bool,
735}
736
737impl FilterConfigGroup {
738    /// グループの開始を表す設定を作成します。
739    pub fn start(name: String) -> Self {
740        Self::start_with_opened(name, true)
741    }
742
743    /// `opened` を指定してグループの開始を表す設定を作成します。
744    pub fn start_with_opened(name: String, opened: bool) -> Self {
745        FilterConfigGroup {
746            name: Some(name),
747            opened,
748        }
749    }
750
751    /// グループの終了を表す設定を作成します。
752    pub fn end() -> Self {
753        FilterConfigGroup {
754            name: None,
755            opened: false,
756        }
757    }
758}
759
760/// セパレーター。
761#[derive(Debug, Clone)]
762pub struct FilterConfigSeparator {
763    /// セパレーターに表示するテキスト。
764    pub name: String,
765}