diff --git a/.dockerignore b/.dockerignore index adcf52b..d1f2474 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,11 +1,10 @@ -/public/style.css +target/ +wallpapers.json -/target/ - -/.idea/ -/.vscode/ +.idea/ +.vscode/ .DS_Store .gitea/ -/readme.md -/Dockerfile -/.dockerignore \ No newline at end of file +readme.md +Dockerfile +.dockerignore 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/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index ea9a879..5eec730 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,5 +1,8 @@ name: Build -on: [push] +on: + push: + branches: + - master jobs: build-docker: 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 4abe9e6..5abd17b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,37 +18,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "aho-corasick" -version = "0.7.20" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "aho-corasick" -version = "1.1.2" +name = "allocator-api2" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -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" @@ -65,6 +59,60 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "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" @@ -75,138 +123,124 @@ dependencies = [ ] [[package]] -name = "askama" -version = "0.12.1" -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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a0fc7dcf8bd4ead96b1d36b41df47c14beedf7b0301fc543d8f2384e66a2ec0" -dependencies = [ - "askama_parser", - "basic-toml", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "serde", - "syn 2.0.39", -] - -[[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.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c268a96e01a4c47c8c5c2472aaa570707e006a875ea63e819f75474ceedaf7b4" -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 = "assert_approx_eq" +name = "async-recursion" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd" - -[[package]] -name = "async-compression" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" -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", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[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]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +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.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -219,24 +253,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "basic-toml" -version = "0.1.7" +name = "base64" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" -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" @@ -246,9 +271,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -260,53 +285,61 @@ dependencies = [ "radium", ] -[[package]] -name = "brotli" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" -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.14.0" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "bytemuck" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +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.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -316,40 +349,155 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "clap_lex" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "convert_case", + "lazy_static", + "nom", + "pathdiff", + "serde", + "toml", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "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.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "percent-encoding", - "time", - "version_check", + "unicode-segmentation", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -357,106 +505,135 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] -name = "crc32fast" -version = "1.3.2" +name = "crunchy" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +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.9" +name = "darling_core" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +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 = [ - "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.4.1", + "fnv", + "ident_case", "proc-macro2", - "proc-macro2-diagnostics", "quote", - "syn 2.0.39", + "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 = [ + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "dms-coordinates" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec6b438cc34e673063a715ff761392e195278759fa5d882d6bc6b78e2e30d90d" +checksum = "7ba8a84e64498d7401299d1f3134c2a398c83b6e3c5223f061d9c5643b92e6f1" dependencies = [ "geo-types", - "gpx", - "initial_conditions", "map_3d", + "num-integer", + "regex", "rust-3d", - "serde", - "serde_derive", "thiserror", ] [[package]] -name = "either" -version = "1.9.0" +name = "drain_filter_polyfill" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" @@ -475,53 +652,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "backtrace", - "version_check", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[package]] -name = "figment" -version = "0.10.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649f3e5d826594057e9a519626304d8da859ea8a0b18ce99500c586b8d45faee" -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", -] +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fnv" @@ -553,17 +696,11 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -576,9 +713,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -586,15 +723,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -603,38 +740,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -649,23 +786,22 @@ dependencies = [ ] [[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +name = "gen-wallpapers" +version = "0.1.0" dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", + "clap", + "dms-coordinates", + "kamadak-exif", + "reqwest", + "serde", + "serde_json", ] [[package]] name = "geo-types" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567495020b114f1ce9bed679b29975aa0bfae06ac22beacd5cfde5dabe7b05d6" +checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61" dependencies = [ "approx", "num-traits", @@ -674,13 +810,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -690,38 +828,52 @@ 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 = "gpx" -version = "0.8.6" +name = "gloo-utils" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b03599b85866c88fd0125db7ca7a683be1550724918682c736c7893a399dc5e" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" dependencies = [ - "assert_approx_eq", - "error-chain", - "geo-types", - "thiserror", - "time", - "xml-rs", + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", ] [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap", "slab", "tokio", "tokio-util", @@ -729,28 +881,68 @@ 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" [[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" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +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.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -759,15 +951,44 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +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" @@ -780,39 +1001,49 @@ 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.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", "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" @@ -820,17 +1051,33 @@ 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 = "iana-time-zone" -version = "0.1.58" +name = "hyper-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -849,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" @@ -861,39 +1114,34 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", - "serde", ] [[package]] -name = "initial_conditions" -version = "0.4.0" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ddf16659f435e5c98d948694a97c33b6660c35adad199ab10362092a45ce921" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "num 0.1.42", + "cfg-if", ] [[package]] -name = "inlinable_string" -version = "0.1.15" +name = "interpolator" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "ipnet" @@ -902,27 +1150,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] -name = "is-terminal" -version = "0.4.9" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", + "either", ] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -943,10 +1189,272 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.150" +name = "leaflet" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -955,10 +1463,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "linux-raw-sys" -version = "0.4.11" +name = "linear-map" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +dependencies = [ + "serde", + "serde_test", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -972,23 +1490,40 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +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]] @@ -998,19 +1533,16 @@ 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" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -1028,30 +1560,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minify-html" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4d9147754a49e80557df835eb59e743eab1bf75410a134f55dc4b9dbb692ad" -dependencies = [ - "aho-corasick 0.7.20", - "css-minify", - "lazy_static", - "memchr", - "minify-js", - "rustc-hash", -] - -[[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", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1060,41 +1568,39 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[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", ] @@ -1104,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" @@ -1138,56 +1638,20 @@ 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.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -dependencies = [ - "num-bigint 0.1.44", - "num-complex 0.1.43", - "num-integer", - "num-iter", - "num-rational 0.1.42", - "num-traits", -] - [[package]] name = "num" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ - "num-bigint 0.2.6", - "num-complex 0.2.4", + "num-bigint", + "num-complex", "num-integer", "num-iter", - "num-rational 0.2.4", + "num-rational", "num-traits", ] -[[package]] -name = "num-bigint" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.4.6", - "rustc-serialize", -] - [[package]] name = "num-bigint" version = "0.2.6" @@ -1199,16 +1663,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-complex" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" -dependencies = [ - "num-traits", - "rustc-serialize", -] - [[package]] name = "num-complex" version = "0.2.4" @@ -1221,37 +1675,24 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", "num-traits", ] -[[package]] -name = "num-rational" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" -dependencies = [ - "num-bigint 0.1.44", - "num-integer", - "num-traits", - "rustc-serialize", -] - [[package]] name = "num-rational" version = "0.2.4" @@ -1259,16 +1700,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg", - "num-bigint 0.2.6", + "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -1286,26 +1727,26 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -1322,7 +1763,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] @@ -1333,9 +1774,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -1344,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" @@ -1369,42 +1810,20 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[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 = "pear" -version = "0.2.7" +name = "pathdiff" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.39", -] +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" @@ -1413,10 +1832,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "pin-project-lite" -version = "0.2.13" +name = "pin-project" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1426,33 +1865,37 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plcom" version = "0.1.0" dependencies = [ - "askama", - "askama_rocket", + "axum", "chrono", - "dms-coordinates", - "kamadak-exif", - "minify-html", - "nanorand", - "reqwest", - "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" @@ -1460,10 +1903,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "proc-macro2" -version = "1.0.70" +name = "prettyplease" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -1476,39 +1963,49 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", "version_check", "yansi", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1517,7 +2014,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1527,24 +2024,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -1554,15 +2036,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1572,85 +2045,50 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "ref-cast" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ - "aho-corasick 1.1.2", + "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick 1.1.2", + "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +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", @@ -1660,9 +2098,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -1675,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.1.0", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand 0.8.5", - "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.1.0", "proc-macro2", + "proc-macro2-diagnostics", "quote", - "rocket_http", - "syn 2.0.39", - "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.1.0", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", + "syn 2.0.58", + "syn_derive", + "thiserror", ] [[package]] @@ -1776,7 +2136,7 @@ checksum = "6ce1e81ec1eec57188d13fdeb9fcdf961914eb0f57e3b40c723be3b9245de9bb" dependencies = [ "bitvec", "fnv", - "num 0.2.1", + "num", ] [[package]] @@ -1791,32 +2151,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" - -[[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.25" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", ] [[package]] @@ -1827,24 +2181,27 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] -name = "schannel" -version = "0.1.22" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "windows-sys", + "winapi-util", ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "schannel" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] [[package]] name = "scopeguard" @@ -1854,9 +2211,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -1867,45 +2224,65 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", ] [[package]] -name = "semver" -version = "1.0.20" +name = "self_cell" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +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" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.193" +name = "serde-wasm-bindgen" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -1913,10 +2290,40 @@ dependencies = [ ] [[package]] -name = "serde_spanned" -version = "0.6.4" +name = "serde_path_to_error" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_test" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" dependencies = [ "serde", ] @@ -1934,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]] @@ -1961,29 +2409,29 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.11.2" +name = "slotmap" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ - "libc", - "winapi", + "serde", + "version_check", ] [[package]] -name = "socket2" -version = "0.5.5" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1993,22 +2441,16 @@ 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", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "state" -version = "0.6.0" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -2023,15 +2465,39 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", "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" @@ -2054,75 +2520,57 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.8.1" +name = "tailwind_fuse" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "itoa", - "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.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" -dependencies = [ - "time-core", + "syn 2.0.58", ] [[package]] @@ -2142,9 +2590,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -2152,10 +2600,9 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2166,7 +2613,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] @@ -2179,17 +2626,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.10" @@ -2199,6 +2635,8 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", + "hashbrown 0.14.3", "pin-project-lite", "tokio", "tracing", @@ -2206,9 +2644,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -2227,17 +2665,64 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ - "indexmap 2.1.0", + "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" @@ -2250,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", @@ -2263,7 +2749,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", ] [[package]] @@ -2273,61 +2759,32 @@ 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]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +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.9" +name = "typed-builder-macro" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" dependencies = [ - "serde", - "version_check", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -2341,9 +2798,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2353,13 +2810,19 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 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" @@ -2378,10 +2841,25 @@ dependencies = [ ] [[package]] -name = "valuable" -version = "0.1.0" +name = "utf8-width" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", +] [[package]] name = "vcpkg" @@ -2395,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" @@ -2412,9 +2900,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2422,24 +2910,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -2449,9 +2937,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2459,28 +2947,41 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +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.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -2502,28 +3003,28 @@ 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", -] - [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -2532,7 +3033,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -2541,13 +3051,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -2556,36 +3081,72 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2593,10 +3154,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winnow" -version = "0.5.19" +name = "windows_x86_64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -2608,20 +3175,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] -name = "xml-rs" -version = "0.8.19" +name = "xxhash-rust" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" [[package]] name = "yansi" -version = "1.0.0-rc.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ - "is-terminal", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", ] diff --git a/Cargo.toml b/Cargo.toml index 3aac3cd..2cfd947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,29 +1,15 @@ -[package] -name = "plcom" -version = "0.1.0" -edition = "2021" -publish = false -default-run = "plcom" +[workspace] +resolver = "2" +members = ["crates/gen-wallpapers", "crates/plcom"] -[[bin]] -name = "plcom" -path = "src/main.rs" +[workspace.dependencies] +serde = "1.0" -[[bin]] -name = "gen-wallpapers" -path = "src/gen-wallpapers.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" -[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" - -# wallpapers -nanorand = { version = "0.7.0", features = ["chacha"] } -kamadak-exif = "0.5.5" -reqwest = { version = "0.11.22", features = ["blocking", "json"] } -serde_json = "1.0.108" -dms-coordinates = "1.1.0" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0e223a4..f3cf91a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,51 @@ -ARG RUST_VERSION=1.74.0 +ARG RUST_VERSION=1.77 +FROM rust:${RUST_VERSION}-bookworm as builder -FROM rust:${RUST_VERSION}-slim-bookworm as builder +# Install cargo-binstall, which makes it easier to install other +# cargo extensions like cargo-leptos +RUN wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz +RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz +RUN cp cargo-binstall /usr/local/cargo/bin -# tailwind -WORKDIR /usr/bin/ -ADD https://github.com/tailwindlabs/tailwindcss/releases/download/v3.3.5/tailwindcss-linux-x64 tailwindcss -RUN chmod +x /usr/bin/tailwindcss +# Install cargo-leptos +RUN cargo binstall cargo-leptos -y -# openssl + CA certs -RUN apt-get update; \ - apt-get install -y --no-install-recommends ca-certificates pkg-config libssl-dev +# Add the WASM target +RUN rustup target add wasm32-unknown-unknown -WORKDIR /usr/src/plcom -COPY css/ css/ -COPY public/ public/ -COPY templates/ templates/ -COPY src/ src/ -COPY build.rs . -COPY Cargo.lock . -COPY Cargo.toml . -COPY tailwind.config.cjs . +# Make an /app dir, which everything will eventually live in +RUN mkdir -p /app +WORKDIR /app +COPY . . -# generate wallpapers -RUN cargo build --bin gen-wallpapers --release -RUN cargo run --bin gen-wallpapers --release +# Generate wallpapers metadata +RUN cargo run -p gen-wallpapers --example cli -- ./public/wallpapers > crates/plcom/wallpapers.json -# build project -RUN cargo build --bin plcom --release +# Build the app +RUN cargo leptos build --release -vv -FROM debian:12-slim -WORKDIR /usr/share/plcom -COPY --from=builder /usr/src/plcom/public /usr/share/plcom/public -COPY --from=builder /usr/src/plcom/target/release/plcom /usr/share/plcom +FROM debian:bookworm-slim as runtime +WORKDIR /app +RUN apt-get update -y \ + && apt-get install -y --no-install-recommends openssl ca-certificates \ + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* -ENV ROCKET_CLI_COLORS=0 -ENV ROCKET_ADDRESS=0.0.0.0 +# Copy the server binary to the /app directory +COPY --from=builder /app/target/release/plcom /app/ + +# /target/site contains our JS/WASM/CSS, etc. +COPY --from=builder /app/target/site /app/site + +# Copy Cargo.toml if it’s needed at runtime +COPY --from=builder /app/Cargo.toml /app/ + +# Set any required env variables and +ENV RUST_LOG="info" +ENV LEPTOS_SITE_ADDR="0.0.0.0:8000" +ENV LEPTOS_SITE_ROOT="site" EXPOSE 8000 -ENTRYPOINT ["/usr/share/plcom/plcom"] + +# Run the server +CMD ["/app/plcom"] 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/Cargo.toml b/crates/gen-wallpapers/Cargo.toml new file mode 100644 index 0000000..4d97310 --- /dev/null +++ b/crates/gen-wallpapers/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "gen-wallpapers" +version = "0.1.0" +edition = "2021" + +[dependencies] +kamadak-exif = "0.5.5" +reqwest = { version = "0.11.22", features = ["blocking", "json"] } +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.108" +dms-coordinates = "1.3.0" + +[dev-dependencies] +clap = { version = "4.5.4", features = ["derive"] } diff --git a/crates/gen-wallpapers/examples/cli.rs b/crates/gen-wallpapers/examples/cli.rs new file mode 100644 index 0000000..45ddf0d --- /dev/null +++ b/crates/gen-wallpapers/examples/cli.rs @@ -0,0 +1,31 @@ +use clap::Parser; +use gen_wallpapers::MetadataList; +use std::fs::read_dir; +use std::path::PathBuf; + +#[derive(Parser)] +struct Cli { + folder: PathBuf, +} + +fn main() { + let cli = Cli::parse(); + let dir = match read_dir(cli.folder) { + Ok(dir) => dir, + Err(e) => { + eprintln!("failed to read folder: {e}"); + return; + } + }; + + let metadata = MetadataList::process_folder(dir, true); + + let json = match metadata.to_pretty_json() { + Ok(json) => json, + Err(e) => { + eprintln!("failed to serialize json: {e}"); + return; + } + }; + println!("{json}"); +} diff --git a/crates/gen-wallpapers/src/lib.rs b/crates/gen-wallpapers/src/lib.rs new file mode 100644 index 0000000..8446cd8 --- /dev/null +++ b/crates/gen-wallpapers/src/lib.rs @@ -0,0 +1,222 @@ +use exif::{DateTime, Exif, In, Tag}; +use serde::Serialize; +use std::fs::ReadDir; +use std::io::BufReader; + +fn parse_coordinates(exif: &Exif, tag: Tag, r#ref: Tag) -> Option { + let mut coord = None; + let mut coord_ref = None; + + // Parse DMS coordinates + if let Some(field) = exif.get_field(tag, In::PRIMARY) { + match field.value { + exif::Value::Rational(ref vec) if !vec.is_empty() => { + let deg = vec[0].to_f64() as u16; + let min = vec[1].to_f64() as u8; + let sec = vec[2].to_f64(); + + coord = Some((deg, min, sec)); + } + _ => {} + } + } + + // Get bearing + if let Some(field) = exif.get_field(r#ref, In::PRIMARY) { + coord_ref = Some(field.display_value().to_string()); + } + + match (coord, coord_ref) { + (Some((deg, min, sec)), Some(r#ref)) => { + use dms_coordinates::Cardinal; + use dms_coordinates::Cardinal::*; + let bearing: Option = match r#ref.as_str() { + "N" => Some(North), + "NE" => Some(NorthEast), + "NW" => Some(NorthWest), + "S" => Some(South), + "SE" => Some(SouthEast), + "SW" => Some(SouthWest), + "E" => Some(East), + "W" => Some(West), + _ => None, + }; + + let dms = dms_coordinates::DMS::new(deg, min, sec, bearing); + + Some(dms.to_ddeg_angle() as f32) + } + (_, _) => None, + } +} + +#[derive(Debug, Serialize)] +struct Metadata { + filename: String, + date: String, + width: u32, + height: u32, + gps: Gps, + location: Option, +} + +#[derive(Debug, Serialize)] +pub struct Location { + precise: String, + broad: String, +} + +#[derive(Debug, Serialize)] +pub struct Gps { + latitude: f32, + longitude: f32, +} + +#[derive(Debug, Serialize)] +pub struct MetadataList(Vec); + +impl MetadataList { + pub fn process_folder(dir: ReadDir, get_location: bool) -> Self { + let mut files = vec![]; + + for file in dir { + let Ok(file) = file else { + continue; + }; + + // Get filename + let Ok(filename) = file.file_name().into_string() else { + continue; + }; + + // Read exif from file + let Ok(file) = std::fs::File::open(file.path()) else { + continue; + }; + let mut reader = BufReader::new(file); + let Ok(exif) = exif::Reader::new().read_from_container(&mut reader) else { + continue; + }; + eprintln!("Processing `{}`", filename); + + // Get GPS coordinates + let latitude = parse_coordinates(&exif, Tag::GPSLatitude, Tag::GPSLatitudeRef); + let longitude = parse_coordinates(&exif, Tag::GPSLongitude, Tag::GPSLongitudeRef); + + // Get date + let mut date = None; + if let Some(field) = exif.get_field(Tag::DateTime, In::PRIMARY) { + match field.value { + exif::Value::Ascii(ref vec) if !vec.is_empty() => { + if let Ok(datetime) = DateTime::from_ascii(&vec[0]) { + let datetime = datetime.to_string(); + let split: Vec<&str> = datetime.split(' ').collect(); + + date = split.first().map(|str| str.to_string()); + } + } + _ => {} + } + } + + // Get image width + let mut width = None; + if let Some(field) = exif.get_field(Tag::PixelXDimension, In::PRIMARY) { + if let Some(exif_width) = field.value.get_uint(0) { + width = Some(exif_width); + } + } + + // Get image height + let mut height = None; + if let Some(field) = exif.get_field(Tag::PixelYDimension, In::PRIMARY) { + if let Some(exif_height) = field.value.get_uint(0) { + height = Some(exif_height); + } + } + + match (date, latitude, longitude, width, height) { + (Some(date), Some(latitude), Some(longitude), Some(width), Some(height)) => { + let gps = Gps { + latitude, + longitude, + }; + let location = if get_location { + eprintln!("Getting location for `{}`", filename); + gps.get_location() + } else { + None + }; + files.push(Metadata { + filename, + width, + height, + date, + gps, + location, + }); + } + _ => { + continue; + } + } + } + + Self(files) + } + + pub fn to_pretty_json(&self) -> serde_json::Result { + serde_json::to_string_pretty(&self.0) + } +} + +impl Gps { + fn get_location(&self) -> Option { + match reqwest::blocking::Client::new() + .get(format!( + // Documentation: https://nominatim.org/release-docs/develop/api/Reverse/ + "https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={}&lon={}&zoom=8", + self.latitude, self.longitude, + )) + .header("User-Agent", "https://philippeloctaux.com") + .send() + .and_then(|data| data.json::()) + { + Ok(data) => { + let location = &data["display_name"]; + + if location.is_string() { + let location = location.to_string(); + // Remove first and last characters (the string is wrapped in double quotes '"') + let location = { + let mut chars = location.chars(); + chars.next(); + chars.next_back(); + chars.as_str() + }; + eprintln!("Raw location is `{}`", location); + + let mut location = location.split(','); + let precise = location.next().unwrap_or("?").to_string(); + + let mut broad: String = + location.collect::>().join(",").trim().to_string(); + if broad.is_empty() { + broad.push('?'); + } + + let location = Location { precise, broad }; + eprintln!("Location is `{:?}`", location); + Some(location) + } else { + eprintln!("Failed to find location."); + None + } + } + Err(_) => { + eprintln!("Failed to make API call to get location."); + None + } + } + } +} 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 + + + <Meta name="viewport" content="width=device-width"/> + + // favicon + <Link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/> + <Link rel="icon" type_="image/png" sizes="32x32" href="/favicon-32x32.png"/> + <Link rel="icon" type_="image/png" sizes="16x16" href="/favicon-16x16.png"/> + <Link rel="manifest" href="/site.webmanifest"/> + <Link + rel="mask-icon" + href="/safari-pinned-tab.svg" + attrs=vec![("color", Attribute::String(Oco::Borrowed("#0c4a6e")))] + /> + <Meta name="msapplication-TileColor" content="#0c4a6e"/> + <Meta name="theme-color" content="#0c4a6e"/> + + // stats + <Script + defer="true" + src="https://plausible.y.z.x4m3.rocks/js/script.js" + attrs=vec![("data-domain", Attribute::String(Oco::Borrowed("philippeloctaux.com")))] + /> + + // actual routes + <Router fallback=|| { + let mut outside_errors = Errors::default(); + outside_errors.insert_with_default_key(AppError::NotFound); + view! { <ErrorTemplate outside_errors/> }.into_view() + }> + <Body class=tw_join!("flex", "flex-col", "min-h-screen", "bg-gray-900", "text-white")/> + <main class=tw_join!("flex-grow")> + <Routes> + <Route path="" view=RootPage ssr=SsrMode::Async/> + <Route path="email" view=EmailPage/> + <Route path="wallpapers" view=WallpapersPage/> + </Routes> + </main> + <footer class=tw_join!("bg-black")> + <div class=tw_join!("container", "mx-auto", "px-4", "py-8")> + <p> + "© 2015 - "{crate::get_year()}" Philippe Loctaux, made with " + <UnderlineLink link=Link::new("https://leptos.dev", "Leptos")/> + </p> + </div> + </footer> + </Router> + } +} + + + 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<ResumeLink>, + } + + #[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<ResumeLink>, + pub logo: Option<Logo>, + pub image: Option<Image>, + } + + #[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! { <OutlineButtonLink link=link/> }.into_view() + } else { + view! { + <span class=tw_join!( + "mt-4", "cursor-not-allowed", "inline-flex", "max-w-fit", "bg-gray-400", + "text-gray-600", "font-semibold", "py-1.5", "px-4", "rounded-xl", + "items-center" + )>{self.label}</span> + } + .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<Logo>, + 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! { + <svg + class=tw_join!("w-6", "h-6", "mr-0", "sm:mr-2") + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" + ></path> + </svg> + }.into_view(), + + Self::Link => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5", "fill-current", "mr-2") + > + <path d="M12.232 4.232a2.5 2.5 0 013.536 3.536l-1.225 1.224a.75.75 0 001.061 1.06l1.224-1.224a4 4 0 00-5.656-5.656l-3 3a4 4 0 00.225 5.865.75.75 0 00.977-1.138 2.5 2.5 0 01-.142-3.667l3-3z"></path> + <path d="M11.603 7.963a.75.75 0 00-.977 1.138 2.5 2.5 0 01.142 3.667l-3 3a2.5 2.5 0 01-3.536-3.536l1.225-1.224a.75.75 0 00-1.061-1.06l-1.224 1.224a4 4 0 105.656 5.656l3-3a4 4 0 00-.225-5.865z"></path> + </svg> + }.into_view(), + + Self::Calendar => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5") + > + <path + fill-rule="evenodd" + d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" + clip-rule="evenodd" + ></path> + </svg> + }.into_view(), + + Self::Location => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5") + > + <path + fill-rule="evenodd" + d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z" + clip-rule="evenodd" + ></path> + </svg> + }.into_view(), + Icon::Twitter => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"></path> + </svg> + }.into_view(), + Icon::Telegram => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"></path> + </svg> + }.into_view(), + Icon::Mastodon => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"></path> + </svg> + }.into_view(), + Icon::Github => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path> + </svg> + }.into_view(), + Icon::Linkedin => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path> + </svg> + }.into_view(), + Icon::Map => view! { + <svg + class=tw_join!("w-5", "h-5") + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fill-rule="evenodd" + d="M8.157 2.175a1.5 1.5 0 00-1.147 0l-4.084 1.69A1.5 1.5 0 002 5.251v10.877a1.5 1.5 0 002.074 1.386l3.51-1.453 4.26 1.763a1.5 1.5 0 001.146 0l4.083-1.69A1.5 1.5 0 0018 14.748V3.873a1.5 1.5 0 00-2.073-1.386l-3.51 1.452-4.26-1.763zM7.58 5a.75.75 0 01.75.75v6.5a.75.75 0 01-1.5 0v-6.5A.75.75 0 017.58 5zm5.59 2.75a.75.75 0 00-1.5 0v6.5a.75.75 0 001.5 0v-6.5z" + clip-rule="evenodd" + ></path> + </svg> + }.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<String>) -> 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<Link>, + #[prop(into, optional)] class: MaybeSignal<String>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + let class = tailwind_fuse::tw_merge!("underline", class.get()); + view! { + <a href=link.get().uri.to_string() {..attributes} class=class target="_blank"> + {link.get().label} + </a> + } +} + +type HideTextSmallDisplay = bool; + +#[component] +pub fn ButtonLink( + #[prop(into)] link: MaybeSignal<Link>, + #[prop(into, optional)] icon: Option<MaybeSignal<Icon>>, + #[prop(into, optional)] hide_text_small_display: Option<MaybeSignal<HideTextSmallDisplay>>, + #[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! { + <a + href=link.get().uri.to_string() + {..attributes} + class=tw_join!( + "inline-flex", "bg-sky-900", "hover:bg-sky-700", "transition-all", "duration-200", + "text-white", "font-bold", "py-2", "px-4", "rounded-xl", "items-center" + ) + > + + {icon} + <div class=tw_join!("inline-flex", "items-center")> + <span class=text_css>{link.get().label}</span> + </div> + </a> + } +} + +#[component] +pub fn OutlineButtonLink( + #[prop(into)] link: MaybeSignal<Link>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + view! { + <a + href=link.get().uri.to_string() + {..attributes} + class=tw_join!( + "mt-4", "inline-flex", "bg-transparent", "hover:bg-sky-700", "text-white", + "font-semibold", "py-1.5", "px-4", "rounded-xl", "items-center", "border", + "border-white", "hover:border-transparent", "transition-all", "duration-200" + ) + > + + {Icon::Link} + <div class=tw_join!("inline-flex", "items-center")> + <span class=tw_join!("ml-2", "sm:ml-0", "text-center")>{link.get().label}</span> + </div> + </a> + } +} + 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<Errors>, + #[prop(optional)] errors: Option<RwSignal<Errors>>, +) -> 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<AppError> = errors + .into_iter() + .filter_map(|(_k, v)| v.downcast_ref::<AppError>().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::<ResponseOptions>(); + if let Some(response) = response { + response.set_status(errors[0].status_code()); + } + } + + view! { + <For + // a function that returns the items we're iterating over; a signal is fine + each=move || { errors.clone().into_iter().enumerate() } + // a unique key for each item as a reference + key=|(index, _error)| *index + // renders each item to a view + children=move |error| { + view! { + <ContentPage title=error.1.canonical_reason()> + <p>{error.1.description()}</p> + </ContentPage> + } + } + /> + } +} 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<LeptosOptions>, req: Request<Body>) -> 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<Response<Body>, (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<String>, + children: Children, +) -> impl IntoView { + view! { + <leptos_meta::Title text=title.get()></leptos_meta::Title> + <div class=tw_join!("container", "mx-auto", "px-4", "py-16")> + <h1 class=tw_join!("text-3xl", "sm:text-4xl", "font-bold")>{title}</h1> + <UnderlineLink link=Link::new("/", "← Home")/> + <div class=tw_join!("mt-8")>{children()}</div> + </div> + } +} 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: + // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain> + // 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! { + <ContentPage title="Email"> + <p> + "Send an email if you want to work with me, propose a project idea, or just to say hi!" + </p> + <div class=tw_join!("my-4")> + <ButtonLink + link=Link::new( + "mailto:wwwATphilippeloctaux~DOT~com", + "www at philippeloctaux dot com", + ) + + icon=Icon::Email + /> + </div> + + <p class=tw_join!( + "mb-2" + )> + "If you want to encrypt your message, I have a " + <UnderlineLink link=Link::new("/pub/pgp-0x69771CD04BA82EC0.txt", "pgp key")/> + " at your disposal." + </p> + <p class=tw_join!( + "mb-2" + )> + "I also have a " <UnderlineLink link=Link::new("/keybase.txt", "Keybase")/> + " account, but I do not check it often." + </p> + </ContentPage> + } +} + 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! { + <Title text="Hello"/> + <hero::Hero wallpaper=random_wallpaper></hero::Hero> + + <div class=tw_join!("container", "mx-auto", "px-4", "md:px-8", "lg:px-16", "py-16")> + <Whoami/> + <div class=tw_join!("my-16", "space-y-16", "md:space-y-32")> + <www::Www></www::Www> + <jobs::Jobs></jobs::Jobs> + <projects::Projects></projects::Projects> + <education::EducationList></education::EducationList> + <talks::Talks></talks::Talks> + <friends::Friends></friends::Friends> + </div> + </div> + } +} + +#[component] +fn Whoami() -> impl IntoView { + view! { + <div class=tw_join!("md:flex", "md:flex-row-reverse", "items-center")> + <div class=tw_join!("md:w-1/2", "mb-4", "md:mb-0")> + <img + src="/phil.png" + alt="Phil" + class=tw_join!( + "rounded-3xl", "bg-sky-900", "h-36", "w-36", "md:mx-auto", "md:h-56", + "md:w-56", "lg:h-64", "lg:w-64", "mb-2", "md:mb-0" + ) + /> + + </div> + + <div class=tw_join!("md:w-1/2")> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"About Phil"</h1> + <h2 class=tw_join!( + "text-2xl", "font-semibold", "mb-4" + )>"Developer of all sorts"</h2> + + <div class=tw_join!("text-lg", "space-y-6")> + <p> + "I got into computer science by learning about the Linux kernel and administrating servers." + </p> + <p> + "After high school, I became a student at Epitech and learned to tackle technical concepts and apply them quickly by working on small projects." + </p> + <p> + "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." + </p> + <p> + "Now I have experience in software engineering, full-stack web and mobile development, system administration and CI/CD, as well as embedded development." + </p> + <p> + "My goal is to use my knowledge and experience to make software helping its users accomplish their needs." + </p> + </div> + </div> + </div> + } +} 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! { + <div class=tw_join!( + "rounded-2xl", "w-full", "bg-amber-950", "p-6" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.institution, + &subtitle, + self.logo.as_ref(), + )} + <div class=tw_join!("space-y-2")> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .courses + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> + + </div> + }.into_view() + } +} + +#[component] +pub fn EducationList() -> impl IntoView { + view! { + <div> + + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Education"</h1> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "md:grid-cols-2", "gap-6", "place-content-center" + )>{resume::EDUCATION.collect_view()}</div> + </div> + } +} + 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<String>, + /// Name of the experience, used in the alt of the image + #[prop(into)] + name: MaybeSignal<String>, + #[prop(into, optional)] background: MaybeSignal<ImageBackground>, + #[prop(into, optional)] class: MaybeSignal<String>, + #[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! { <img {..attributes} loading="lazy" src=image.get() alt=alt class=class/> } +} + +struct ExperienceLogo { + file: String, + options: Option<LogoOptions>, +} + +pub struct ExperienceHeader { + name: String, + description: String, + date_start: Date, + date_end: Option<Date>, + logo: Option<ExperienceLogo>, +} + +impl IntoView for ExperienceHeader { + fn into_view(self) -> View { + let logo = match self.logo { + Some(logo) => view! { + <ExperienceLogo + image=logo.file + name=self.name.clone() + background=logo.options.map(|o| o.background).unwrap_or_default() + class=tw_join!("mr-4") + /> + } + .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! { + <div class=tw_join!("flex", "flex-col")> + <div class=tw_join!( + "flex", "flex-row" + )> + {logo} <div class=tw_join!("flex", "flex-col", "justify-evenly")> + <div class=tw_join!( + "text-xl", "md:text-2xl", "font-semibold" + )>{self.name}</div> + <div class=tw_join!("text-xs", "md:text-sm")>{date}</div> + </div> + </div> + <p class=tw_join!( + "text-xl", "md:text-2xl", "font-semibold", "my-4" + )>{self.description}</p> + </div> + } + .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<String>, +} + +impl Name { + pub fn nick(nick: impl Into<String>) -> Self { + Self { + first: nick.into(), + last: None, + } + } + pub fn new(first: impl Into<String>, last: impl Into<String>) -> 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<String>, uri: &'static str) -> Self { + Self { + name: Name::nick(nick), + uri: Uri::from_static(uri), + } + } + + pub fn new(first: impl Into<String>, last: impl Into<String>, 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<Friend>) -> impl IntoView { + view! { + <a + href=friend.get().uri.to_string() + target="_blank" + class=tw_join!( + "hover:bg-gray-500", "transition-all", "duration-200", "flex", "items-center", + "rounded-lg", "p-2" + ) + > + + <span class=tw_join!( + "rounded-full", "flex-shrink-0", "mr-4", "w-10", "h-10", "bg-sky-900", "text-white", + "flex", "items-center", "justify-center", "text-lg", "font-medium" + )>{friend.get().name.initials()}</span> + <div> + <p class=tw_join!("font-bold")>{friend.get().name.to_string()}</p> + <p>{friend.get().domain_name()}</p> + </div> + </a> + } +} + +#[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! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Friends"</h1> + <p class=tw_join!("text-lg")>"Folks I worked with, or I like what they do."</p> + + <ul class=tw_join!( + "my-4", "grid", "grid-cols-1", "sm:grid-cols-2", "md:grid-cols-3", "lg:grid-cols-4", + "sm:gap-4" + )> + {friends + .into_iter() + .map(|f| { + view! { + <li class=tw_join!("py-2")> + <Friend friend=f/> + </li> + } + }) + .collect_view()} + </ul> + + <p>"If you do not appear here and we know each other, hit me up!"</p> + </div> + } +} 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! { + <div class=tw_join!( + "absolute", "bottom-3", "sm:bottom-5", "left-2", "sm:left-5", "inline-block", + "backdrop-blur-lg", "backdrop-brightness-75", "rounded-xl", "shadow-2xl", "p-2", + "space-y-0.5", "sm:space-y-2" + )> + + // See more + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Map} + <a + class=tw_join!("ml-1", "text-sm", "underline") + href="/wallpapers" + target="_blank" + > + "See more!" + </a> + </div> + </div> + + // Location + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Location} + <span class=tw_join!( + "ml-1", "text-sm" + )> + {wallpaper.location.precise} + <span class=tw_join!( + "hidden", "md:inline" + )>", "{wallpaper.location.broad}</span> + </span> + </div> + </div> + + // Date + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Calendar} <span class=tw_join!("ml-1", "text-sm")>{wallpaper.date}</span> + </div> + </div> + + </div> + } +} + +#[component] +pub fn Hero(#[prop(into)] wallpaper: Option<&'static Wallpaper>) -> impl IntoView { + let (wallpaper_info, background_image) = match wallpaper { + Some(wallpaper) => ( + view! { <WallpaperInfo wallpaper=wallpaper/> }.into_view(), + format!("background-image: url(/wallpapers/{});", wallpaper.filename), + ), + None => (view! {}.into_view(), "".into()), + }; + + view! { + <div class=tw_join!("bg-gradient-to-r", "from-red-900", "via-teal-900", "to-fuchsia-900")> + <div + id="wallpaper" + class=tw_join!( + "relative", "text-white", "w-full", "h-almostscreen", "bg-center", "bg-cover" + ) + + style=background_image + > + + <div class=tw_join!( + "container", "mx-auto", "px-8", "py-16", "w-full", "h-full", "justify-center", + "items-center", "flex", "flex-col" + )> + <div class=tw_join!( + "inline-block", "backdrop-blur-lg", "backdrop-brightness-75", "rounded-3xl", + "shadow-2xl", "px-4", "py-6", "sm:px-8", "sm:py-12", "space-y-4" + )> + <h1 class=tw_join!( + "text-3xl", "sm:text-4xl", "font-bold" + )>"Philippe Loctaux"</h1> + <h2 class=tw_join!( + "sm:text-xl", "font-semibold" + )>"Developer of all sorts. Epitech alumni, class of 2023."</h2> + </div> + </div> + + {wallpaper_info} + </div> + </div> + } +} 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! { + <div class=tw_join!("w-full", "rounded-2xl", "bg-sky-950")> + + <div class=tw_join!( + "p-6", "justify-between", "h-full" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.name, + self.position, + Some(&self.logo), + )} <div class=tw_join!("space-y-2")> + + <p>{self.description}</p> + + <div> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .highlights + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> + + <div> + <div class=tw_join!( + "mt-6 flex flex-wrap gap-x-6 gap-y-4" + )> + {self + .technologies + .iter() + .map(|t| { + view! { + <span class=tw_join!( + "inline-flex", "items-center", "rounded-md", "bg-blue-100", + "px-2", "py-1", "font-medium", "text-blue-700" + )>{*t}</span> + } + }) + .collect_view()} + </div> + + </div> + </div> {self.link} + + </div> + </div> + }.into_view() + } +} + +#[component] +pub fn Jobs() -> impl IntoView { + view! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Professional Experiences"</h1> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "sm:grid-cols-2", "gap-4" + )>{resume::WORK.collect_view()}</div> + </div> + } +} + 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! { + <div class=tw_join!( + "w-full", "rounded-2xl", "bg-pink-950", css_image_position + )> + + {if let Some(image) = self.image { + view! { + <img + loading="lazy" + src=image.file + alt=format!("{} image", self.name) + class=css_image_position_corner + /> + } + .into_view() + } else { + view! {}.into_view() + }} + <div class=tw_join!( + "p-6", "justify-between", "h-full" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.name, + self.description, + self.logo.as_ref(), + )} + <div class=tw_join!( + "space-y-2" + )> + + {self + .presentation + .iter() + .map(|p| { + view! { <p>{*p}</p> } + }) + .collect_view()} <div> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .highlights + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> <div> + + <div class=tw_join!( + "mt-6", "grid", "grid-cols-2", "sm:grid-cols-3", "gap-x-6", + "gap-y-4" + )> + {self + .keywords + .iter() + .map(|t| { + view! { + <span class=tw_join!( + "items-center", "rounded-md", "bg-blue-100", "px-2", "py-1", + "font-medium", "text-blue-700", + )>{*t}</span> + } + }) + .collect_view()} + </div> + + </div> + </div> {self.link} + + </div> + </div> + }.into_view() + } +} + + type ImageProject = Project; + type TextProject = Project; + + enum DisplayProject { + Text(Box<(TextProject, Option<TextProject>)>), + 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! { + <div class=tw_join!( + "my-4", "grid", "grid-cols-1", "sm:grid-cols-2", "gap-4" + )>{text1} {text2}</div> + }.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! { + <div> + + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Projects"</h1> + + {projects} + </div> + } +} 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<String>, + date: Date, + location: impl Into<String>, + link: Link, + ) -> Self { + Self { + title: title.into(), + date, + location: location.into(), + link, + } + } +} + +#[component] +fn Talk(#[prop(into)] talk: MaybeSignal<Talk>) -> impl IntoView { + view! { + <div class=tw_join!("rounded-2xl", "w-full", "bg-teal-950", "p-6")> + + <h3 class=tw_join!("text-xl", "font-semibold", "mb-4")>{talk.get().title}</h3> + + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Calendar} + <span class=tw_join!("ml-2")>{talk.get().date.to_string()}</span> + </div> + </div> + + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )>{Icon::Location} <span class=tw_join!("ml-2")>{talk.get().location}</span></div> + </div> + + <OutlineButtonLink link=talk.get().link/> + </div> + } +} + +#[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! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Talks"</h1> + <p class=tw_join!( + "text-lg" + )> + "Giving a talk is the opportunity to share what I know, and helps me reduce my fear of public speaking." + </p> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "sm:grid-cols-2", "lg:grid-cols-3", "gap-6", + "place-content-center" + )> + {talks + .into_iter() + .map(|t| { + view! { <Talk talk=t/> } + }) + .collect_view()} + </div> + + </div> + } +} + 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! { + <div class=tw_join!( + "grid", "grid-cols-3", "lg:grid-cols-6", "gap-4", "place-content-center" + )> + + {www + .into_iter() + .map(|w| { + view! { + <div class=tw_join!("w-full", "h-auto", "md:w-auto")> + <div class=tw_join!("text-center")> + <ButtonLink + link=w.link + icon=w.icon + hide_text_small_display=true + attributes=vec![ + ("target", Attribute::String(Oco::Borrowed("_blank"))), + ] + /> + + </div> + </div> + } + }) + .collect_view()} + + </div> + } +} 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! { + <ContentPage title="Wallpapers"> + <Stylesheet href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> + <Script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"/> + <p class=tw_join!("mb-2")>"Pictures I took around the world"</p> + <MapContainer + center=Position::new(0.0, 0.0) + zoom=1.0 + class=tw_join!("w-full", "h-halfscreen") + > + <TileLayer + url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" + attribution="© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" + /> + <For + each=move || wallpapers + key=|w| w.filename + children=move |w: Wallpaper| { + let uri = format!("/wallpapers/{}", w.filename); + view! { + <Marker position=position!( + w.gps.latitude.into(), w.gps.longitude.into() + )> + <Popup> + <a target="_blank" href=uri> + {w.location.precise} + </a> + <br/> + {w.location.broad} + <br/> + <b>{w.date}</b> + </Popup> + </Marker> + } + } + /> + + </MapContainer> + </ContentPage> + } +} 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 0000000..f234b17 Binary files /dev/null and b/public/icons/rubycat.png differ diff --git a/public/icons/uqac.png b/public/icons/uqac.png new file mode 100644 index 0000000..a64b12c Binary files /dev/null and b/public/icons/uqac.png differ diff --git a/public/phil.png b/public/phil.png new file mode 100644 index 0000000..656150b Binary files /dev/null and b/public/phil.png differ diff --git a/public/pub/phil.png b/public/pub/phil.png deleted file mode 100644 index 9306695..0000000 Binary files a/public/pub/phil.png and /dev/null differ diff --git a/public/pub/philt3r.png b/public/pub/philt3r.png deleted file mode 100644 index 7c8a799..0000000 Binary files a/public/pub/philt3r.png and /dev/null differ diff --git a/readme.md b/readme.md index 1c2aa18..8f56143 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ http://philippeloctaux.com ## tech -- https://rocket.rs +- https://leptos.dev - https://tailwindcss.com ## colors @@ -16,9 +16,10 @@ http://philippeloctaux.com ## wallpapers 1. place **JPEG** files in `public/wallpapers` and make sure they have exif data (GPS + date) -2. run `cargo run --bin gen-wallpapers` to generate wallpaper metadata +2. run `cargo run -p gen-wallpapers --example cli -- ./public/wallpapers > ./crates/plcom/wallpapers.json` to generate wallpaper metadata ## icons - https://simpleicons.org for brand icons -- https://heroicons.com for the rest \ No newline at end of file +- https://heroicons.com for the rest + diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index 16fdc1f..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,54 +0,0 @@ -use rocket::fairing::{self, Fairing}; -use rocket::http::{ContentType, Header}; -use rocket::{Request, Response}; - -#[derive(Debug)] -pub struct CacheControl { - duration_secs: u32, - types: Vec<ContentType>, - routes: Vec<&'static str>, -} - -impl Default for CacheControl { - fn default() -> Self { - CacheControl { - duration_secs: 60 * 60, // 60 secs * 60 minutes - types: vec![ContentType::CSS, ContentType::JavaScript], - routes: vec!["/wallpapers", "/pub", "/images", "/icons"], - } - } -} - -#[rocket::async_trait] -impl Fairing for CacheControl { - fn info(&self) -> fairing::Info { - fairing::Info { - name: "Cache Control", - kind: fairing::Kind::Response, - } - } - - async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) { - let mut should_cache = false; - - // Check if content type matches - if let Some(content_type) = response.content_type() { - if self.types.contains(&content_type) { - should_cache = true; - } - } - - // Check if route matches - self.routes - .iter() - .filter(|s| request.uri().path().starts_with(*s)) - .for_each(|_| should_cache = true); - - if should_cache { - response.set_header(Header::new( - "Cache-Control", - format!("public, max-age={}", self.duration_secs), - )); - } - } -} diff --git a/src/filters.rs b/src/filters.rs deleted file mode 100644 index f27ebf4..0000000 --- a/src/filters.rs +++ /dev/null @@ -1,11 +0,0 @@ -const SUFFIX: &str = "Philippe Loctaux"; - -pub fn title<T: std::fmt::Display>(s: T) -> ::askama::Result<String> { - let prefix = s.to_string(); - - Ok(if prefix != SUFFIX { - format!("{prefix} - {SUFFIX}") - } else { - prefix - }) -} diff --git a/src/gen-wallpapers.rs b/src/gen-wallpapers.rs deleted file mode 100644 index 046ff44..0000000 --- a/src/gen-wallpapers.rs +++ /dev/null @@ -1,215 +0,0 @@ -use dms_coordinates::Bearing; -use exif::{DateTime, Exif, In, Tag}; -use std::fs::read_dir; -use std::io::{BufReader, Write}; -use std::path::PathBuf; - -const WALLPAPERS_PATH: &str = "/public/wallpapers"; -const CRATE_PATH: &str = env!("CARGO_MANIFEST_DIR"); - -const IMPORTS: &str = r#"use crate::types::{Gps, Location, Wallpaper};"#; - -#[derive(Debug)] -struct Metadata { - file: String, - date: String, - latitude: f32, - longitude: f32, -} - -fn parse_coordinates(exif: &Exif, tag: Tag, r#ref: Tag) -> Option<f32> { - let mut coord = None; - let mut coord_ref = None; - - // Parse DMS coordinates - if let Some(field) = exif.get_field(tag, In::PRIMARY) { - match field.value { - exif::Value::Rational(ref vec) if !vec.is_empty() => { - let deg = vec[0].to_f64() as i32; - let min = vec[1].to_f64() as i32; - let sec = vec[2].to_f64(); - - coord = Some((deg, min, sec)); - } - _ => {} - } - } - - // Get bearing - if let Some(field) = exif.get_field(r#ref, In::PRIMARY) { - coord_ref = Some(field.display_value().to_string()); - } - - match (coord, coord_ref) { - (Some((deg, min, sec)), Some(r#ref)) => { - use Bearing::*; - let bearing = match r#ref.as_str() { - "N" => North, - "NE" => NorthEast, - "NW" => NorthWest, - "S" => South, - "SE" => SouthEast, - "SW" => SouthWest, - "E" => East, - "W" => West, - _ => return None, - }; - - let dms = dms_coordinates::DMS::new(deg, min, sec, bearing); - - Some(dms.to_decimal_degrees() as f32) - } - (_, _) => None, - } -} - -fn main() { - let mut wallpapers = format!( - "// AUTO GENERATED FILE.\n// PLEASE DO NOT EDIT MANUALLY.\n{IMPORTS}\npub static WALLPAPERS: &[&Wallpaper] = &[\n" - ); - - // Get list of files - let local_wallpaper_path = format!("{}{}", CRATE_PATH, WALLPAPERS_PATH); - let metadata: Vec<Metadata> = match read_dir(local_wallpaper_path) { - Ok(dir) => { - let mut files = vec![]; - - for file in dir { - let Ok(file) = file else { - continue; - }; - - // Get filename - let Ok(filename) = file.file_name().into_string() else { - continue; - }; - - // Read exif from file - let Ok(file) = std::fs::File::open(file.path()) else { - continue; - }; - let mut reader = BufReader::new(file); - let Ok(exif) = exif::Reader::new().read_from_container(&mut reader) else { - continue; - }; - - // Get GPS coordinates - let latitude = parse_coordinates(&exif, Tag::GPSLatitude, Tag::GPSLatitudeRef); - let longitude = parse_coordinates(&exif, Tag::GPSLongitude, Tag::GPSLongitudeRef); - - // Get date - let mut date = None; - if let Some(field) = exif.get_field(Tag::DateTime, In::PRIMARY) { - match field.value { - exif::Value::Ascii(ref vec) if !vec.is_empty() => { - if let Ok(datetime) = DateTime::from_ascii(&vec[0]) { - let datetime = datetime.to_string(); - let split: Vec<&str> = datetime.split(' ').collect(); - - date = split.first().map(|str| str.to_string()); - } - } - _ => {} - } - } - - match (date, latitude, longitude) { - (Some(date), Some(latitude), Some(longitude)) => files.push(Metadata { - file: format!("/wallpapers/{}", filename), - date, - latitude, - longitude, - }), - (_, _, _) => { - continue; - } - } - } - - files - } - Err(_) => vec![], - }; - - for wallpaper in metadata { - println!("\nProcessing `{}`", wallpaper.file); - - let client = reqwest::blocking::Client::new(); - let (precise, broad) = match client - .get(format!( - // Documentation: https://nominatim.org/release-docs/develop/api/Reverse/ - "https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={}&lon={}&zoom=8", - wallpaper.latitude, wallpaper.longitude, - )) - .header("User-Agent", "https://philippeloctaux.com") - .send() - .and_then(|data| data.json::<serde_json::Value>()) - { - Ok(data) => { - let location = &data["display_name"]; - - let (precise, broad) = if location.is_string() { - let location = location.to_string(); - // Remove first and last characters (the string is wrapped in double quotes '"') - let location = { - let mut chars = location.chars(); - chars.next(); - chars.next_back(); - chars.as_str() - }; - println!("Raw location is `{}`", location); - - let mut location = location.split(','); - let precise = location.next().unwrap_or("?").to_string(); - - let mut broad: String = - location.collect::<Vec<&str>>().join(",").trim().to_string(); - if broad.is_empty() { - broad.push('?'); - } - - (precise, broad) - } else { - println!("Failed to find location."); - ("?".into(), "?".into()) - }; - - (precise, broad) - } - Err(_) => { - println!("Failed to make API call to get location."); - ("?".into(), "?".into()) - } - }; - - println!("Precise location is `{}`", precise); - println!("Broad location is `{}`", broad); - - // Construct structs - let gps_struct = format!( - "Gps {{ latitude: {}, longitude: {} }}", - wallpaper.latitude, wallpaper.longitude - ); - let location_struct = format!( - "Location {{ precise: \"{}\", broad: \"{}\", gps: {} }}", - precise, broad, gps_struct - ); - let wallpaper_struct = format!( - "&Wallpaper {{ file: \"{}\", date: \"{}\", location: {} }},\n", - wallpaper.file, wallpaper.date, location_struct - ); - - wallpapers.push_str(&wallpaper_struct); - } - - wallpapers.push_str("];\n"); - - // Write string to file - let mut output_wallpapers_path: PathBuf = CRATE_PATH.into(); - output_wallpapers_path.push("src/wallpapers.rs"); - let mut output_file = - std::fs::File::create(&output_wallpapers_path).expect("file already exists"); - output_file - .write_all(wallpapers.as_bytes()) - .expect("write dictionary to file"); -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index ea737de..0000000 --- a/src/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -use self::types::*; -use chrono::Datelike; -use rocket::fs::FileServer; -use rocket::{catch, catchers, get, launch, routes}; - -mod cache; -mod filters; -mod minify; -mod templates; -mod types; -mod wallpapers; - -#[launch] -fn rocket() -> _ { - let server = rocket::build() - .mount("/", FileServer::from("public")) - .mount("/", routes![root, email, wallpapers_route]) - .register("/", catchers![not_found]); - - if cfg!(debug_assertions) { - server - } else { - server - .attach( - rocket_async_compression::CachedCompression::path_suffix_fairing(vec![ - // Code - ".js".into(), - ".css".into(), - // Documents - ".pdf".into(), - ".txt".into(), - ]), - ) - .attach(cache::CacheControl::default()) - .attach(minify::Minify) - } -} - -#[catch(404)] -fn not_found() -> templates::NotFound<'static> { - templates::NotFound { - title: "404 Not found", - year: chrono::Utc::now().year(), - } -} - -#[get("/?<wallpaper>")] -fn root(wallpaper: Option<&str>) -> templates::Root<'static> { - templates::Root { - title: "Philippe Loctaux", - year: chrono::Utc::now().year(), - wallpaper: wallpaper - .and_then(Wallpaper::find) - .or_else(Wallpaper::random), - networks: Network::new(), - jobs: Job::new(), - talks: Talk::new(), - friends: Friend::new(), - projects: ProjectKind::new(), - } -} - -#[get("/email")] -fn email() -> templates::Email<'static> { - templates::Email { - title: "Email", - year: chrono::Utc::now().year(), - } -} - -#[get("/wallpapers")] -fn wallpapers_route() -> templates::Wallpapers<'static> { - templates::Wallpapers { - title: "Wallpapers", - year: chrono::Utc::now().year(), - wallpapers: WALLPAPERS, - } -} diff --git a/src/minify.rs b/src/minify.rs deleted file mode 100644 index 17848bc..0000000 --- a/src/minify.rs +++ /dev/null @@ -1,39 +0,0 @@ -use rocket::fairing::{self, Fairing, Kind}; -use rocket::http::ContentType; -use rocket::{Request, Response}; - -pub struct Minify; - -#[rocket::async_trait] -impl Fairing for Minify { - fn info(&self) -> fairing::Info { - fairing::Info { - name: "Minify HTML", - kind: Kind::Response, - } - } - - async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) { - if response.content_type() == Some(ContentType::HTML) { - let body = response.body_mut(); - - if let Ok(original) = body.to_bytes().await { - let cfg = minify_html::Cfg { - // Be HTML spec compliant - do_not_minify_doctype: true, - ensure_spec_compliant_unquoted_attribute_values: true, - keep_spaces_between_attributes: true, - - // The rest - keep_closing_tags: true, - keep_html_and_head_opening_tags: true, - minify_css: false, - minify_js: true, - ..Default::default() - }; - let minified = minify_html::minify(&original, &cfg); - response.set_sized_body(minified.len(), std::io::Cursor::new(minified)); - } - } - } -} diff --git a/src/templates.rs b/src/templates.rs deleted file mode 100644 index 18265bd..0000000 --- a/src/templates.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::filters; -use crate::types::*; - -#[derive(askama::Template)] -#[template(path = "pages/root.html")] -pub struct Root<'a> { - pub title: &'a str, - pub year: i32, - pub wallpaper: Option<&'static &'static Wallpaper>, - pub networks: Vec<Network>, - pub jobs: Vec<Job>, - pub talks: Vec<Talk>, - pub friends: Vec<Friend>, - pub projects: Vec<ProjectKind>, -} - -#[derive(rocket::Responder)] -struct RootResponder<'a> { - template: Root<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/404.html")] -pub struct NotFound<'a> { - pub title: &'a str, - pub year: i32, -} - -#[derive(rocket::Responder)] -struct NotFoundResponder<'a> { - template: NotFound<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/email.html")] -pub struct Email<'a> { - pub title: &'a str, - pub year: i32, -} - -#[derive(rocket::Responder)] -struct EmailResponder<'a> { - template: Email<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/wallpapers.html")] -pub struct Wallpapers<'a> { - pub title: &'a str, - pub year: i32, - pub wallpapers: &'static [&'static Wallpaper], -} - -#[derive(rocket::Responder)] -struct WallpapersResponder<'a> { - template: Wallpapers<'a>, -} diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 364fccc..0000000 --- a/src/types.rs +++ /dev/null @@ -1,65 +0,0 @@ -use rocket::http::uri::Absolute; - -pub mod friend; -pub mod job; -pub mod network; -pub mod project; -pub mod talk; - -pub use self::friend::*; -pub use self::job::*; -pub use self::network::*; -pub use self::project::*; -pub use self::talk::*; -pub use crate::wallpapers::WALLPAPERS; - -pub struct Logo { - pub file: &'static str, - pub transparent_background: bool, -} - -pub struct Link { - pub label: &'static str, - pub uri: Absolute<'static>, -} - -pub struct Location { - pub precise: &'static str, - pub broad: &'static str, - pub gps: Gps, -} - -pub struct Gps { - pub latitude: f32, - pub longitude: f32, -} - -impl std::fmt::Display for Gps { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}, {}]", self.latitude, self.longitude) - } -} - -pub struct Wallpaper { - pub file: &'static str, - pub date: &'static str, - pub location: Location, -} - -impl Wallpaper { - pub fn random() -> Option<&'static &'static Wallpaper> { - use nanorand::{ChaCha20, Rng}; - use std::ops::Range; - - let range = Range { - start: 0, - end: WALLPAPERS.len(), - }; - - WALLPAPERS.get(ChaCha20::new().generate_range(range)) - } - - pub fn find(filename: &str) -> Option<&'static &'static Wallpaper> { - WALLPAPERS.iter().find(|w| w.file.contains(filename)) - } -} diff --git a/src/types/friend.rs b/src/types/friend.rs deleted file mode 100644 index a6fb0c4..0000000 --- a/src/types/friend.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rocket::http::uri::Absolute; -use rocket::uri; - -pub struct Friend { - pub first_name: &'static str, - pub last_name: &'static str, - pub uri: Absolute<'static>, -} - -impl Friend { - pub fn initials(&self) -> String { - let first = self - .first_name - .to_uppercase() - .chars() - .next() - .expect("Invalid first name"); - let last = self - .last_name - .to_uppercase() - .chars() - .next() - .expect("Invalid last name"); - - format!("{first}{last}") - } - - pub fn domain_name(&self) -> String { - match self.uri.authority() { - Some(authority) => authority.to_string(), - None => self.uri.to_string(), - } - } - - pub fn new() -> Vec<Self> { - vec![ - Friend { - first_name: "Jamie", - last_name: "Bishop", - uri: uri!("https://jamiebi.shop"), - }, - Friend { - first_name: "Ayden", - last_name: "Panhuyzen", - uri: uri!("https://ayden.dev"), - }, - Friend { - first_name: "Corbin", - last_name: "Crutchley", - uri: uri!("https://crutchcorn.dev"), - }, - Friend { - first_name: "James", - last_name: "Fenn", - uri: uri!("https://jfenn.me"), - }, - Friend { - first_name: "Alex", - last_name: "Dueppen", - uri: uri!("https://ajd.sh"), - }, - Friend { - first_name: "Peter", - last_name: "Sobolev", - uri: uri!("https://petersoboyejo.com"), - }, - Friend { - first_name: "Alexandre", - last_name: "Wagner", - uri: uri!("https://wagnerwave.com"), - }, - Friend { - first_name: "Aidan", - last_name: "Follestad", - uri: uri!("https://af.codes"), - }, - Friend { - first_name: "Victor", - last_name: "Simon", - uri: uri!("https://simonvictor.com"), - }, - ] - } -} diff --git a/src/types/job.rs b/src/types/job.rs deleted file mode 100644 index 0be7de9..0000000 --- a/src/types/job.rs +++ /dev/null @@ -1,111 +0,0 @@ -use crate::types::Logo; - -pub struct Job { - pub company: &'static str, - pub title: &'static str, - pub dates: &'static str, - pub logo: Logo, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, -} - -impl Job { - pub fn new() -> Vec<Self> { - vec![ - Job { - company: "Acklio", - title: "Rust developer", - dates: "March 2023 - May 2023", - logo: Logo { - file: "/icons/acklio.png", - transparent_background: true, - }, - description: vec!["The first usage of the SCHC framework (RFC 8724) on Rust!"], - accomplishments: vec![ - "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: vec!["Rust", "SCHC", "STM32 controllers", "LoRa", "LoRaWAN"], - }, - Job { - company: "Vélorail du Kreiz Breizh", - title: "Freelance developer", - dates: "August 2021 - April 2022", - logo: Logo { - file: "/icons/velorail.png", - transparent_background: true, - }, - description: vec![ - "Creation of an online booking platform focused on the tourist activity of rail biking (vélorail).", - "During the first 5 months with the platform, 43% of the bookings were made online.", - ], - accomplishments: vec![ - "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", - ], - technologies: vec!["Angular", "NestJS", "GraphQL", "Rust", "Stripe"], - }, - Job { - company: "Yaakadev", - title: "Full-Stack developer", - dates: "April 2021 - July 2021", - logo: Logo { - file: "/icons/yaakadev.png", - transparent_background: false, - }, - description: vec![ - "Maintenance of existing projects for clients", - "Design, development and deployment of multiple projects from scratch:", - ], - accomplishments: vec![ - "Admin dashboard of a local merchants solution", - "Calendar planning application with filtering and custom views", - "Intranet to upload and download documents", - ], - technologies: vec!["NodeJS", "ExpressJS", "Angular", "MongoDB", "CI/CD"], - }, - Job { - company: "Epitech", - title: "Teaching assistant (AER)", - dates: "February 2020 - April 2021, September 2022 - February 2023", - logo: Logo { - file: "/icons/epitech.png", - transparent_background: true, - }, - description: vec![ - "Pedagogical supervision of three classes of students.", - "Conducting educational activities throughout the school year.", - ], - accomplishments: vec![ - "Start of projects", - "Technical help and guidance", - "Proctoring exams", - "Grading students on their work", - ], - technologies: vec!["C", "C++", "Haskell", "Rust", "Web and mobile development"], - }, - Job { - company: "Ubiscale", - title: "Embedded developer", - dates: "August 2019 - December 2019", - logo: Logo { - file: "/icons/ubiscale.png", - transparent_background: true, - }, - description: vec!["Creation of a home Wifi gateway for an IoT object."], - accomplishments: vec![ - "Research, reverse engineering of existing products", - "Design and implementation.", - ], - technologies: vec!["C on a ESP8266 controller", "Wi-Fi", "Bluetooth"], - }, - ] - } -} diff --git a/src/types/network.rs b/src/types/network.rs deleted file mode 100644 index 7307f25..0000000 --- a/src/types/network.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rocket::http::uri::Absolute; -use rocket::uri; -pub enum Icon { - Email, - Github, - Linkedin, - Mastodon, - Telegram, - Twitter, -} - -pub struct Network { - pub name: &'static str, - pub uri: Absolute<'static>, - pub icon: Icon, -} -impl Network { - pub fn new() -> Vec<Self> { - vec![ - Network { - name: "Twitter", - uri: uri!("https://twitter.com/philippeloctaux"), - icon: Icon::Twitter, - }, - Network { - name: "Telegram", - uri: uri!("https://t.me/philippeloctaux"), - icon: Icon::Telegram, - }, - Network { - name: "Mastodon", - uri: uri!("https://mastodon.social/@philt3r"), - icon: Icon::Mastodon, - }, - Network { - name: "GitHub", - uri: uri!("https://github.com/x4m3"), - icon: Icon::Github, - }, - Network { - name: "LinkedIn", - uri: uri!("https://linkedin.com/in/philippeloctaux"), - icon: Icon::Linkedin, - }, - Network { - name: "Email", - uri: uri!("https://philippeloctaux.com/email"), - icon: Icon::Email, - }, - ] - } -} diff --git a/src/types/project.rs b/src/types/project.rs deleted file mode 100644 index e34c81e..0000000 --- a/src/types/project.rs +++ /dev/null @@ -1,247 +0,0 @@ -use crate::types::{Link, Logo}; -use rocket::uri; - -pub enum Position { - Left, - Right, -} - -pub struct Image { - pub file: &'static str, - pub position: Position, -} -pub enum ProjectLink { - Available(Link), - NotAvailable, -} - -pub struct ProjectWithImage { - pub name: &'static str, - pub tagline: &'static str, - pub dates: &'static str, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, - - pub link: Option<ProjectLink>, - pub logo: Option<Logo>, - pub image: Image, -} - -#[derive(Default)] -pub struct ProjectWithoutImage { - pub name: &'static str, - pub tagline: &'static str, - pub dates: &'static str, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, - - pub link: Option<ProjectLink>, - pub logo: Option<Logo>, -} - -pub enum ProjectKind { - WithImage(ProjectWithImage), - WithoutImage((ProjectWithoutImage, ProjectWithoutImage)), -} - -impl ProjectKind { - pub fn new() -> Vec<Self> { - vec![ - ProjectKind::WithoutImage(( - ProjectWithoutImage { - name: "ezidam", - tagline: "Identity and Access Management system", - dates: "January - July 2023", - - description: vec![ - "A simple identity and access management system for SMEs or personal use.", - "Low maintenance required, easy to deploy and to backup.", - ], - accomplishments: vec![ - "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", - ], - technologies: vec![ - "Rust", - "SQLite", - "OAuth2 / OIDC", - "TOTP", - "SMTP", - "Docker", - ], - - logo: Some(Logo { - file: "/icons/ezidam.png", - transparent_background: true, - }), - ..Default::default() - }, - ProjectWithoutImage { - name: "pass4thewin", - tagline: "Password manager", - dates: "November 2020 - January 2021", - - description: vec![ - "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.", - ], - accomplishments: vec![ - "Creation of a store", - "List secrets", - "Decrypt secret", - "Insert or generate secrets", - "Edit existing secrets", - "Synchronisation with git", - "TOTP support", - ], - technologies: vec![ - "Windows", - "Rust", - "OpenPGP", - "libgit2", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/pass4thewin"), - label: "Source code", - })), - ..Default::default() - }, - )), - ProjectKind::WithImage( - - ProjectWithImage{ - name: "NaviaRent", - tagline: "Epitech Innovative Project", - dates: "September 2020 - January 2023", - - description: vec!["A B2B platform helping rentals of standup paddle boards."], - accomplishments: vec![ - "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", - ], - technologies: vec![ - "NodeJS", - "Angular", - "Kotlin", - "SwiftUI", - "Docker", - "GitLab CI/CD", - "Raspberry Pi", - "ESP32", - ], - - logo: Some(Logo { - file: "/icons/naviarent.png", - transparent_background: false, - }), - image: Image { - file: "/images/naviarent.jpg", - position: Position::Right, - }, - link: Some(ProjectLink::NotAvailable), - }, - ), - ProjectKind::WithoutImage(( - - ProjectWithoutImage{ - name: "epitok", - tagline: "Presence system at Epitech", - dates: "June 2020 - September 2020", - - description: vec![ - "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.", - ], - accomplishments: vec![ - "Reverse engineering of a partially documented web API", - "Design, conception", - "User experience", - "Improvements based of usage of the application", - ], - technologies: vec![ - "Rust", - "HTML", - "Bootstrap", - "jQuery", - "Docker", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/epitok"), - label: "Source code", - })), - ..Default::default() - }, - ProjectWithoutImage{ - name: "epi.today", - tagline: "Calendar for Epitech", - dates: "December 2019 - February 2020", - - description: vec![ - "A viewer of the Epitech intranet calendar.", - "Students and teachers glance at their planning without the need to go on the school's intranet.", - ], - accomplishments: vec![], - technologies: vec![ - "TypeScript", - "HTML", - "Bootstrap", - "Docker", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/epi.today"), - label: "Source code", - })), - ..Default::default() - }, - )), - - ProjectKind::WithImage( - - ProjectWithImage{ - name: "canvas.place", - tagline: "Timelapse", - dates: "April 2017 - January 2020", - - description: vec![ - "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." - ], - accomplishments: vec![], - technologies: vec!["FFmpeg", "Shell scripting", "nginx"], - - logo: Some(Logo { - file: "/icons/canvas.png", - transparent_background: false, - }), - image: Image { - file: "/images/canvas.png", - position: Position::Left, - }, - link: Some(ProjectLink::Available(Link { - uri: uri!("https://timelapse.canvas.place"), - label: "Website", - })), - }, - ) - ] - } -} diff --git a/src/types/talk.rs b/src/types/talk.rs deleted file mode 100644 index d2e985c..0000000 --- a/src/types/talk.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::types::Link; -use rocket::uri; - -pub struct Talk { - pub title: &'static str, - pub date: &'static str, - pub location: &'static str, - pub link: Link, -} - -impl Talk { - pub fn new() -> Vec<Self> { - vec![ - Talk { - title: "Vim", - date: "February 2023", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/vim.pdf"), - label: "Slides", - }, - }, - Talk { - title: "CLion", - date: "March 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/clion.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git & devops 2", - date: "February 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-devops2.pdf"), - label: "Slides", - }, - }, - Talk { - title: "pass4thewin", - date: "February 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/pass4thewin.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git & devops", - date: "May 2020", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-devops.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git gud", - date: "May 2019", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-tek.pdf"), - label: "Slides", - }, - }, - ] - } -} diff --git a/tailwind.config.cjs b/tailwind.config.cjs deleted file mode 100644 index 9836a4e..0000000 --- a/tailwind.config.cjs +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: { - files: ["./templates/**/*.html"], - }, - theme: { - extend: { - height: { - almostscreen: '90vh', - halfscreen: '60vh', - } - }, - }, - plugins: [], -} diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 0f81623..0000000 --- a/templates/base.html +++ /dev/null @@ -1,26 +0,0 @@ -<html lang="en"> -<head> - <meta charset="utf-8"/> - <meta name="viewport" content="width=device-width"/> - <title>{{ title|title }} - - - - - - - - - - - -
-{% block container %}{% endblock %} -
-
-
-

© 2015 - {{ year }} Philippe Loctaux

-
-
- - diff --git a/templates/content.html b/templates/content.html deleted file mode 100644 index 2f9c2fb..0000000 --- a/templates/content.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} - -{% block container %} -
-

{{ title }}

-
- {% block content %}{% endblock %} -
-
-{% endblock %} diff --git a/templates/icons/calendar.html b/templates/icons/calendar.html deleted file mode 100644 index 4ab9864..0000000 --- a/templates/icons/calendar.html +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/templates/icons/email.html b/templates/icons/email.html deleted file mode 100644 index 2f043f7..0000000 --- a/templates/icons/email.html +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/templates/icons/github.html b/templates/icons/github.html deleted file mode 100644 index ed83dee..0000000 --- a/templates/icons/github.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/link.html b/templates/icons/link.html deleted file mode 100644 index dcbe31f..0000000 --- a/templates/icons/link.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/templates/icons/linkedin.html b/templates/icons/linkedin.html deleted file mode 100644 index 3159cb8..0000000 --- a/templates/icons/linkedin.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/location.html b/templates/icons/location.html deleted file mode 100644 index 75c6baa..0000000 --- a/templates/icons/location.html +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/templates/icons/map.html b/templates/icons/map.html deleted file mode 100644 index 7951df7..0000000 --- a/templates/icons/map.html +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/templates/icons/mastodon.html b/templates/icons/mastodon.html deleted file mode 100644 index c871737..0000000 --- a/templates/icons/mastodon.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/telegram.html b/templates/icons/telegram.html deleted file mode 100644 index cb50586..0000000 --- a/templates/icons/telegram.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/twitter.html b/templates/icons/twitter.html deleted file mode 100644 index 6b2710d..0000000 --- a/templates/icons/twitter.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/pages/404.html b/templates/pages/404.html deleted file mode 100644 index 2febece..0000000 --- a/templates/pages/404.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

This page could not be found.

-{% endblock %} diff --git a/templates/pages/email.html b/templates/pages/email.html deleted file mode 100644 index 6bcec6b..0000000 --- a/templates/pages/email.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

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 - pgp key at your disposal. -

-

- I also have a Keybase account, but I do not check it often. -

-{% endblock %} diff --git a/templates/pages/root.html b/templates/pages/root.html deleted file mode 100644 index 78240ed..0000000 --- a/templates/pages/root.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base.html" %} - -{% block container %} -{% include "pages/root/hero-image.html" %} -
- {% include "pages/root/whoami.html" %} -
- {% include "pages/root/www.html" %} - {% include "pages/root/jobs.html" %} - {% include "pages/root/projects.html" %} - {% include "pages/root/talks.html" %} - {% include "pages/root/friends.html" %} -
-
-{% endblock %} \ No newline at end of file diff --git a/templates/pages/root/friends.html b/templates/pages/root/friends.html deleted file mode 100644 index 68dca92..0000000 --- a/templates/pages/root/friends.html +++ /dev/null @@ -1,24 +0,0 @@ -
-

Friends

-

Folks I worked with, or I like what they do.

- - -
diff --git a/templates/pages/root/hero-content.html b/templates/pages/root/hero-content.html deleted file mode 100644 index f73cb16..0000000 --- a/templates/pages/root/hero-content.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
-

Philippe Loctaux

-

- Developer of all sorts. Epitech alumni, class of 2023. -

-
-
diff --git a/templates/pages/root/hero-image.html b/templates/pages/root/hero-image.html deleted file mode 100644 index 3dc97b4..0000000 --- a/templates/pages/root/hero-image.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- {% let wallpaper_uri -%} - {% match wallpaper %} - {% when Some with (wallpaper) %} - {% let wallpaper_uri = wallpaper.file -%} - {% when None %} - {% let wallpaper_uri = "" -%} - {% endmatch %} - -
- - {% include "pages/root/hero-content.html" %} - - {% match wallpaper %} - {% when Some with (wallpaper) %} - -
- -
-
- {% include "icons/map.html" %} - See more! -
-
- - -
-
- {% include "icons/location.html" %} - - {{ wallpaper.location.precise }} - -
-
- - -
-
- {% include "icons/calendar.html" %} - {{ wallpaper.date }} -
-
-
- {% when None %} - {% endmatch %} -
-
diff --git a/templates/pages/root/jobs.html b/templates/pages/root/jobs.html deleted file mode 100644 index ab0c305..0000000 --- a/templates/pages/root/jobs.html +++ /dev/null @@ -1,64 +0,0 @@ -
-

Professional Experiences

- -
- {% for job in jobs %} - -
- -
- - -
-
- {% let css -%} - {# If css updates make sure to update in both places! #} - {% if job.logo.transparent_background == true -%} - {% let css = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - {{ job.company }} logo -
-
{{ job.company }}
-
{{ job.dates }}
-
-
-

{{ job.title }}

-
- - -
- - {% for desc in job.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in job.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in job.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- -
-
- - {% endfor %} -
-
diff --git a/templates/pages/root/project-with-image.html b/templates/pages/root/project-with-image.html deleted file mode 100644 index db236c6..0000000 --- a/templates/pages/root/project-with-image.html +++ /dev/null @@ -1,108 +0,0 @@ -{% let css_image_position -%} -{% let css_image_position_corner -%} -{# If css updates make sure to update in both places! #} -{% match project.image.position %} - -{% when Position::Left %} -{% let css_image_position = "2xl:flex items-stretch justify-between" -%} -{% let css_image_position_corner = "flex w-full 2xl:w-1/2 grow rounded-t-2xl object-cover 2xl:rounded-tr-none 2xl:rounded-l-2xl" -%} - -{% when Position::Right %} -{% let css_image_position = "2xl:flex items-stretch justify-between 2xl:flex-row-reverse" -%} -{% let css_image_position_corner = "flex w-full 2xl:w-1/2 grow rounded-t-2xl object-cover 2xl:rounded-tl-none 2xl:rounded-r-2xl" -%} - -{% endmatch %} -
- - - {{ project.name }} image - -
- - -
-
- {% match project.logo %} - {% when Some with (logo) %} - - {% let css_logo -%} - {# If css updates make sure to update in both places! #} - {% if logo.transparent_background == true -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - - - {% when None %} - {% endmatch %} - -
-
{{ project.name }}
-
{{ project.dates }}
-
-
-

{{ project.tagline }}

-
- - -
- - {% for desc in project.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in project.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in project.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- - - {% match project.link %} - - {% when Some with (link) %} - {% match link %} - - {% when ProjectLink::Available(project_link) %} - -
- {% include "icons/link.html" %} - {{ project_link.label }} -
-
- - - {% when ProjectLink::NotAvailable %} - - No longer available - - - {% endmatch %} - - {% when None %} - {% endmatch %} - -
- -
diff --git a/templates/pages/root/project-without-image.html b/templates/pages/root/project-without-image.html deleted file mode 100644 index 77c5ceb..0000000 --- a/templates/pages/root/project-without-image.html +++ /dev/null @@ -1,91 +0,0 @@ -
- -
- - -
-
- {% match project.logo %} - {% when Some with (logo) %} - - {% let css_logo -%} - {# If css updates make sure to update in both places! #} - {% if logo.transparent_background == true -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - - - {% when None %} - {% endmatch %} - -
-
{{ project.name }}
-
{{ project.dates }}
-
-
-

{{ project.tagline }}

-
- - -
- - {% for desc in project.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in project.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in project.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- - - {% match project.link %} - - {% when Some with (link) %} - {% match link %} - - {% when ProjectLink::Available(project_link) %} - -
- {% include "icons/link.html" %} - {{ project_link.label }} -
-
- - - {% when ProjectLink::NotAvailable %} - - No longer available - - - {% endmatch %} - - {% when None %} - {% endmatch %} - -
- -
diff --git a/templates/pages/root/projects.html b/templates/pages/root/projects.html deleted file mode 100644 index 9ca5f57..0000000 --- a/templates/pages/root/projects.html +++ /dev/null @@ -1,23 +0,0 @@ -
-

Projects

- -
- - {% for project in projects %} - - {% match project %} - {% when ProjectKind::WithoutImage((project1, project2)) %} -
- {% let project = project1 -%} - {% include "pages/root/project-without-image.html" %} - - {% let project = project2 -%} - {% include "pages/root/project-without-image.html" %} -
- {% when ProjectKind::WithImage(project) %} - {% include "pages/root/project-with-image.html" %} - {% endmatch %} - - {% endfor %} -
-
diff --git a/templates/pages/root/talks.html b/templates/pages/root/talks.html deleted file mode 100644 index d24b3dd..0000000 --- a/templates/pages/root/talks.html +++ /dev/null @@ -1,46 +0,0 @@ -
-

Talks

-

- Giving a talk is the opportunity to share what I know, and helps me - reduce my fear of public speaking. -

- -
- - {% for talk in talks %} -
- - -

{{talk.title}}

- - -
-
- {% include "icons/calendar.html" %} - {{talk.date}} -
-
- - -
-
- {% include "icons/location.html" %} - {{talk.location}} -
-
- - - -
- {% include "icons/link.html" %} - {{talk.link.label}} -
-
-
- {% endfor %} -
-
diff --git a/templates/pages/root/whoami.html b/templates/pages/root/whoami.html deleted file mode 100644 index 9812f1f..0000000 --- a/templates/pages/root/whoami.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
- Phil -
- -
-

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/templates/pages/root/www.html b/templates/pages/root/www.html deleted file mode 100644 index 8bc6833..0000000 --- a/templates/pages/root/www.html +++ /dev/null @@ -1,39 +0,0 @@ - diff --git a/templates/pages/wallpapers.html b/templates/pages/wallpapers.html deleted file mode 100644 index 4609e28..0000000 --- a/templates/pages/wallpapers.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

Pictures I took around the world

- - - - -
- - - -{% for wallpaper in wallpapers %} - -{% endfor %} -{% endblock %}