1use crate::common::LeakManager;
2use aviutl2_sys::plugin2::EDIT_SECTION;
3use parking_lot::lock_api::RawRwLock;
4use std::mem::MaybeUninit;
5use std::{ffi::c_void, ptr::NonNull};
6
7pub trait FilterConfigItems: Sized {
22 fn to_config_items() -> Vec<crate::filter::FilterConfigItem>;
24
25 fn from_config_items(items: &[crate::filter::FilterConfigItem]) -> Self;
31}
32#[doc(inline)]
33pub use aviutl2_macros::filter_config_items;
34
35pub trait FilterConfigItemSliceExt {
37 fn to_struct<T: crate::filter::FilterConfigItems>(&self) -> T;
39}
40
41impl FilterConfigItemSliceExt for &[FilterConfigItem] {
42 fn to_struct<T: FilterConfigItems>(&self) -> T {
43 T::from_config_items(self)
44 }
45}
46
47#[allow(clippy::large_enum_variant)]
49#[derive(Debug, Clone)]
50pub enum FilterConfigItem {
51 Track(FilterConfigTrack),
53 Check(FilterConfigCheck),
55 CheckSection(FilterConfigCheckSection),
57 Color(FilterConfigColor),
59 Select(FilterConfigSelect),
61 File(FilterConfigFile),
63 String(FilterConfigString),
65 Text(FilterConfigText),
67 Folder(FilterConfigFolder),
69 Data(ErasedFilterConfigData),
75 Group(FilterConfigGroup),
77 Separator(FilterConfigSeparator),
79 Button(FilterConfigButton),
81 TrackGroup(FilterConfigTrackGroup),
83 HideRule(FilterConfigHideRule),
85}
86
87#[derive(Debug, Clone, PartialEq)]
88pub(crate) enum FilterConfigItemValue {
89 Track(f64),
90 Check(bool),
91 CheckSection(bool),
92 Color(FilterConfigColorValue),
93 Select(i32),
94 File(String),
95 String(String),
96 Text(String),
97 Folder(String),
98 Data {
99 value: *mut std::ffi::c_void,
100 size: usize,
101 },
102 Group,
103 Separator,
104 Button,
105 TrackGroup,
106 HideRule,
107}
108
109impl FilterConfigItem {
110 pub fn name(&self) -> &str {
116 match self {
117 FilterConfigItem::Track(item) => &item.name,
118 FilterConfigItem::Check(item) => &item.name,
119 FilterConfigItem::Color(item) => &item.name,
120 FilterConfigItem::Select(item) => &item.name,
121 FilterConfigItem::File(item) => &item.name,
122 FilterConfigItem::String(item) => &item.name,
123 FilterConfigItem::Text(item) => &item.name,
124 FilterConfigItem::Folder(item) => &item.name,
125 FilterConfigItem::Data(item) => &item.name,
126 FilterConfigItem::Group(item) => item.name.as_deref().unwrap_or(""),
127 FilterConfigItem::Separator(item) => &item.name,
128 FilterConfigItem::Button(item) => &item.name,
129 FilterConfigItem::CheckSection(item) => &item.name,
130 FilterConfigItem::TrackGroup(item) => &item.name,
131 FilterConfigItem::HideRule(item) => &item.name,
132 }
133 }
134
135 pub(crate) fn to_raw(&self, leak_manager: &LeakManager) -> aviutl2_sys::filter2::FILTER_ITEM {
136 match self {
137 FilterConfigItem::Track(item) => aviutl2_sys::filter2::FILTER_ITEM {
138 track: aviutl2_sys::filter2::FILTER_ITEM_TRACK {
139 r#type: leak_manager.leak_as_wide_string("track2"),
140 name: leak_manager.leak_as_wide_string(&item.name),
141 value: item.value,
142 s: *item.range.start(),
143 e: *item.range.end(),
144 step: item.step,
145 zero_display: item
146 .zero_display
147 .as_ref()
148 .map_or(std::ptr::null(), |s| leak_manager.leak_as_wide_string(s)),
149 slider_ratio: item.slider_ratio,
150 },
151 },
152 FilterConfigItem::Check(item) => aviutl2_sys::filter2::FILTER_ITEM {
153 check: aviutl2_sys::filter2::FILTER_ITEM_CHECK {
154 r#type: leak_manager.leak_as_wide_string("check"),
155 name: leak_manager.leak_as_wide_string(&item.name),
156 value: item.value,
157 },
158 },
159 FilterConfigItem::Color(item) => aviutl2_sys::filter2::FILTER_ITEM {
160 color: aviutl2_sys::filter2::FILTER_ITEM_COLOR {
161 r#type: leak_manager.leak_as_wide_string("color"),
162 name: leak_manager.leak_as_wide_string(&item.name),
163 value: item.value.into(),
164 },
165 },
166 FilterConfigItem::Select(item) => {
167 let mut raw_items: Vec<aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM> = item
168 .items
169 .iter()
170 .map(|i| aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
171 name: leak_manager.leak_as_wide_string(&i.name),
172 value: i.value,
173 })
174 .collect();
175 raw_items.push(aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM {
176 name: std::ptr::null(),
177 value: 0,
178 }); let raw_items_ptrs = leak_manager.leak_value_vec(raw_items);
180 aviutl2_sys::filter2::FILTER_ITEM {
181 select: aviutl2_sys::filter2::FILTER_ITEM_SELECT {
182 r#type: leak_manager.leak_as_wide_string("select"),
183 name: leak_manager.leak_as_wide_string(&item.name),
184 value: item.value,
185 items: raw_items_ptrs,
186 },
187 }
188 }
189 FilterConfigItem::File(item) => {
190 let raw_filters = crate::common::format_file_filters(&item.filters);
191 aviutl2_sys::filter2::FILTER_ITEM {
192 file: aviutl2_sys::filter2::FILTER_ITEM_FILE {
193 r#type: leak_manager.leak_as_wide_string("file"),
194 name: leak_manager.leak_as_wide_string(&item.name),
195 value: leak_manager.leak_as_wide_string(&item.value),
196 filefilter: leak_manager.leak_as_wide_string(&raw_filters),
197 },
198 }
199 }
200 FilterConfigItem::String(item) => aviutl2_sys::filter2::FILTER_ITEM {
201 string: aviutl2_sys::filter2::FILTER_ITEM_STRING {
202 r#type: leak_manager.leak_as_wide_string("string"),
203 name: leak_manager.leak_as_wide_string(&item.name),
204 value: leak_manager.leak_as_wide_string(&item.value),
205 },
206 },
207 FilterConfigItem::Text(item) => aviutl2_sys::filter2::FILTER_ITEM {
208 text: aviutl2_sys::filter2::FILTER_ITEM_TEXT {
209 r#type: leak_manager.leak_as_wide_string("text"),
210 name: leak_manager.leak_as_wide_string(&item.name),
211 value: leak_manager.leak_as_wide_string(&item.value),
212 },
213 },
214 FilterConfigItem::Folder(item) => aviutl2_sys::filter2::FILTER_ITEM {
215 folder: aviutl2_sys::filter2::FILTER_ITEM_FOLDER {
216 r#type: leak_manager.leak_as_wide_string("folder"),
217 name: leak_manager.leak_as_wide_string(&item.name),
218 value: leak_manager.leak_as_wide_string(&item.value),
219 },
220 },
221 FilterConfigItem::Data(item) => {
222 let mut data = aviutl2_sys::filter2::FILTER_ITEM_DATA {
223 r#type: leak_manager.leak_as_wide_string("data"),
224 name: leak_manager.leak_as_wide_string(&item.name),
225 value: std::ptr::null_mut(),
226 size: item.size as i32,
227 default_value: [MaybeUninit::new(0); 16 * 1024],
228 };
229 assert!(
230 item.size <= 16 * 1024,
231 "FilterConfigData size must be <= 16 KiB"
232 );
233 unsafe {
234 std::ptr::copy_nonoverlapping(
238 item.default_value().as_ptr(),
239 data.default_value.as_mut_ptr() as *mut u8,
240 item.size,
241 );
242 }
243
244 aviutl2_sys::filter2::FILTER_ITEM { data }
245 }
246 FilterConfigItem::Group(item) => aviutl2_sys::filter2::FILTER_ITEM {
247 group: aviutl2_sys::filter2::FILTER_ITEM_GROUP {
248 r#type: leak_manager.leak_as_wide_string("group"),
249 name: leak_manager.leak_as_wide_string(item.name.as_deref().unwrap_or("")),
250 default_visible: item.opened,
251 },
252 },
253 FilterConfigItem::Separator(item) => aviutl2_sys::filter2::FILTER_ITEM {
254 separator: aviutl2_sys::filter2::FILTER_ITEM_SEPARATOR {
255 r#type: leak_manager.leak_as_wide_string("separator"),
256 name: leak_manager.leak_as_wide_string(&item.name),
257 },
258 },
259 FilterConfigItem::Button(item) => aviutl2_sys::filter2::FILTER_ITEM {
260 button: aviutl2_sys::filter2::FILTER_ITEM_BUTTON {
261 r#type: leak_manager.leak_as_wide_string("button"),
262 name: leak_manager.leak_as_wide_string(&item.name),
263 callback: item.callback,
264 },
265 },
266 FilterConfigItem::CheckSection(filter_config_check_section) => {
267 aviutl2_sys::filter2::FILTER_ITEM {
268 check_section: aviutl2_sys::filter2::FILTER_ITEM_CHECK_SECTION {
269 r#type: leak_manager.leak_as_wide_string("checksection2"),
270 name: leak_manager.leak_as_wide_string(&filter_config_check_section.name),
271 value: filter_config_check_section.value,
272 multi_section: filter_config_check_section.multi_section,
273 },
274 }
275 }
276 FilterConfigItem::TrackGroup(filter_config_track_group) => {
277 let mut tracks = Vec::new();
278 for item in &filter_config_track_group.tracks {
279 let raw = FilterConfigItem::to_raw(
280 &FilterConfigItem::Track(item.clone()),
281 leak_manager,
282 );
283 tracks.push(leak_manager.leak(raw));
284 }
285 tracks.push(std::ptr::null_mut());
286
287 let pointers = leak_manager.leak_value_vec(tracks);
288
289 aviutl2_sys::filter2::FILTER_ITEM {
290 track_group: aviutl2_sys::filter2::FILTER_ITEM_TRACK_GROUP {
291 r#type: leak_manager.leak_as_wide_string("trackgroup"),
292 name: leak_manager.leak_as_wide_string(&filter_config_track_group.name),
293 tracks: pointers as _,
294 },
295 }
296 }
297 FilterConfigItem::HideRule(filter_config_hide_rule) => {
298 aviutl2_sys::filter2::FILTER_ITEM {
299 hide_rule: aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE {
300 r#type: leak_manager.leak_as_wide_string("hiderule"),
301 name: leak_manager.leak_as_wide_string(&filter_config_hide_rule.name),
302 condition_name: filter_config_hide_rule
303 .condition_name
304 .as_ref()
305 .map_or(std::ptr::null(), |s| leak_manager.leak_as_wide_string(s)),
306 condition_operator:
307 aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR::from(
308 filter_config_hide_rule.condition_operator,
309 ),
310 condition_value: filter_config_hide_rule.condition_value,
311 },
312 }
313 }
314 }
315 }
316
317 pub(crate) unsafe fn get_value(
321 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
322 ) -> FilterConfigItemValue {
323 let item_type = unsafe {
324 crate::common::load_wide_string(
325 *(raw.cast::<aviutl2_sys::common::LPCWSTR>()),
327 )
328 };
329 match item_type.as_str() {
330 "track" | "track2" => {
331 let raw_track = unsafe { &(*raw).track };
332 FilterConfigItemValue::Track(raw_track.value)
333 }
334 "check" => {
335 let raw_check = unsafe { &(*raw).check };
336 FilterConfigItemValue::Check(raw_check.value)
337 }
338 "color" => {
339 let raw_color = unsafe { &(*raw).color };
340 FilterConfigItemValue::Color(raw_color.value.into())
341 }
342 "select" => {
343 let raw_select = unsafe { &(*raw).select };
344 FilterConfigItemValue::Select(raw_select.value)
345 }
346 "file" => {
347 let raw_file = unsafe { &(*raw).file };
348 let value = unsafe { crate::common::load_wide_string(raw_file.value) };
349 FilterConfigItemValue::File(value)
350 }
351 "string" => {
352 let raw_string = unsafe { &(*raw).string };
353 let value = unsafe { crate::common::load_wide_string(raw_string.value) };
354 FilterConfigItemValue::String(value)
355 }
356 "text" => {
357 let raw_text = unsafe { &(*raw).text };
358 let value = unsafe { crate::common::load_wide_string(raw_text.value) };
359 FilterConfigItemValue::Text(value)
360 }
361 "folder" => {
362 let raw_folder = unsafe { &(*raw).folder };
363 let value = unsafe { crate::common::load_wide_string(raw_folder.value) };
364 FilterConfigItemValue::Folder(value)
365 }
366 "data" => {
367 let raw_size = unsafe { (*raw).data.size };
370 let raw_data = unsafe { (*raw).data.value };
371 let size =
372 usize::try_from(raw_size).expect("FILTER_ITEM_DATA size must not be negative");
373 assert!(
374 size <= 16 * 1024,
375 "FILTER_ITEM_DATA size must be 16 KiB or less"
376 );
377 FilterConfigItemValue::Data {
378 value: raw_data,
379 size,
380 }
381 }
382 "group" => FilterConfigItemValue::Group,
383 "separator" => FilterConfigItemValue::Separator,
384 "button" => FilterConfigItemValue::Button,
385 "checksection" | "checksection2" => {
386 let raw_check_section = unsafe { &(*raw).check_section };
387 FilterConfigItemValue::CheckSection(raw_check_section.value)
388 }
389 "trackgroup" => FilterConfigItemValue::TrackGroup,
390 "hiderule" => FilterConfigItemValue::HideRule,
391 _ => panic!("Unknown filter config item type: {}", item_type),
392 }
393 }
394
395 pub(crate) unsafe fn should_apply_from_raw(
399 &self,
400 raw: *const aviutl2_sys::filter2::FILTER_ITEM,
401 ) -> bool {
402 let value = unsafe { Self::get_value(raw) };
403 match (self, value) {
404 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => item.value != v,
405 (FilterConfigItem::Check(item), FilterConfigItemValue::Check(v)) => item.value != v,
406 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => item.value != v,
407 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => item.value != v,
408 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => item.value != v,
409 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => item.value != v,
410 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => item.value != v,
411 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => item.value != v,
412 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
413 let size_changed = item.size != size;
414 let ptr_changed = match (item.value, NonNull::new(value)) {
415 (Some(old), Some(new)) => old != new,
416 (None, None) => false,
417 _ => true,
418 };
419
420 let binding_changed = item.variable_state.as_ref().is_some_and(|state| {
421 let state = state.read();
422 !matches!(
423 *state,
424 VariableLengthDataBacking::Host(pointer)
425 if std::ptr::eq(
426 pointer.as_ptr(),
427 raw.cast_mut().cast::<aviutl2_sys::filter2::FILTER_ITEM_DATA>()
428 )
429 )
430 });
431
432 size_changed || ptr_changed || binding_changed
433 }
434 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => false,
435 (FilterConfigItem::Separator(_), FilterConfigItemValue::Separator) => false,
436 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => false,
437 (FilterConfigItem::CheckSection(item), FilterConfigItemValue::CheckSection(v)) => {
438 item.value != v
439 }
440 (FilterConfigItem::TrackGroup(_), FilterConfigItemValue::TrackGroup) => false,
441 (FilterConfigItem::HideRule(_), FilterConfigItemValue::HideRule) => false,
442 _ => {
443 panic!("Mismatched filter config item type");
444 }
445 }
446 }
447
448 pub(crate) unsafe fn apply_from_raw(&mut self, raw: *const aviutl2_sys::filter2::FILTER_ITEM) {
452 let value = unsafe { Self::get_value(raw) };
453 match (self, value) {
454 (FilterConfigItem::Track(item), FilterConfigItemValue::Track(v)) => {
455 item.value = v;
456 }
457 (FilterConfigItem::Check(item), FilterConfigItemValue::Check(v)) => {
458 item.value = v;
459 }
460 (FilterConfigItem::Color(item), FilterConfigItemValue::Color(v)) => {
461 item.value = v;
462 }
463 (FilterConfigItem::Select(item), FilterConfigItemValue::Select(v)) => {
464 item.value = v;
465 }
466 (FilterConfigItem::File(item), FilterConfigItemValue::File(v)) => {
467 item.value = v;
468 }
469 (FilterConfigItem::String(item), FilterConfigItemValue::String(v)) => {
470 item.value = v;
471 }
472 (FilterConfigItem::Text(item), FilterConfigItemValue::Text(v)) => {
473 item.value = v;
474 }
475 (FilterConfigItem::Folder(item), FilterConfigItemValue::Folder(v)) => {
476 item.value = v;
477 }
478 (FilterConfigItem::Data(item), FilterConfigItemValue::Data { value, size }) => {
479 item.size = size;
480 item.value = NonNull::new(value);
481 if let Some(state) = &item.variable_state {
482 let raw = NonNull::new(
483 raw.cast_mut()
484 .cast::<aviutl2_sys::filter2::FILTER_ITEM_DATA>(),
485 )
486 .expect("FILTER_ITEM_DATA pointer must not be null");
487 *state.write() = VariableLengthDataBacking::Host(raw);
488 }
489 }
490 (FilterConfigItem::Group(_), FilterConfigItemValue::Group) => {
491 }
493 (FilterConfigItem::Separator(_), FilterConfigItemValue::Separator) => {
494 }
496 (FilterConfigItem::Button(_), FilterConfigItemValue::Button) => {
497 }
499 (FilterConfigItem::CheckSection(item), FilterConfigItemValue::CheckSection(v)) => {
500 item.value = v;
501 }
502 (FilterConfigItem::TrackGroup(_), FilterConfigItemValue::TrackGroup) => {
503 }
505 (FilterConfigItem::HideRule(_), FilterConfigItemValue::HideRule) => {
506 }
508 _ => {
509 panic!("Mismatched filter config item type");
510 }
511 }
512 }
513}
514
515#[derive(Debug, Clone)]
517pub struct FilterConfigTrack {
518 pub name: String,
520
521 pub value: f64,
523
524 pub range: std::ops::RangeInclusive<f64>,
526
527 pub step: f64,
529
530 pub zero_display: Option<String>,
532
533 pub slider_ratio: f64,
535}
536
537#[derive(Debug, Clone)]
539pub struct FilterConfigCheck {
540 pub name: String,
542
543 pub value: bool,
545}
546
547#[derive(Debug, Clone)]
549pub struct FilterConfigCheckSection {
550 pub name: String,
552
553 pub value: bool,
555
556 pub multi_section: bool,
558}
559
560#[derive(Debug, Clone)]
562pub struct FilterConfigColor {
563 pub name: String,
565 pub value: FilterConfigColorValue,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
571pub struct FilterConfigColorValue(pub u32);
572impl FilterConfigColorValue {
573 pub fn to_rgb(&self) -> (u8, u8, u8) {
575 let r = ((self.0 >> 16) & 0xFF) as u8;
576 let g = ((self.0 >> 8) & 0xFF) as u8;
577 let b = (self.0 & 0xFF) as u8;
578 (r, g, b)
579 }
580
581 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
583 let value = (r as u32) << 16 | (g as u32) << 8 | (b as u32);
584 FilterConfigColorValue(value)
585 }
586}
587impl From<u32> for FilterConfigColorValue {
588 fn from(value: u32) -> Self {
589 FilterConfigColorValue(value)
590 }
591}
592impl From<FilterConfigColorValue> for u32 {
593 fn from(value: FilterConfigColorValue) -> Self {
594 value.0
595 }
596}
597impl From<aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE> for FilterConfigColorValue {
598 fn from(value: aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE) -> Self {
599 unsafe { FilterConfigColorValue(value.code) }
600 }
601}
602impl From<FilterConfigColorValue> for aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE {
603 fn from(value: FilterConfigColorValue) -> Self {
604 aviutl2_sys::filter2::FILTER_ITEM_COLOR_VALUE { code: value.0 }
605 }
606}
607impl std::fmt::Display for FilterConfigColorValue {
608 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
609 let (r, g, b) = self.to_rgb();
610 write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
611 }
612}
613impl std::fmt::LowerHex for FilterConfigColorValue {
614 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
615 write!(f, "{:06x}", self.0 & 0xFFFFFF)
616 }
617}
618impl std::fmt::UpperHex for FilterConfigColorValue {
619 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
620 write!(f, "{:06X}", self.0 & 0xFFFFFF)
621 }
622}
623
624#[derive(Debug, Clone)]
626pub struct FilterConfigSelect {
627 pub name: String,
629 pub value: i32,
631 pub items: Vec<FilterConfigSelectItem>,
633}
634
635#[derive(Debug, Clone)]
637pub struct FilterConfigSelectItem {
638 pub name: String,
640 pub value: i32,
642}
643
644pub trait FilterConfigSelectItems {
660 fn to_select_items() -> Vec<crate::filter::FilterConfigSelectItem>;
662
663 fn from_select_item_value(item: i32) -> Self;
669
670 fn to_select_item_value(&self) -> i32;
672}
673
674#[doc(inline)]
675pub use aviutl2_macros::FilterConfigSelectItems;
676
677#[derive(Debug, Clone)]
679pub struct FilterConfigFile {
680 pub name: String,
682 pub value: String,
684 pub filters: Vec<crate::common::FileFilter>,
686}
687
688#[derive(Debug, Clone)]
690pub struct FilterConfigButton {
691 pub name: String,
693 pub callback: extern "C" fn(*mut EDIT_SECTION),
695}
696
697#[derive(Debug, Clone)]
699pub struct FilterConfigString {
700 pub name: String,
702 pub value: String,
704}
705
706#[derive(Debug, Clone)]
708pub struct FilterConfigText {
709 pub name: String,
711 pub value: String,
713}
714
715#[derive(Debug, Clone)]
717pub struct FilterConfigFolder {
718 pub name: String,
720 pub value: String,
722}
723
724#[derive(Debug, Clone)]
731pub struct ErasedFilterConfigData {
732 pub name: String,
734 pub size: usize,
740 pub value: Option<NonNull<std::ffi::c_void>>,
742 default_value: [u8; 16 * 1024],
743 variable_state: Option<std::sync::Arc<parking_lot::RwLock<VariableLengthDataBacking>>>,
744}
745
746impl ErasedFilterConfigData {
747 pub fn new<T: Copy + Default + 'static>(name: String) -> Self {
754 Self::with_default_value(name, T::default())
755 }
756
757 pub fn with_default_value<T: Copy + 'static>(name: String, default_value: T) -> Self {
764 assert!(
765 std::mem::size_of::<T>() <= 16 * 1024,
766 "FilterConfigData<T> size must be <= 16 KiB"
767 );
768 let size = std::mem::size_of::<T>();
769 let mut default_value_bytes = [0u8; 16 * 1024];
770 let default_value_ptr = (&raw const default_value).cast::<u8>();
771 default_value_bytes[..size]
772 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
773
774 ErasedFilterConfigData {
775 name,
776 size,
777 value: None,
778 default_value: default_value_bytes,
779 variable_state: None,
780 }
781 }
782
783 pub fn default_value(&self) -> &[u8] {
785 &self.default_value[..self.size]
786 }
787
788 pub unsafe fn into_typed<T: Copy + 'static>(self) -> FilterConfigData<T> {
797 let expected_size = std::mem::size_of::<T>();
798 assert_eq!(
799 self.size, expected_size,
800 "Size mismatch when converting ErasedFilterConfigData to FilterConfigData<T>"
801 );
802 let value = self
803 .value
804 .map(|v| NonNull::new(v.as_ptr().cast::<T>()).unwrap());
805 let default_value_ptr = self.default_value.as_ptr().cast::<T>();
806 let default_value = unsafe { *default_value_ptr };
807 FilterConfigData {
808 name: self.name,
809 value,
810 default_value,
811 }
812 }
813}
814
815#[derive(Debug, Clone)]
823pub struct FilterConfigData<T: Copy + 'static> {
824 pub name: String,
826 pub value: Option<NonNull<T>>,
828 pub default_value: T,
830}
831
832impl<T: Copy + 'static> FilterConfigData<T> {
833 pub fn erase_type(&self) -> ErasedFilterConfigData {
839 assert!(
840 std::mem::size_of::<T>() <= 16 * 1024,
841 "FilterConfigData<T> size must be <= 16 KiB"
842 );
843 let size = std::mem::size_of::<T>();
844 let mut default_value = [0u8; 16 * 1024];
845 let default_value_ptr = (&raw const self.default_value).cast::<u8>();
846 default_value[..size]
847 .copy_from_slice(unsafe { std::slice::from_raw_parts(default_value_ptr, size) });
848
849 ErasedFilterConfigData {
850 name: self.name.clone(),
851 size,
852 value: self
853 .value
854 .map(|v| NonNull::new(v.as_ptr().cast::<c_void>()).unwrap()),
855 default_value,
856 variable_state: None,
857 }
858 }
859}
860
861impl<T: Copy + 'static> From<FilterConfigData<T>> for ErasedFilterConfigData {
862 fn from(value: FilterConfigData<T>) -> Self {
863 value.erase_type()
864 }
865}
866
867#[derive(Debug, Clone)]
872pub struct VariableLengthFilterConfigData {
873 pub name: String,
875 pub value: Option<NonNull<[u8]>>,
877 pub size: usize,
879 pub default_value: Vec<u8>,
881}
882
883impl VariableLengthFilterConfigData {
884 pub fn new(name: String, size: usize) -> Self {
890 Self::with_default_value(name, vec![0u8; size])
891 }
892
893 pub fn with_default_value(name: String, default_value: Vec<u8>) -> Self {
899 assert!(
900 default_value.len() <= 16 * 1024,
901 "VariableLengthFilterConfigData default_value size must be <= 16 KiB"
902 );
903 let size = default_value.len();
904 VariableLengthFilterConfigData {
905 name,
906 value: None,
907 size,
908 default_value,
909 }
910 }
911}
912impl From<VariableLengthFilterConfigData> for ErasedFilterConfigData {
913 fn from(value: VariableLengthFilterConfigData) -> Self {
914 let size = value.size;
915 let mut default_value = [0u8; 16 * 1024];
916 assert!(
917 size <= 16 * 1024,
918 "VariableLengthFilterConfigData size must be <= 16 KiB"
919 );
920 assert_eq!(
921 size,
922 value.default_value.len(),
923 "VariableLengthFilterConfigData size must match default_value length"
924 );
925 default_value[..size].copy_from_slice(&value.default_value);
926 let backing = match value.value {
927 Some(value) => VariableLengthDataBacking::Borrowed {
928 pointer: value.as_ptr().cast(),
929 size,
930 },
931 None => VariableLengthDataBacking::Owned(value.default_value),
932 };
933
934 ErasedFilterConfigData {
935 name: value.name,
936 size,
937 value: None,
938 default_value,
939 variable_state: Some(std::sync::Arc::new(parking_lot::RwLock::new(backing))),
940 }
941 }
942}
943
944#[derive(Debug, Clone)]
946pub struct FilterConfigGroup {
947 pub name: Option<String>,
950
951 pub opened: bool,
953}
954
955impl FilterConfigGroup {
956 pub fn start(name: String) -> Self {
958 Self::start_with_opened(name, true)
959 }
960
961 pub fn start_with_opened(name: String, opened: bool) -> Self {
963 FilterConfigGroup {
964 name: Some(name),
965 opened,
966 }
967 }
968
969 pub fn end() -> Self {
971 FilterConfigGroup {
972 name: None,
973 opened: false,
974 }
975 }
976}
977
978#[derive(Debug, Clone)]
980pub struct FilterConfigSeparator {
981 pub name: String,
983}
984
985static HANDLES: std::sync::LazyLock<dashmap::DashMap<usize, parking_lot::RawRwLock>> =
986 std::sync::LazyLock::new(dashmap::DashMap::new);
987static OWNED_REFERENCES: std::sync::LazyLock<
988 std::sync::Arc<dashmap::DashMap<usize, std::sync::atomic::AtomicUsize>>,
989> = std::sync::LazyLock::new(|| std::sync::Arc::new(dashmap::DashMap::new()));
990
991#[derive(Debug)]
992enum VariableLengthDataBacking {
993 Borrowed { pointer: *mut u8, size: usize },
994 Owned(Vec<u8>),
995 Host(NonNull<aviutl2_sys::filter2::FILTER_ITEM_DATA>),
996}
997
998unsafe impl Send for VariableLengthDataBacking {}
999unsafe impl Sync for VariableLengthDataBacking {}
1000
1001impl VariableLengthDataBacking {
1002 fn as_slice(&self) -> &[u8] {
1003 match self {
1004 Self::Borrowed { pointer, size } => unsafe {
1005 std::slice::from_raw_parts(non_null_slice_pointer(*pointer, *size), *size)
1006 },
1007 Self::Owned(value) => value,
1008 Self::Host(raw) => {
1009 let raw = unsafe { raw.as_ref() };
1010 let size =
1011 usize::try_from(raw.size).expect("FILTER_ITEM_DATA size must not be negative");
1012 assert!(size <= 16 * 1024, "FILTER_ITEM_DATA size must be <= 16 KiB");
1013 unsafe {
1014 std::slice::from_raw_parts(non_null_slice_pointer(raw.value.cast(), size), size)
1015 }
1016 }
1017 }
1018 }
1019
1020 fn as_mut_slice(&mut self) -> &mut [u8] {
1021 match self {
1022 Self::Borrowed { pointer, size } => unsafe {
1023 std::slice::from_raw_parts_mut(non_null_slice_pointer(*pointer, *size), *size)
1024 },
1025 Self::Owned(value) => value,
1026 Self::Host(raw) => {
1027 let raw = unsafe { raw.as_mut() };
1028 let size =
1029 usize::try_from(raw.size).expect("FILTER_ITEM_DATA size must not be negative");
1030 assert!(size <= 16 * 1024, "FILTER_ITEM_DATA size must be <= 16 KiB");
1031 unsafe {
1032 std::slice::from_raw_parts_mut(
1033 non_null_slice_pointer(raw.value.cast(), size),
1034 size,
1035 )
1036 }
1037 }
1038 }
1039 }
1040}
1041
1042fn non_null_slice_pointer(pointer: *mut u8, size: usize) -> *mut u8 {
1043 if size == 0 {
1044 NonNull::<u8>::dangling().as_ptr()
1045 } else {
1046 assert!(
1047 !pointer.is_null(),
1048 "filter config data pointer must not be null"
1049 );
1050 pointer
1051 }
1052}
1053
1054#[derive(Debug, Clone)]
1056pub struct VariableLengthFilterConfigDataHandle {
1057 inner: std::sync::Arc<parking_lot::RwLock<VariableLengthDataBacking>>,
1058}
1059
1060impl VariableLengthFilterConfigDataHandle {
1061 #[doc(hidden)]
1062 pub fn __from_erased(erased: &ErasedFilterConfigData) -> Self {
1063 Self {
1064 inner: erased
1065 .variable_state
1066 .as_ref()
1067 .expect("expected variable-length filter config data")
1068 .clone(),
1069 }
1070 }
1071
1072 #[doc(hidden)]
1073 pub fn __new_owned(value: Vec<u8>) -> Self {
1074 assert!(
1075 value.len() <= 16 * 1024,
1076 "VariableLengthFilterConfigData size must be <= 16 KiB"
1077 );
1078 Self {
1079 inner: std::sync::Arc::new(parking_lot::RwLock::new(VariableLengthDataBacking::Owned(
1080 value,
1081 ))),
1082 }
1083 }
1084
1085 pub fn read(&self) -> VariableLengthFilterConfigDataReadGuard<'_> {
1087 VariableLengthFilterConfigDataReadGuard {
1088 inner: self.inner.read(),
1089 }
1090 }
1091
1092 pub fn try_read(&self) -> Option<VariableLengthFilterConfigDataReadGuard<'_>> {
1094 self.inner
1095 .try_read()
1096 .map(|inner| VariableLengthFilterConfigDataReadGuard { inner })
1097 }
1098
1099 pub fn write(&self) -> VariableLengthFilterConfigDataWriteGuard<'_> {
1101 VariableLengthFilterConfigDataWriteGuard {
1102 inner: self.inner.write(),
1103 }
1104 }
1105
1106 pub fn try_write(&self) -> Option<VariableLengthFilterConfigDataWriteGuard<'_>> {
1108 self.inner
1109 .try_write()
1110 .map(|inner| VariableLengthFilterConfigDataWriteGuard { inner })
1111 }
1112
1113 pub(crate) fn try_resize(
1114 &mut self,
1115 size: usize,
1116 resize: impl FnOnce(*mut c_void, i32),
1117 ) -> crate::filter::FilterProcResult<()> {
1118 if size > 16 * 1024 {
1119 return Err(crate::filter::FilterProcError::ValueOutOfRange);
1120 }
1121 let size =
1122 i32::try_from(size).map_err(|_| crate::filter::FilterProcError::ValueOutOfRange)?;
1123 let mut backing = self
1124 .inner
1125 .try_write()
1126 .ok_or(crate::filter::FilterProcError::FilterConfigDataLocked)?;
1127 let VariableLengthDataBacking::Host(raw) = &mut *backing else {
1128 return Err(crate::filter::FilterProcError::FilterConfigDataNotBound);
1129 };
1130 resize(raw.as_ptr().cast(), size);
1131 let backing = &mut *backing;
1132 match backing {
1133 VariableLengthDataBacking::Host(raw) => {
1134 unsafe { raw.as_mut() }.size = size;
1135 }
1136 VariableLengthDataBacking::Borrowed {
1137 pointer: _,
1138 size: s,
1139 } => {
1140 *s = size as usize;
1141 }
1142 VariableLengthDataBacking::Owned(vec) => {
1143 vec.resize(size as usize, 0);
1144 }
1145 }
1146 Ok(())
1147 }
1148}
1149
1150pub struct VariableLengthFilterConfigDataReadGuard<'handle> {
1152 inner: parking_lot::RwLockReadGuard<'handle, VariableLengthDataBacking>,
1153}
1154
1155impl std::ops::Deref for VariableLengthFilterConfigDataReadGuard<'_> {
1156 type Target = [u8];
1157
1158 fn deref(&self) -> &Self::Target {
1159 self.inner.as_slice()
1160 }
1161}
1162
1163pub struct VariableLengthFilterConfigDataWriteGuard<'handle> {
1165 inner: parking_lot::RwLockWriteGuard<'handle, VariableLengthDataBacking>,
1166}
1167
1168impl std::ops::Deref for VariableLengthFilterConfigDataWriteGuard<'_> {
1169 type Target = [u8];
1170
1171 fn deref(&self) -> &Self::Target {
1172 self.inner.as_slice()
1173 }
1174}
1175
1176impl std::ops::DerefMut for VariableLengthFilterConfigDataWriteGuard<'_> {
1177 fn deref_mut(&mut self) -> &mut Self::Target {
1178 self.inner.as_mut_slice()
1179 }
1180}
1181
1182#[derive(Debug)]
1185pub struct FilterConfigDataHandle<T: Copy> {
1186 pub(crate) inner: *mut T,
1187}
1188
1189unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataHandle<T> {}
1190unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataHandle<T> {}
1191
1192impl<T: Copy> Clone for FilterConfigDataHandle<T> {
1193 fn clone(&self) -> Self {
1194 if !self.inner.is_null() {
1195 let addr = self.inner as usize;
1196 if let Some(entry) = OWNED_REFERENCES.get(&addr) {
1197 entry.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1198 }
1199 }
1200 Self { inner: self.inner }
1201 }
1202}
1203impl<T: Copy> Drop for FilterConfigDataHandle<T> {
1204 fn drop(&mut self) {
1205 if !self.inner.is_null() {
1206 let addr = self.inner as usize;
1207 if let Some(entry) = OWNED_REFERENCES.get(&addr) {
1208 let prev = entry.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
1209 if prev == 1 {
1210 unsafe {
1211 let _boxed = Box::from_raw(self.inner);
1212 }
1213 drop(entry);
1215 OWNED_REFERENCES.remove(&addr);
1216 }
1217 }
1218 }
1219 }
1220}
1221
1222impl<T: Copy> FilterConfigDataHandle<T> {
1223 #[doc(hidden)]
1224 pub fn __generics_default_value() -> T
1225 where
1226 T: Default,
1227 {
1228 T::default()
1229 }
1230
1231 #[doc(hidden)]
1232 pub fn __from_erased(erased: &ErasedFilterConfigData) -> Self {
1233 Self {
1234 inner: erased.value.map_or(std::ptr::null_mut(), |v| v.as_ptr()) as *mut T,
1235 }
1236 }
1237
1238 #[doc(hidden)]
1239 pub fn __new_owned(value: T) -> Self {
1240 let boxed = Box::new(value);
1241 let pointer = Box::into_raw(boxed);
1242 let addr = pointer as *mut () as usize;
1243 OWNED_REFERENCES.insert(addr, std::sync::atomic::AtomicUsize::new(1));
1244 Self { inner: pointer }
1245 }
1246
1247 #[inline]
1248 fn inner_ptr(&self) -> Option<*mut T> {
1249 if self.inner.is_null() {
1250 None
1251 } else {
1252 Some(self.inner)
1253 }
1254 }
1255
1256 #[inline]
1257 fn inner_addr(&self) -> Option<usize> {
1258 self.inner_ptr().map(|inner| inner as *mut () as usize)
1259 }
1260
1261 pub fn read<'handle>(&'handle self) -> FilterConfigDataReadGuard<'handle, T> {
1263 let inner = self
1264 .inner_ptr()
1265 .expect("FilterConfigDataHandle::read called with a null inner pointer");
1266 let addr = self
1267 .inner_addr()
1268 .expect("FilterConfigDataHandle::read called with a null inner pointer");
1269 let lock = HANDLES
1270 .entry(addr)
1271 .or_insert_with(|| parking_lot::RawRwLock::INIT);
1272 let lock = lock.value();
1273
1274 lock.lock_shared();
1275 FilterConfigDataReadGuard::new(inner)
1276 }
1277
1278 pub fn try_read<'handle>(&'handle self) -> Option<FilterConfigDataReadGuard<'handle, T>> {
1281 let inner = self.inner_ptr()?;
1282 let addr = self.inner_addr()?;
1283 let lock = HANDLES
1284 .entry(addr)
1285 .or_insert_with(|| parking_lot::RawRwLock::INIT);
1286 let lock = lock.value();
1287
1288 if lock.try_lock_shared() {
1289 Some(FilterConfigDataReadGuard::new(inner))
1290 } else {
1291 None
1292 }
1293 }
1294
1295 pub fn write<'handle>(&'handle self) -> FilterConfigDataWriteGuard<'handle, T> {
1297 let addr = self.inner as *mut () as usize;
1298 let lock = HANDLES
1299 .entry(addr)
1300 .or_insert_with(|| parking_lot::RawRwLock::INIT);
1301 let lock = lock.value();
1302 lock.lock_exclusive();
1303 FilterConfigDataWriteGuard::new(self.inner)
1304 }
1305
1306 pub fn try_write<'handle>(&'handle self) -> Option<FilterConfigDataWriteGuard<'handle, T>> {
1309 let addr = self.inner as *mut () as usize;
1310 let lock = HANDLES
1311 .entry(addr)
1312 .or_insert_with(|| parking_lot::RawRwLock::INIT);
1313 let lock = lock.value();
1314 if lock.try_lock_exclusive() {
1315 Some(FilterConfigDataWriteGuard::new(self.inner))
1316 } else {
1317 None
1318 }
1319 }
1320
1321 pub fn as_ptr(&self) -> *mut T {
1327 self.inner
1328 }
1329}
1330
1331#[doc(hidden)]
1332pub trait FilterConfigDataHandleType: Sized {
1333 type Value;
1334
1335 fn __default_value() -> Self::Value
1336 where
1337 Self::Value: Default,
1338 {
1339 Self::Value::default()
1340 }
1341
1342 fn __to_erased(name: String, value: Self::Value) -> ErasedFilterConfigData;
1343 fn __from_erased_data(data: &ErasedFilterConfigData) -> Self;
1344 fn __new_owned_data(value: Self::Value) -> Self;
1345}
1346
1347impl<T: Copy + 'static> FilterConfigDataHandleType for FilterConfigDataHandle<T> {
1348 type Value = T;
1349
1350 fn __to_erased(name: String, value: Self::Value) -> ErasedFilterConfigData {
1351 ErasedFilterConfigData::with_default_value(name, value)
1352 }
1353
1354 fn __from_erased_data(data: &ErasedFilterConfigData) -> Self {
1355 Self::__from_erased(data)
1356 }
1357
1358 fn __new_owned_data(value: Self::Value) -> Self {
1359 Self::__new_owned(value)
1360 }
1361}
1362
1363impl FilterConfigDataHandleType for VariableLengthFilterConfigDataHandle {
1364 type Value = Vec<u8>;
1365
1366 fn __to_erased(name: String, value: Self::Value) -> ErasedFilterConfigData {
1367 VariableLengthFilterConfigData::with_default_value(name, value).into()
1368 }
1369
1370 fn __from_erased_data(data: &ErasedFilterConfigData) -> Self {
1371 Self::__from_erased(data)
1372 }
1373
1374 fn __new_owned_data(value: Self::Value) -> Self {
1375 Self::__new_owned(value)
1376 }
1377}
1378
1379#[derive(Debug, Clone)]
1387pub struct FilterConfigTrackGroup {
1388 pub name: String,
1390 pub tracks: Vec<FilterConfigTrack>,
1392}
1393
1394#[doc(hidden)]
1395#[expect(private_bounds)]
1396pub fn __string_to_pathbuf_or_option_pathbuf<T: StringToPathBufOrOptionPathBuf>(s: &str) -> T {
1397 T::__string_to_pathbuf_or_option_pathbuf(s)
1398}
1399
1400trait StringToPathBufOrOptionPathBuf: Sized {
1401 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self;
1402}
1403impl StringToPathBufOrOptionPathBuf for std::path::PathBuf {
1404 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self {
1405 std::path::PathBuf::from(s)
1406 }
1407}
1408impl StringToPathBufOrOptionPathBuf for Option<std::path::PathBuf> {
1409 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self {
1410 if s.is_empty() {
1411 None
1412 } else {
1413 Some(std::path::PathBuf::from(s))
1414 }
1415 }
1416}
1417
1418pub struct FilterConfigDataReadGuard<'handle, T: Copy> {
1420 pub(crate) inner: *mut T,
1421 _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
1422}
1423unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataReadGuard<'_, T> {}
1424unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataReadGuard<'_, T> {}
1425impl<T: Copy> FilterConfigDataReadGuard<'_, T> {
1426 fn new<'handle>(inner: *mut T) -> FilterConfigDataReadGuard<'handle, T> {
1427 FilterConfigDataReadGuard {
1428 inner,
1429 _handle: std::marker::PhantomData,
1430 }
1431 }
1432}
1433impl<T: Copy> Drop for FilterConfigDataReadGuard<'_, T> {
1434 fn drop(&mut self) {
1435 let addr = self.inner as *mut () as usize;
1436 if let Some(entry) = HANDLES.get(&addr) {
1437 let lock = entry.value();
1438 unsafe { lock.unlock_shared() };
1439 }
1440 }
1441}
1442impl<T: Copy> std::convert::AsRef<T> for FilterConfigDataReadGuard<'_, T> {
1443 fn as_ref(&self) -> &T {
1444 unsafe { &*self.inner }
1445 }
1446}
1447impl<T: Copy> std::ops::Deref for FilterConfigDataReadGuard<'_, T> {
1448 type Target = T;
1449
1450 fn deref(&self) -> &Self::Target {
1451 self.as_ref()
1452 }
1453}
1454
1455pub struct FilterConfigDataWriteGuard<'handle, T: Copy> {
1457 pub(crate) inner: *mut T,
1458 _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
1459}
1460
1461unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataWriteGuard<'_, T> {}
1462unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataWriteGuard<'_, T> {}
1463impl<T: Copy> FilterConfigDataWriteGuard<'_, T> {
1464 fn new<'handle>(inner: *mut T) -> FilterConfigDataWriteGuard<'handle, T> {
1465 FilterConfigDataWriteGuard {
1466 inner,
1467 _handle: std::marker::PhantomData,
1468 }
1469 }
1470}
1471impl<T: Copy> Drop for FilterConfigDataWriteGuard<'_, T> {
1472 fn drop(&mut self) {
1473 let addr = self.inner as *mut () as usize;
1474 if let Some(entry) = HANDLES.get(&addr) {
1475 let lock = entry.value();
1476 unsafe { lock.unlock_exclusive() };
1477 }
1478 }
1479}
1480impl<T: Copy> std::convert::AsMut<T> for FilterConfigDataWriteGuard<'_, T> {
1481 fn as_mut(&mut self) -> &mut T {
1482 unsafe { &mut *self.inner }
1483 }
1484}
1485impl<T: Copy> std::ops::Deref for FilterConfigDataWriteGuard<'_, T> {
1486 type Target = T;
1487 fn deref(&self) -> &Self::Target {
1488 unsafe { &*self.inner }
1489 }
1490}
1491impl<T: Copy> std::ops::DerefMut for FilterConfigDataWriteGuard<'_, T> {
1492 fn deref_mut(&mut self) -> &mut Self::Target {
1493 self.as_mut()
1494 }
1495}
1496
1497#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1499pub enum FilterConfigHideRuleOperator {
1500 Equal,
1502 NotEqual,
1504 Greater,
1506 Less,
1508}
1509
1510impl From<FilterConfigHideRuleOperator> for aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR {
1511 fn from(value: FilterConfigHideRuleOperator) -> Self {
1512 match value {
1513 FilterConfigHideRuleOperator::Equal => {
1514 aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR::EQUAL
1515 }
1516 FilterConfigHideRuleOperator::NotEqual => {
1517 aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR::NOT_EQUAL
1518 }
1519 FilterConfigHideRuleOperator::Greater => {
1520 aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR::GREATER
1521 }
1522 FilterConfigHideRuleOperator::Less => {
1523 aviutl2_sys::filter2::FILTER_ITEM_HIDE_RULE_OPERATOR::LESS
1524 }
1525 }
1526 }
1527}
1528
1529#[derive(Debug, Clone)]
1531pub struct FilterConfigHideRule {
1532 pub name: String,
1538 pub condition_name: Option<String>,
1550 pub condition_operator: FilterConfigHideRuleOperator,
1552 pub condition_value: i32,
1554}
1555
1556#[cfg(test)]
1557mod tests {
1558 use super::*;
1559
1560 #[test]
1561 fn value_less_filter_config_items_are_no_op_when_applying_raw_values() {
1562 let leak_manager = LeakManager::new();
1563 let mut items = [
1564 FilterConfigItem::Separator(FilterConfigSeparator {
1565 name: "Separator".to_string(),
1566 }),
1567 FilterConfigItem::HideRule(FilterConfigHideRule {
1568 name: "Target".to_string(),
1569 condition_name: None,
1570 condition_operator: FilterConfigHideRuleOperator::Equal,
1571 condition_value: 0,
1572 }),
1573 ];
1574
1575 for item in &mut items {
1576 let raw = item.to_raw(&leak_manager);
1577 assert!(!unsafe { item.should_apply_from_raw(&raw) });
1578 unsafe { item.apply_from_raw(&raw) };
1579 }
1580 }
1581
1582 #[test]
1583 fn filter_config_data_handle_reads_initial_value() {
1584 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
1585 let read_guard = handle.read();
1586 assert_eq!(*read_guard, 42);
1587 }
1588
1589 #[test]
1590 fn filter_config_data_handle_writes_and_reads_updated_value() {
1591 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
1592 {
1593 let mut write_guard = handle.write();
1594 *write_guard = 100;
1595 }
1596 let read_guard = handle.read();
1597 assert_eq!(*read_guard, 100);
1598 }
1599
1600 #[test]
1601 fn filter_config_data_handle_try_read_fails_when_locked_for_write() {
1602 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
1603 let _write_guard = handle.write();
1604 let try_read_guard = handle.try_read();
1605 assert!(try_read_guard.is_none());
1606 }
1607
1608 #[test]
1609 fn filter_config_data_handle_try_write_fails_when_locked_for_read() {
1610 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
1611 let _read_guard = handle.read();
1612 let try_write_guard = handle.try_write();
1613 assert!(try_write_guard.is_none());
1614 }
1615
1616 #[test]
1617 fn filter_config_data_handle_clone_shares_state() {
1618 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
1619 let cloned_handle = handle.clone();
1620 {
1621 let mut write_guard = handle.write();
1622 *write_guard = 100;
1623 }
1624 let read_guard = cloned_handle.read();
1625 assert_eq!(*read_guard, 100);
1626 }
1627
1628 #[test]
1629 fn filter_config_data_handle_never_drops_data_for_borrowed() {
1630 let mut data =
1631 crate::filter::ErasedFilterConfigData::with_default_value("test".to_string(), 42);
1632 let data_ptr = Box::into_raw(Box::new(42u32));
1633 data.value = Some(std::ptr::NonNull::new(data_ptr as _).unwrap());
1634 let handle = FilterConfigDataHandle::<u32>::__from_erased(&data);
1635 drop(handle);
1636
1637 assert_eq!(unsafe { *data_ptr }, 42);
1638 }
1639
1640 #[test]
1641 fn filter_config_data_handle_reads_value_from_erased_data() {
1642 let boxed = Box::new(77u32);
1643 let ptr = std::ptr::NonNull::from(boxed.as_ref());
1644 let data = crate::filter::FilterConfigData {
1645 name: "test".to_string(),
1646 value: Some(ptr),
1647 default_value: 0,
1648 };
1649 let erased = data.erase_type();
1650 let handle = FilterConfigDataHandle::<u32>::__from_erased(&erased);
1651 let read_guard = handle.read();
1652
1653 assert_eq!(*read_guard, 77);
1654 assert_eq!(erased.value.unwrap().as_ptr() as *mut u32, ptr.as_ptr());
1655
1656 drop(read_guard);
1657 drop(handle);
1658 drop(boxed);
1659 }
1660
1661 #[test]
1662 fn variable_length_filter_config_data_handle_reads_and_writes_owned_data() {
1663 let handle = VariableLengthFilterConfigDataHandle::__new_owned(vec![1, 2, 3]);
1664 let cloned = handle.clone();
1665
1666 assert_eq!(&*handle.read(), &[1, 2, 3]);
1667 {
1668 let mut value = cloned.write();
1669 value[1] = 9;
1670 }
1671 assert_eq!(&*handle.read(), &[1, 9, 3]);
1672 }
1673
1674 #[test]
1675 fn variable_length_filter_config_data_resize_updates_all_clones() {
1676 let mut old_value = vec![1u8, 2, 3];
1677 let mut new_value = vec![4u8, 5, 6, 7];
1678 let mut raw = aviutl2_sys::filter2::FILTER_ITEM_DATA {
1679 r#type: std::ptr::null(),
1680 name: std::ptr::null(),
1681 value: old_value.as_mut_ptr().cast(),
1682 size: old_value.len() as i32,
1683 default_value: [MaybeUninit::new(0); 16 * 1024],
1684 };
1685 let mut handle = VariableLengthFilterConfigDataHandle {
1686 inner: std::sync::Arc::new(parking_lot::RwLock::new(VariableLengthDataBacking::Host(
1687 NonNull::from(&mut raw),
1688 ))),
1689 };
1690 let cloned = handle.clone();
1691
1692 handle
1693 .try_resize(new_value.len(), |raw, size| {
1694 let raw = unsafe { &mut *raw.cast::<aviutl2_sys::filter2::FILTER_ITEM_DATA>() };
1695 raw.value = new_value.as_mut_ptr().cast();
1696 raw.size = size;
1697 })
1698 .unwrap();
1699
1700 assert_eq!(&*cloned.read(), &[4, 5, 6, 7]);
1701 }
1702
1703 #[test]
1704 fn variable_length_filter_config_data_rejects_oversized_resize() {
1705 let mut handle = VariableLengthFilterConfigDataHandle::__new_owned(Vec::new());
1706
1707 let result = handle.try_resize(16 * 1024 + 1, |_, _| panic!("resize must not be called"));
1708
1709 assert!(matches!(
1710 result,
1711 Err(crate::filter::FilterProcError::ValueOutOfRange)
1712 ));
1713 }
1714}