From 1ab5a03aeffa332354080ed4b625c3ccbc9a29c1 Mon Sep 17 00:00:00 2001 From: Steven Pecht Date: Sun, 13 Aug 2023 10:25:17 +1000 Subject: [PATCH 01/11] Use to identify cargo build artifact if present (#1344) --- packages/cli/src/builder.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index 3fa8e7818..332490fda 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -93,18 +93,21 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("assets").join("dioxus"); - let release_type = match config.release { - true => "release", - false => "debug", + let build_profile = if config.custom_profile.is_some() { + config.custom_profile.as_ref().unwrap() + } else if config.release { + "release" + } else { + "debug" }; let input_path = match executable { ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir - .join(format!("wasm32-unknown-unknown/{}", release_type)) + .join(format!("wasm32-unknown-unknown/{}", build_profile)) .join(format!("{}.wasm", name)), ExecutableType::Example(name) => target_dir - .join(format!("wasm32-unknown-unknown/{}/examples", release_type)) + .join(format!("wasm32-unknown-unknown/{}/examples", build_profile)) .join(format!("{}.wasm", name)), }; From 2d5a4f82bf645672370b4154caeac2aa81487844 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 13 Aug 2023 08:47:27 -0500 Subject: [PATCH 02/11] Fix the context API in lists --- packages/core/src/diff.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 270c81730..e3ca19c3a 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -15,6 +15,7 @@ use DynamicNode::*; impl<'b> VirtualDom { pub(super) fn diff_scope(&mut self, scope: ScopeId) { + self.runtime.scope_stack.borrow_mut().push(scope); let scope_state = &mut self.get_scope(scope).unwrap(); unsafe { // Load the old and new bump arenas @@ -45,6 +46,7 @@ impl<'b> VirtualDom { (Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]), }; } + self.runtime.scope_stack.borrow_mut().pop(); } fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, p: &'b VPlaceholder) { From 943c76b1dad62c4fa719c4942ef49ebe0e4e9ef7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 15 Aug 2023 12:19:05 -0500 Subject: [PATCH 03/11] Fix memory leak in core --- packages/core/src/create.rs | 8 ++++++-- packages/core/src/nodes.rs | 14 +++++++------- packages/rsx/src/lib.rs | 3 ++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index 7fbc8a9c8..a61a8cace 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -87,8 +87,12 @@ impl<'b> VirtualDom { } } - // Intialize the root nodes slice - *node.root_ids.borrow_mut() = vec![ElementId(0); node.template.get().roots.len()]; + // Initialize the root nodes slice + { + let mut nodes_mut = node.root_ids.borrow_mut(); + let len = node.template.get().roots.len(); + nodes_mut.resize(len, ElementId::default()); + }; // The best renderers will have templates prehydrated and registered // Just in case, let's create the template using instructions anyways diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index a108f3577..2399c16b8 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -54,7 +54,7 @@ pub struct VNode<'a> { /// The IDs for the roots of this template - to be used when moving the template around and removing it from /// the actual Dom - pub root_ids: RefCell>, + pub root_ids: RefCell>, /// The dynamic parts of the template pub dynamic_nodes: &'a [DynamicNode<'a>], @@ -65,11 +65,11 @@ pub struct VNode<'a> { impl<'a> VNode<'a> { /// Create a template with no nodes that will be skipped over during diffing - pub fn empty() -> Element<'a> { + pub fn empty(cx: &'a ScopeState) -> Element<'a> { Some(VNode { key: None, parent: None, - root_ids: Default::default(), + root_ids: RefCell::new(bumpalo::collections::Vec::new_in(cx.bump())), dynamic_nodes: &[], dynamic_attrs: &[], template: Cell::new(Template { @@ -698,7 +698,7 @@ impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> { impl<'a, 'b> IntoDynNode<'b> for &'a str { fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> { DynamicNode::Text(VText { - value: bumpalo::collections::String::from_str_in(self, cx.bump()).into_bump_str(), + value: cx.bump().alloc_str(self), id: Default::default(), }) } @@ -741,10 +741,10 @@ impl<'a> IntoTemplate<'a> for VNode<'a> { } } impl<'a> IntoTemplate<'a> for Element<'a> { - fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> { + fn into_template(self, cx: &'a ScopeState) -> VNode<'a> { match self { - Some(val) => val.into_template(_cx), - _ => VNode::empty().unwrap(), + Some(val) => val.into_template(cx), + _ => VNode::empty(cx).unwrap(), } } } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 783dd3322..f52ecad0e 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -231,6 +231,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> { // Render and release the mutable borrow on context let roots = quote! { #( #root_printer ),* }; + let root_count = self.roots.len(); let node_printer = &context.dynamic_nodes; let dyn_attr_printer = &context.dynamic_attributes; let node_paths = context.node_paths.iter().map(|it| quote!(&[#(#it),*])); @@ -247,7 +248,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> { parent: None, key: #key_tokens, template: std::cell::Cell::new(TEMPLATE), - root_ids: Default::default(), + root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()).into(), dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]), dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]), } From b8600d51442ce3343c4b34614fd8b2c6b343503e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 15 Aug 2023 13:11:03 -0500 Subject: [PATCH 04/11] fix tests --- packages/core/src/diff.rs | 8 +++++++- packages/core/tests/fuzzing.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 270c81730..2a6425eaf 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -126,7 +126,13 @@ impl<'b> VirtualDom { }); // Make sure the roots get transferred over while we're here - *right_template.root_ids.borrow_mut() = left_template.root_ids.borrow().clone(); + { + let mut right = right_template.root_ids.borrow_mut(); + right.clear(); + for &element in left_template.root_ids.borrow().iter() { + right.push(element); + } + } let root_ids = right_template.root_ids.borrow(); diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index 40bb5dcbf..1523c4e22 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -179,7 +179,7 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { node_paths: &[&[0]], attr_paths: &[], }), - root_ids: Default::default(), + root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(), dynamic_nodes: cx.bump().alloc([cx.component( create_random_element, DepthProps { depth, root: false }, @@ -276,7 +276,7 @@ fn create_random_element(cx: Scope) -> Element { key: None, parent: None, template: Cell::new(template), - root_ids: Default::default(), + root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(), dynamic_nodes: { let dynamic_nodes: Vec<_> = dynamic_node_types .iter() From 6876d2da1c7f03be9b28926f2d66516de2cb2d29 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 15 Aug 2023 13:22:06 -0500 Subject: [PATCH 05/11] fix clippy --- docs/guide/examples/readme_expanded.rs | 3 ++- packages/native-core/tests/fuzzing.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/guide/examples/readme_expanded.rs b/docs/guide/examples/readme_expanded.rs index 896d365e5..1ec30e57b 100644 --- a/docs/guide/examples/readme_expanded.rs +++ b/docs/guide/examples/readme_expanded.rs @@ -89,7 +89,8 @@ fn app(cx: Scope) -> Element { key: None, // The static template this node will use. The template is stored in a Cell so it can be replaced with a new template when hot rsx reloading is enabled template: std::cell::Cell::new(TEMPLATE), - root_ids: Default::default(), + root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(__cx.bump()) + .into(), dynamic_nodes: __cx.bump().alloc([ // The dynamic count text node (dynamic node id 0) __cx.text_node(format_args!("High-Five counter: {0}", count)), diff --git a/packages/native-core/tests/fuzzing.rs b/packages/native-core/tests/fuzzing.rs index 1d1bd881c..4f0a34b70 100644 --- a/packages/native-core/tests/fuzzing.rs +++ b/packages/native-core/tests/fuzzing.rs @@ -187,7 +187,7 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { node_paths: &[&[0]], attr_paths: &[], }), - root_ids: Default::default(), + root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(cx.bump()).into(), dynamic_nodes: cx.bump().alloc([cx.component( create_random_element, DepthProps { depth, root: false }, @@ -257,7 +257,8 @@ fn create_random_element(cx: Scope) -> Element { key: None, parent: None, template: Cell::new(template), - root_ids: Default::default(), + root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(cx.bump()) + .into(), dynamic_nodes: { let dynamic_nodes: Vec<_> = dynamic_node_types .iter() From 555f4d583413db983cfe15cf02c959bc5cdf0ad6 Mon Sep 17 00:00:00 2001 From: Daniel Albl <66034631+DanielAlbl@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:27:19 -0600 Subject: [PATCH 06/11] Add 'with' and 'with_mut' to 'use_shared_state' (#1353) --- packages/hooks/src/use_shared_state.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index 019d55942..b900b9dc6 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/packages/hooks/src/use_shared_state.rs @@ -282,6 +282,20 @@ impl UseSharedState { ), } } + + /// Take a reference to the inner value temporarily and produce a new value + #[cfg_attr(debug_assertions, track_caller)] + #[cfg_attr(debug_assertions, inline(never))] + pub fn with(&self, immutable_callback: impl FnOnce(&T) -> O) -> O { + immutable_callback(&*self.read()) + } + + /// Take a mutable reference to the inner value temporarily and produce a new value + #[cfg_attr(debug_assertions, track_caller)] + #[cfg_attr(debug_assertions, inline(never))] + pub fn with_mut(&self, mutable_callback: impl FnOnce(&mut T) -> O) -> O { + mutable_callback(&mut *self.write()) + } } impl Clone for UseSharedState { From 3e24b3d34a10e218bcb20b94554dfc2a7cf54b1f Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Tue, 15 Aug 2023 11:41:32 -0700 Subject: [PATCH 07/11] Publish CLI binaries for cargo-binstall (#1358) * add test release pipeline * fix * debug * oops forgot checkout * debug * fix if * blah * fix working dir * fix cargo build path * add publish step * add cache * fix path * use package instead of working dir * fix cache path * fix typo in checksum * fix upload artifact loop * add more comments * add binstall metadata * test simplified action * fix target * remove dispatch * fix permissions * remove mac arm config * update comments * update comment * replace unmaintained actions-rs --- .github/workflows/cli_release.yml | 54 +++++++++++++++++++++++++++++++ packages/cli/Cargo.toml | 6 ++++ 2 files changed, 60 insertions(+) create mode 100644 .github/workflows/cli_release.yml diff --git a/.github/workflows/cli_release.yml b/.github/workflows/cli_release.yml new file mode 100644 index 000000000..c6c2b81e8 --- /dev/null +++ b/.github/workflows/cli_release.yml @@ -0,0 +1,54 @@ +name: Build CLI for Release + +# Will run automatically on every new release +on: + release: + types: [published] + +jobs: + build-and-upload: + permissions: + contents: write + runs-on: ${{ matrix.platform.os }} + strategy: + matrix: + platform: + - { + target: x86_64-pc-windows-msvc, + os: windows-latest, + toolchain: "1.70.0", + } + - { + target: x86_64-apple-darwin, + os: macos-latest, + toolchain: "1.70.0", + } + - { + target: x86_64-unknown-linux-gnu, + os: ubuntu-latest, + toolchain: "1.70.0", + } + steps: + - uses: actions/checkout@v3 + - name: Install stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.platform.toolchain }} + targets: ${{ matrix.platform.target }} + + # Setup the Github Actions Cache for the CLI package + - name: Setup cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: packages/cli -> ../../target + + # This neat action can build and upload the binary in one go! + - name: Build and upload binary + uses: taiki-e/upload-rust-binary-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + target: ${{ matrix.platform.target }} + bin: dx + archive: dx-${{ matrix.platform.target }} + checksum: sha256 + manifest_path: packages/cli/Cargo.toml diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 892399a9d..a37798eee 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -100,3 +100,9 @@ name = "dx" [dev-dependencies] tempfile = "3.3" + +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }{ archive-suffix }" + +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-fmt = "zip" From bcebaafb5db7baab6823b41f9ff921f4b8a40f3f Mon Sep 17 00:00:00 2001 From: Dillon Roller Date: Wed, 16 Aug 2023 18:04:58 -0400 Subject: [PATCH 08/11] Update README.md to use 0.4 version of guide (#1370) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52e4408d8..5b6a99ad3 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ | Examples | - Guide + Guide | 中文 | From 8946944d68f592d5c1f089dd6cc6ed15c012945e Mon Sep 17 00:00:00 2001 From: Vinicius Gobbo Antunes de Oliveira Date: Thu, 17 Aug 2023 14:57:09 +0200 Subject: [PATCH 09/11] DOC: Add note about form prevent_default behavior. (#1343) * Add note about form prevent_default behavior. * Make the default behavior clear on forms with attached event handlers. --- docs/guide/src/en/interactivity/event_handlers.md | 2 ++ docs/guide/src/pt-br/interactivity/event_handlers.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index 4c77cef1e..4e332adf9 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -51,6 +51,8 @@ Any event handlers will still be called. > Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does _not_ currently support this behavior. Note: this means you cannot conditionally prevent default behavior based on the data in the event. +> Note about forms: if an event handler is attached to the `onsubmit` event of a form, default behavior is to **not submit it**, meaning having `prevent_default: "onsubmit"` will submit it in this case. + ## Handler Props Sometimes, you might want to make a component that accepts an event handler. A simple example would be a `FancyButton` component, which accepts an `on_click` handler: diff --git a/docs/guide/src/pt-br/interactivity/event_handlers.md b/docs/guide/src/pt-br/interactivity/event_handlers.md index 12aad321e..8b3d757ef 100644 --- a/docs/guide/src/pt-br/interactivity/event_handlers.md +++ b/docs/guide/src/pt-br/interactivity/event_handlers.md @@ -64,3 +64,5 @@ Então, você pode usá-lo como qualquer outro manipulador: > Nota: assim como qualquer outro atributo, você pode nomear os manipuladores como quiser! Embora eles devam começar com `on`, para que o prop seja automaticamente transformado em um `EventHandler` no local da chamada. > > Você também pode colocar dados personalizados no evento, em vez de, por exemplo, `MouseData` + +> Nota sobre formulários: se um manipulador de evento está anexado ao evento `onsubmit` em um formulário, o comportamento padrão é de **não submetê-lo**. Portanto, especificar `prevent_default: "onsubmit"` irá submetê-lo. \ No newline at end of file From fa39408d582d8efd63ca780c044b134d38bcf542 Mon Sep 17 00:00:00 2001 From: Vinicius Gobbo Antunes de Oliveira Date: Thu, 17 Aug 2023 14:58:56 +0200 Subject: [PATCH 10/11] If mkcert not enabled, require cert_path and key_path to be specified or error. (#1342) --- packages/cli/src/server/web/mod.rs | 123 +++++++++++++++-------------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/packages/cli/src/server/web/mod.rs b/packages/cli/src/server/web/mod.rs index e08d2297a..78eddb574 100644 --- a/packages/cli/src/server/web/mod.rs +++ b/packages/cli/src/server/web/mod.rs @@ -5,7 +5,7 @@ use crate::{ output::{print_console_info, PrettierOptions, WebServerInfo}, setup_file_watcher, setup_file_watcher_hot_reload, }, - BuildResult, CrateConfig, Result, + BuildResult, CrateConfig, Result, WebHttpsConfig, }; use axum::{ body::{Full, HttpBody}, @@ -218,67 +218,12 @@ async fn get_rustls(config: &CrateConfig) -> Result> { return Ok(None); } - let (cert_path, key_path) = match web_config.mkcert { + let (cert_path, key_path) = if let Some(true) = web_config.mkcert { // mkcert, use it - Some(true) => { - // Get paths to store certs, otherwise use ssl/item.pem - let key_path = web_config - .key_path - .clone() - .unwrap_or(DEFAULT_KEY_PATH.to_string()); - - let cert_path = web_config - .cert_path - .clone() - .unwrap_or(DEFAULT_CERT_PATH.to_string()); - - // Create ssl directory if using defaults - if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH { - _ = fs::create_dir("ssl"); - } - - let cmd = Command::new("mkcert") - .args([ - "-install", - "-key-file", - &key_path, - "-cert-file", - &cert_path, - "localhost", - "::1", - "127.0.0.1", - ]) - .spawn(); - - match cmd { - Err(e) => { - match e.kind() { - io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."), - e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()), - }; - return Err("failed to generate mkcert certificates".into()); - } - Ok(mut cmd) => { - cmd.wait()?; - } - } - - (cert_path, key_path) - } - // not mkcert - Some(false) => { - // get paths to cert & key - if let (Some(key), Some(cert)) = - (web_config.key_path.clone(), web_config.cert_path.clone()) - { - (cert, key) - } else { - // missing cert or key - return Err("https is enabled but cert or key path is missing".into()); - } - } - // other - _ => return Ok(None), + get_rustls_with_mkcert(web_config)? + } else { + // if mkcert not specified or false, don't use it + get_rustls_without_mkcert(web_config)? }; Ok(Some( @@ -286,6 +231,62 @@ async fn get_rustls(config: &CrateConfig) -> Result> { )) } +fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { + // Get paths to store certs, otherwise use ssl/item.pem + let key_path = web_config + .key_path + .clone() + .unwrap_or(DEFAULT_KEY_PATH.to_string()); + + let cert_path = web_config + .cert_path + .clone() + .unwrap_or(DEFAULT_CERT_PATH.to_string()); + + // Create ssl directory if using defaults + if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH { + _ = fs::create_dir("ssl"); + } + + let cmd = Command::new("mkcert") + .args([ + "-install", + "-key-file", + &key_path, + "-cert-file", + &cert_path, + "localhost", + "::1", + "127.0.0.1", + ]) + .spawn(); + + match cmd { + Err(e) => { + match e.kind() { + io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."), + e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()), + }; + return Err("failed to generate mkcert certificates".into()); + } + Ok(mut cmd) => { + cmd.wait()?; + } + } + + Ok((cert_path, key_path)) +} + +fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { + // get paths to cert & key + if let (Some(key), Some(cert)) = (web_config.key_path.clone(), web_config.cert_path.clone()) { + Ok((cert, key)) + } else { + // missing cert or key + Err("https is enabled but cert or key path is missing".into()) + } +} + /// Sets up and returns a router async fn setup_router( config: CrateConfig, From f652474a6e163a1ce543d83da02367cbfd88a193 Mon Sep 17 00:00:00 2001 From: wooden-worm <93303706+wooden-worm@users.noreply.github.com> Date: Thu, 17 Aug 2023 06:03:56 -0700 Subject: [PATCH 11/11] FileEngine: expose native file types on different platforms (#1258) * Get native file * Correct visibility --- packages/html/src/events/form.rs | 11 ++++++++- .../src/native_bind/native_file_engine.rs | 6 +++++ packages/web/src/file_engine.rs | 23 +++++++++++++++++++ packages/web/src/lib.rs | 1 + 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 1fe59a640..fab13482b 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::Debug}; +use std::{any::Any, collections::HashMap, fmt::Debug}; use dioxus_core::Event; @@ -45,6 +45,12 @@ impl FileEngine for SerializedFileEngine { .await .map(|bytes| String::from_utf8_lossy(&bytes).to_string()) } + + async fn get_native_file(&self, file: &str) -> Option> { + self.read_file(file) + .await + .map(|val| Box::new(val) as Box) + } } #[cfg(feature = "serialize")] @@ -89,6 +95,9 @@ pub trait FileEngine { // read a file to string async fn read_file_to_string(&self, file: &str) -> Option; + + // returns a file in platform's native representation + async fn get_native_file(&self, file: &str) -> Option>; } impl_event! { diff --git a/packages/html/src/native_bind/native_file_engine.rs b/packages/html/src/native_bind/native_file_engine.rs index e3291fa36..88b996a4d 100644 --- a/packages/html/src/native_bind/native_file_engine.rs +++ b/packages/html/src/native_bind/native_file_engine.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::path::PathBuf; use crate::FileEngine; @@ -40,4 +41,9 @@ impl FileEngine for NativeFileEngine { Some(contents) } + + async fn get_native_file(&self, file: &str) -> Option> { + let file = File::open(file).await.ok()?; + Some(Box::new(file)) + } } diff --git a/packages/web/src/file_engine.rs b/packages/web/src/file_engine.rs index 6552be084..f98f726e2 100644 --- a/packages/web/src/file_engine.rs +++ b/packages/web/src/file_engine.rs @@ -1,3 +1,5 @@ +use std::any::Any; + use dioxus_html::FileEngine; use futures_channel::oneshot; use js_sys::Uint8Array; @@ -100,4 +102,25 @@ impl FileEngine for WebFileEngine { None } } + + async fn get_native_file(&self, file: &str) -> Option> { + let file = self.find(file)?; + Some(Box::new(file)) + } +} + +/// Helper trait for WebFileEngine +#[async_trait::async_trait(?Send)] +pub trait WebFileEngineExt { + /// returns web_sys::File + async fn get_web_file(&self, file: &str) -> Option; +} + +#[async_trait::async_trait(?Send)] +impl WebFileEngineExt for std::sync::Arc { + async fn get_web_file(&self, file: &str) -> Option { + let native_file = self.get_native_file(file).await?; + let ret = native_file.downcast::().ok()?; + Some(*ret) + } } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 8bea4a460..261232607 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -54,6 +54,7 @@ // - Do DOM work in the next requestAnimationFrame callback pub use crate::cfg::Config; +pub use crate::file_engine::WebFileEngineExt; use dioxus_core::{Element, Scope, VirtualDom}; use futures_util::{ future::{select, Either},