diff --git a/.github/workflows/wipe_cache.yml b/.github/workflows/wipe_cache.yml index 2ecced4af..5a78a1e57 100644 --- a/.github/workflows/wipe_cache.yml +++ b/.github/workflows/wipe_cache.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Clear cache - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{ secrets.cache_controller }} script: | diff --git a/packages/cli/src/cli/create.rs b/packages/cli/src/cli/create.rs index 92b0334f5..73eda7d0b 100644 --- a/packages/cli/src/cli/create.rs +++ b/packages/cli/src/cli/create.rs @@ -21,49 +21,54 @@ impl Create { let path = cargo_generate::generate(args)?; - // first run cargo fmt - let mut cmd = Command::new("cargo"); - let cmd = cmd.arg("fmt").current_dir(&path); - let output = cmd.output().expect("failed to execute process"); - if !output.status.success() { - log::error!("cargo fmt failed"); - log::error!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - log::error!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - } - - // then format the toml - let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")]; - for toml_path in &toml_paths { - let toml = std::fs::read_to_string(toml_path)?; - let mut toml = toml.parse::().map_err(|e| { - anyhow::anyhow!( - "failed to parse toml at {}: {}", - toml_path.display(), - e.to_string() - ) - })?; - - toml.as_table_mut().fmt(); - - let as_string = toml.to_string(); - let new_string = remove_triple_newlines(&as_string); - let mut file = std::fs::File::create(toml_path)?; - file.write_all(new_string.as_bytes())?; - } - - // remove any triple newlines from the readme - let readme_path = path.join("README.md"); - let readme = std::fs::read_to_string(&readme_path)?; - let new_readme = remove_triple_newlines(&readme); - let mut file = std::fs::File::create(readme_path)?; - file.write_all(new_readme.as_bytes())?; - - log::info!("Generated project at {}", path.display()); - - Ok(()) + post_create(&path) } } +// being also used by `init` +pub fn post_create(path: &PathBuf) -> Result<()> { + // first run cargo fmt + let mut cmd = Command::new("cargo"); + let cmd = cmd.arg("fmt").current_dir(path); + let output = cmd.output().expect("failed to execute process"); + if !output.status.success() { + log::error!("cargo fmt failed"); + log::error!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + log::error!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + } + + // then format the toml + let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")]; + for toml_path in &toml_paths { + let toml = std::fs::read_to_string(toml_path)?; + let mut toml = toml.parse::().map_err(|e| { + anyhow::anyhow!( + "failed to parse toml at {}: {}", + toml_path.display(), + e.to_string() + ) + })?; + + toml.as_table_mut().fmt(); + + let as_string = toml.to_string(); + let new_string = remove_triple_newlines(&as_string); + let mut file = std::fs::File::create(toml_path)?; + file.write_all(new_string.as_bytes())?; + } + + // remove any triple newlines from the readme + let readme_path = path.join("README.md"); + let readme = std::fs::read_to_string(&readme_path)?; + let new_readme = remove_triple_newlines(&readme); + let mut file = std::fs::File::create(readme_path)?; + file.write_all(new_readme.as_bytes())?; + + log::info!("Generated project at {}", path.display()); + + Ok(()) +} + fn remove_triple_newlines(string: &str) -> String { let mut new_string = String::new(); for char in string.chars() { diff --git a/packages/cli/src/cli/init.rs b/packages/cli/src/cli/init.rs new file mode 100644 index 000000000..1ac0602c3 --- /dev/null +++ b/packages/cli/src/cli/init.rs @@ -0,0 +1,33 @@ +use super::*; +use cargo_generate::{GenerateArgs, TemplatePath}; + +#[derive(Clone, Debug, Default, Deserialize, Parser)] +#[clap(name = "init")] +pub struct Init { + /// Template path + #[clap(default_value = "gh:dioxuslabs/dioxus-template", long)] + template: String, +} + +impl Init { + pub fn init(self) -> Result<()> { + // get dir name + let name = std::env::current_dir()? + .file_name() + .map(|f| f.to_str().unwrap().to_string()); + + let args = GenerateArgs { + template_path: TemplatePath { + auto_path: Some(self.template), + ..Default::default() + }, + name, + init: true, + ..Default::default() + }; + + let path = cargo_generate::generate(args)?; + + create::post_create(&path) + } +} diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 4295bfa9b..21d1b63c6 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -6,6 +6,7 @@ pub mod check; pub mod clean; pub mod config; pub mod create; +pub mod init; pub mod plugin; pub mod serve; pub mod translate; @@ -56,9 +57,12 @@ pub enum Commands { /// Build, watch & serve the Rust WASM app and all of its assets. Serve(serve::Serve), - /// Init a new project for Dioxus. + /// Create a new project for Dioxus. Create(create::Create), + /// Init a new project for Dioxus + Init(init::Init), + /// Clean output artifacts. Clean(clean::Clean), @@ -94,6 +98,7 @@ impl Display for Commands { Commands::Translate(_) => write!(f, "translate"), Commands::Serve(_) => write!(f, "serve"), Commands::Create(_) => write!(f, "create"), + Commands::Init(_) => write!(f, "init"), Commands::Clean(_) => write!(f, "clean"), Commands::Config(_) => write!(f, "config"), Commands::Version(_) => write!(f, "version"), diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index b4362729f..5a483ec54 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -23,16 +23,16 @@ impl Serve { crate_config.with_release(self.serve.release); crate_config.with_verbose(self.serve.verbose); - if self.serve.example.is_some() { - crate_config.as_example(self.serve.example.unwrap()); + if let Some(example) = self.serve.example { + crate_config.as_example(example); } - if self.serve.profile.is_some() { - crate_config.set_profile(self.serve.profile.unwrap()); + if let Some(profile) = self.serve.profile { + crate_config.set_profile(profile); } - if self.serve.features.is_some() { - crate_config.set_features(self.serve.features.unwrap()); + if let Some(features) = self.serve.features { + crate_config.set_features(features); } if let Some(target) = self.serve.target { diff --git a/packages/cli/src/logging.rs b/packages/cli/src/logging.rs index 31d088737..0d902f86f 100644 --- a/packages/cli/src/logging.rs +++ b/packages/cli/src/logging.rs @@ -25,7 +25,6 @@ pub fn set_up_logging() { colors_line.get_color(&record.level()).to_fg_str() ), level = colors_level.color(record.level()), - message = message, )); }) .level(match std::env::var("DIOXUS_LOG") { diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index a603f7929..4114d629a 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -43,51 +43,23 @@ async fn main() -> anyhow::Result<()> { set_up_logging(); - let bin = get_bin(args.bin); - - if let Ok(bin) = &bin { - let _dioxus_config = DioxusConfig::load(Some(bin.clone())) - .map_err(|e| anyhow!("Failed to load Dioxus config because: {e}"))? - .unwrap_or_else(|| { - log::info!("You appear to be creating a Dioxus project from scratch; we will use the default config"); - DioxusConfig::default() - }); - - #[cfg(feature = "plugin")] - PluginManager::init(_dioxus_config.plugin) - .map_err(|e| anyhow!("🚫 Plugin system initialization failed: {e}"))?; - } - match args.action { Translate(opts) => opts .translate() .map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)), - Build(opts) if bin.is_ok() => opts - .build(Some(bin.unwrap().clone()), None) - .map_err(|e| anyhow!("🚫 Building project failed: {}", e)), - - Clean(opts) if bin.is_ok() => opts - .clean(Some(bin.unwrap().clone())) - .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)), - - Serve(opts) if bin.is_ok() => opts - .serve(Some(bin.unwrap().clone())) - .await - .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)), - Create(opts) => opts .create() .map_err(|e| anyhow!("🚫 Creating new project failed: {}", e)), + Init(opts) => opts + .init() + .map_err(|e| anyhow!("🚫 Initialising a new project failed: {}", e)), + Config(opts) => opts .config() .map_err(|e| anyhow!("🚫 Configuring new project failed: {}", e)), - Bundle(opts) if bin.is_ok() => opts - .bundle(Some(bin.unwrap().clone())) - .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)), - #[cfg(feature = "plugin")] Plugin(opts) => opts .plugin() @@ -110,6 +82,39 @@ async fn main() -> anyhow::Result<()> { Ok(()) } - _ => Err(anyhow::anyhow!(bin.unwrap_err())), + action => { + let bin = get_bin(args.bin)?; + let _dioxus_config = DioxusConfig::load(Some(bin.clone())) + .map_err(|e| anyhow!("Failed to load Dioxus config because: {e}"))? + .unwrap_or_else(|| { + log::info!("You appear to be creating a Dioxus project from scratch; we will use the default config"); + DioxusConfig::default() + }); + + #[cfg(feature = "plugin")] + PluginManager::init(_dioxus_config.plugin) + .map_err(|e| anyhow!("🚫 Plugin system initialization failed: {e}"))?; + + match action { + Build(opts) => opts + .build(Some(bin.clone()), None) + .map_err(|e| anyhow!("🚫 Building project failed: {}", e)), + + Clean(opts) => opts + .clean(Some(bin.clone())) + .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)), + + Serve(opts) => opts + .serve(Some(bin.clone())) + .await + .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)), + + Bundle(opts) => opts + .bundle(Some(bin.clone())) + .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)), + + _ => unreachable!(), + } + } } } diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index d22a5219f..75cc51978 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,5 +1,6 @@ use crate::file_data::FileEngine; use crate::file_data::HasFileData; +use std::ops::Deref; use std::{collections::HashMap, fmt::Debug}; use dioxus_core::Event; @@ -19,6 +20,37 @@ pub enum FormValue { VecText(Vec), } +impl From for Vec { + fn from(value: FormValue) -> Self { + match value { + FormValue::Text(s) => vec![s], + FormValue::VecText(vec) => vec, + } + } +} + +impl Deref for FormValue { + type Target = [String]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl FormValue { + /// Convenient way to represent Value as slice + pub fn as_slice(&self) -> &[String] { + match self { + FormValue::Text(s) => std::slice::from_ref(s), + FormValue::VecText(vec) => vec.as_slice(), + } + } + /// Convert into Vec + pub fn to_vec(self) -> Vec { + self.into() + } +} + /* DOMEvent: Send + SyncTarget relatedTarget */ pub struct FormData { inner: Box, diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 2e9632eae..e79b55fe8 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -389,7 +389,25 @@ impl HasFormData for WebFormData { } fn values(&self) -> HashMap { - let mut values = std::collections::HashMap::new(); + let mut values = HashMap::new(); + + fn insert_value(map: &mut HashMap, key: String, new_value: String) { + match map.entry(key) { + std::collections::hash_map::Entry::Occupied(mut o) => { + let first_value = match o.get_mut() { + FormValue::Text(data) => std::mem::take(data), + FormValue::VecText(vec) => { + vec.push(new_value); + return; + } + }; + let _ = o.insert(FormValue::VecText(vec![first_value, new_value])); + } + std::collections::hash_map::Entry::Vacant(v) => { + let _ = v.insert(FormValue::Text(new_value)); + } + } + } // try to fill in form values if let Some(form) = self.element.dyn_ref::() { @@ -398,20 +416,18 @@ impl HasFormData for WebFormData { if let Ok(array) = value.dyn_into::() { if let Some(name) = array.get(0).as_string() { if let Ok(item_values) = array.get(1).dyn_into::() { - let item_values: Vec = - item_values.iter().filter_map(|v| v.as_string()).collect(); - - values.insert(name, FormValue::VecText(item_values)); + item_values + .iter() + .filter_map(|v| v.as_string()) + .for_each(|v| insert_value(&mut values, name.clone(), v)); } else if let Ok(item_value) = array.get(1).dyn_into::() { - values.insert(name, FormValue::Text(item_value.as_string().unwrap())); + insert_value(&mut values, name, item_value.as_string().unwrap()); } } } } - } - - // try to fill in select element values - if let Some(select) = self.element.dyn_ref::() { + } else if let Some(select) = self.element.dyn_ref::() { + // try to fill in select element values let options = get_select_data(select); values.insert("options".to_string(), FormValue::VecText(options)); } @@ -536,19 +552,7 @@ export function get_form_data(form) { const formData = new FormData(form); for (let name of formData.keys()) { - const fieldType = form.elements[name].type; - console.log(fieldType); - - switch (fieldType) { - case "select-multiple": - values.set(name, formData.getAll(name)); - break; - - // add cases for fieldTypes that can hold multiple values here - default: - values.set(name, formData.get(name)); - break; - } + values.set(name, formData.getAll(name)); } return values;