Skip to main content

ferroui_dialogs/internal/
resource_selector_converter.rs

1use ferroui_base::utilities::CultureInfo;
2use ferroui_base::controls::{ResourceDictionary, ResourceKey, ResourceProviderImpl};
3use ferroui_base::data::converters::IValueConverter;
4use ferroui_base::data::core::ValueType;
5use ferroui_base::data::BindingError;
6use ferroui_base::{ferro_class, ferro_class_info, instantiate, BoxedValue, FerroObjectImpl, Ref};
7use std::rc::Rc;
8
9/// A resource dictionary that converts a resource key to the resource it
10/// holds under that key.
11#[repr(C)]
12pub struct ResourceSelectorConverter {
13    base: ResourceDictionary,
14}
15
16ferro_class!(ResourceSelectorConverter: ResourceDictionary);
17ferro_class_info!(ResourceSelectorConverter {
18    new: ResourceSelectorConverter::new,
19    interfaces: [Rc<dyn IValueConverter> => ResourceSelectorConverter::as_value_converter],
20});
21
22impl FerroObjectImpl for ResourceSelectorConverter {}
23
24impl ResourceProviderImpl for ResourceSelectorConverter {}
25
26impl ResourceSelectorConverter {
27    pub fn construct() -> Self {
28        Self { base: ResourceDictionary::construct() }
29    }
30
31    pub fn new() -> Ref<Self> {
32        instantiate(Self::construct())
33    }
34
35    /// The converter as its contract (the class implements it in the
36    /// original).
37    pub fn as_value_converter(this: Ref<Self>) -> Rc<dyn IValueConverter> {
38        Rc::new(ResourceSelectorConverterHandle(this))
39    }
40
41    /// `Convert`: the resource of the dictionary whose key is the text
42    /// `key`. Fails when `key` is not text.
43    pub fn convert(&self, key: Option<&BoxedValue>, _culture: &CultureInfo) -> Result<Option<BoxedValue>, BindingError> {
44        let Some(key) = key.and_then(|key| key.downcast_ref::<String>()) else {
45            return Err(BindingError::message("The resource key of the converter must be a string."));
46        };
47
48        Ok(self.try_get_resource(&ResourceKey::from(key.as_str()), None).flatten())
49    }
50
51    /// `ConvertBack`: fails, the conversion has no inverse.
52    pub fn convert_back(&self, _value: Option<&BoxedValue>, _culture: &CultureInfo) -> Result<Option<BoxedValue>, BindingError> {
53        Err(BindingError::message("The method or operation is not implemented."))
54    }
55}
56
57/// The value converter contract of a [`ResourceSelectorConverter`].
58struct ResourceSelectorConverterHandle(Ref<ResourceSelectorConverter>);
59
60impl IValueConverter for ResourceSelectorConverterHandle {
61    fn convert(
62        &self,
63        value: Option<&BoxedValue>,
64        _target_type: ValueType,
65        _parameter: Option<&BoxedValue>,
66        _culture: &CultureInfo,
67    ) -> Result<Option<BoxedValue>, BindingError> {
68        self.0.convert(value, &ferroui_base::utilities::CultureInfo::invariant_culture())
69    }
70
71    fn convert_back(
72        &self,
73        value: Option<&BoxedValue>,
74        _target_type: ValueType,
75        _parameter: Option<&BoxedValue>,
76        _culture: &CultureInfo,
77    ) -> Result<Option<BoxedValue>, BindingError> {
78        self.0.convert_back(value, &ferroui_base::utilities::CultureInfo::invariant_culture())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    // Not from upstream: the upstream project has no tests.
85    use super::*;
86
87    #[test]
88    fn converts_a_key_to_the_resource_of_the_dictionary() {
89        let converter = ResourceSelectorConverter::new();
90        converter.add("Icon_File", Some(Rc::new(42i32) as BoxedValue));
91        let contract = ResourceSelectorConverter::as_value_converter(converter);
92
93        let found = contract.convert(Some(&(Rc::new("Icon_File".to_string()) as BoxedValue)), ValueType::of::<i32>(), None, &ferroui_base::utilities::CultureInfo::invariant_culture());
94        assert_eq!(Some(&42), found.unwrap().unwrap().downcast_ref::<i32>());
95
96        let missing = contract.convert(Some(&(Rc::new("Icon_Folder".to_string()) as BoxedValue)), ValueType::of::<i32>(), None, &ferroui_base::utilities::CultureInfo::invariant_culture());
97        assert!(missing.unwrap().is_none());
98
99        assert!(contract.convert(Some(&(Rc::new(1i32) as BoxedValue)), ValueType::of::<i32>(), None, &ferroui_base::utilities::CultureInfo::invariant_culture()).is_err());
100        assert!(contract.convert(None, ValueType::of::<i32>(), None, &ferroui_base::utilities::CultureInfo::invariant_culture()).is_err());
101    }
102}