From b274a419392b448d569c2795a31c9b0eddca0fe1 Mon Sep 17 00:00:00 2001
From: Philippe Loctaux
Date: Thu, 2 May 2024 18:36:00 +0200
Subject: [PATCH] rewrote website with leptos
---
.editorconfig | 13 +
.gitignore | 14 +-
Cargo.lock | 1893 ++++++++++-------
Cargo.toml | 33 +-
build.rs | 21 -
crates/gen-wallpapers/examples/cli.rs | 2 +-
crates/gen-wallpapers/src/lib.rs | 4 +-
crates/plcom/.gitignore | 1 +
crates/plcom/Cargo.toml | 118 +
crates/plcom/build.rs | 365 ++++
crates/plcom/css/main.css | 24 +
crates/plcom/resume.json | 428 ++++
crates/plcom/src/app.rs | 71 +
crates/plcom/src/common.rs | 137 ++
crates/plcom/src/common/icon.rs | 140 ++
crates/plcom/src/common/link.rs | 99 +
crates/plcom/src/error_template.rs | 83 +
crates/plcom/src/fileserv.rs | 42 +
crates/plcom/src/lib.rs | 45 +
crates/plcom/src/main.rs | 38 +
crates/plcom/src/pages.rs | 7 +
crates/plcom/src/pages/email.rs | 37 +
crates/plcom/src/pages/root.rs | 76 +
crates/plcom/src/pages/root/education.rs | 51 +
crates/plcom/src/pages/root/experience.rs | 134 ++
crates/plcom/src/pages/root/friends.rs | 145 ++
crates/plcom/src/pages/root/hero.rs | 100 +
crates/plcom/src/pages/root/jobs.rs | 76 +
crates/plcom/src/pages/root/projects.rs | 163 ++
crates/plcom/src/pages/root/talks.rs | 137 ++
crates/plcom/src/pages/root/www.rs | 65 +
crates/plcom/src/pages/wallpapers.rs | 49 +
crates/plcom/tailwind.config.js | 16 +
css/tailwind.css | 3 -
public/icons/rubycat.png | Bin 0 -> 507543 bytes
public/icons/uqac.png | Bin 0 -> 96526 bytes
public/phil.png | Bin 0 -> 261160 bytes
public/pub/phil.png | Bin 262391 -> 0 bytes
public/pub/philt3r.png | Bin 79342 -> 0 bytes
readme.md | 7 +-
src/cache.rs | 54 -
src/filters.rs | 11 -
src/main.rs | 78 -
src/minify.rs | 39 -
src/templates.rs | 57 -
src/types.rs | 65 -
src/types/friend.rs | 84 -
src/types/job.rs | 111 -
src/types/network.rs | 52 -
src/types/project.rs | 247 ---
src/types/talk.rs | 70 -
tailwind.config.cjs | 15 -
templates/base.html | 26 -
templates/content.html | 10 -
templates/icons/calendar.html | 11 -
templates/icons/email.html | 14 -
templates/icons/github.html | 9 -
templates/icons/link.html | 13 -
templates/icons/linkedin.html | 9 -
templates/icons/location.html | 11 -
templates/icons/map.html | 10 -
templates/icons/mastodon.html | 9 -
templates/icons/telegram.html | 9 -
templates/icons/twitter.html | 9 -
templates/pages/404.html | 5 -
templates/pages/email.html | 25 -
templates/pages/root.html | 15 -
templates/pages/root/friends.html | 24 -
templates/pages/root/hero-content.html | 8 -
templates/pages/root/hero-image.html | 76 -
templates/pages/root/jobs.html | 64 -
templates/pages/root/project-with-image.html | 108 -
.../pages/root/project-without-image.html | 91 -
templates/pages/root/projects.html | 23 -
templates/pages/root/talks.html | 46 -
templates/pages/root/whoami.html | 39 -
templates/pages/root/www.html | 39 -
templates/pages/wallpapers.html | 27 -
78 files changed, 3853 insertions(+), 2397 deletions(-)
create mode 100644 .editorconfig
delete mode 100644 build.rs
create mode 100644 crates/plcom/.gitignore
create mode 100644 crates/plcom/Cargo.toml
create mode 100644 crates/plcom/build.rs
create mode 100644 crates/plcom/css/main.css
create mode 100644 crates/plcom/resume.json
create mode 100644 crates/plcom/src/app.rs
create mode 100644 crates/plcom/src/common.rs
create mode 100644 crates/plcom/src/common/icon.rs
create mode 100644 crates/plcom/src/common/link.rs
create mode 100644 crates/plcom/src/error_template.rs
create mode 100644 crates/plcom/src/fileserv.rs
create mode 100644 crates/plcom/src/lib.rs
create mode 100644 crates/plcom/src/main.rs
create mode 100644 crates/plcom/src/pages.rs
create mode 100644 crates/plcom/src/pages/email.rs
create mode 100644 crates/plcom/src/pages/root.rs
create mode 100644 crates/plcom/src/pages/root/education.rs
create mode 100644 crates/plcom/src/pages/root/experience.rs
create mode 100644 crates/plcom/src/pages/root/friends.rs
create mode 100644 crates/plcom/src/pages/root/hero.rs
create mode 100644 crates/plcom/src/pages/root/jobs.rs
create mode 100644 crates/plcom/src/pages/root/projects.rs
create mode 100644 crates/plcom/src/pages/root/talks.rs
create mode 100644 crates/plcom/src/pages/root/www.rs
create mode 100644 crates/plcom/src/pages/wallpapers.rs
create mode 100644 crates/plcom/tailwind.config.js
delete mode 100644 css/tailwind.css
create mode 100644 public/icons/rubycat.png
create mode 100644 public/icons/uqac.png
create mode 100644 public/phil.png
delete mode 100644 public/pub/phil.png
delete mode 100644 public/pub/philt3r.png
delete mode 100644 src/cache.rs
delete mode 100644 src/filters.rs
delete mode 100644 src/main.rs
delete mode 100644 src/minify.rs
delete mode 100644 src/templates.rs
delete mode 100644 src/types.rs
delete mode 100644 src/types/friend.rs
delete mode 100644 src/types/job.rs
delete mode 100644 src/types/network.rs
delete mode 100644 src/types/project.rs
delete mode 100644 src/types/talk.rs
delete mode 100644 tailwind.config.cjs
delete mode 100644 templates/base.html
delete mode 100644 templates/content.html
delete mode 100644 templates/icons/calendar.html
delete mode 100644 templates/icons/email.html
delete mode 100644 templates/icons/github.html
delete mode 100644 templates/icons/link.html
delete mode 100644 templates/icons/linkedin.html
delete mode 100644 templates/icons/location.html
delete mode 100644 templates/icons/map.html
delete mode 100644 templates/icons/mastodon.html
delete mode 100644 templates/icons/telegram.html
delete mode 100644 templates/icons/twitter.html
delete mode 100644 templates/pages/404.html
delete mode 100644 templates/pages/email.html
delete mode 100644 templates/pages/root.html
delete mode 100644 templates/pages/root/friends.html
delete mode 100644 templates/pages/root/hero-content.html
delete mode 100644 templates/pages/root/hero-image.html
delete mode 100644 templates/pages/root/jobs.html
delete mode 100644 templates/pages/root/project-with-image.html
delete mode 100644 templates/pages/root/project-without-image.html
delete mode 100644 templates/pages/root/projects.html
delete mode 100644 templates/pages/root/talks.html
delete mode 100644 templates/pages/root/whoami.html
delete mode 100644 templates/pages/root/www.html
delete mode 100644 templates/pages/wallpapers.html
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..22b6c3a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+
+[*.js]
+indent_style = space
+indent_size = 2
+
+[*.json]
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
index 51174d6..e54971b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,12 @@
-# wallpapers
-/src/wallpapers.rs
-
# built css
/public/style.css
+# wallpapers
+wallpapers.json
+
# build output
-/target
+target/
+pkg
# environment variables
.env
@@ -15,4 +16,7 @@
.DS_Store
# ide
-.idea/
\ No newline at end of file
+.idea/
+
+# rustfmt
+**/*.rs.bk
diff --git a/Cargo.lock b/Cargo.lock
index c783cd6..5abd17b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -29,15 +29,6 @@ dependencies = [
"zerocopy",
]
-[[package]]
-name = "aho-corasick"
-version = "0.7.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -48,19 +39,10 @@ dependencies = [
]
[[package]]
-name = "alloc-no-stdlib"
-version = "2.0.4"
+name = "allocator-api2"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
-
-[[package]]
-name = "alloc-stdlib"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
-dependencies = [
- "alloc-no-stdlib",
-]
+checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "android-tzdata"
@@ -125,6 +107,12 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
[[package]]
name = "approx"
version = "0.5.1"
@@ -135,89 +123,10 @@ dependencies = [
]
[[package]]
-name = "askama"
-version = "0.12.1"
+name = "async-recursion"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
-dependencies = [
- "askama_derive",
- "askama_escape",
- "humansize",
- "num-traits",
- "percent-encoding",
-]
-
-[[package]]
-name = "askama_derive"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
-dependencies = [
- "askama_parser",
- "basic-toml",
- "mime",
- "mime_guess",
- "proc-macro2",
- "quote",
- "serde",
- "syn 2.0.58",
-]
-
-[[package]]
-name = "askama_escape"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
-
-[[package]]
-name = "askama_parser"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
-dependencies = [
- "nom",
-]
-
-[[package]]
-name = "askama_rocket"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebf814a0f98928b2ea4a0625ad7563d737ecf0707260b8360f4ad2a4b51918e6"
-dependencies = [
- "askama",
- "rocket",
-]
-
-[[package]]
-name = "async-compression"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905"
-dependencies = [
- "brotli",
- "flate2",
- "futures-core",
- "memchr",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "async-stream"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
-dependencies = [
- "async-stream-impl",
- "futures-core",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-stream-impl"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5"
dependencies = [
"proc-macro2",
"quote",
@@ -236,18 +145,33 @@ dependencies = [
]
[[package]]
-name = "atomic"
-version = "0.5.3"
+name = "attribute-derive"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
+checksum = "8b48808b337d6b74c15ff9becfc0e139fe2b4e2b224d670a0ecdb46b0b2d3d9b"
+dependencies = [
+ "attribute-derive-macro",
+ "derive-where",
+ "manyhow",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
[[package]]
-name = "atomic"
-version = "0.6.0"
+name = "attribute-derive-macro"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
+checksum = "5b19cbd63850ecff821c413e12846a67ec9f4ce7309c70959b94ecf9b2575ee2"
dependencies = [
- "bytemuck",
+ "collection_literals",
+ "interpolator",
+ "manyhow",
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
+ "quote-use",
+ "syn 2.0.58",
]
[[package]]
@@ -256,6 +180,62 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+[[package]]
+name = "axum"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "hyper 1.2.0",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "multer",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.0",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 0.1.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "backtrace"
version = "0.3.71"
@@ -278,19 +258,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
-name = "basic-toml"
-version = "0.1.9"
+name = "base64"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "binascii"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
+checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "bitflags"
@@ -314,45 +285,56 @@ dependencies = [
"radium",
]
-[[package]]
-name = "brotli"
-version = "3.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
-dependencies = [
- "alloc-no-stdlib",
- "alloc-stdlib",
- "brotli-decompressor",
-]
-
-[[package]]
-name = "brotli-decompressor"
-version = "2.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
-dependencies = [
- "alloc-no-stdlib",
- "alloc-stdlib",
-]
-
[[package]]
name = "bumpalo"
version = "3.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
-[[package]]
-name = "bytemuck"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
-
[[package]]
name = "bytes"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+[[package]]
+name = "cached"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90eb5776f28a149524d1d8623035760b4454ec881e8cf3838fa8d7e1b11254b3"
+dependencies = [
+ "cached_proc_macro",
+ "cached_proc_macro_types",
+ "hashbrown 0.13.2",
+ "instant",
+ "once_cell",
+ "thiserror",
+]
+
+[[package]]
+name = "cached_proc_macro"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f"
+dependencies = [
+ "darling 0.14.4",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "cached_proc_macro_types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
+
+[[package]]
+name = "camino"
+version = "1.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
+
[[package]]
name = "cc"
version = "1.0.90"
@@ -379,6 +361,33 @@ dependencies = [
"windows-targets 0.52.4",
]
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
[[package]]
name = "clap"
version = "4.5.4"
@@ -398,7 +407,7 @@ dependencies = [
"anstream",
"anstyle",
"clap_lex",
- "strsim",
+ "strsim 0.11.1",
]
[[package]]
@@ -419,6 +428,12 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+[[package]]
+name = "collection_literals"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
+
[[package]]
name = "colorchoice"
version = "1.0.0"
@@ -426,20 +441,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
-name = "convert_case"
-version = "0.4.0"
+name = "config"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
+dependencies = [
+ "convert_case",
+ "lazy_static",
+ "nom",
+ "pathdiff",
+ "serde",
+ "toml",
+]
[[package]]
-name = "cookie"
-version = "0.18.1"
+name = "console_error_panic_hook"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
- "percent-encoding",
- "time",
- "version_check",
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "const_format"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
+dependencies = [
+ "const_format_proc_macros",
+]
+
+[[package]]
+name = "const_format_proc_macros"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
]
[[package]]
@@ -459,76 +510,101 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
-name = "crc32fast"
-version = "1.4.0"
+name = "crunchy"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "darling"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
- "cfg-if",
+ "darling_core 0.14.4",
+ "darling_macro 0.14.4",
]
[[package]]
-name = "css-minify"
-version = "0.3.1"
+name = "darling"
+version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd"
+checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
dependencies = [
- "derive_more",
- "indexmap 1.9.3",
- "nom",
+ "darling_core 0.20.8",
+ "darling_macro 0.20.8",
]
[[package]]
-name = "deranged"
-version = "0.3.11"
+name = "darling_core"
+version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
- "powerfmt",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
-dependencies = [
- "convert_case",
+ "fnv",
+ "ident_case",
"proc-macro2",
"quote",
- "rustc_version",
+ "strsim 0.10.0",
"syn 1.0.109",
]
[[package]]
-name = "devise"
-version = "0.4.1"
+name = "darling_core"
+version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8"
+checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core 0.14.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
+dependencies = [
+ "darling_core 0.20.8",
+ "quote",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown 0.14.3",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "derive-where"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
dependencies = [
- "devise_codegen",
- "devise_core",
-]
-
-[[package]]
-name = "devise_codegen"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6"
-dependencies = [
- "devise_core",
- "quote",
-]
-
-[[package]]
-name = "devise_core"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
-dependencies = [
- "bitflags 2.5.0",
"proc-macro2",
- "proc-macro2-diagnostics",
"quote",
"syn 2.0.58",
]
@@ -547,6 +623,12 @@ dependencies = [
"thiserror",
]
+[[package]]
+name = "drain_filter_polyfill"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408"
+
[[package]]
name = "either"
version = "1.10.0"
@@ -584,30 +666,6 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
-[[package]]
-name = "figment"
-version = "0.10.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6"
-dependencies = [
- "atomic 0.6.0",
- "pear",
- "serde",
- "toml",
- "uncased",
- "version_check",
-]
-
-[[package]]
-name = "flate2"
-version = "1.0.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
-dependencies = [
- "crc32fast",
- "miniz_oxide",
-]
-
[[package]]
name = "fnv"
version = "1.0.7"
@@ -739,19 +797,6 @@ dependencies = [
"serde_json",
]
-[[package]]
-name = "generator"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
-dependencies = [
- "cc",
- "libc",
- "log",
- "rustversion",
- "windows",
-]
-
[[package]]
name = "geo-types"
version = "0.7.13"
@@ -770,8 +815,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -781,10 +828,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
-name = "glob"
-version = "0.3.1"
+name = "gloo-net"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "http 0.2.12",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
[[package]]
name = "h2"
@@ -797,8 +872,8 @@ dependencies = [
"futures-core",
"futures-sink",
"futures-util",
- "http",
- "indexmap 2.2.6",
+ "http 0.2.12",
+ "indexmap",
"slab",
"tokio",
"tokio-util",
@@ -806,26 +881,30 @@ dependencies = [
]
[[package]]
-name = "hashbrown"
-version = "0.12.3"
+name = "half"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
-dependencies = [
- "ahash",
- "bumpalo",
-]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+]
[[package]]
name = "heck"
@@ -839,6 +918,15 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+[[package]]
+name = "html-escape"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
+dependencies = [
+ "utf8-width",
+]
+
[[package]]
name = "http"
version = "0.2.12"
@@ -850,6 +938,17 @@ dependencies = [
"itoa",
]
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
[[package]]
name = "http-body"
version = "0.4.6"
@@ -857,10 +956,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
- "http",
+ "http 0.2.12",
"pin-project-lite",
]
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http 1.1.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range-header"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe"
+
[[package]]
name = "httparse"
version = "1.8.0"
@@ -873,15 +1001,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
-[[package]]
-name = "humansize"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
-dependencies = [
- "libm",
-]
-
[[package]]
name = "hyper"
version = "0.14.28"
@@ -893,8 +1012,8 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
- "http",
- "http-body",
+ "http 0.2.12",
+ "http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
@@ -906,6 +1025,25 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
[[package]]
name = "hyper-tls"
version = "0.5.0"
@@ -913,12 +1051,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
- "hyper",
+ "hyper 0.14.28",
"native-tls",
"tokio",
"tokio-native-tls",
]
+[[package]]
+name = "hyper-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "hyper 1.2.0",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+]
+
[[package]]
name = "iana-time-zone"
version = "0.1.60"
@@ -942,6 +1096,12 @@ dependencies = [
"cc",
]
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
[[package]]
name = "idna"
version = "0.5.0"
@@ -952,16 +1112,6 @@ dependencies = [
"unicode-normalization",
]
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
-]
-
[[package]]
name = "indexmap"
version = "2.2.6"
@@ -970,14 +1120,28 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
- "serde",
]
[[package]]
-name = "inlinable_string"
-version = "0.1.15"
+name = "instant"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "interpolator"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
+
+[[package]]
+name = "inventory"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
[[package]]
name = "ipnet"
@@ -985,17 +1149,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
-[[package]]
-name = "is-terminal"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys 0.52.0",
-]
-
[[package]]
name = "itertools"
version = "0.12.1"
@@ -1035,6 +1188,268 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+[[package]]
+name = "leaflet"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1ca4f4ecc791314ac38d50a5af6dc6506977aa7c7f0231b1bfd66989a9c4be3"
+dependencies = [
+ "js-sys",
+ "paste",
+ "url",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd996d00a55895327b2eb5d2d7d440ab756b4a9fe43ec78411c74199306808c"
+dependencies = [
+ "cfg-if",
+ "leptos_config",
+ "leptos_dom",
+ "leptos_macro",
+ "leptos_reactive",
+ "leptos_server",
+ "server_fn",
+ "tracing",
+ "typed-builder",
+ "typed-builder-macro",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos-leaflet"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c65a03c5300f8ad496c3029e4e0f565c7e60d42f7ea7a1a69d2cf1e8414e2771"
+dependencies = [
+ "getrandom",
+ "js-sys",
+ "leaflet",
+ "leptos",
+ "leptos_meta",
+ "paste",
+ "rand",
+ "serde-wasm-bindgen",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_axum"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0572d6549c7b4d59d6c6c8240c0faa25c1d20af144db7b2e372ffc537fd0aaa3"
+dependencies = [
+ "axum",
+ "cfg-if",
+ "futures",
+ "http-body-util",
+ "leptos",
+ "leptos_integration_utils",
+ "leptos_macro",
+ "leptos_meta",
+ "leptos_router",
+ "once_cell",
+ "parking_lot",
+ "serde_json",
+ "server_fn",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "leptos_config"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc190458a62433fb1d31f65f1afe7f1044650c667cdf9166956b48907ac821bd"
+dependencies = [
+ "config",
+ "regex",
+ "serde",
+ "thiserror",
+ "typed-builder",
+]
+
+[[package]]
+name = "leptos_dom"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ffd84c459d7c517fb8c9bfff704e819f3e6a2f5baf574d8636cb23840323d0a"
+dependencies = [
+ "async-recursion",
+ "cfg-if",
+ "drain_filter_polyfill",
+ "futures",
+ "getrandom",
+ "html-escape",
+ "indexmap",
+ "itertools",
+ "js-sys",
+ "leptos_reactive",
+ "once_cell",
+ "pad-adapter",
+ "paste",
+ "rustc-hash",
+ "serde",
+ "serde_json",
+ "server_fn",
+ "smallvec",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_hot_reload"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da2f157fc6d4a356e3dd2734c6a65cba9e5eadb7c7cfd979c0adb752d293dba1"
+dependencies = [
+ "anyhow",
+ "camino",
+ "indexmap",
+ "parking_lot",
+ "proc-macro2",
+ "quote",
+ "rstml",
+ "serde",
+ "syn 2.0.58",
+ "walkdir",
+]
+
+[[package]]
+name = "leptos_integration_utils"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046984e2ca5e2136eb1021ced1f1dc1725b4045f4a370d28f2594b23604477ac"
+dependencies = [
+ "futures",
+ "leptos",
+ "leptos_config",
+ "leptos_hot_reload",
+ "leptos_meta",
+ "tracing",
+]
+
+[[package]]
+name = "leptos_macro"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69a20596eb0afe56296d3187c8f46680bb8ea1df58b6566e2588c9c53c549581"
+dependencies = [
+ "attribute-derive",
+ "cfg-if",
+ "convert_case",
+ "html-escape",
+ "itertools",
+ "leptos_hot_reload",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "rstml",
+ "server_fn_macro",
+ "syn 2.0.58",
+ "tracing",
+ "uuid",
+]
+
+[[package]]
+name = "leptos_meta"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a85d2f3e3c4eb7ffb8a97abe6c79eefcb0a2c6ae5bacd7645b5a5df1ba0a212"
+dependencies = [
+ "cfg-if",
+ "indexmap",
+ "leptos",
+ "tracing",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_reactive"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b9152b4b9af932896ae69d08b8ab7108513b9cdddf8e2922d5c234dab7ac9af"
+dependencies = [
+ "base64 0.22.0",
+ "cfg-if",
+ "futures",
+ "indexmap",
+ "js-sys",
+ "paste",
+ "pin-project",
+ "rustc-hash",
+ "self_cell",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "slotmap",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_router"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e42ebcdc1663ab03f249b91c367daa00f164d84c5f30eaec492bd45dffd3148"
+dependencies = [
+ "cached",
+ "cfg-if",
+ "gloo-net",
+ "itertools",
+ "js-sys",
+ "lazy_static",
+ "leptos",
+ "leptos_integration_utils",
+ "leptos_meta",
+ "linear-map",
+ "lru",
+ "once_cell",
+ "percent-encoding",
+ "regex",
+ "send_wrapper",
+ "serde",
+ "serde_json",
+ "serde_qs",
+ "thiserror",
+ "tracing",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_server"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bed1e82ded6bfbfd61ad2f77066a789b94dc889513a47223e3c6a3e8ca2b109f"
+dependencies = [
+ "inventory",
+ "lazy_static",
+ "leptos_macro",
+ "leptos_reactive",
+ "serde",
+ "server_fn",
+ "thiserror",
+ "tracing",
+]
+
[[package]]
name = "libc"
version = "0.2.153"
@@ -1047,6 +1462,16 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+[[package]]
+name = "linear-map"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee"
+dependencies = [
+ "serde",
+ "serde_test",
+]
+
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
@@ -1070,18 +1495,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
-name = "loom"
-version = "0.5.6"
+name = "lru"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
+checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21"
dependencies = [
- "cfg-if",
- "generator",
- "scoped-tls",
- "serde",
- "serde_json",
- "tracing",
- "tracing-subscriber",
+ "hashbrown 0.14.3",
+]
+
+[[package]]
+name = "manyhow"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91ea592d76c0b6471965708ccff7e6a5d277f676b90ab31f4d3f3fc77fade64"
+dependencies = [
+ "manyhow-macros",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "manyhow-macros"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64621e2c08f2576e4194ea8be11daf24ac01249a4f53cd8befcbb7077120ead"
+dependencies = [
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
]
[[package]]
@@ -1091,13 +1533,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fc1b6107fbd06c96e5e481fcf3e6575b873eb84f5b68f1f5706cde0fed42c4"
[[package]]
-name = "matchers"
-version = "0.1.0"
+name = "matchit"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata 0.1.10",
-]
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "memchr"
@@ -1121,59 +1560,6 @@ dependencies = [
"unicase",
]
-[[package]]
-name = "minify-html"
-version = "0.11.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c467a6330fab16054a5676afe3efc354f11a0f81b2a7f59bc0af6a20feb076ba"
-dependencies = [
- "aho-corasick 0.7.20",
- "css-minify",
- "lazy_static",
- "memchr",
- "minify-html-common",
- "minify-js 0.5.6",
- "rustc-hash",
-]
-
-[[package]]
-name = "minify-html-common"
-version = "0.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0deb4840e5f4aee1ef0cd2e8131bdd475dd56e3649cacf41dd7e871557700c7f"
-dependencies = [
- "aho-corasick 0.7.20",
- "css-minify",
- "itertools",
- "lazy_static",
- "memchr",
- "minify-js 0.4.3",
- "reqwest",
- "rustc-hash",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "minify-js"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4"
-dependencies = [
- "lazy_static",
- "parse-js 0.10.3",
-]
-
-[[package]]
-name = "minify-js"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22d6c512a82abddbbc13b70609cb2beff01be2c7afff534d6e5e1c85e438fc8b"
-dependencies = [
- "lazy_static",
- "parse-js 0.17.0",
-]
-
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -1202,21 +1588,19 @@ dependencies = [
[[package]]
name = "multer"
-version = "2.1.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
+checksum = "a15d522be0a9c3e46fd2632e272d178f56387bdb5c9fbb3a36c649062e9b5219"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
- "http",
+ "http 1.1.0",
"httparse",
"log",
"memchr",
"mime",
"spin",
- "tokio",
- "tokio-util",
"version_check",
]
@@ -1226,12 +1610,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
-[[package]]
-name = "nanorand"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
-
[[package]]
name = "native-tls"
version = "0.2.11"
@@ -1260,16 +1638,6 @@ dependencies = [
"minimal-lexical",
]
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
[[package]]
name = "num"
version = "0.2.1"
@@ -1305,12 +1673,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -1423,10 +1785,10 @@ dependencies = [
]
[[package]]
-name = "overload"
+name = "pad-adapter"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+checksum = "56d80efc4b6721e8be2a10a5df21a30fa0b470f1539e53d8b4e6e75faf938b63"
[[package]]
name = "parking_lot"
@@ -1452,51 +1814,16 @@ dependencies = [
]
[[package]]
-name = "parse-js"
-version = "0.10.3"
+name = "paste"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5"
-dependencies = [
- "aho-corasick 0.7.20",
- "lazy_static",
- "memchr",
-]
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
-name = "parse-js"
-version = "0.17.0"
+name = "pathdiff"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ec3b11d443640ec35165ee8f6f0559f1c6f41878d70330fe9187012b5935f02"
-dependencies = [
- "aho-corasick 0.7.20",
- "bumpalo",
- "hashbrown 0.13.2",
- "lazy_static",
- "memchr",
-]
-
-[[package]]
-name = "pear"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467"
-dependencies = [
- "inlinable_string",
- "pear_codegen",
- "yansi",
-]
-
-[[package]]
-name = "pear_codegen"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147"
-dependencies = [
- "proc-macro2",
- "proc-macro2-diagnostics",
- "quote",
- "syn 2.0.58",
-]
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "percent-encoding"
@@ -1504,6 +1831,26 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@@ -1526,27 +1873,79 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
name = "plcom"
version = "0.1.0"
dependencies = [
- "askama",
- "askama_rocket",
+ "axum",
"chrono",
- "minify-html",
- "nanorand",
- "rocket",
- "rocket_async_compression",
+ "console_error_panic_hook",
+ "gen-wallpapers",
+ "getrandom",
+ "http 1.1.0",
+ "leptos",
+ "leptos-leaflet",
+ "leptos_axum",
+ "leptos_meta",
+ "leptos_router",
+ "rand",
+ "serde",
+ "serde_json",
+ "tailwind_fuse",
+ "thiserror",
+ "tokio",
+ "tower",
+ "tower-http",
+ "tracing",
+ "wasm-bindgen",
]
-[[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+[[package]]
+name = "prettyplease"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-utils"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "smallvec",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.79"
@@ -1578,6 +1977,29 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "quote-use"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b393938dcaab992375d7b3df7887fa98cc91c2f3590598251e7c609e2b788139"
+dependencies = [
+ "quote",
+ "quote-use-macros",
+]
+
+[[package]]
+name = "quote-use-macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d8772387900c205780e2c240cfe4dd01355ab4f96a503d99bdf34ad73180ef"
+dependencies = [
+ "derive-where",
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
[[package]]
name = "radium"
version = "0.3.0"
@@ -1623,45 +2045,16 @@ dependencies = [
"bitflags 1.3.2",
]
-[[package]]
-name = "ref-cast"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
-dependencies = [
- "ref-cast-impl",
-]
-
-[[package]]
-name = "ref-cast-impl"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.58",
-]
-
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
- "aho-corasick 1.1.3",
+ "aho-corasick",
"memchr",
- "regex-automata 0.4.6",
- "regex-syntax 0.8.3",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -1670,17 +2063,11 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
- "aho-corasick 1.1.3",
+ "aho-corasick",
"memchr",
- "regex-syntax 0.8.3",
+ "regex-syntax",
]
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
[[package]]
name = "regex-syntax"
version = "0.8.3"
@@ -1693,15 +2080,15 @@ version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
- "base64",
+ "base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
- "http",
- "http-body",
- "hyper",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
"hyper-tls",
"ipnet",
"js-sys",
@@ -1715,7 +2102,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
- "sync_wrapper",
+ "sync_wrapper 0.1.2",
"system-configuration",
"tokio",
"tokio-native-tls",
@@ -1728,97 +2115,17 @@ dependencies = [
]
[[package]]
-name = "rocket"
-version = "0.5.0"
+name = "rstml"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
+checksum = "fe542870b8f59dd45ad11d382e5339c9a1047cde059be136a7016095bbdefa77"
dependencies = [
- "async-stream",
- "async-trait",
- "atomic 0.5.3",
- "binascii",
- "bytes",
- "either",
- "figment",
- "futures",
- "indexmap 2.2.6",
- "log",
- "memchr",
- "multer",
- "num_cpus",
- "parking_lot",
- "pin-project-lite",
- "rand",
- "ref-cast",
- "rocket_codegen",
- "rocket_http",
- "serde",
- "state",
- "tempfile",
- "time",
- "tokio",
- "tokio-stream",
- "tokio-util",
- "ubyte",
- "version_check",
- "yansi",
-]
-
-[[package]]
-name = "rocket_async_compression"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50253076f89fd47fa68b1d571f811f188ef90b606d82fa952326c34cd955669e"
-dependencies = [
- "async-compression",
- "futures",
- "lazy_static",
- "log",
- "rocket",
-]
-
-[[package]]
-name = "rocket_codegen"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
-dependencies = [
- "devise",
- "glob",
- "indexmap 2.2.6",
"proc-macro2",
+ "proc-macro2-diagnostics",
"quote",
- "rocket_http",
"syn 2.0.58",
- "unicode-xid",
- "version_check",
-]
-
-[[package]]
-name = "rocket_http"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
-dependencies = [
- "cookie",
- "either",
- "futures",
- "http",
- "hyper",
- "indexmap 2.2.6",
- "log",
- "memchr",
- "pear",
- "percent-encoding",
- "pin-project-lite",
- "ref-cast",
- "serde",
- "smallvec",
- "stable-pattern",
- "state",
- "time",
- "tokio",
- "uncased",
+ "syn_derive",
+ "thiserror",
]
[[package]]
@@ -1844,15 +2151,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-[[package]]
-name = "rustc_version"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
-dependencies = [
- "semver",
-]
-
[[package]]
name = "rustix"
version = "0.38.32"
@@ -1872,7 +2170,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
- "base64",
+ "base64 0.21.7",
]
[[package]]
@@ -1887,6 +2185,15 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
[[package]]
name = "schannel"
version = "0.1.23"
@@ -1896,12 +2203,6 @@ dependencies = [
"windows-sys 0.52.0",
]
-[[package]]
-name = "scoped-tls"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
-
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -1932,10 +2233,19 @@ dependencies = [
]
[[package]]
-name = "semver"
-version = "1.0.22"
+name = "self_cell"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
+
+[[package]]
+name = "send_wrapper"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
+dependencies = [
+ "futures-core",
+]
[[package]]
name = "serde"
@@ -1946,6 +2256,17 @@ dependencies = [
"serde_derive",
]
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
[[package]]
name = "serde_derive"
version = "1.0.197"
@@ -1968,6 +2289,27 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_qs"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
+dependencies = [
+ "percent-encoding",
+ "serde",
+ "thiserror",
+]
+
[[package]]
name = "serde_spanned"
version = "0.6.5"
@@ -1977,6 +2319,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_test"
+version = "1.0.176"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -1990,21 +2341,62 @@ dependencies = [
]
[[package]]
-name = "sharded-slab"
-version = "0.1.7"
+name = "server_fn"
+version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+checksum = "15a46a2ffdecb81430ecfb995989218a18b6e94c1ead50cb806b5927c986a8ce"
dependencies = [
- "lazy_static",
+ "axum",
+ "bytes",
+ "ciborium",
+ "const_format",
+ "dashmap",
+ "futures",
+ "gloo-net",
+ "http 1.1.0",
+ "http-body-util",
+ "hyper 1.2.0",
+ "inventory",
+ "js-sys",
+ "once_cell",
+ "send_wrapper",
+ "serde",
+ "serde_json",
+ "serde_qs",
+ "server_fn_macro_default",
+ "thiserror",
+ "tower",
+ "tower-layer",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "xxhash-rust",
]
[[package]]
-name = "signal-hook-registry"
-version = "1.4.1"
+name = "server_fn_macro"
+version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+checksum = "324a248dedb786315ba738f3618dbd65ba0c1b22ebea76f15a3e96a04643a73f"
dependencies = [
- "libc",
+ "const_format",
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "server_fn_macro_default"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af19028131998f73134a9adcdefb6d9de2eeaed5b01ef74f6d902a1659d2a32f"
+dependencies = [
+ "server_fn_macro",
+ "syn 2.0.58",
]
[[package]]
@@ -2016,6 +2408,16 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "serde",
+ "version_check",
+]
+
[[package]]
name = "smallvec"
version = "1.13.2"
@@ -2039,22 +2441,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
-name = "stable-pattern"
-version = "0.1.0"
+name = "strsim"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "state"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
-dependencies = [
- "loom",
-]
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strsim"
@@ -2084,12 +2474,30 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "syn_derive"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+[[package]]
+name = "sync_wrapper"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c"
+
[[package]]
name = "system-configuration"
version = "0.5.1"
@@ -2111,6 +2519,28 @@ dependencies = [
"libc",
]
+[[package]]
+name = "tailwind_fuse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540816d3bd4e690d75d0cfcad47c8129487f0f730fc32ffa913aeef0b7634a8"
+dependencies = [
+ "nom",
+ "tailwind_fuse_macro",
+]
+
+[[package]]
+name = "tailwind_fuse_macro"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "473b0a76a09cf00567794d9aa58fafa568ddb66053cf75ae776f7034584c2e46"
+dependencies = [
+ "darling 0.20.8",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
[[package]]
name = "tempfile"
version = "3.10.1"
@@ -2143,47 +2573,6 @@ dependencies = [
"syn 2.0.58",
]
-[[package]]
-name = "thread_local"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
-[[package]]
-name = "time"
-version = "0.3.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
[[package]]
name = "tinyvec"
version = "1.6.0"
@@ -2211,7 +2600,6 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
- "signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
@@ -2238,17 +2626,6 @@ dependencies = [
"tokio",
]
-[[package]]
-name = "tokio-stream"
-version = "0.1.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
[[package]]
name = "tokio-util"
version = "0.7.10"
@@ -2258,6 +2635,8 @@ dependencies = [
"bytes",
"futures-core",
"futures-sink",
+ "futures-util",
+ "hashbrown 0.14.3",
"pin-project-lite",
"tokio",
"tracing",
@@ -2290,13 +2669,60 @@ version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
dependencies = [
- "indexmap 2.2.6",
+ "indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+dependencies = [
+ "bitflags 2.5.0",
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "http-range-header",
+ "httpdate",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
[[package]]
name = "tower-service"
version = "0.3.2"
@@ -2309,6 +2735,7 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
+ "log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -2332,36 +2759,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
-dependencies = [
- "matchers",
- "nu-ansi-term",
- "once_cell",
- "regex",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
]
[[package]]
@@ -2371,22 +2768,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
-name = "ubyte"
-version = "0.10.4"
+name = "typed-builder"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
+checksum = "444d8748011b93cb168770e8092458cb0f8854f931ff82fdf6ddfbd72a9c933e"
dependencies = [
- "serde",
+ "typed-builder-macro",
]
[[package]]
-name = "uncased"
-version = "0.9.10"
+name = "typed-builder-macro"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
+checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352"
dependencies = [
- "serde",
- "version_check",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
]
[[package]]
@@ -2419,6 +2817,12 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
[[package]]
name = "unicode-xid"
version = "0.2.4"
@@ -2436,6 +2840,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "utf8-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
+
[[package]]
name = "utf8parse"
version = "0.2.1"
@@ -2443,10 +2853,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
-name = "valuable"
-version = "0.1.0"
+name = "uuid"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+dependencies = [
+ "getrandom",
+]
[[package]]
name = "vcpkg"
@@ -2460,6 +2873,16 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
[[package]]
name = "want"
version = "0.3.1"
@@ -2541,6 +2964,19 @@ version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+[[package]]
+name = "wasm-streams"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "web-sys"
version = "0.3.69"
@@ -2567,21 +3003,21 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-[[package]]
-name = "windows"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
[[package]]
name = "windows-core"
version = "0.52.0"
@@ -2742,14 +3178,17 @@ dependencies = [
"windows-sys 0.48.0",
]
+[[package]]
+name = "xxhash-rust"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03"
+
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
-dependencies = [
- "is-terminal",
-]
[[package]]
name = "zerocopy"
diff --git a/Cargo.toml b/Cargo.toml
index 5b3fc8c..2cfd947 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,26 +1,15 @@
[workspace]
-members = ["crates/gen-wallpapers"]
+resolver = "2"
+members = ["crates/gen-wallpapers", "crates/plcom"]
-[package]
-name = "plcom"
-version = "0.1.0"
-edition = "2021"
-publish = false
-default-run = "plcom"
+[workspace.dependencies]
+serde = "1.0"
-[[bin]]
-name = "plcom"
-path = "src/main.rs"
+# Defines a size-optimized profile for the WASM bundle in release mode
+[profile.wasm-release]
+inherits = "release"
+opt-level = 'z'
+lto = true
+codegen-units = 1
+panic = "abort"
-[[bin]]
-name = "gen-wallpapers"
-path = "src/gen-wallpapers.rs"
-
-[dependencies]
-rocket = "0.5"
-rocket_async_compression = "0.5"
-askama = { version = "0.12.1", features = ["with-rocket"] }
-askama_rocket = "0.12.0"
-chrono = "0.4.31"
-minify-html = "0.11.1"
-nanorand = { version = "0.7.0", features = ["chacha"] }
diff --git a/build.rs b/build.rs
deleted file mode 100644
index 46f4344..0000000
--- a/build.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-fn main() -> Result<(), Box> {
- println!("cargo:rerun-if-changed=build.rs");
- println!("cargo:rerun-if-changed=tailwind.config.cjs");
- println!("cargo:rerun-if-changed=templates/");
-
- let result = std::process::Command::new("tailwindcss")
- .arg("-i")
- .arg("./css/tailwind.css")
- .arg("-o")
- .arg("./public/style.css")
- .output()
- .unwrap();
-
- println!("{:?}", result);
-
- if !result.status.success() {
- panic!("Failed to run tailwindcss")
- }
-
- Ok(())
-}
diff --git a/crates/gen-wallpapers/examples/cli.rs b/crates/gen-wallpapers/examples/cli.rs
index 2e9fa7c..45ddf0d 100644
--- a/crates/gen-wallpapers/examples/cli.rs
+++ b/crates/gen-wallpapers/examples/cli.rs
@@ -20,7 +20,7 @@ fn main() {
let metadata = MetadataList::process_folder(dir, true);
- let json = match metadata.to_json() {
+ let json = match metadata.to_pretty_json() {
Ok(json) => json,
Err(e) => {
eprintln!("failed to serialize json: {e}");
diff --git a/crates/gen-wallpapers/src/lib.rs b/crates/gen-wallpapers/src/lib.rs
index 9a7e2de..8446cd8 100644
--- a/crates/gen-wallpapers/src/lib.rs
+++ b/crates/gen-wallpapers/src/lib.rs
@@ -165,8 +165,8 @@ impl MetadataList {
Self(files)
}
- pub fn to_json(&self) -> serde_json::Result {
- serde_json::to_string(&self.0)
+ pub fn to_pretty_json(&self) -> serde_json::Result {
+ serde_json::to_string_pretty(&self.0)
}
}
diff --git a/crates/plcom/.gitignore b/crates/plcom/.gitignore
new file mode 100644
index 0000000..cc4c514
--- /dev/null
+++ b/crates/plcom/.gitignore
@@ -0,0 +1 @@
+css/tailwind-output.css
diff --git a/crates/plcom/Cargo.toml b/crates/plcom/Cargo.toml
new file mode 100644
index 0000000..70f4f6c
--- /dev/null
+++ b/crates/plcom/Cargo.toml
@@ -0,0 +1,118 @@
+[package]
+name = "plcom"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+# leptos + axum
+axum = { version = "0.7", optional = true }
+console_error_panic_hook = "0.1"
+leptos = { version = "0.6" }
+leptos_axum = { version = "0.6", optional = true }
+leptos_meta = { version = "0.6" }
+leptos_router = { version = "0.6" }
+tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
+tower = { version = "0.4", optional = true }
+tower-http = { version = "0.5", features = ["fs"], optional = true }
+wasm-bindgen = "=0.2.92"
+thiserror = "1"
+tracing = { version = "0.1", optional = true }
+http = "1"
+
+# external crates
+tailwind_fuse = { version = "0.2.0", features = ["variant"] }
+chrono = "0.4.37"
+getrandom = { version = "0.2", features = ["js"] }
+rand = "0.8.5"
+leptos-leaflet = "0.8.0"
+
+[build-dependencies]
+serde = { workspace = true }
+serde_json = "1.0"
+gen-wallpapers = { path = "../../crates/gen-wallpapers" }
+
+[features]
+hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate", "leptos-leaflet/hydrate"]
+ssr = [
+ "dep:axum",
+ "dep:tokio",
+ "dep:tower",
+ "dep:tower-http",
+ "dep:leptos_axum",
+ "leptos/ssr",
+ "leptos_meta/ssr",
+ "leptos_router/ssr",
+ "dep:tracing",
+ "leptos-leaflet/ssr"
+]
+
+[package.metadata.leptos]
+# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
+output-name = "plcom"
+
+# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
+site-root = "target/site"
+
+# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
+# Defaults to pkg
+site-pkg-dir = "pkg"
+
+# The tailwind input file.
+#
+# Optional, Activates the tailwind build
+tailwind-input-file = "css/main.css"
+
+# The tailwind config file.
+#
+# Optional, defaults to "tailwind.config.js" which if is not present
+# is generated for you
+tailwind-config-file = "tailwind.config.js"
+
+# Assets source dir. All files found here will be copied and synchronized to site-root.
+# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
+#
+# Optional. Env: LEPTOS_ASSETS_DIR.
+assets-dir = "../../public"
+
+# Additional files triggering recompilation
+watch-additional-files = ["resume.json", "wallpapers.json"]
+
+# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
+site-addr = "0.0.0.0:3000"
+
+# The port to use for automatic reload monitoring
+reload-port = 3001
+
+# The browserlist query used for optimizing the CSS.
+browserquery = "defaults"
+
+# The environment Leptos will run in, usually either "DEV" or "PROD"
+env = "DEV"
+
+# The features to use when compiling the bin target
+#
+# Optional. Can be over-ridden with the command line parameter --bin-features
+bin-features = ["ssr"]
+
+# If the --no-default-features flag should be used when compiling the bin target
+#
+# Optional. Defaults to false.
+bin-default-features = false
+
+# The features to use when compiling the lib target
+#
+# Optional. Can be over-ridden with the command line parameter --lib-features
+lib-features = ["hydrate"]
+
+# If the --no-default-features flag should be used when compiling the lib target
+#
+# Optional. Defaults to false.
+lib-default-features = false
+
+# The profile to use for the lib target when compiling for release
+#
+# Optional. Defaults to "release".
+lib-profile-release = "wasm-release"
diff --git a/crates/plcom/build.rs b/crates/plcom/build.rs
new file mode 100644
index 0000000..cf92435
--- /dev/null
+++ b/crates/plcom/build.rs
@@ -0,0 +1,365 @@
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Work {
+ name: String,
+ position: String,
+ start_date: String,
+ end_date: Option,
+ logo: Logo,
+ description: String,
+ highlights: Vec,
+ technologies: Vec,
+ link: Option ,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Project {
+ name: String,
+ description: String,
+ start_date: String,
+ end_date: Option,
+ presentation: Vec,
+ highlights: Vec,
+ keywords: Vec,
+ link: Option ,
+ logo: Option,
+ image: Option,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Image {
+ file: String,
+ // TODO: use enum directly instead of string
+ // position: ImagePosition,
+ position: String,
+}
+
+#[derive(Debug, Deserialize)]
+// TODO: possible implementation: https://www.reddit.com/r/rust/comments/10bab4v/serdejson_how_to_deserialize_and_serialize_an/
+enum ImagePosition {
+ Left,
+ Right,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Link {
+ uri: String,
+ label: String,
+ not_available: bool,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Logo {
+ file: String,
+ transparent_background: bool,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Education {
+ institution: String,
+ study_type: String,
+ area: String,
+ start_date: String,
+ end_date: Option,
+ logo: Option,
+ courses: Vec,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Wallpaper {
+ filename: String,
+ date: String,
+ gps: Gps,
+ location: Location,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Gps {
+ latitude: f32,
+ longitude: f32,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Location {
+ precise: String,
+ broad: String,
+}
+
+fn vec_strings(vec: &Vec) -> String {
+ let mut string = String::new();
+ string.push('[');
+ for el in vec {
+ string.push_str(&format!("{:?},", el));
+ }
+ string.push(']');
+ string
+}
+
+fn resume(dest: &std::path::Path, source: std::fs::File) {
+ let reader = std::io::BufReader::new(source);
+ let data: serde_json::Value = serde_json::from_reader(reader).expect("Failed to parse JSON");
+ let mut final_ser = String::new();
+
+ // Work
+ let work_json = &data["work"];
+ let work_data: Vec =
+ serde_json::from_value(work_json.clone()).expect("Failed to parse work");
+ let mut work_str = String::new();
+ work_str.push('[');
+ for work in &work_data {
+ work_str.push_str("Work {");
+ work_str.push_str(&format!("name: {:?},", work.name));
+ work_str.push_str(&format!("position: {:?},", work.position));
+ work_str.push_str(&format!("start_date: {:?},", work.start_date));
+
+ work_str.push_str(&format!(
+ "end_date: {},",
+ match &work.end_date {
+ Some(end) => format!("Some({:?})", end),
+ None => "None".into(),
+ }
+ ));
+
+ // Optional struct
+ // TODO: refacto
+ match &work.link {
+ // TODO: enum with 2 variants for unavailable link
+ Some(link) => match link.not_available {
+ true => work_str.push_str(&format!(
+ "link: Some(ResumeLink {{ uri: {:?}, label: \"Not available\", not_available: true }}),",
+ link.uri
+ )),
+ false => work_str.push_str(&format!(
+ "link: Some(ResumeLink {{ uri: {:?}, label: {:?}, not_available: false }}),",
+ link.uri, link.label
+ )),
+ },
+ None => work_str.push_str("link: None,"),
+ }
+
+ // Struct
+ work_str.push_str(&format!(
+ "logo: Logo {{ file: {:?}, transparent_background: {} }},",
+ work.logo.file, work.logo.transparent_background
+ ));
+
+ work_str.push_str(&format!("description: {:?},", work.description));
+
+ // Vector
+ let mut highlights = String::new();
+ highlights.push('[');
+ for high in &work.highlights {
+ highlights.push_str(&format!("{:?},", high));
+ }
+ highlights.push(']');
+ work_str.push_str(&format!("highlights: &{},", highlights));
+
+ // Vector
+ let mut technologies = String::new();
+ technologies.push('[');
+ for tech in &work.technologies {
+ technologies.push_str(&format!("{:?},", tech));
+ }
+ technologies.push(']');
+ work_str.push_str(&format!("technologies: &{},", technologies));
+
+ work_str.push_str("},\n");
+ }
+ work_str.push(']');
+ let work_ser = format!(
+ "pub const WORK: [Work; {}] = \n{};\n",
+ work_data.len(),
+ work_str
+ );
+ final_ser.push_str(&work_ser);
+
+ // Projects
+ let projects_json = &data["projects"];
+ let projects_data: Vec =
+ serde_json::from_value(projects_json.clone()).expect("Failed to parse projects");
+ let mut projects_str = String::new();
+ projects_str.push('[');
+ for project in &projects_data {
+ projects_str.push_str("Project {");
+ projects_str.push_str(&format!("name: {:?},", project.name));
+ projects_str.push_str(&format!("description: {:?},", project.description));
+ projects_str.push_str(&format!("start_date: {:?},", project.start_date));
+
+ projects_str.push_str(&format!(
+ "end_date: {},",
+ match &project.end_date {
+ Some(end) => format!("Some({:?})", end),
+ None => "None".into(),
+ }
+ ));
+
+ // Optional struct
+ match &project.link {
+ // TODO: enum with 2 variants for unavailable link
+ Some(link) => match link.not_available {
+ true => projects_str.push_str(&format!(
+ "link: Some(ResumeLink {{ uri: {:?}, label: \"Not available\", not_available: true }}),",
+ link.uri
+ )),
+ false => projects_str.push_str(&format!(
+ "link: Some(ResumeLink {{ uri: {:?}, label: {:?}, not_available: false }}),",
+ link.uri, link.label
+ )),
+ },
+ None => projects_str.push_str("link: None,"),
+ }
+
+ // Vector
+ projects_str.push_str(&format!(
+ "presentation: &{},",
+ vec_strings(&project.presentation)
+ ));
+
+ // Vector
+ projects_str.push_str(&format!(
+ "highlights : &{},",
+ vec_strings(&project.highlights)
+ ));
+
+ // Vector
+ projects_str.push_str(&format!("keywords: &{},", vec_strings(&project.keywords)));
+
+ // Optional struct
+ match &project.logo {
+ Some(logo) => projects_str.push_str(&format!(
+ "logo: Some(Logo {{ file: {:?}, transparent_background: {} }}),",
+ logo.file, logo.transparent_background
+ )),
+ None => projects_str.push_str("logo: None,"),
+ }
+
+ // Optional struct
+ match &project.image {
+ Some(image) => projects_str.push_str(&format!(
+ "image: Some(Image {{ file: {:?}, position: {:?} }}),",
+ image.file, image.position
+ )),
+ None => projects_str.push_str("image: None,"),
+ }
+
+ projects_str.push_str("},\n");
+ }
+ projects_str.push(']');
+ let projects_ser = format!(
+ "pub const PROJECTS: [Project; {}] = \n{};\n",
+ projects_data.len(),
+ projects_str
+ );
+ final_ser.push_str(&projects_ser);
+
+ // Education
+ let education_json = &data["education"];
+ let education_data: Vec =
+ serde_json::from_value(education_json.clone()).expect("Failed to parse education");
+ let mut education_str = String::new();
+ education_str.push('[');
+ for education in &education_data {
+ education_str.push_str("Education {");
+ education_str.push_str(&format!("institution: {:?},", education.institution));
+ education_str.push_str(&format!("area: {:?},", education.area));
+ education_str.push_str(&format!("study_type: {:?},", education.study_type));
+ education_str.push_str(&format!("start_date: {:?},", education.start_date));
+ education_str.push_str(&format!(
+ "end_date: {},",
+ match &education.end_date {
+ Some(end) => format!("Some({:?})", end),
+ None => "None".into(),
+ }
+ ));
+
+ // Optional struct
+ match &education.logo {
+ Some(logo) => education_str.push_str(&format!(
+ "logo: Some(Logo {{ file: {:?}, transparent_background: {} }}),",
+ logo.file, logo.transparent_background
+ )),
+ None => education_str.push_str("logo: None,"),
+ }
+
+ // Vector
+ let mut courses = String::new();
+ courses.push('[');
+ for course in &education.courses {
+ courses.push_str(&format!("{:?},", course));
+ }
+ courses.push(']');
+ education_str.push_str(&format!("courses: &{},", courses));
+ education_str.push_str("},\n");
+ }
+ education_str.push(']');
+ let education_ser = format!(
+ "pub const EDUCATION: [Education; {}] = \n{};\n",
+ education_data.len(),
+ education_str
+ );
+ final_ser.push_str(&education_ser);
+
+ std::fs::write(dest, final_ser).unwrap();
+}
+
+fn wallpapers(dest: &std::path::Path, source: std::fs::File) {
+ let reader = std::io::BufReader::new(source);
+ let wallpapers_data: Vec =
+ serde_json::from_reader(reader).expect("Failed to parse JSON");
+ let mut final_ser = String::new();
+
+ let mut wallpapers_str = String::new();
+ wallpapers_str.push('[');
+
+ for wallpaper in &wallpapers_data {
+ wallpapers_str.push_str("Wallpaper {");
+ wallpapers_str.push_str(&format!("filename: {:?},", wallpaper.filename));
+ wallpapers_str.push_str(&format!("date: {:?},", wallpaper.date));
+ wallpapers_str.push_str(&format!(
+ "gps: Gps {{ latitude: {}, longitude: {} }},",
+ wallpaper.gps.latitude, wallpaper.gps.longitude
+ ));
+ wallpapers_str.push_str(&format!(
+ "location: Location {{ precise: {:?}, broad: {:?} }},",
+ wallpaper.location.precise, wallpaper.location.broad
+ ));
+ wallpapers_str.push_str("},\n");
+ }
+
+ wallpapers_str.push(']');
+ let wallpapers_ser = format!(
+ "pub const WALLPAPERS: [Wallpaper; {}] = \n{};\n",
+ wallpapers_data.len(),
+ wallpapers_str
+ );
+ final_ser.push_str(&wallpapers_ser);
+
+ std::fs::write(dest, final_ser).unwrap();
+}
+
+fn main() {
+ println!("cargo::rerun-if-changed=resume.json");
+ println!("cargo::rerun-if-changed=wallpapers.json");
+
+ let out_dir = std::env::var_os("OUT_DIR").unwrap();
+
+ let resume_dest = std::path::Path::new(&out_dir).join("resume.rs");
+ let resume_source = std::fs::File::open("resume.json").expect("Failed to open file");
+ resume(&resume_dest, resume_source);
+
+ let wallpapers_dest = std::path::Path::new(&out_dir).join("wallpapers.rs");
+ match std::fs::File::open("wallpapers.json") {
+ Ok(source) => wallpapers(&wallpapers_dest, source),
+ Err(_) => println!("cargo::warning=skipping wallpapers, file not found"),
+ }
+}
diff --git a/crates/plcom/css/main.css b/crates/plcom/css/main.css
new file mode 100644
index 0000000..2dcc3d7
--- /dev/null
+++ b/crates/plcom/css/main.css
@@ -0,0 +1,24 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* Fixed background when scrolling in tailwind
+ * Disabled on iOS devices
+
+ * https://tailwindcss.com/docs/background-attachment
+ * https://stackoverflow.com/a/60220757/4809297
+ */
+
+@supports (-webkit-touch-callout: none) {
+ /* CSS specific to iOS devices */
+ #wallpaper {
+ background-attachment: scroll;
+ }
+}
+
+@supports not (-webkit-touch-callout: none) {
+ /* CSS for other than iOS devices */
+ #wallpaper {
+ background-attachment: fixed;
+ }
+}
diff --git a/crates/plcom/resume.json b/crates/plcom/resume.json
new file mode 100644
index 0000000..e6ca3bb
--- /dev/null
+++ b/crates/plcom/resume.json
@@ -0,0 +1,428 @@
+{
+ "$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
+ "basics": {
+ "name": "Philippe Loctaux",
+ "url": "https://philippeloctaux.com",
+ "label": "Developer of all sorts",
+ "email": "www@philippeloctaux.com",
+ "location": {
+ "countryCode": "FR",
+ "city": "Rennes"
+ }
+ },
+ "languages": [
+ {
+ "language": "French"
+ },
+ {
+ "language": "English"
+ },
+ {
+ "language": "Russian"
+ }
+ ],
+ "education": [
+ {
+ "institution": "Epitech",
+ "studyType": "Master",
+ "area": "Computer software engineering",
+ "startDate": "2018-10",
+ "endDate": "2023-08",
+ "logo": {
+ "file": "/icons/epitech.png",
+ "transparentBackground": true
+ },
+ "courses": [
+ "Many small projects in C to learn a technical concept during first year",
+ "my_teams: Microsoft Teams clone in C (protocol design, networking, server, client)",
+ "Arcade: Small gaming platform capable of loading dynamic libraries with a high abstraction level in C++",
+ "Epicture: Android client in Kotlin of the Imgur image service (OAuth2, listing, viewing, uploading images)"
+ ]
+ },
+ {
+ "institution": "UQAC",
+ "studyType": "Year abroad",
+ "area": "Computer software engineering",
+ "startDate": "2021-09",
+ "endDate": "2022-05",
+ "logo": {
+ "file": "/icons/uqac.png",
+ "transparentBackground": true
+ },
+ "courses": [
+ "Android app development",
+ "Object Oriented Programming",
+ "Software engineering",
+ "Entreprise application development"
+ ]
+ }
+ ],
+ "work": [
+ {
+ "name": "Rubycat",
+ "position": "Software engineer",
+ "startDate": "2023-06",
+ "logo": {
+ "file": "/icons/rubycat.png",
+ "transparentBackground": true
+ },
+ "description": "Maintenance and improvement of PROVE IT, a Privileged Access Management software platform.",
+ "highlights": [
+ "Rewrote an internal component in Rust, making it faster and simpler to use for the development team",
+ "Bug investigation and fixes",
+ "Public speaking about the product"
+ ],
+ "technologies": [
+ "Rust",
+ "Angular",
+ "Python",
+ "PostgreSQL",
+ "GitLab"
+ ],
+ "link": {
+ "uri": "https://rubycat.eu",
+ "label": "Company website",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "Acklio",
+ "position": "Rust developer",
+ "startDate": "2023-03",
+ "endDate": "2023-05",
+ "logo": {
+ "file": "/icons/acklio.png",
+ "transparentBackground": true
+ },
+ "description": "The first usage of the SCHC framework (RFC 8724) on Rust!",
+ "highlights": [
+ "Creation of Rust bindings of a C library implementing the SCHC framework",
+ "Demonstration of SCHC with applications in Rust on x86 platform",
+ "Proof of concept usage of embedded STM32 controllers exclusively in Rust",
+ "Transmission of knowledge to the technical team"
+ ],
+ "technologies": [
+ "Rust",
+ "SCHC",
+ "STM32 controllers",
+ "LoRa",
+ "LoRaWAN"
+ ],
+ "link": {
+ "uri": "https://ackl.io",
+ "label": "Company website",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "Vélorail du Kreiz Breizh",
+ "position": "Freelance developer",
+ "startDate": "2021-08",
+ "endDate": "2022-04",
+ "logo": {
+ "file": "/icons/velorail.png",
+ "transparentBackground": true
+ },
+ "description": "Creation of an online booking platform focused on the tourist activity of rail biking (vélorail).",
+ "highlights": [
+ "Design, UX, booking and payment flow for customers",
+ "Dashboard for managers with calendar view, manual bookings, slots management",
+ "Ability to generate invoices, booking recaps for managers",
+ "Sending emails to customers and managers about bookings",
+ "Online deployment, maintenance of the service",
+ "5 months after the initial deployment, 43% of the bookings were made with the online platform",
+ "Focus to use the least amount of external services, resulting in implementation of a invoice generation service, an image-based captcha, and a templating system for transactional emails"
+ ],
+ "technologies": [
+ "Angular",
+ "NestJS",
+ "GraphQL",
+ "Rust",
+ "Stripe",
+ "Amazon SES",
+ "GitLab CI/CD"
+ ],
+ "link": {
+ "uri": "https://resa.velorail.bzh",
+ "label": "Booking platform",
+ "notAvailable": true
+ }
+ },
+ {
+ "name": "Yaakadev",
+ "position": "Full-Stack developer",
+ "startDate": "2021-04",
+ "endDate": "2021-07",
+ "logo": {
+ "file": "/icons/yaakadev.png",
+ "transparentBackground": false
+ },
+ "description": "Design, development, deployment and maintenance of many projects for various clients.",
+ "highlights": [
+ "Admin dashboard of a local merchants solution",
+ "Calendar planning application with filtering and custom views for a SaaS for the agency",
+ "Intranet to upload and download documents for a client"
+ ],
+ "technologies": [
+ "NodeJS",
+ "ExpressJS",
+ "Angular",
+ "MongoDB",
+ "CI/CD"
+ ],
+ "link": {
+ "uri": "https://yaakadev.com",
+ "label": "Agency website",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "Epitech",
+ "position": "Teaching assistant",
+ "startDate": "2020-02",
+ "endDate": "2021-03",
+ "logo": {
+ "file": "/icons/epitech.png",
+ "transparentBackground": true
+ },
+ "description": "Pedagogical supervision of three classes of students, conducting educational activites throughout the school year.",
+ "highlights": [
+ "Start of projects",
+ "Technical help and guidance",
+ "Proctoring exams",
+ "Grading students on their work"
+ ],
+ "technologies": [
+ "C",
+ "C++",
+ "Haskell",
+ "Rust",
+ "Web and mobile development"
+ ],
+ "link": {
+ "uri": "https://www.epitech.eu/ecole-informatique-rennes",
+ "label": "School",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "Ubiscale",
+ "position": "Embedded developer",
+ "startDate": "2019-08",
+ "endDate": "2019-12",
+ "logo": {
+ "file": "/icons/ubiscale.png",
+ "transparentBackground": true
+ },
+ "description": "Creation of a home Wifi gateway for a commercial IoT object.",
+ "highlights": [
+ "Research, reverse engineering of existing products",
+ "Design and implementation"
+ ],
+ "technologies": [
+ "C on a ESP8266 controller",
+ "Wi-Fi",
+ "Bluetooth"
+ ],
+ "link": {
+ "uri": "https://ubiscale.com",
+ "label": "Company website",
+ "notAvailable": false
+ }
+ }
+ ],
+ "projects": [
+ {
+ "name": "ezidam",
+ "description": "Identity and Access Management system",
+ "presentation": [
+ "A simple identity and access management system for SMEs or personal use.",
+ "Low maintenance required, easy to deploy and to backup."
+ ],
+ "highlights": [
+ "Users management",
+ "Roles management",
+ "Assign users to roles and the other way around",
+ "OAuth2 / OIDC applications (code flow)",
+ "Multi-Factor Authentication (TOTP)",
+ "Password reset (via email or backup token)",
+ "Simple administration panel",
+ "Good security measures for users and administrators"
+ ],
+ "keywords": [
+ "Rust",
+ "SQLite",
+ "OAuth2 / OIDC",
+ "TOTP",
+ "SMTP",
+ "Docker"
+ ],
+ "startDate": "2023-01",
+ "endDate": "2023-07",
+ "logo": {
+ "file": "/icons/ezidam.png",
+ "transparentBackground": true
+ },
+ "link": null
+ },
+ {
+ "name": "pass4thewin",
+ "description": "Password manager",
+ "startDate": "2020-11",
+ "endDate": "2021-01",
+ "presentation": [
+ "Port of passwordstore, the standard unix password manager on the Windows platform.",
+ "Warning! Unfinished command line application, may cause data corruption when using existing passwords."
+ ],
+ "highlights": [
+ "Creation of a store",
+ "List secrets",
+ "Decrypt secret",
+ "Insert or generate secrets",
+ "Edit existing secrets",
+ "Synchronisation with git",
+ "TOTP support"
+ ],
+ "keywords": [
+ "Windows",
+ "Rust",
+ "OpenPGP",
+ "libgit2"
+ ],
+ "link": {
+ "uri": "https://github.com/x4m3/pass4thewin",
+ "label": "Source code",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "NaviaRent",
+ "description": "Epitech Innovative Project",
+ "startDate": "2020-09",
+ "endDate": "2023-01",
+ "presentation": [
+ "A B2B platform helping rentals of standup paddle boards."
+ ],
+ "highlights": [
+ "DevOps of all software in the NaviaRent stack",
+ "Creation of the iOS application",
+ "Contributions to the Android application",
+ "Contributions to the backend server",
+ "Creation and contributions to the web client",
+ "Server administration, backups",
+ "Meetings managements spread across 3 timezones",
+ "Technical writing",
+ "Public presentations"
+ ],
+ "keywords": [
+ "NodeJS",
+ "Angular",
+ "Kotlin",
+ "SwiftUI",
+ "Docker",
+ "GitLab CI/CD",
+ "Raspberry Pi",
+ "ESP32"
+ ],
+ "logo": {
+ "file": "/icons/naviarent.png",
+ "transparentBackground": false
+ },
+ "image": {
+ "file": "/images/naviarent.jpg",
+ "position": "right"
+ },
+ "link": {
+ "uri": "https://naviarent.fr",
+ "label": "Website",
+ "notAvailable": true
+ }
+ },
+ {
+ "name": "epitok",
+ "description": "Presence system at Epitech",
+ "startDate": "2020-06",
+ "endDate": "2020-09",
+ "presentation": [
+ "A library and web client to simplify students presence at Epitech.",
+ "Students are handed a piece of paper with a 6 digits number (called a \"token\") to verify their presence at school events.",
+ "Teachers use epitok to scan student cards with QR codes on them instead of printing and handing tokens to students."
+ ],
+ "highlights": [
+ "Reverse engineering of a partially documented web API",
+ "Design, conception",
+ "User experience",
+ "Improvements based of usage of the application"
+ ],
+ "keywords": [
+ "Rust",
+ "HTML",
+ "Bootstrap",
+ "jQuery",
+ "Docker"
+ ],
+ "link": {
+ "uri": "https://github.com/x4m3/epitok",
+ "label": "Source code",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "epi.today",
+ "description": "Calendar for Epitech",
+ "startDate": "2019-11",
+ "endDate": "2019-02",
+ "presentation": [
+ "A viewer of the Epitech intranet calendar.",
+ "Students and teachers glance at their planning without the need to go on the school's intranet."
+ ],
+ "highlights": [
+ "Reverse engineering of a web api",
+ "Design a web page",
+ "Mobile UX",
+ "Deployment on a server"
+ ],
+ "keywords": [
+ "TypeScript",
+ "HTML",
+ "Bootstrap",
+ "Docker"
+ ],
+ "link": {
+ "uri": "https://github.com/x4m3/epi.today",
+ "label": "Source code",
+ "notAvailable": false
+ }
+ },
+ {
+ "name": "canvas.place",
+ "description": "Timelapse",
+ "startDate": "2017-04",
+ "endDate": "2020-01",
+ "presentation": [
+ "canvas.place is a shared place to express creativity.",
+ "People from all over the world share one single canvas to paint on.",
+ "I created and maintained a timelapse of the virtual canvas."
+ ],
+ "highlights": [],
+ "keywords": [
+ "FFmpeg",
+ "Shell scripting",
+ "nginx"
+ ],
+ "logo": {
+ "file": "/icons/canvas.png",
+ "transparentBackground": false
+ },
+ "image": {
+ "file": "/images/canvas.png",
+ "position": "left"
+ },
+ "link": {
+ "uri": "https://timelapse.canvas.place",
+ "label": "Website",
+ "notAvailable": false
+ }
+ }
+ ]
+}
diff --git a/crates/plcom/src/app.rs b/crates/plcom/src/app.rs
new file mode 100644
index 0000000..9adf2f4
--- /dev/null
+++ b/crates/plcom/src/app.rs
@@ -0,0 +1,71 @@
+use crate::{
+ error_template::{AppError, ErrorTemplate},
+ Link, UnderlineLink,pages::*
+};
+use leptos::*;
+use leptos_meta::*;
+use leptos_router::*;
+use tailwind_fuse::*;
+
+#[component]
+pub fn App() -> impl IntoView {
+ // Provides context that manages stylesheets, titles, meta tags, etc.
+ provide_meta_context();
+ let formatter = |text| format!("{text} — Philippe Loctaux");
+
+ view! {
+
+
+ // sets the document title
+
+
+
+
+ // favicon
+
+
+
+
+
+
+
+
+ // stats
+
+
+ // actual routes
+ }.into_view()
+ }>
+
+
+
+
+
+
+
+
+
+
+ }
+}
+
+
+
diff --git a/crates/plcom/src/common.rs b/crates/plcom/src/common.rs
new file mode 100644
index 0000000..b286fbb
--- /dev/null
+++ b/crates/plcom/src/common.rs
@@ -0,0 +1,137 @@
+pub mod icon;
+pub mod link;
+
+pub fn get_year() -> i32 {
+ use chrono::Datelike;
+ chrono::Utc::now().year()
+}
+
+#[derive(Clone, PartialEq)]
+pub struct Date {
+ pub year: u32,
+ pub month: u8,
+}
+
+impl std::fmt::Display for Date {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}-{:02}", self.year, self.month)
+ }
+}
+
+pub mod resume {
+ #[derive(Clone, Copy)]
+ pub struct Work {
+ pub name: &'static str,
+ pub position: &'static str,
+ pub start_date: &'static str,
+ pub end_date: Option<&'static str>,
+ pub logo: Logo,
+ pub description: &'static str,
+ pub highlights: &'static [&'static str],
+ pub technologies: &'static [&'static str],
+ pub link: Option,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Logo {
+ pub file: &'static str,
+ pub transparent_background: bool,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Project {
+ pub name: &'static str,
+ pub description: &'static str,
+ pub start_date: &'static str,
+ pub end_date: Option<&'static str>,
+ pub presentation: &'static [&'static str],
+ pub highlights: &'static [&'static str],
+ pub keywords: &'static [&'static str],
+ pub link: Option,
+ pub logo: Option,
+ pub image: Option,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Image {
+ pub file: &'static str,
+ pub position: &'static str,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ResumeLink {
+ pub uri: &'static str,
+ pub label: &'static str,
+ pub not_available: bool,
+ }
+
+ use crate::{Link, OutlineButtonLink};
+ use http::Uri;
+ use leptos::*;
+ use tailwind_fuse::tw_join;
+ impl IntoView for ResumeLink {
+ fn into_view(self) -> View {
+ if !self.not_available {
+ let link = Link {
+ label: self.label.into(),
+ uri: Uri::from_static(self.uri),
+ };
+ view! { }.into_view()
+ } else {
+ view! {
+ {self.label}
+ }
+ .into_view()
+ }
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Education {
+ pub institution: &'static str,
+ pub study_type: &'static str,
+ pub area: &'static str,
+ pub start_date: &'static str,
+ pub end_date: Option<&'static str>,
+ pub logo: Option,
+ pub courses: &'static [&'static str],
+ }
+
+ include!(concat!(env!("OUT_DIR"), "/resume.rs"));
+}
+
+pub mod wallpapers {
+
+ #[derive(Clone, Copy)]
+ pub struct Wallpaper {
+ pub filename: &'static str,
+ pub date: &'static str,
+ pub gps: Gps,
+ pub location: Location,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Gps {
+ pub latitude: f32,
+ pub longitude: f32,
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct Location {
+ pub precise: &'static str,
+ pub broad: &'static str,
+ }
+
+ impl Wallpaper {
+ pub fn random() -> Option<&'static Wallpaper> {
+ let random_value = rand::Rng::gen_range(&mut rand::thread_rng(), 0..WALLPAPERS.len());
+ WALLPAPERS.get(random_value)
+ }
+ }
+
+ include!(concat!(env!("OUT_DIR"), "/wallpapers.rs"));
+}
diff --git a/crates/plcom/src/common/icon.rs b/crates/plcom/src/common/icon.rs
new file mode 100644
index 0000000..58b52c3
--- /dev/null
+++ b/crates/plcom/src/common/icon.rs
@@ -0,0 +1,140 @@
+use leptos::*;
+use tailwind_fuse::tw_join;
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum Icon {
+ Email,
+ Link,
+ Calendar,
+ Location,
+ Twitter,
+ Telegram,
+ Mastodon,
+ Github,
+ Linkedin,
+ Map,
+}
+
+impl IntoView for Icon {
+ fn into_view(self) -> View {
+ match self {
+ Self::Email => view! {
+
+
+
+ }.into_view(),
+
+ Self::Link => view! {
+
+
+
+
+ }.into_view(),
+
+ Self::Calendar => view! {
+
+
+
+ }.into_view(),
+
+ Self::Location => view! {
+
+
+
+ }.into_view(),
+ Icon::Twitter => view! {
+
+
+
+ }.into_view(),
+ Icon::Telegram => view! {
+
+
+
+ }.into_view(),
+ Icon::Mastodon => view! {
+
+
+
+ }.into_view(),
+ Icon::Github => view! {
+
+
+
+ }.into_view(),
+ Icon::Linkedin => view! {
+
+
+
+ }.into_view(),
+ Icon::Map => view! {
+
+
+
+ }.into_view(),
+ }
+ }
+}
diff --git a/crates/plcom/src/common/link.rs b/crates/plcom/src/common/link.rs
new file mode 100644
index 0000000..a5ab605
--- /dev/null
+++ b/crates/plcom/src/common/link.rs
@@ -0,0 +1,99 @@
+use http::Uri;
+use leptos::*;
+use tailwind_fuse::tw_join;
+
+use crate::Icon;
+
+#[derive(Clone, PartialEq)]
+pub struct Link {
+ pub label: String,
+ pub uri: Uri,
+}
+
+impl Link {
+ pub fn new(uri: &'static str, label: impl Into) -> Self {
+ Self {
+ uri: Uri::from_static(uri),
+ label: label.into(),
+ }
+ }
+ pub fn slides(uri: &'static str) -> Self {
+ Self::new(uri, "Slides")
+ }
+}
+
+#[component]
+pub fn UnderlineLink(
+ #[prop(into)] link: MaybeSignal ,
+ #[prop(into, optional)] class: MaybeSignal,
+ #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>,
+) -> impl IntoView {
+ let class = tailwind_fuse::tw_merge!("underline", class.get());
+ view! {
+
+ {link.get().label}
+
+ }
+}
+
+type HideTextSmallDisplay = bool;
+
+#[component]
+pub fn ButtonLink(
+ #[prop(into)] link: MaybeSignal ,
+ #[prop(into, optional)] icon: Option>,
+ #[prop(into, optional)] hide_text_small_display: Option>,
+ #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>,
+) -> impl IntoView {
+ let text_css = hide_text_small_display
+ .and_then(|hide| {
+ if hide.get() {
+ Some(tw_join!("hidden", "sm:inline"))
+ } else {
+ None
+ }
+ })
+ .unwrap_or(tw_join!("ml-2", "sm:ml-0", "text-center"));
+
+ view! {
+
+
+ {icon}
+
+ {link.get().label}
+
+
+ }
+}
+
+#[component]
+pub fn OutlineButtonLink(
+ #[prop(into)] link: MaybeSignal ,
+ #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>,
+) -> impl IntoView {
+ view! {
+
+
+ {Icon::Link}
+
+ {link.get().label}
+
+
+ }
+}
+
diff --git a/crates/plcom/src/error_template.rs b/crates/plcom/src/error_template.rs
new file mode 100644
index 0000000..14bfc35
--- /dev/null
+++ b/crates/plcom/src/error_template.rs
@@ -0,0 +1,83 @@
+use crate::ContentPage;
+use http::status::StatusCode;
+use leptos::*;
+use thiserror::Error;
+
+#[derive(Clone, Debug, Error)]
+pub enum AppError {
+ #[error("Not Found")]
+ NotFound,
+}
+
+impl AppError {
+ pub fn status_code(&self) -> StatusCode {
+ match self {
+ AppError::NotFound => StatusCode::NOT_FOUND,
+ }
+ }
+
+ pub fn canonical_reason(&self) -> String {
+ match self {
+ AppError::NotFound => StatusCode::NOT_FOUND.to_string(),
+ }
+ }
+
+ pub fn description(&self) -> String {
+ match self {
+ AppError::NotFound => "This page could not be found.".to_string(),
+ }
+ }
+}
+
+// A basic function to display errors served by the error boundaries.
+// Feel free to do more complicated things here than just displaying the error.
+#[component]
+pub fn ErrorTemplate(
+ #[prop(optional)] outside_errors: Option,
+ #[prop(optional)] errors: Option>,
+) -> impl IntoView {
+ let errors = match outside_errors {
+ Some(e) => create_rw_signal(e),
+ None => match errors {
+ Some(e) => e,
+ None => panic!("No Errors found and we expected errors!"),
+ },
+ };
+ // Get Errors from Signal
+ let errors = errors.get_untracked();
+
+ // Downcast lets us take a type that implements `std::error::Error`
+ let errors: Vec = errors
+ .into_iter()
+ .filter_map(|(_k, v)| v.downcast_ref::().cloned())
+ .collect();
+ println!("Errors: {errors:#?}");
+
+ // Only the response code for the first error is actually sent from the server
+ // this may be customized by the specific application
+ #[cfg(feature = "ssr")]
+ {
+ use leptos_axum::ResponseOptions;
+ let response = use_context::();
+ if let Some(response) = response {
+ response.set_status(errors[0].status_code());
+ }
+ }
+
+ view! {
+
+ {error.1.description()}
+
+ }
+ }
+ />
+ }
+}
diff --git a/crates/plcom/src/fileserv.rs b/crates/plcom/src/fileserv.rs
new file mode 100644
index 0000000..e843576
--- /dev/null
+++ b/crates/plcom/src/fileserv.rs
@@ -0,0 +1,42 @@
+use axum::{
+ body::Body,
+ extract::State,
+ response::IntoResponse,
+ http::{Request, Response, StatusCode, Uri},
+};
+use axum::response::Response as AxumResponse;
+use tower::ServiceExt;
+use tower_http::services::ServeDir;
+use leptos::*;
+use crate::app::App;
+
+pub async fn file_and_error_handler(uri: Uri, State(options): State, req: Request) -> AxumResponse {
+ let root = options.site_root.clone();
+ let res = get_static_file(uri.clone(), &root).await.unwrap();
+
+ if res.status() == StatusCode::OK {
+ res.into_response()
+ } else {
+ let handler = leptos_axum::render_app_to_stream(options.to_owned(), App);
+ handler(req).await.into_response()
+ }
+}
+
+async fn get_static_file(
+ uri: Uri,
+ root: &str,
+) -> Result, (StatusCode, String)> {
+ let req = Request::builder()
+ .uri(uri.clone())
+ .body(Body::empty())
+ .unwrap();
+ // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
+ // This path is relative to the cargo root
+ match ServeDir::new(root).oneshot(req).await {
+ Ok(res) => Ok(res.into_response()),
+ Err(err) => Err((
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("Something went wrong: {err}"),
+ )),
+ }
+}
diff --git a/crates/plcom/src/lib.rs b/crates/plcom/src/lib.rs
new file mode 100644
index 0000000..cd69f0b
--- /dev/null
+++ b/crates/plcom/src/lib.rs
@@ -0,0 +1,45 @@
+use leptos::*;
+use prelude::*;
+
+pub mod app;
+pub mod common;
+pub mod error_template;
+#[cfg(feature = "ssr")]
+pub mod fileserv;
+pub mod pages;
+
+#[cfg(feature = "hydrate")]
+#[wasm_bindgen::prelude::wasm_bindgen]
+pub fn hydrate() {
+ use crate::app::*;
+ console_error_panic_hook::set_once();
+ mount_to_body(App);
+}
+
+pub mod prelude {
+ pub use super::ContentPage;
+ pub use crate::common::icon::*;
+ pub use crate::common::link::*;
+ pub use crate::common::resume::*;
+ pub use crate::common::wallpapers::*;
+ pub use crate::common::*;
+ pub use leptos::*;
+ pub use leptos_meta::*;
+ pub use tailwind_fuse::tw_join;
+ pub use http::Uri;
+}
+
+#[component]
+pub fn ContentPage(
+ #[prop(into, optional)] title: MaybeSignal,
+ children: Children,
+) -> impl IntoView {
+ view! {
+
+
+
{title}
+
+
{children()}
+
+ }
+}
diff --git a/crates/plcom/src/main.rs b/crates/plcom/src/main.rs
new file mode 100644
index 0000000..d084ee4
--- /dev/null
+++ b/crates/plcom/src/main.rs
@@ -0,0 +1,38 @@
+#[cfg(feature = "ssr")]
+#[tokio::main]
+async fn main() {
+ use axum::Router;
+ use leptos::*;
+ use leptos_axum::{generate_route_list, LeptosRoutes};
+ use plcom::app::*;
+ use plcom::fileserv::file_and_error_handler;
+
+ // Setting get_configuration(None) means we'll be using cargo-leptos's env values
+ // For deployment these variables are:
+ //
+ // Alternately a file can be specified such as Some("Cargo.toml")
+ // The file would need to be included with the executable when moved to deployment
+ let conf = get_configuration(None).await.unwrap();
+ let leptos_options = conf.leptos_options;
+ let addr = leptos_options.site_addr;
+ let routes = generate_route_list(App);
+
+ // build our application with a route
+ let app = Router::new()
+ .leptos_routes(&leptos_options, routes, App)
+ .fallback(file_and_error_handler)
+ .with_state(leptos_options);
+
+ let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
+ logging::log!("listening on http://{}", &addr);
+ axum::serve(listener, app.into_make_service())
+ .await
+ .unwrap();
+}
+
+#[cfg(not(feature = "ssr"))]
+pub fn main() {
+ // no client-side main function
+ // unless we want this to work with e.g., Trunk for a purely client-side app
+ // see lib.rs for hydration function instead
+}
diff --git a/crates/plcom/src/pages.rs b/crates/plcom/src/pages.rs
new file mode 100644
index 0000000..94a291f
--- /dev/null
+++ b/crates/plcom/src/pages.rs
@@ -0,0 +1,7 @@
+pub mod root;
+pub mod email;
+pub mod wallpapers;
+
+pub use root::RootPage;
+pub use email::EmailPage;
+pub use wallpapers::WallpapersPage;
diff --git a/crates/plcom/src/pages/email.rs b/crates/plcom/src/pages/email.rs
new file mode 100644
index 0000000..1c65814
--- /dev/null
+++ b/crates/plcom/src/pages/email.rs
@@ -0,0 +1,37 @@
+use crate::prelude::*;
+
+#[component]
+pub fn EmailPage() -> impl IntoView {
+ view! {
+
+
+ "Send an email if you want to work with me, propose a project idea, or just to say hi!"
+
+
+
+
+
+
+ "If you want to encrypt your message, I have a "
+
+ " at your disposal."
+
+
+ "I also have a "
+ " account, but I do not check it often."
+
+
+ }
+}
+
diff --git a/crates/plcom/src/pages/root.rs b/crates/plcom/src/pages/root.rs
new file mode 100644
index 0000000..48d008e
--- /dev/null
+++ b/crates/plcom/src/pages/root.rs
@@ -0,0 +1,76 @@
+use crate::prelude::*;
+
+mod hero;
+mod www;
+mod experience;
+mod jobs;
+mod projects;
+mod education;
+mod talks;
+mod friends;
+
+#[component]
+pub fn RootPage() -> impl IntoView {
+ let random_wallpaper = Wallpaper::random();
+
+ view! {
+
+
+
+
+ }
+}
+
+#[component]
+fn Whoami() -> impl IntoView {
+ view! {
+
+
+
+
+
+
+
+
"About Phil"
+
"Developer of all sorts"
+
+
+
+ "I got into computer science by learning about the Linux kernel and administrating servers."
+
+
+ "After high school, I became a student at Epitech and learned to tackle technical concepts and apply them quickly by working on small projects."
+
+
+ "During my studies at Epitech, I had the opportunity to be a teacher. My role was to assist students with technical problems in their projects."
+
+
+ "Now I have experience in software engineering, full-stack web and mobile development, system administration and CI/CD, as well as embedded development."
+
+
+ "My goal is to use my knowledge and experience to make software helping its users accomplish their needs."
+
+
+
+
+ }
+}
diff --git a/crates/plcom/src/pages/root/education.rs b/crates/plcom/src/pages/root/education.rs
new file mode 100644
index 0000000..223b80b
--- /dev/null
+++ b/crates/plcom/src/pages/root/education.rs
@@ -0,0 +1,51 @@
+use crate::prelude::*;
+use super::experience::*;
+
+impl IntoView for Education {
+ fn into_view(self) -> View {
+ let subtitle = format!("{} in {}", self.study_type, self.area);
+ view! {
+
+
+ {ExperienceHeader::new(
+ self.start_date,
+ self.end_date,
+ self.institution,
+ &subtitle,
+ self.logo.as_ref(),
+ )}
+
+
+ {self
+ .courses
+ .iter()
+ .map(|h| {
+ view! { {*h} }
+ })
+ .collect_view()}
+
+
+
+
+ }.into_view()
+ }
+}
+
+#[component]
+pub fn EducationList() -> impl IntoView {
+ view! {
+
+
+
"Education"
+
+
{resume::EDUCATION.collect_view()}
+
+ }
+}
+
diff --git a/crates/plcom/src/pages/root/experience.rs b/crates/plcom/src/pages/root/experience.rs
new file mode 100644
index 0000000..5b76f83
--- /dev/null
+++ b/crates/plcom/src/pages/root/experience.rs
@@ -0,0 +1,134 @@
+use tailwind_fuse::*;
+use leptos::*;
+use crate::common::Date;
+use crate::common::resume::Logo;
+
+#[derive(TwClass, Clone, Copy, PartialEq)]
+#[tw(class = r#"h-16 w-16 rounded-xl"#)]
+struct LogoOptions {
+ background: ImageBackground,
+}
+
+#[derive(TwVariant, PartialEq)]
+enum ImageBackground {
+ #[tw(class = "p-2 bg-white")]
+ Transparent,
+ #[tw(default, class = "")]
+ Plain,
+}
+
+#[component]
+fn ExperienceLogo(
+ #[prop(into)] image: MaybeSignal,
+ /// Name of the experience, used in the alt of the image
+ #[prop(into)]
+ name: MaybeSignal,
+ #[prop(into, optional)] background: MaybeSignal,
+ #[prop(into, optional)] class: MaybeSignal,
+ #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>,
+) -> impl IntoView {
+ let class = create_memo(move |_| {
+ let background = background.get();
+ let logo = LogoOptions { background };
+ logo.with_class(class.get())
+ });
+ let alt = format!("{} logo", name.get());
+
+ view! { }
+}
+
+struct ExperienceLogo {
+ file: String,
+ options: Option,
+}
+
+pub struct ExperienceHeader {
+ name: String,
+ description: String,
+ date_start: Date,
+ date_end: Option,
+ logo: Option,
+}
+
+impl IntoView for ExperienceHeader {
+ fn into_view(self) -> View {
+ let logo = match self.logo {
+ Some(logo) => view! {
+
+ }
+ .into_view(),
+ None => view! {}.into_view(),
+ };
+
+ let date = match self.date_end {
+ Some(end) => format!("{} - {}", self.date_start, end),
+ None => format!("Since {}", self.date_start),
+ };
+
+ view! {
+
+ }
+ .into_view()
+ }
+}
+
+impl ExperienceHeader {
+ pub fn new(
+ start_date: &str,
+ end_date: Option<&str>,
+ name: &str,
+ description: &str,
+ logo: Option<&Logo>,
+ ) -> Self {
+ let date_start: Vec<_> = start_date.split('-').collect();
+
+ let date_end = end_date.map(|end| {
+ let end: Vec<_> = end.split('-').collect();
+ Date {
+ year: end[0].parse().expect("not a number"),
+ month: end[1].parse().expect("not a number"),
+ }
+ });
+
+ let logo = logo.map(|logo| ExperienceLogo {
+ file: logo.file.into(),
+ options: if logo.transparent_background {
+ Some(LogoOptions {
+ background: ImageBackground::Transparent,
+ })
+ } else {
+ None
+ },
+ });
+
+ Self {
+ name: name.into(),
+ description: description.into(),
+ date_start: Date {
+ year: date_start[0].parse().expect("not a number"),
+ month: date_start[1].parse().expect("not a number"),
+ },
+ date_end,
+ logo,
+ }
+ }
+}
diff --git a/crates/plcom/src/pages/root/friends.rs b/crates/plcom/src/pages/root/friends.rs
new file mode 100644
index 0000000..d034ec9
--- /dev/null
+++ b/crates/plcom/src/pages/root/friends.rs
@@ -0,0 +1,145 @@
+use crate::prelude::*;
+
+#[derive(Clone, PartialEq)]
+struct Name {
+ first: String,
+ last: Option,
+}
+
+impl Name {
+ pub fn nick(nick: impl Into) -> Self {
+ Self {
+ first: nick.into(),
+ last: None,
+ }
+ }
+ pub fn new(first: impl Into, last: impl Into) -> Self {
+ Self {
+ first: first.into(),
+ last: Some(last.into()),
+ }
+ }
+
+ pub fn initials(&self) -> String {
+ let first = self
+ .first
+ .to_uppercase()
+ .chars()
+ .next()
+ .expect("Invalid first name");
+ let last = self
+ .last
+ .as_ref()
+ .and_then(|last| last.to_uppercase().chars().next());
+
+ match last {
+ Some(last) => format!("{first}{last}"),
+ None => first.into(),
+ }
+ }
+}
+
+impl std::fmt::Display for Name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match &self.last {
+ Some(last) => write!(f, "{} {}", self.first, last),
+ None => write!(f, "{}", self.first),
+ }
+ }
+}
+
+#[derive(Clone, PartialEq)]
+struct Friend {
+ name: Name,
+ uri: Uri,
+}
+
+impl Friend {
+ pub fn nick(nick: impl Into, uri: &'static str) -> Self {
+ Self {
+ name: Name::nick(nick),
+ uri: Uri::from_static(uri),
+ }
+ }
+
+ pub fn new(first: impl Into, last: impl Into, uri: &'static str) -> Self {
+ Self {
+ name: Name::new(first, last),
+ uri: Uri::from_static(uri),
+ }
+ }
+
+ pub fn domain_name(&self) -> String {
+ self.uri
+ .authority()
+ .map(|authority| authority.to_string())
+ .unwrap_or_else(|| self.uri.to_string())
+ }
+}
+
+#[component]
+fn Friend(#[prop(into)] friend: MaybeSignal) -> impl IntoView {
+ view! {
+
+
+ {friend.get().name.initials()}
+
+
{friend.get().name.to_string()}
+
{friend.get().domain_name()}
+
+
+ }
+}
+
+#[component]
+pub fn Friends() -> impl IntoView {
+ let friends = [
+ Friend::new("Paolo", "Rotolo", "https://rotolo.dev"),
+ Friend::new("Polly", "Bishop", "https://github.com/itspolly"),
+ Friend::new("Ayden", "Panhuyzen", "https://ayden.dev"),
+ Friend::new("Corbin", "Crutchley", "https://crutchcorn.dev"),
+ Friend::new("James", "Fenn", "https://jfenn.me"),
+ Friend::new("Alex", "Dueppen", "https://ajd.sh"),
+ Friend::new("Lyra", "Messier", "https://lyramsr.co"),
+ Friend::new("Peter", "Soboyejo", "https://twitter.com/pxtvr"),
+ Friend::nick("Millomaker", "https://youtube.com/millomaker"),
+ Friend::new("Alexandre", "Wagner", "https://wagnerwave.com"),
+ Friend::new("Aidan", "Follestad", "https://af.codes"),
+ Friend::new("Victor", "Simon", "https://simonvictor.com"),
+ ];
+
+ view! {
+
+
"Friends"
+
"Folks I worked with, or I like what they do."
+
+
+ {friends
+ .into_iter()
+ .map(|f| {
+ view! {
+
+
+
+ }
+ })
+ .collect_view()}
+
+
+
"If you do not appear here and we know each other, hit me up!"
+
+ }
+}
diff --git a/crates/plcom/src/pages/root/hero.rs b/crates/plcom/src/pages/root/hero.rs
new file mode 100644
index 0000000..93b2b91
--- /dev/null
+++ b/crates/plcom/src/pages/root/hero.rs
@@ -0,0 +1,100 @@
+use crate::prelude::*;
+
+#[component]
+fn WallpaperInfo(#[prop(into)] wallpaper: &'static Wallpaper) -> impl IntoView {
+ view! {
+
+
+ // See more
+
+
+ // Location
+
+
+ {Icon::Location}
+
+ {wallpaper.location.precise}
+ ", "{wallpaper.location.broad}
+
+
+
+
+ // Date
+
+
+ {Icon::Calendar} {wallpaper.date}
+
+
+
+
+ }
+}
+
+#[component]
+pub fn Hero(#[prop(into)] wallpaper: Option<&'static Wallpaper>) -> impl IntoView {
+ let (wallpaper_info, background_image) = match wallpaper {
+ Some(wallpaper) => (
+ view! { }.into_view(),
+ format!("background-image: url(/wallpapers/{});", wallpaper.filename),
+ ),
+ None => (view! {}.into_view(), "".into()),
+ };
+
+ view! {
+
+
+
+
+
+
"Philippe Loctaux"
+ "Developer of all sorts. Epitech alumni, class of 2023."
+
+
+
+ {wallpaper_info}
+
+
+ }
+}
diff --git a/crates/plcom/src/pages/root/jobs.rs b/crates/plcom/src/pages/root/jobs.rs
new file mode 100644
index 0000000..2275b40
--- /dev/null
+++ b/crates/plcom/src/pages/root/jobs.rs
@@ -0,0 +1,76 @@
+use crate::prelude::*;
+use super::experience::*;
+
+impl IntoView for Work {
+ fn into_view(self) -> View {
+ view! {
+
+
+
+
+ {ExperienceHeader::new(
+ self.start_date,
+ self.end_date,
+ self.name,
+ self.position,
+ Some(&self.logo),
+ )}
+
+
{self.description}
+
+
+
+ {self
+ .highlights
+ .iter()
+ .map(|h| {
+ view! { {*h} }
+ })
+ .collect_view()}
+
+
+
+
+
+ {self
+ .technologies
+ .iter()
+ .map(|t| {
+ view! {
+ {*t}
+ }
+ })
+ .collect_view()}
+
+
+
+
{self.link}
+
+
+
+ }.into_view()
+ }
+}
+
+#[component]
+pub fn Jobs() -> impl IntoView {
+ view! {
+
+
"Professional Experiences"
+
+
{resume::WORK.collect_view()}
+
+ }
+}
+
diff --git a/crates/plcom/src/pages/root/projects.rs b/crates/plcom/src/pages/root/projects.rs
new file mode 100644
index 0000000..1bcc3fe
--- /dev/null
+++ b/crates/plcom/src/pages/root/projects.rs
@@ -0,0 +1,163 @@
+use crate::prelude::*;
+use super::experience::*;
+
+impl IntoView for Project {
+ fn into_view(self) -> View {
+ let (css_image_position, css_image_position_corner) = match self.image {
+ Some(image) => {
+ let position = match image.position {
+ "left" => tw_join!("2xl:flex items-stretch justify-between"),
+ "right" => {
+ tw_join!("2xl:flex items-stretch justify-between 2xl:flex-row-reverse")
+ }
+ _ => todo!("match on an enum instead of raw strings"),
+ };
+
+ let corner = match image.position {
+ "left" => tw_join!("2xl:rounded-tr-none", "2xl:rounded-l-2xl"),
+ "right" => tw_join!("2xl:rounded-tl-none", "2xl:rounded-r-2xl"),
+ _ => todo!("match on an enum instead of raw strings"),
+ };
+ let corner = tw_join!(
+ "flex",
+ "w-full",
+ "2xl:w-1/2",
+ "grow",
+ "rounded-t-2xl",
+ "object-cover",
+ corner
+ );
+
+ (position, corner)
+ }
+ None => ("".into(), "".into()),
+ };
+
+ view! {
+
+
+ {if let Some(image) = self.image {
+ view! {
+
+ }
+ .into_view()
+ } else {
+ view! {}.into_view()
+ }}
+
+
+ {ExperienceHeader::new(
+ self.start_date,
+ self.end_date,
+ self.name,
+ self.description,
+ self.logo.as_ref(),
+ )}
+
+
+ {self
+ .presentation
+ .iter()
+ .map(|p| {
+ view! {
{*p}
}
+ })
+ .collect_view()}
+
+ {self
+ .highlights
+ .iter()
+ .map(|h| {
+ view! { {*h} }
+ })
+ .collect_view()}
+
+
+
+
+ {self
+ .keywords
+ .iter()
+ .map(|t| {
+ view! {
+ {*t}
+ }
+ })
+ .collect_view()}
+
+
+
+
{self.link}
+
+
+
+ }.into_view()
+ }
+}
+
+ type ImageProject = Project;
+ type TextProject = Project;
+
+ enum DisplayProject {
+ Text(Box<(TextProject, Option)>),
+ Image(ImageProject)
+ }
+
+impl IntoView for DisplayProject {
+ fn into_view(self) -> View {
+ match self {
+ Self::Image(image) => image.into_view(),
+ Self::Text(boxy) => {
+ let (text1, text2) = *boxy;
+ view! {
+ {text1} {text2}
+ }.into_view()
+
+ }
+ }
+ }
+}
+
+#[component]
+pub fn Projects() -> impl IntoView {
+ let mut projects = vec![];
+ let mut iter = resume::PROJECTS.iter();
+
+ while let Some(cur_proj) = iter.next() {
+ if cur_proj.image.is_some() {
+ projects.push(DisplayProject::Image(*cur_proj));
+ } else {
+ let next_text = iter.next();
+ projects.push(DisplayProject::Text(Box::new((*cur_proj, next_text.copied()))));
+ }
+ }
+
+ view! {
+
+
+
"Projects"
+
+ {projects}
+
+ }
+}
diff --git a/crates/plcom/src/pages/root/talks.rs b/crates/plcom/src/pages/root/talks.rs
new file mode 100644
index 0000000..25d845c
--- /dev/null
+++ b/crates/plcom/src/pages/root/talks.rs
@@ -0,0 +1,137 @@
+use crate::prelude::*;
+
+#[derive(Clone, PartialEq)]
+struct Talk {
+ title: String,
+ date: Date,
+ location: String,
+ link: Link,
+}
+
+impl Talk {
+ pub fn new(
+ title: impl Into,
+ date: Date,
+ location: impl Into,
+ link: Link,
+ ) -> Self {
+ Self {
+ title: title.into(),
+ date,
+ location: location.into(),
+ link,
+ }
+ }
+}
+
+#[component]
+fn Talk(#[prop(into)] talk: MaybeSignal) -> impl IntoView {
+ view! {
+
+
+
{talk.get().title}
+
+
+
+ {Icon::Calendar}
+ {talk.get().date.to_string()}
+
+
+
+
+
{Icon::Location} {talk.get().location}
+
+
+
+
+ }
+}
+
+#[component]
+pub fn Talks() -> impl IntoView {
+ let talks = [
+ Talk::new(
+ "Vim",
+ Date {
+ year: 2023,
+ month: 2,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/vim.pdf"),
+ ),
+ Talk::new(
+ "CLion",
+ Date {
+ year: 2021,
+ month: 3,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/clion.pdf"),
+ ),
+ Talk::new(
+ "git & devops 2",
+ Date {
+ year: 2021,
+ month: 2,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/git-devops2.pdf"),
+ ),
+ Talk::new(
+ "pass4thewin",
+ Date {
+ year: 2021,
+ month: 2,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/pass4thewin.pdf"),
+ ),
+ Talk::new(
+ "git & devops",
+ Date {
+ year: 2020,
+ month: 5,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/git-devops.pdf"),
+ ),
+ Talk::new(
+ "git gud",
+ Date {
+ year: 2019,
+ month: 5,
+ },
+ "Epitech Rennes",
+ Link::slides("/pub/talks/git-tek.pdf"),
+ ),
+ ];
+
+ view! {
+
+
"Talks"
+
+ "Giving a talk is the opportunity to share what I know, and helps me reduce my fear of public speaking."
+
+
+
+ {talks
+ .into_iter()
+ .map(|t| {
+ view! { }
+ })
+ .collect_view()}
+
+
+
+ }
+}
+
diff --git a/crates/plcom/src/pages/root/www.rs b/crates/plcom/src/pages/root/www.rs
new file mode 100644
index 0000000..325b4b4
--- /dev/null
+++ b/crates/plcom/src/pages/root/www.rs
@@ -0,0 +1,65 @@
+use crate::prelude::*;
+
+#[derive(Clone, PartialEq)]
+struct Www {
+ link: Link,
+ icon: Icon,
+}
+
+#[component]
+pub fn Www() -> impl IntoView {
+ let www = [
+ Www {
+ link: Link::new("https://twitter.com/philippeloctaux", "Twitter"),
+ icon: Icon::Twitter,
+ },
+ Www {
+ link: Link::new("https://t.me/philippeloctaux", "Telegram"),
+ icon: Icon::Telegram,
+ },
+ Www {
+ link: Link::new("https://mastodon.social/@philt3r", "Mastodon"),
+ icon: Icon::Mastodon,
+ },
+ Www {
+ link: Link::new("https://github.com/x4m3", "GitHub"),
+ icon: Icon::Github,
+ },
+ Www {
+ link: Link::new("https://linkedin.com/in/philippeloctaux", "LinkedIn"),
+ icon: Icon::Linkedin,
+ },
+ Www {
+ link: Link::new("/email", "Email"),
+ icon: Icon::Email,
+ },
+ ];
+ view! {
+
+
+ {www
+ .into_iter()
+ .map(|w| {
+ view! {
+
+ }
+ })
+ .collect_view()}
+
+
+ }
+}
diff --git a/crates/plcom/src/pages/wallpapers.rs b/crates/plcom/src/pages/wallpapers.rs
new file mode 100644
index 0000000..c5dba14
--- /dev/null
+++ b/crates/plcom/src/pages/wallpapers.rs
@@ -0,0 +1,49 @@
+use crate::prelude::*;
+
+#[component]
+pub fn WallpapersPage() -> impl IntoView {
+ use leptos_leaflet::{TileLayer, MapContainer, Position, Marker, position, Popup};
+
+ let wallpapers = WALLPAPERS;
+
+ view! {
+
+
+
+ "Pictures I took around the world"
+
+ OpenStreetMap contributors"
+ />
+
+
+
+ {w.location.precise}
+
+
+ {w.location.broad}
+
+ {w.date}
+
+
+ }
+ }
+ />
+
+
+
+ }
+}
diff --git a/crates/plcom/tailwind.config.js b/crates/plcom/tailwind.config.js
new file mode 100644
index 0000000..f3e50e1
--- /dev/null
+++ b/crates/plcom/tailwind.config.js
@@ -0,0 +1,16 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: {
+ relative: true,
+ files: ["./src/**/*.rs"],
+ },
+ theme: {
+ extend: {
+ height: {
+ almostscreen: "90vh",
+ halfscreen: "60vh",
+ }
+ },
+ },
+ plugins: [],
+ }
diff --git a/css/tailwind.css b/css/tailwind.css
deleted file mode 100644
index bd6213e..0000000
--- a/css/tailwind.css
+++ /dev/null
@@ -1,3 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
\ No newline at end of file
diff --git a/public/icons/rubycat.png b/public/icons/rubycat.png
new file mode 100644
index 0000000000000000000000000000000000000000..f234b17dccd564f5f8891b8b78b79980460cd6bc
GIT binary patch
literal 507543
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