Skip to main content

ferroui_xaml_interactions_custom/file_upload/
upload_file_behavior_base.rs

1//! Port of `FileUpload/UploadFileBehaviorBase.cs`.
2
3use ferroui_base::{
4    ferro_class, ferro_properties, BoxedValue, FerroObjectImpl, FerroObjectImplExt, FerroProperty, StyledElementImpl,
5    StyledProperty, Visual,
6};
7use ferroui_xaml_interactivity::helpers::member_lookup::object_of;
8use ferroui_xaml_interactivity::{InvokeCommandBehaviorBase, StyledElementBehaviorImpl};
9
10/// Base class for behaviors that upload a file to a URL.
11///
12/// The class is abstract: a deriving class calls [`execute`](Self::execute) when its event fires.
13///
14/// Deviation (DEVIATIONS.md, Behaviors): the reference posts the file with `HttpClient` and runs
15/// the command with the response. The framework has no HTTP client service, so the port sends no
16/// request: [`execute`](Self::execute) checks what the reference checks (enabled, a command, a
17/// file path, a URL, an existing file) and then ends as the reference ends when the request fails
18/// (the error is ignored and the command does not run).
19#[repr(C)]
20pub struct UploadFileBehaviorBase {
21    base: InvokeCommandBehaviorBase,
22}
23
24ferro_class!(UploadFileBehaviorBase: InvokeCommandBehaviorBase);
25ferroui_base::ferro_impl_classes!(UploadFileBehaviorBase: StyledElementImpl, StyledElementBehaviorImpl);
26
27ferro_properties! {
28    impl UploadFileBehaviorBase {
29        /// Identifies the `FilePath` property.
30        pub fn file_path_property() -> StyledProperty<Option<String>> {
31            FerroProperty::register::<UploadFileBehaviorBase, _>("FilePath", None)
32        }
33
34        /// Identifies the `Url` property.
35        pub fn url_property() -> StyledProperty<Option<String>> {
36            FerroProperty::register::<UploadFileBehaviorBase, _>("Url", None)
37        }
38    }
39}
40
41ferroui_base::ferro_overrides! {
42    impl FerroObjectImpl for UploadFileBehaviorBase {
43        /// The constructor body: the event args are passed to the command.
44        fn constructed(this: &Self) {
45            Self::parent_constructed(this);
46
47            this.set_pass_event_args_to_command(true);
48        }
49    }
50}
51
52impl UploadFileBehaviorBase {
53    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
54    pub fn construct() -> Self {
55        Self { base: InvokeCommandBehaviorBase::construct() }
56    }
57
58    /// Gets or sets the path of the file to upload. This is a property of the object model.
59    pub fn file_path(&self) -> Option<String> {
60        self.get_value(Self::file_path_property())
61    }
62
63    pub fn set_file_path(&self, value: Option<String>) {
64        self.set_value(Self::file_path_property(), value)
65    }
66
67    /// Gets or sets the destination URL. This is a property of the object model.
68    pub fn url(&self) -> Option<String> {
69        self.get_value(Self::url_property())
70    }
71
72    pub fn set_url(&self, value: Option<String>) {
73        self.set_value(Self::url_property(), value)
74    }
75
76    /// Executes the upload using the provided sender (the control that triggered the behavior)
77    /// and command parameter.
78    pub fn execute(&self, sender: Option<&BoxedValue>, _parameter: Option<&BoxedValue>) {
79        let is_visual = sender.and_then(object_of).is_some_and(|sender| sender.cast::<Visual>().is_some());
80        if !is_visual {
81            return;
82        }
83
84        self.upload_async();
85    }
86
87    fn upload_async(&self) {
88        if !self.is_enabled() || self.command().is_none() {
89            return;
90        }
91
92        let (Some(file_path), Some(_url)) = (self.file_path(), self.url()) else {
93            return;
94        };
95
96        if !file_exists(&file_path) {
97            return;
98        }
99
100        // Deviation (DEVIATIONS.md, Behaviors): no HTTP client service in the framework; the
101        // request is not sent and, as for a failed request in the reference, the command does
102        // not run.
103    }
104}
105
106/// `File.Exists(path)`.
107#[cfg(not(target_arch = "wasm32"))]
108pub(crate) fn file_exists(path: &str) -> bool {
109    std::path::Path::new(path).is_file()
110}
111
112/// The browser has no local files.
113#[cfg(target_arch = "wasm32")]
114pub(crate) fn file_exists(_path: &str) -> bool {
115    false
116}