diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 00000000..35049cbc
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --"
diff --git a/Cargo.lock b/Cargo.lock
index 0020ff1b..a061c458 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -863,6 +863,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "futures"
version = "0.3.28"
@@ -1985,16 +1991,6 @@ dependencies = [
"libm",
]
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
[[package]]
name = "num_enum"
version = "0.5.11"
@@ -2331,9 +2327,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rayon"
-version = "1.7.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@@ -2341,14 +2337,12 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.11.0"
+version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
]
[[package]]
@@ -3447,6 +3441,21 @@ dependencies = [
"nom",
]
+[[package]]
+name = "xflags"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d9e15fbb3de55454b0106e314b28e671279009b363e6f1d8e39fdc3bf048944"
+dependencies = [
+ "xflags-macros",
+]
+
+[[package]]
+name = "xflags-macros"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155"
+
[[package]]
name = "xi-unicode"
version = "0.3.0"
@@ -3465,6 +3474,15 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "flate2",
+ "fs_extra",
+ "xflags",
+]
+
[[package]]
name = "zerocopy"
version = "0.7.32"
diff --git a/Cargo.toml b/Cargo.toml
index 6b1082eb..eacb6a46 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,10 @@ readme = "README.md"
description = "A simple portable multimedia layer to create apps or games easily"
[workspace]
-members = ["crates/*"]
+members = [
+ "crates/*",
+ "xtask"
+]
[workspace.package]
version = "0.12.1"
diff --git a/scripts/build_docs.ps1 b/scripts/build_docs.ps1
deleted file mode 100644
index 2d5805f0..00000000
--- a/scripts/build_docs.ps1
+++ /dev/null
@@ -1 +0,0 @@
-Start-Process cargo -ArgumentList "doc --all-features" -NoNewWindow -Wait
diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh
deleted file mode 100755
index 0f362426..00000000
--- a/scripts/build_docs.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-cargo doc --all-features
-
diff --git a/scripts/build_msvc_examples.ps1 b/scripts/build_msvc_examples.ps1
deleted file mode 100644
index 85d31b8f..00000000
--- a/scripts/build_msvc_examples.ps1
+++ /dev/null
@@ -1,19 +0,0 @@
-# Create directory if it doesn't exist
-[void](New-Item -ItemType Directory -Path ".\docs\msvc_examples\examples\assets" -ErrorAction SilentlyContinue)
-
-# Copy assets to docs/examples directory
-Copy-Item -Path ".\examples\assets" -Destination ".\docs\msvc_examples" -Recurse -ErrorAction SilentlyContinue
-
-# Function to compile each example
-function Compile {
- param (
- [string]$example
- )
-
- .\scripts\msvc_example.ps1 $example --release --no-assets
-}
-
-# Loop through each .rs file in examples directory
-Get-ChildItem -Path ".\examples\*.rs" | ForEach-Object {
- Compile $_.Basename
-}
diff --git a/scripts/build_web_examples.ps1 b/scripts/build_web_examples.ps1
deleted file mode 100644
index 0277f17c..00000000
--- a/scripts/build_web_examples.ps1
+++ /dev/null
@@ -1,36 +0,0 @@
-# Create directory if it doesn't exist
-[void](New-Item -ItemType Directory -Path ".\docs\examples\assets" -ErrorAction SilentlyContinue)
-
-# Copy assets to docs/examples directory
-Copy-Item -Path ".\examples\assets" -Destination ".\docs\examples" -Recurse -ErrorAction SilentlyContinue
-
-# Initialize document body
-$doc_body = "
`n"
-
-# Function to compile each example
-function Compile {
- param (
- [string]$example
- )
-
- .\scripts\web_example.ps1 $example --release --no-assets
-
- $url = "examples/${example}.html"
- $image = "examples/images/${example}.jpg"
- $doc_body += "`n$example
"
-}
-
-# Loop through each .rs file in examples directory
-Get-ChildItem -Path ".\examples\*.rs" | ForEach-Object {
- Compile $_.Basename
-}
-
-# Wait for compilation to finish
-#Wait-Process -Name "web_example"
-
-$doc_body += "`n
"
-
-# Copy docs.html to index.html and replace body placeholder
-Copy-Item -Path ".\scripts\docs.html" -Destination ".\docs\index.html"
-$index = (Get-Content ".\scripts\docs.html") -replace "{{ BODY }}", $doc_body
-$index | Set-Content -Path ".\docs\index.html"
diff --git a/scripts/build_web_examples.sh b/scripts/build_web_examples.sh
index 1a9102e4..ed6ae5b3 100755
--- a/scripts/build_web_examples.sh
+++ b/scripts/build_web_examples.sh
@@ -1,27 +1 @@
-#!/bin/bash
-mkdir -p ./docs/examples/assets
-cp -R ./examples/assets ./docs/examples
-
-doc_body="\n"
-
-compile() {
- f=$1
- f=${f/\.\/examples\//""}
- f=${f/.rs/""}
- ./scripts/web_example.sh $f --release --no-assets
-
- url="examples/${f}.html"
- image="examples/images/${f}.jpg"
- doc_body="${doc_body}\n${f}
"
-}
-
-for f in ./examples/*.rs; do
- compile "$f"
-done
-
-wait
-
-doc_body="${doc_body}\n
"
-cp ./scripts/docs.html ./docs/index.html
-index=$(sed "s#{{ BODY }}#${doc_body}#g" "./scripts/docs.html")
-echo "${index}" > ./docs/index.html
+cargo xtask examples web --release
\ No newline at end of file
diff --git a/scripts/msvc_example.ps1 b/scripts/msvc_example.ps1
deleted file mode 100644
index 88e656f9..00000000
--- a/scripts/msvc_example.ps1
+++ /dev/null
@@ -1,22 +0,0 @@
-param(
- [string]$1,
- [string]$2,
- [string]$3
-)
-
-$features = "glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file"
-if ($2 -eq "--release") {
- # Build release version
- cargo build --target x86_64-pc-windows-msvc --release --example $1 --features=$features
- Copy-Item .\target\x86_64-pc-windows-msvc\release\examples\$1.exe .\docs\msvc_examples\$1.exe
-}
-else {
- # Build debug version
- cargo build --target x86_64-pc-windows-msvc --example $1 --features=$features
- Copy-Item .\target\x86_64-pc-windows-msvc\debug\examples\$1.exe .\docs\msvc_examples\$1.exe
-}
-
-if ($3 -ne "--no-assets") {
- # Copy assets folder
- Copy-Item .\examples\assets .\docs\msvc_examples -Recurse -Force
-}
diff --git a/scripts/web_example.ps1 b/scripts/web_example.ps1
deleted file mode 100644
index f14f284d..00000000
--- a/scripts/web_example.ps1
+++ /dev/null
@@ -1,38 +0,0 @@
-param(
- [string]$1,
- [string]$2,
- [string]$3
-)
-
-[void](New-Item -ItemType Directory -Path ".\docs\examples\$1" -Force)
-
-$env:RUSTFLAGS = "--cfg=web_sys_unstable_apis"
-$features = "glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file"
-
-if ($2 -eq "--release") {
- # Build release version
- cargo build --target wasm32-unknown-unknown --release --example $1 --features=$features
- wasm-bindgen .\target\wasm32-unknown-unknown\release\examples\$1.wasm --out-dir .\docs\examples\$1 --no-modules --browser
- wasm-opt -O -o .\docs\examples\$1\${1}_bg.wasm .\docs\examples\$1\${1}_bg.wasm
-
- if ($3 -eq "--gzip") {
- # Gzip the wasm files
- Compress-Archive -Path ".\docs\examples\$1\$1_bg.wasm" -DestinationPath ".\docs\examples\$1\$1_bg.wasm.gz" -Force
- Compress-Archive -Path ".\docs\examples\$1\$1.js" -DestinationPath ".\docs\examples\$1\$1.js.gz" -Force
- }
-}
-else {
- # Build debug version
- cargo build --target wasm32-unknown-unknown --example $1 --features=$features
- wasm-bindgen .\target\wasm32-unknown-unknown\debug\examples\$1.wasm --out-dir .\docs\examples\$1 --no-modules --browser --keep-debug --debug
-}
-
-# Copy example.html and replace placeholder with example name
-Copy-Item .\scripts\example.html .\docs\examples\$1.html -Force
-$index = Get-Content .\scripts\example.html | ForEach-Object { $_ -replace '{{ EXAMPLE }}', $1 }
-$index | Set-Content .\docs\examples\$1.html -Force
-
-if ($3 -ne "--no-assets") {
- # Copy assets folder
- Copy-Item .\examples\assets .\docs\examples -Recurse -Force
-}
diff --git a/scripts/web_example.sh b/scripts/web_example.sh
deleted file mode 100755
index 5e880618..00000000
--- a/scripts/web_example.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-mkdir -p ./docs/examples/$1
-
-# web_sys_unstable_apis is required to enable the web_sys clipboard API
-# https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html
-export RUSTFLAGS=--cfg=web_sys_unstable_apis
-features=glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file
-
-if [[ $2 == '--release' ]];
-then
- cargo build --target wasm32-unknown-unknown --release --example $1 --features=$features
- wasm-bindgen ./target/wasm32-unknown-unknown/release/examples/$1.wasm --out-dir ./docs/examples/$1 --no-modules --browser
- wasm-opt -O -o ./docs/examples/$1/$1_bg.wasm ./docs/examples/$1/$1_bg.wasm
-
- if [[ $3 == '--gzip' ]];
- then
- gzip -f ./docs/examples/$1/$1_bg.wasm
- gzip -f ./docs/examples/$1/$1.js
- fi
-
-else
- cargo build --target wasm32-unknown-unknown --example $1 --features=$features
- wasm-bindgen ./target/wasm32-unknown-unknown/debug/examples/$1.wasm --out-dir ./docs/examples/$1 --no-modules --browser --keep-debug --debug
-fi
-
-cp ./scripts/example.html ./docs/examples/$1.html
-index=$(sed "s/{{ EXAMPLE }}/${1}/g" "./scripts/example.html")
-echo "${index}" > ./docs/examples/$1.html
-
-if [[ $3 != '--no-assets' ]];
-then
- cp -R ./examples/assets ./docs/examples
-fi
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
new file mode 100644
index 00000000..0cab6a08
--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+publish = false
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+readme = "README.md"
+description = ""
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+flate2 = "1.0"
+fs_extra = "1.3.0"
+xflags = "0.3.2"
diff --git a/xtask/README.md b/xtask/README.md
new file mode 100644
index 00000000..09cb263a
--- /dev/null
+++ b/xtask/README.md
@@ -0,0 +1,4 @@
+xtask
+===
+
+Adds useful development commands using xtask. Use the command `cargo xtask` to show the help.
diff --git a/scripts/docs.html b/xtask/res/docs.html
similarity index 100%
rename from scripts/docs.html
rename to xtask/res/docs.html
diff --git a/scripts/example.html b/xtask/res/example.html
similarity index 100%
rename from scripts/example.html
rename to xtask/res/example.html
diff --git a/xtask/src/cli.rs b/xtask/src/cli.rs
new file mode 100644
index 00000000..e95c17c5
--- /dev/null
+++ b/xtask/src/cli.rs
@@ -0,0 +1,98 @@
+#![allow(unreachable_pub)]
+
+use std::str::FromStr;
+
+xflags::xflags! {
+ src "./src/cli.rs"
+
+ cmd cli {
+ cmd docs {}
+ cmd example {
+ required name: String
+ required target: TargetType
+
+ optional --release
+ optional --no-assets
+ optional --gzip
+ }
+
+ cmd examples {
+ required target: TargetType
+
+ optional --release
+ optional --gzip
+ }
+ }
+}
+
+// generated start
+// The following code is generated by `xflags` macro.
+// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
+#[derive(Debug)]
+pub struct Cli {
+ pub subcommand: CliCmd,
+}
+
+#[derive(Debug)]
+pub enum CliCmd {
+ Docs(Docs),
+ Example(Example),
+ Examples(Examples),
+}
+
+#[derive(Debug)]
+pub struct Docs;
+
+#[derive(Debug)]
+pub struct Example {
+ pub name: String,
+ pub target: TargetType,
+
+ pub release: bool,
+ pub no_assets: bool,
+ pub gzip: bool,
+}
+
+#[derive(Debug)]
+pub struct Examples {
+ pub target: TargetType,
+
+ pub release: bool,
+ pub gzip: bool,
+}
+
+impl Cli {
+ #[allow(dead_code)]
+ pub fn from_env_or_exit() -> Self {
+ Self::from_env_or_exit_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_env() -> xflags::Result {
+ Self::from_env_()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec) -> xflags::Result {
+ Self::from_vec_(args)
+ }
+}
+// generated end
+
+#[derive(Copy, Clone, Debug, Default)]
+pub enum TargetType {
+ #[default]
+ Msvc,
+ Web,
+}
+
+impl FromStr for TargetType {
+ type Err = String;
+ fn from_str(s: &str) -> Result {
+ match s {
+ "msvc" => Ok(Self::Msvc),
+ "web" => Ok(Self::Web),
+ _ => Err("Invalid option".to_owned()),
+ }
+ }
+}
diff --git a/xtask/src/cli_docs.rs b/xtask/src/cli_docs.rs
new file mode 100644
index 00000000..e36b282a
--- /dev/null
+++ b/xtask/src/cli_docs.rs
@@ -0,0 +1,40 @@
+use std::path::PathBuf;
+use std::process::Command;
+use std::{env, fs};
+
+use crate::cli::Docs;
+use crate::{project_root, DynError};
+
+impl Docs {
+ pub(crate) fn run(self) -> Result<(), DynError> {
+ docs_clean()?;
+ docs_run()?;
+
+ Ok(())
+ }
+}
+
+fn docs_clean() -> Result<(), DynError> {
+ let _ = fs::remove_dir_all(dist_doc_dir());
+ fs::create_dir_all(dist_doc_dir())?;
+
+ Ok(())
+}
+
+fn docs_run() -> Result<(), DynError> {
+ let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+ let status = Command::new(cargo)
+ .current_dir(project_root())
+ .args(["doc", "--all-features"])
+ .status()?;
+
+ if !status.success() {
+ Err("Command 'cargo doc --all-features' failed")?;
+ }
+
+ Ok(())
+}
+
+fn dist_doc_dir() -> PathBuf {
+ project_root().join("target/doc")
+}
diff --git a/xtask/src/cli_example.rs b/xtask/src/cli_example.rs
new file mode 100644
index 00000000..68102af7
--- /dev/null
+++ b/xtask/src/cli_example.rs
@@ -0,0 +1,13 @@
+use crate::cli::{Example, TargetType};
+use crate::DynError;
+
+impl Example {
+ pub(crate) fn run(self) -> Result<(), DynError> {
+ match self.target {
+ TargetType::Msvc => self.run_msvc()?,
+ TargetType::Web => self.run_web()?,
+ }
+
+ Ok(())
+ }
+}
diff --git a/xtask/src/cli_example_msvc.rs b/xtask/src/cli_example_msvc.rs
new file mode 100644
index 00000000..8df06234
--- /dev/null
+++ b/xtask/src/cli_example_msvc.rs
@@ -0,0 +1,52 @@
+use std::fs;
+use std::path::PathBuf;
+
+use crate::cli::{Example, TargetType};
+use crate::{cargo_build, copy_assets, project_root, DynError};
+
+impl Example {
+ pub(crate) fn run_msvc(self) -> Result<(), DynError> {
+ let status = match self.release {
+ true => cargo_build(TargetType::Msvc, "release", self.name.as_str())?,
+ false => cargo_build(TargetType::Msvc, "dev", self.name.as_str())?,
+ };
+
+ if !status.success() {
+ Err("Command 'cargo build' failed")?;
+ }
+
+ let name_str = self.name.as_str();
+ let executable = format!("{name_str}.exe");
+
+ fs::create_dir_all(docs_msvc_dir(self.release))?;
+ let _ = fs::copy(
+ dist_msvc_dir(self.release).join(executable.as_str()),
+ docs_msvc_dir(self.release).join(executable.as_str()),
+ );
+
+ if !self.no_assets {
+ copy_assets(docs_msvc_dir(self.release));
+ }
+
+ Ok(())
+ }
+}
+
+pub fn docs_msvc_dir(release: bool) -> PathBuf {
+ project_root()
+ .join("docs/msvc_examples/")
+ .join(match release {
+ true => "release",
+ false => "debug",
+ })
+}
+
+fn dist_msvc_dir(release: bool) -> PathBuf {
+ project_root()
+ .join("target/x86_64-pc-windows-msvc")
+ .join(match release {
+ true => "release",
+ false => "debug",
+ })
+ .join("examples")
+}
diff --git a/xtask/src/cli_example_web.rs b/xtask/src/cli_example_web.rs
new file mode 100644
index 00000000..4064f491
--- /dev/null
+++ b/xtask/src/cli_example_web.rs
@@ -0,0 +1,109 @@
+use std::fs::File;
+use std::io::{BufRead, BufReader, Write};
+use std::path::PathBuf;
+
+use crate::cli::{Example, TargetType};
+use crate::{cargo_build, copy_assets, gz_file, project_root, wasm_bindgen, wasm_opt, DynError};
+
+impl Example {
+ pub(crate) fn run_web(self) -> Result<(), DynError> {
+ let status = match self.release {
+ true => cargo_build(TargetType::Web, "release", self.name.as_str())?,
+ false => cargo_build(TargetType::Web, "dev", self.name.as_str())?,
+ };
+
+ if !status.success() {
+ Err("Command 'cargo build' failed")?;
+ }
+
+ let name_str = self.name.as_str();
+ let wasm = format!("{name_str}.wasm");
+
+ let input = dist_web_dir(self.release)
+ .join(wasm.as_str())
+ .to_string_lossy()
+ .into_owned();
+ let output = docs_web_dir(name_str).to_string_lossy().into_owned();
+
+ let status = wasm_bindgen(input.as_str(), output.as_str(), !self.release)?;
+ if !status.success() {
+ Err("Command 'wasm-bindgen' failed")?;
+ }
+
+ if self.release {
+ let wasm_file = format!("{name_str}_bg.wasm");
+ let inout = docs_web_dir(name_str)
+ .join(wasm_file.as_str())
+ .to_string_lossy()
+ .into_owned();
+
+ let status = wasm_opt(inout.as_str(), inout.as_str())?;
+ if !status.success() {
+ Err("Command 'wasm-opt' failed")?;
+ }
+
+ if self.gzip {
+ let wasm_gz = format!("{wasm_file}.gz");
+ let wasm_gz_output = docs_web_dir(name_str)
+ .join(wasm_gz)
+ .to_string_lossy()
+ .into_owned();
+
+ let js_gz_input = docs_web_dir(name_str)
+ .join(format!("{name_str}.js"))
+ .to_string_lossy()
+ .into_owned();
+ let js_gz_output = docs_web_dir(name_str)
+ .join(format!("{name_str}.js.gz"))
+ .to_string_lossy()
+ .into_owned();
+
+ gz_file(inout.as_str(), wasm_gz_output.as_str());
+ gz_file(js_gz_input.as_str(), js_gz_output.as_str());
+ }
+ }
+
+ let example_file = format!("{name_str}.html");
+
+ let file_in = File::open(res_dir().join("example.html"))?;
+ let reader = BufReader::new(file_in);
+
+ let mut output_lines = Vec::new();
+ for line in reader.lines() {
+ let mut line = line?;
+
+ line = line.replace("{{ EXAMPLE }}", self.name.as_str());
+ output_lines.push(line);
+ }
+
+ let mut file_out = File::create(docs_web_dir("").join(example_file.as_str()))?;
+
+ for line in &output_lines {
+ writeln!(file_out, "{}", line)?;
+ }
+
+ if !self.no_assets {
+ copy_assets(docs_web_dir(""))
+ }
+
+ Ok(())
+ }
+}
+
+pub fn docs_web_dir(name: &str) -> PathBuf {
+ project_root().join("docs").join("examples").join(name)
+}
+
+fn dist_web_dir(release: bool) -> PathBuf {
+ project_root()
+ .join("target/wasm32-unknown-unknown")
+ .join(match release {
+ true => "release",
+ false => "debug",
+ })
+ .join("examples")
+}
+
+pub fn res_dir() -> PathBuf {
+ project_root().join("xtask/res")
+}
diff --git a/xtask/src/cli_examples.rs b/xtask/src/cli_examples.rs
new file mode 100644
index 00000000..4c1893de
--- /dev/null
+++ b/xtask/src/cli_examples.rs
@@ -0,0 +1,41 @@
+use std::fs;
+use std::path::PathBuf;
+
+use crate::cli::{Examples, TargetType};
+use crate::DynError;
+
+impl Examples {
+ pub(crate) fn run(self) -> Result<(), DynError> {
+ match self.target {
+ TargetType::Msvc => self.run_msvc()?,
+ TargetType::Web => self.run_web()?,
+ }
+
+ Ok(())
+ }
+
+ pub(crate) fn list_files(self, path: &str) -> Result, DynError> {
+ let entries = fs::read_dir(path)?;
+ let dir_entries: Vec<_> = entries
+ .filter_map(Result::ok)
+ .filter_map(|entry| {
+ let metadata = entry.metadata().ok()?;
+ let is_file = metadata.is_file();
+ let is_hidden = entry
+ .file_name()
+ .to_str()
+ .map(|name| name.starts_with('.'))
+ .unwrap_or_default();
+ let is_valid = is_file && !is_hidden;
+ if is_valid {
+ Some(entry)
+ } else {
+ None
+ }
+ })
+ .map(|entry| entry.path())
+ .collect();
+
+ Ok(dir_entries.into_iter())
+ }
+}
diff --git a/xtask/src/cli_examples_msvc.rs b/xtask/src/cli_examples_msvc.rs
new file mode 100644
index 00000000..44629cf0
--- /dev/null
+++ b/xtask/src/cli_examples_msvc.rs
@@ -0,0 +1,35 @@
+use crate::cli::{Example, Examples};
+use crate::cli_example_msvc::docs_msvc_dir;
+use crate::{copy_assets, project_root, DynError};
+
+impl Examples {
+ pub(crate) fn run_msvc(self) -> Result<(), DynError> {
+ copy_assets(docs_msvc_dir(self.release).join("assets"));
+
+ let examples_path = project_root()
+ .join("examples")
+ .to_string_lossy()
+ .into_owned();
+
+ let target = self.target;
+ let release = self.release;
+ let gzip = self.gzip;
+
+ self.list_files(examples_path.as_str())?
+ .for_each(|example| {
+ let name = example.file_stem().unwrap().to_str().unwrap().to_owned();
+ // eprintln!("{}", name);
+ let example = Example {
+ name,
+ target,
+ release,
+ no_assets: true,
+ gzip,
+ };
+
+ let _ = example.run();
+ });
+
+ Ok(())
+ }
+}
diff --git a/xtask/src/cli_examples_web.rs b/xtask/src/cli_examples_web.rs
new file mode 100644
index 00000000..e43b0ddc
--- /dev/null
+++ b/xtask/src/cli_examples_web.rs
@@ -0,0 +1,66 @@
+use std::fs::File;
+use std::io::{BufRead, BufReader, Write};
+
+use crate::cli::{Example, Examples};
+use crate::cli_example_web::docs_web_dir;
+use crate::{copy_assets, project_root, DynError};
+
+impl Examples {
+ pub(crate) fn run_web(self) -> Result<(), DynError> {
+ copy_assets(docs_web_dir("assets"));
+
+ let mut doc_body = String::from("\n");
+
+ let examples_path = project_root()
+ .join("examples")
+ .to_string_lossy()
+ .into_owned();
+
+ let target = self.target;
+ let release = self.release;
+ let gzip = self.gzip;
+
+ self.list_files(examples_path.as_str())?
+ .for_each(|example| {
+ let name = example.file_stem().unwrap().to_str().unwrap().to_owned();
+ let name_str = name.as_str().to_owned();
+
+ let example = Example {
+ name,
+ target,
+ release,
+ no_assets: true,
+ gzip,
+ };
+
+ let _ = example.run();
+
+ let url = format!("examples/{name_str}.html");
+ let image = format!("examples/images/{name_str}.jpg");
+ let tmp = format!("\n{name_str}
");
+
+ doc_body.push_str(tmp.as_str());
+ });
+
+ doc_body.push_str("\n
");
+
+ let file_in = File::open(crate::cli_example_web::res_dir().join("docs.html"))?;
+ let reader = BufReader::new(file_in);
+
+ let mut output_lines = Vec::new();
+ for line in reader.lines() {
+ let mut line = line?;
+
+ line = line.replace("{{ BODY }}", doc_body.as_str());
+ output_lines.push(line);
+ }
+
+ let mut file_out = File::create(docs_web_dir("../").join("index.html"))?;
+
+ for line in &output_lines {
+ writeln!(file_out, "{}", line)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
new file mode 100644
index 00000000..ca579a94
--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,133 @@
+mod cli;
+
+mod cli_docs;
+mod cli_example;
+mod cli_example_msvc;
+mod cli_example_web;
+mod cli_examples;
+mod cli_examples_msvc;
+mod cli_examples_web;
+
+use crate::cli::TargetType;
+use cli::{Cli, CliCmd};
+use flate2::write::GzEncoder;
+use flate2::Compression;
+use std::env;
+use std::fs::File;
+use std::io::{copy, BufReader, Error};
+use std::path::PathBuf;
+use std::process::{Command, ExitStatus};
+
+type DynError = Box;
+
+fn main() {
+ if let Err(e) = try_main() {
+ eprintln!("{}", e);
+ std::process::exit(-1);
+ }
+}
+
+fn try_main() -> Result<(), DynError> {
+ let flags = Cli::from_env_or_exit();
+
+ match flags.subcommand {
+ CliCmd::Docs(cmd) => cmd.run()?,
+ CliCmd::Example(cmd) => cmd.run()?,
+ CliCmd::Examples(cmd) => cmd.run()?,
+ }
+
+ Ok(())
+}
+
+fn project_root() -> PathBuf {
+ let dir =
+ env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned());
+
+ PathBuf::from(dir).parent().unwrap().to_owned()
+}
+
+fn assets_dir() -> PathBuf {
+ project_root().join("examples/assets")
+}
+
+fn copy_assets(to: PathBuf) {
+ let options = fs_extra::dir::CopyOptions::new()
+ .overwrite(true)
+ .copy_inside(true);
+
+ let paths = vec![assets_dir().as_path().to_owned()];
+ let _ = fs_extra::copy_items(&paths, to, &options);
+}
+
+fn cargo_build(target: TargetType, profile: &str, name: &str) -> Result {
+ let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+
+ // We want this to be configurable?
+ let features =
+ "glyph,egui,text,extra,audio,links,drop_files,clipboard,save_file,texture_to_file";
+ let features_arg = format!("--features={features}");
+
+ match target {
+ TargetType::Msvc => Command::new(cargo)
+ .current_dir(project_root())
+ .args([
+ "build",
+ "--target",
+ "x86_64-pc-windows-msvc",
+ "--profile",
+ profile,
+ "--example",
+ name,
+ features_arg.as_str(),
+ ])
+ .status(),
+
+ TargetType::Web => Command::new(cargo)
+ .current_dir(project_root())
+ .env("RUSTFLAGS", "--cfg=web_sys_unstable_apis")
+ .args([
+ "build",
+ "--target",
+ "wasm32-unknown-unknown",
+ "--profile",
+ profile,
+ "--example",
+ name,
+ features_arg.as_str(),
+ ])
+ .status(),
+ }
+}
+
+fn wasm_bindgen(input: &str, output: &str, debug: bool) -> Result {
+ Command::new("wasm-bindgen")
+ .current_dir(project_root())
+ .args(
+ &[
+ [input, "--out-dir", output, "--no-modules", "--browser"].as_slice(),
+ match debug {
+ true => ["--keep-debug", "--debug"].as_slice(),
+ false => [].as_slice(),
+ },
+ ]
+ .concat(),
+ )
+ .status()
+}
+
+fn wasm_opt(input: &str, output: &str) -> Result {
+ Command::new("wasm-opt")
+ .current_dir(project_root())
+ .args(["-O", "-o", input, output])
+ .status()
+}
+
+fn gz_file(input: &str, output: &str) {
+ let mut input_wasm = BufReader::new(File::open(input).unwrap());
+ let output_wasm = File::create(output).unwrap();
+
+ let mut encoder_wasm = GzEncoder::new(output_wasm, Compression::default());
+
+ copy(&mut input_wasm, &mut encoder_wasm).unwrap();
+ encoder_wasm.finish().unwrap();
+}