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<FilterConfigItem>;
23
24    /// [`Vec<FilterConfigItem>`] から変換します。
25    ///
26    /// # Panics
27    ///
28    /// `items` の内容が不正な場合、パニックします。
29    fn from_config_items(items: &[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: 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    Button(FilterConfigButton),
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub(crate) enum FilterConfigItemValue {
76    Track(f64),
77    Checkbox(bool),
78    Color(FilterConfigColorValue),
79    Select(i32),
80    File(String),
81    String(String),
82    Text(String),
83    Folder(String),
84    Data {
85        value: *mut std::ffi::c_void,
86        size: usize,
87    },
88    Group,
89    Button,
90}
91
92impl FilterConfigItem {
93    /// 設定名を取得します。
94    ///
95    /// # Note
96    ///
97    /// `FilterConfigItem::Group` の場合、`name` が `None` のときは空文字列を返します。
98    pub fn name(&self) -> &str {
99        match self {
100            FilterConfigItem::Track(item) => &item.name,
101            FilterConfigItem::Checkbox(item) => &item.name,
102            FilterConfigItem::Color(item) => &item.name,
103            FilterConfigItem::Select(item) => &item.name,
104            FilterConfigItem::File(item) => &item.name,
105            FilterConfigItem::String(item) => &item.name,
106            FilterConfigItem::Text(item) => &item.name,
107            FilterConfigItem::Folder(item) => &item.name,
108            FilterConfigItem::Data(item) => &item.name,
109            FilterConfigItem::Group(item) => item.name.as_deref().unwrap_or(""),
110            FilterConfigItem::Button(item) => &item.name,
111        }
112    }
113
114    pub(crate) fn to_raw(&self, leak_manager: &LeakManager) -> aviutl2_sys::filter2::FILTER_ITEM {
115        match self {
116            FilterConfigItem::Track(item) => {
117                let step: f64 = item.step.into();
118                aviutl2_sys::filter2::FILTER_ITEM {
119                    track: aviutl2_sys::filter2::FILTER_ITEM_TRACK {
120                        r#type: leak_manager.leak_as_wide_string("track"),
121                        name: leak_manager.leak_as_wide_string(&item.name),
122                        value: item.value,
123                        s: *item.range.start(),
124                        e: *item.range.end(),
125                        step,
126                    },
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::Button(item) => aviutl2_sys::filter2::FILTER_ITEM {
228                button: aviutl2_sys::filter2::FILTER_ITEM_BUTTON {
229                    r#type: leak_manager.leak_as_wide_string("button"),
230                    name: leak_manager.leak_as_wide_string(&item.name),
231                    callback: item.callback,
232                },
233            },
234        }
235    }
236
237    /// # Safety
238    ///
239    /// `raw` は有効なポインタである必要があります。
240    pub(crate) unsafe fn get_value(
241        raw: *const aviutl2_sys::filter2::FILTER_ITEM,
242    ) -> FilterConfigItemValue {
243        let item_type = unsafe {
244            crate::common::load_wide_string(
245                // SAFETY: aviutl2_sys::filter2::FILTER_ITEM の最初のメンバーはLPCWSTRなので問題ないはず
246                *(raw.cast::<aviutl2_sys::common::LPCWSTR>()),
247            )
248        };
249        match item_type.as_str() {
250            "track" => {
251                let raw_track = unsafe { &(*raw).track };
252                FilterConfigItemValue::Track(raw_track.value)
253            }
254            "check" => {
255                let raw_checkbox = unsafe { &(*raw).checkbox };
256                FilterConfigItemValue::Checkbox(raw_checkbox.value)
257            }
258            "color" => {
259                let raw_color = unsafe { &(*raw).color };
260                FilterConfigItemValue::Color(raw_color.value.into())
261            }
262            "select" => {
263                let raw_select = unsafe { &(*raw).select };
264                FilterConfigItemValue::Select(raw_select.value)
265            }
266            "file" => {
267                let raw_file = unsafe { &(*raw).file };
268                let value = unsafe { crate::common::load_wide_string(raw_file.value) };
269                FilterConfigItemValue::File(value)
270            }
271            "string" => {
272                let raw_string = unsafe { &(*raw).string };
273                let value = unsafe { crate::common::load_wide_string(raw_string.value) };
274                FilterConfigItemValue::String(value)
275            }
276            "text" => {
277                let raw_text = unsafe { &(*raw).text };
278                let value = unsafe { crate::common::load_wide_string(raw_text.value) };
279                FilterConfigItemValue::Text(value)
280            }
281            "folder" => {
282                let raw_folder = unsafe { &(*raw).folder };
283                let value = unsafe { crate::common::load_wide_string(raw_folder.value) };
284                FilterConfigItemValue::Folder(value)
285            }
286            "data" => {
287                // NOTE:
288                // `&(*raw).data`だと最後の方がアクセス違反になりかねないメモリを指す可能性があるのでしない
289                let raw_size = unsafe { (*raw).data.size };
290                let raw_data = unsafe { (*raw).data.value };
291                let size =
292                    usize::try_from(raw_size).expect("FILTER_ITEM_DATA size must not be negative");
293                assert!(
294                    size <= 1024,
295                    "FILTER_ITEM_DATA size must be 1024 bytes or less"
296                );
297                FilterConfigItemValue::Data {
298                    value: raw_data,
299                    size,
300                }
301            }
302            "group" => FilterConfigItemValue::Group,
303            "button" => FilterConfigItemValue::Button,
304            _ => panic!("Unknown filter config item type: {}", item_type),
305        }
306    }
307
308    /// # Safety
309    ///
310    /// `raw` は有効なポインタである必要があります。
311    pub(crate) unsafe fn should_apply_from_raw(
312        &self,
313        raw: *const aviutl2_sys::filter2::FILTER_ITEM,
314    ) -> bool {
315        let value = unsafe { Self::get_value(raw) };
316        match (self, value) {
317            (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => item.value != v,
318            (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
319                item.value != v
320            }
321            (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => item.value != v,
322            (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => item.value != v,
323            (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => item.value != v,
324            (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => item.value != v,
325            (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => item.value != v,
326            (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => item.value != v,
327            (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
328                let size_changed = item.size != size;
329                let ptr_changed = match (item.value, NonNull::new(value)) {
330                    (Some(old), Some(new)) => old != new,
331                    (None, None) => false,
332                    _ => true,
333                };
334
335                size_changed || ptr_changed
336            }
337            (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => false,
338            (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => false,
339            _ => {
340                panic!("Mismatched filter config item type");
341            }
342        }
343    }
344
345    /// # Safety
346    ///
347    /// `raw` は有効なポインタである必要があります。
348    pub(crate) unsafe fn apply_from_raw(&mut self, raw: *const aviutl2_sys::filter2::FILTER_ITEM) {
349        let value = unsafe { Self::get_value(raw) };
350        match (self, value) {
351            (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => {
352                item.value = v;
353            }
354            (FilterConfigItem::Checkbox(item), FilterConfigItemValue::Checkbox(v)) => {
355                item.value = v;
356            }
357            (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => {
358                item.value = v;
359            }
360            (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => {
361                item.value = v;
362            }
363            (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => {
364                item.value = v;
365            }
366            (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => {
367                item.value = v;
368            }
369            (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => {
370                item.value = v;
371            }
372            (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => {
373                item.value = v;
374            }
375            (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
376                item.size = size;
377                item.value = NonNull::new(value);
378            }
379            (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => {
380                // グループは値を持たないので何もしない
381            }
382            (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => {
383                // ボタンは値を持たないので何もしない
384            }
385            _ => {
386                panic!("Mismatched filter config item type");
387            }
388        }
389    }
390}
391
392/// トラックバー。
393#[derive(Debug, Clone)]
394pub struct FilterConfigTrack {
395    /// 設定名。
396    pub name: String,
397
398    /// 設定値。
399    pub value: f64,
400
401    /// 設定値の範囲。
402    pub range: std::ops::RangeInclusive<f64>,
403
404    /// 設定値の単位。
405    pub step: FilterConfigTrackStep,
406}
407
408/// トラックバーの設定値の単位。
409#[derive(Clone, Copy)]
410pub enum FilterConfigTrackStep {
411    /// 1.0
412    One,
413    /// 0.1
414    PointOne,
415    /// 0.01
416    PointZeroOne,
417    /// 0.001
418    PointZeroZeroOne,
419}
420impl TryFrom<f64> for FilterConfigTrackStep {
421    type Error = anyhow::Error;
422    fn try_from(value: f64) -> Result<Self, Self::Error> {
423        match value {
424            1.0 => Ok(FilterConfigTrackStep::One),
425            0.1 => Ok(FilterConfigTrackStep::PointOne),
426            0.01 => Ok(FilterConfigTrackStep::PointZeroOne),
427            0.001 => Ok(FilterConfigTrackStep::PointZeroZeroOne),
428            _ => Err(anyhow::anyhow!("Invalid step value: {}", value)),
429        }
430    }
431}
432impl From<FilterConfigTrackStep> for f64 {
433    fn from(value: FilterConfigTrackStep) -> Self {
434        match value {
435            FilterConfigTrackStep::One => 1.0,
436            FilterConfigTrackStep::PointOne => 0.1,
437            FilterConfigTrackStep::PointZeroOne => 0.01,
438            FilterConfigTrackStep::PointZeroZeroOne => 0.001,
439        }
440    }
441}
442impl std::fmt::Debug for FilterConfigTrackStep {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        let value: f64 = (*self).into();
445        f.debug_tuple("FilterConfigTrackStep")
446            .field(&value)
447            .finish()
448    }
449}
450
451/// チェックボックス。
452#[derive(Debug, Clone)]
453pub struct FilterConfigCheckbox {
454    /// 設定名。
455    pub name: String,
456
457    /// 設定値。
458    pub value: bool,
459}
460
461/// 色選択。
462#[derive(Debug, Clone)]
463pub struct FilterConfigColor {
464    /// 設定名。
465    pub name: String,
466    /// 設定値。
467    pub value: FilterConfigColorValue,
468}
469
470/// 色選択の設定値の色。
471#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
472pub struct FilterConfigColorValue(pub u32);
473impl FilterConfigColorValue {
474    /// 色をRGB形式の各成分に分解して取得します。
475    pub fn to_rgb(&self) -> (u8, u8, u8) {
476        let r = ((self.0 >> 16) & 0xFF) as u8;
477        let g = ((self.0 >> 8) & 0xFF) as u8;
478        let b = (self.0 & 0xFF) as u8;
479        (r, g, b)
480    }
481
482    /// RGB形式の各成分から色を作成します。
483    pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
484        let value = (r as u32) << 16 | (g as u32) << 8 | (b as u32);
485        FilterConfigColorValue(value)
486    }
487}
488impl From<u32> for FilterConfigColorValue {
489    fn from(value: u32) -> Self {
490        FilterConfigColorValue(value)
491    }
492}
493impl From<FilterConfigColorValue> for u32 {
494    fn from(value: FilterConfigColorValue) -> Self {
495        value.0
496    }
497}
498impl From<aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE> for FilterConfigColorValue {
499    fn from(value: aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE) -> Self {
500        unsafe { FilterConfigColorValue(value.code) }
501    }
502}
503impl From<FilterConfigColorValue> for aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE {
504    fn from(value: FilterConfigColorValue) -> Self {
505        aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE { code: value.0 }
506    }
507}
508impl std::fmt::Display for FilterConfigColorValue {
509    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510        let (r, g, b) = self.to_rgb();
511        write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
512    }
513}
514impl std::fmt::LowerHex for FilterConfigColorValue {
515    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516        write!(f, "{:06x}", self.0 & 0xFFFFFF)
517    }
518}
519impl std::fmt::UpperHex for FilterConfigColorValue {
520    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521        write!(f, "{:06X}", self.0 & 0xFFFFFF)
522    }
523}
524
525/// 選択リスト。
526#[derive(Debug, Clone)]
527pub struct FilterConfigSelect {
528    /// 設定名。
529    pub name: String,
530    /// 設定値。
531    pub value: i32,
532    /// 選択肢リスト。
533    pub items: Vec<FilterConfigSelectItem>,
534}
535
536/// 選択リストの選択肢。
537#[derive(Debug, Clone)]
538pub struct FilterConfigSelectItem {
539    /// 選択肢の名前。
540    pub name: String,
541    /// 選択肢の値。
542    pub value: i32,
543}
544
545/// `[Vec<FilterConfigSelectItem>]`に変換したり、AviUtl2側の値から変換するためのトレイト。
546///
547/// 基本的にはこのトレイトを手動で実装する必要はありません。
548/// [`derive@FilterConfigSelectItems`] マクロを使用してください。
549///
550/// <div class="warning">
551///
552/// [`FilterConfigSelect`]は[`Vec<FilterConfigSelectItems>`]との相互変換が可能であれば十分に機能します。
553/// このトレイトを手動で実装する必要はありません。
554///
555/// </div>
556///
557/// # See Also
558///
559/// [derive@FilterConfigSelectItems]
560pub trait FilterConfigSelectItems {
561    /// [`Vec<FilterConfigSelectItem>`] に変換します。
562    fn to_select_items() -> Vec<FilterConfigSelectItem>;
563
564    /// [`i32`] から変換します。
565    ///
566    /// # Panics
567    ///
568    /// `item` の内容が不正な場合、パニックします。
569    fn from_select_item_value(item: i32) -> Self;
570
571    /// [`i32`] へ変換します。
572    fn to_select_item_value(&self) -> i32;
573}
574
575#[doc(inline)]
576pub use aviutl2_macros::FilterConfigSelectItems;
577
578/// ファイル選択。
579#[derive(Debug, Clone)]
580pub struct FilterConfigFile {
581    /// 設定名。
582    pub name: String,
583    /// 設定値。
584    pub value: String,
585    /// ファイルフィルタ。
586    pub filters: Vec<crate::common::FileFilter>,
587}
588
589/// ボタン。
590#[derive(Debug, Clone)]
591pub struct FilterConfigButton {
592    /// 設定名。
593    pub name: String,
594    /// コールバック関数。
595    pub callback: extern "C" fn(*mut EDIT_SECTION),
596}
597
598/// 文字列。
599#[derive(Debug, Clone)]
600pub struct FilterConfigString {
601    /// 設定名。
602    pub name: String,
603    /// 設定値。
604    pub value: String,
605}
606
607/// テキスト。
608#[derive(Debug, Clone)]
609pub struct FilterConfigText {
610    /// 設定名。
611    pub name: String,
612    /// 設定値。
613    pub value: String,
614}
615
616/// フォルダ選択。
617#[derive(Debug, Clone)]
618pub struct FilterConfigFolder {
619    /// 設定名。
620    pub name: String,
621    /// 設定値。
622    pub value: String,
623}
624
625/// 型を消去した汎用データ。
626///
627/// # Warning
628///
629/// この型は型が全くついていません。
630/// 基本的には[`FilterConfigData`]を使用してください。
631#[derive(Debug, Clone)]
632pub struct ErasedFilterConfigData {
633    /// 設定名。
634    pub name: String,
635    /// データのサイズ。
636    ///
637    /// # Note
638    ///
639    /// 1024バイトを超えることはできません。
640    pub size: usize,
641    /// 現在の値を指すポインタ。
642    pub value: Option<NonNull<std::ffi::c_void>>,
643    default_value: [u8; 1024],
644}
645
646impl ErasedFilterConfigData {
647    /// 新しく作成します。
648    /// `value` は `None` になります。
649    ///
650    /// # Panics
651    ///
652    /// Tが1024バイトを超える場合、パニックします。
653    pub fn new<T: Copy + Default + 'static>(name: String) -> Self {
654        Self::with_default_value(name, T::default())
655    }
656
657    /// デフォルト値を指定して新しく作成します。
658    /// `value` は `None` になります。
659    ///
660    /// # Panics
661    ///
662    /// Tが1024バイトを超える場合、パニックします。
663    pub fn with_default_value<T: Copy + 'static>(name: String, default_value: T) -> Self {
664        assert!(
665            std::mem::size_of::<T>() <= 1024,
666            "FilterConfigData<T> size must be <= 1024 bytes"
667        );
668        let size = std::mem::size_of::<T>();
669        let mut default_value_bytes = [0u8; 1024];
670        let default_value_ptr = (&raw const default_value).cast::<u8>();
671        default_value_bytes[..size]
672            .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
673
674        ErasedFilterConfigData {
675            name,
676            size,
677            value: None,
678            default_value: default_value_bytes,
679        }
680    }
681
682    /// デフォルト値のスライスを取得します。
683    pub fn default_value(&self) -> &[u8] {
684        &self.default_value[..self.size]
685    }
686
687    /// 型付きの汎用データに変換します。
688    ///
689    /// # Safety
690    ///
691    /// - `self` を消去する前の型Tと同じ型で呼び出す必要があります。
692    /// - Tのサイズが`self.size`と一致している必要があります。
693    /// - `self.value`が指すポインタが有効である必要があります。
694    /// - `self.default_value`はTとして有効なデータである必要があります。
695    pub unsafe fn into_typed<T: Copy + 'static>(self) -> FilterConfigData<T> {
696        let expected_size = std::mem::size_of::<T>();
697        assert_eq!(
698            self.size, expected_size,
699            "Size mismatch when converting ErasedFilterConfigData to FilterConfigData<T>"
700        );
701        let value = self
702            .value
703            .map(|v| NonNull::new(v.as_ptr().cast::<T>()).unwrap());
704        let default_value_ptr = self.default_value.as_ptr().cast::<T>();
705        let default_value = unsafe { *default_value_ptr };
706        FilterConfigData {
707            name: self.name,
708            value,
709            default_value,
710        }
711    }
712}
713
714/// 汎用データ。
715///
716/// # Note
717///
718/// Tのサイズが変わったとき、値はデフォルト値にリセットされます。
719#[derive(Debug, Clone)]
720pub struct FilterConfigData<T: Copy + 'static> {
721    /// 設定名。
722    pub name: String,
723    /// 設定値。
724    pub value: Option<NonNull<T>>,
725    /// デフォルト値。
726    pub default_value: T,
727}
728
729impl<T: Copy + 'static> FilterConfigData<T> {
730    /// 型を消去した汎用データに変換します。
731    ///
732    /// # Panics
733    ///
734    /// Tが1024バイトを超える場合、パニックします。
735    pub fn erase_type(&self) -> ErasedFilterConfigData {
736        assert!(
737            std::mem::size_of::<T>() <= 1024,
738            "FilterConfigData<T> size must be <= 1024 bytes"
739        );
740        let size = std::mem::size_of::<T>();
741        let mut default_value = [0u8; 1024];
742        let default_value_ptr = (&raw const self.default_value).cast::<u8>();
743        default_value[..size]
744            .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
745
746        ErasedFilterConfigData {
747            name: self.name.clone(),
748            size,
749            value: self
750                .value
751                .map(|v| NonNull::new(v.as_ptr().cast::<c_void>()).unwrap()),
752            default_value,
753        }
754    }
755}
756
757impl<T: Copy + 'static> From<FilterConfigData<T>> for ErasedFilterConfigData {
758    fn from(value: FilterConfigData<T>) -> Self {
759        value.erase_type()
760    }
761}
762
763/// グループ。
764#[derive(Debug, Clone)]
765pub struct FilterConfigGroup {
766    /// 設定名。
767    /// Noneの場合、グループの終端として扱われます。
768    pub name: Option<String>,
769
770    /// デフォルトで開いているかどうか。
771    pub opened: bool,
772}
773
774impl FilterConfigGroup {
775    /// グループの開始を表す設定を作成します。
776    pub fn start(name: String) -> Self {
777        Self::start_with_opened(name, true)
778    }
779
780    /// `opened` を指定してグループの開始を表す設定を作成します。
781    pub fn start_with_opened(name: String, opened: bool) -> Self {
782        FilterConfigGroup {
783            name: Some(name),
784            opened,
785        }
786    }
787
788    /// グループの終了を表す設定を作成します。
789    pub fn end() -> Self {
790        FilterConfigGroup {
791            name: None,
792            opened: false,
793        }
794    }
795}