From 0412bc04c80b4105321efb86fe6ce5d76904a6fe Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Tue, 31 Dec 2024 20:50:26 -0500 Subject: [PATCH 01/19] gtting closer, now must support ports --- .gitignore | 14 +- Makefile | 38 +- README.md | 3 +- backend/Cargo.lock | 1846 ++++++++++++++++++++++ backend/Cargo.toml | 15 + backend/src/main.rs | 82 + frontend/Makefile | 7 + elm.json => frontend/elm.json | 0 index.html => frontend/index.html | 2 + optimize.sh => frontend/optimize.sh | 0 {src => frontend/src}/Body.elm | 20 +- {src => frontend/src}/Header.elm | 0 {src => frontend/src}/Main.elm | 9 +- {src => frontend/src}/Page/About.elm | 0 {src => frontend/src}/Page/Contact.elm | 0 {src => frontend/src}/Page/Landing.elm | 0 frontend/src/Page/NotFound.elm | 16 + {src => frontend/src}/Page/Products.elm | 0 {src => frontend/src}/Page/Resources.elm | 0 frontend/src/Ports.elm | 13 + frontend/src/ports.websocket.js | 60 + 21 files changed, 2109 insertions(+), 16 deletions(-) create mode 100644 backend/Cargo.lock create mode 100644 backend/Cargo.toml create mode 100644 backend/src/main.rs create mode 100644 frontend/Makefile rename elm.json => frontend/elm.json (100%) rename index.html => frontend/index.html (80%) rename optimize.sh => frontend/optimize.sh (100%) rename {src => frontend/src}/Body.elm (71%) rename {src => frontend/src}/Header.elm (100%) rename {src => frontend/src}/Main.elm (88%) rename {src => frontend/src}/Page/About.elm (100%) rename {src => frontend/src}/Page/Contact.elm (100%) rename {src => frontend/src}/Page/Landing.elm (100%) create mode 100644 frontend/src/Page/NotFound.elm rename {src => frontend/src}/Page/Products.elm (100%) rename {src => frontend/src}/Page/Resources.elm (100%) create mode 100644 frontend/src/Ports.elm create mode 100644 frontend/src/ports.websocket.js diff --git a/.gitignore b/.gitignore index a22bdea..a550ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -elm-stuff/ -elm.js -elm.min.js -node_modules/ -package-lock.json -package.json \ No newline at end of file +frontend/elm-stuff/ +frontend/elm.js +frontend/elm.min.js +frontend/node_modules/ +frontend/package-lock.json +frontend/package.json +backend/target +public/* diff --git a/Makefile b/Makefile index c6f0e2c..d7a000b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,36 @@ -SRC_FILES := $(shell find src -name "*.elm") +# Directories +FRONTEND_DIR := ./frontend +BACKEND_DIR := ./backend +PUBLIC_DIR := ./public +ASSET_DIR := ./assets -all: elm.min.js +# Commands +ELM_MAKE := elm make +CARGO_BUILD := cargo build +CARGO_RUN := cargo run + +# Targets +.PHONY: all frontend backend clean serve + +all: frontend backend + +frontend: + make -C $(FRONTEND_DIR) elm.min.js + cp $(FRONTEND_DIR)/index.html $(PUBLIC_DIR)/ + cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ + cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ + mkdir -p $(PUBLIC_DIR)/assets + +backend: + $(CARGO_BUILD) --manifest-path=$(BACKEND_DIR)/Cargo.toml serve: all - python3 -m http.server + $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml + +serve_debug: all + RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml + +clean: + rm -rf $(PUBLIC_DIR)/* + rm -rf $(BACKEND_DIR)/target -elm.min.js: $(SRC_FILES) - rm -f elm.min.js elm.js - ./optimize.sh src/Main.elm diff --git a/README.md b/README.md index 16526c2..f6ea50a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Now open `http://localhost:8000` in your browser. # TODO - [x] Add Makefile - - [ ] Determine if `src/Body.elm` or pages in `sr/Page` should have subscription functions + - [ ] Add GPLV3 License + - [ ] Determine if `src/Body.elm` or pages in `src/Page` should have subscription functions - [ ] use actix backend that maps most root requests to serve `actix_file::Files` - [ ] Submit to slack for feedback... - [ ] Refactor into router page diff --git a/backend/Cargo.lock b/backend/Cargo.lock new file mode 100644 index 0000000..9c845a6 --- /dev/null +++ b/backend/Cargo.lock @@ -0,0 +1,1846 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" +dependencies = [ + "actix-macros", + "actix-rt", + "actix_derive", + "bitflags", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-files" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0773d59061dedb49a8aed04c67291b9d8cf2fe0b60130a381aab53c6dd86e9be" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "v_htmlescape", +] + +[[package]] +name = "actix-http" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-actors" +version = "4.3.1+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98c5300b38fd004fe7d2a964f9a90813fdbe8a81fed500587e78b1b71c6f980" +dependencies = [ + "actix", + "actix-codec", + "actix-http", + "actix-web", + "bytes", + "bytestring", + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix_derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "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 = "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", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backend" +version = "0.1.0" +dependencies = [ + "actix", + "actix-files", + "actix-web", + "actix-web-actors", + "chrono", + "env_logger", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "bytestring" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "v_htmlescape" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..5ff9bae --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4.0" +actix-files = "0.6" +actix-web-actors = "4.0" +actix = "0.13" +chrono = "0.4" # For timestamp generation +env_logger = "0.10" # For logging +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +log = "0.4" diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..d69d44c --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,82 @@ +use actix_files::Files; +use actix_web::{web, App, HttpServer, Responder, HttpRequest, HttpResponse, Error}; +use actix_web_actors::ws; +use log::{info}; +use serde::{Deserialize, Serialize}; +use std::{fs, time::Duration}; +use actix::prelude::*; + +/// Greeting API structures +#[derive(Serialize, Deserialize)] +struct GreetingRequest { + name: String, +} + +#[derive(Serialize)] +struct GreetingResponse { + message: String, +} + +async fn greet(req: web::Json) -> impl Responder { + info!("Received request to /api/greet with name: {}", req.name); + let message = format!("Hello, {}!", req.name); + web::Json(GreetingResponse { message }) +} + +/// WebSocket actor +struct MyWebSocket; + +impl Actor for MyWebSocket { + type Context = ws::WebsocketContext; + + fn started(&mut self, ctx: &mut Self::Context) { + info!("WebSocket actor started"); + // Send messages every second + ctx.run_interval(Duration::from_secs(1), |_, ctx| { + let message = format!("{{\"time\" : \"{:?}\" }}", chrono::Local::now()); + ctx.text(message); + }); + info!("Leaving started"); + } +} + +impl StreamHandler> for MyWebSocket { + fn handle(&mut self, msg: Result, ctx: &mut ws::WebsocketContext) { + if let Ok(ws::Message::Ping(msg)) = msg { + ctx.pong(&msg); + } + } +} + +async fn websocket_handler(req: HttpRequest, stream: web::Payload) -> Result { + ws::start(MyWebSocket {}, &req, stream) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + env_logger::init(); + + let address = "127.0.0.1"; + let port = 8080; + + info!("Starting server at http://{}:{}", address, port); + + HttpServer::new(|| { + App::new() + .route("/api/greet", web::post().to(greet)) // Greeting API + .route("/ws/", web::get().to(websocket_handler)) // WebSocket endpoint + .service(Files::new("/assets", "./public/assets")) + .service(Files::new("/", "./public").index_file("index.html")) // Serve frontend + .default_service(web::route().to(|| async { + // Serve the `index.html` file + let index_html = fs::read_to_string("./public/index.html") + .unwrap_or_else(|_| "404 Not Found".to_string()); + HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(index_html) + })) + }) + .bind((address, port))? + .run() + .await +} diff --git a/frontend/Makefile b/frontend/Makefile new file mode 100644 index 0000000..c37a9c6 --- /dev/null +++ b/frontend/Makefile @@ -0,0 +1,7 @@ +SRC_FILES := $(shell find src -name "*.elm") + +all: elm.min.js + +elm.min.js: $(SRC_FILES) + rm -f elm.min.js elm.js + ./optimize.sh src/Main.elm diff --git a/elm.json b/frontend/elm.json similarity index 100% rename from elm.json rename to frontend/elm.json diff --git a/index.html b/frontend/index.html similarity index 80% rename from index.html rename to frontend/index.html index f77df87..2149339 100644 --- a/index.html +++ b/frontend/index.html @@ -7,10 +7,12 @@
+ diff --git a/optimize.sh b/frontend/optimize.sh similarity index 100% rename from optimize.sh rename to frontend/optimize.sh diff --git a/src/Body.elm b/frontend/src/Body.elm similarity index 71% rename from src/Body.elm rename to frontend/src/Body.elm index a88b20e..db7cadc 100644 --- a/src/Body.elm +++ b/frontend/src/Body.elm @@ -1,4 +1,4 @@ -module Body exposing (Msg(..), Model(..), init, update, view) +module Body exposing (Msg(..), Model(..), init, update, view, handleRoute) import Element @@ -7,6 +7,7 @@ import Page.Contact import Page.Landing import Page.Products import Page.Resources +import Page.NotFound type Msg = MsgLanding Page.Landing.Msg @@ -14,6 +15,7 @@ type Msg | MsgResources Page.Resources.Msg | MsgAbout Page.About.Msg | MsgContact Page.Contact.Msg + | MsgNotFound Page.NotFound.Msg type Model = ModelLanding Page.Landing.Model @@ -21,10 +23,25 @@ type Model | ModelResources Page.Resources.Model | ModelAbout Page.About.Model | ModelContact Page.Contact.Model + | ModelNotFound Page.NotFound.Model init : () -> Model init flags = ModelLanding (Page.Landing.init flags) +handleRoute : String -> Model +handleRoute path = + let + page = + case path of + "/" -> ModelLanding <| Page.Landing.init () + "/Products" -> ModelProducts <| Page.Products.init () + "/Resources" -> ModelResources <| Page.Resources.init () + "/About" -> ModelAbout <| Page.About.init () + "/Contact" -> ModelContact <| Page.Contact.init () + _ -> ModelNotFound <| Page.NotFound.init () + in + page + update : Msg -> Model -> (Model, Cmd Msg) update bodyMsg bodyModel = let @@ -62,5 +79,6 @@ view model = ModelResources m -> Page.Resources.view m |> Element.map MsgResources ModelAbout m -> Page.About.view m |> Element.map MsgAbout ModelContact m -> Page.Contact.view m |> Element.map MsgContact + ModelNotFound m -> Page.NotFound.view m |> Element.map MsgNotFound in Element.el [Element.centerY ,Element.centerX] content diff --git a/src/Header.elm b/frontend/src/Header.elm similarity index 100% rename from src/Header.elm rename to frontend/src/Header.elm diff --git a/src/Main.elm b/frontend/src/Main.elm similarity index 88% rename from src/Main.elm rename to frontend/src/Main.elm index aaedc4b..9b556d7 100644 --- a/src/Main.elm +++ b/frontend/src/Main.elm @@ -34,12 +34,13 @@ init flags url key = model = { key = key , url = url - , page = page + , page = Body.handleRoute url.path , header = header } in (model, Cmd.none) + update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of @@ -51,7 +52,11 @@ update msg model = UrlChanged url -> ( {model | url = url}, Cmd.none ) UrlRequest (Browser.Internal url) -> - ( model, Browser.Navigation.pushUrl model.key (Url.toString url) ) + let + -- newModel = Body.handleRoute url.path + newModel = {model | page = Body.handleRoute url.path} + in + ( newModel, Browser.Navigation.pushUrl model.key (Url.toString url) ) _ -> (model, Cmd.none) subscriptions : Model -> Sub Msg diff --git a/src/Page/About.elm b/frontend/src/Page/About.elm similarity index 100% rename from src/Page/About.elm rename to frontend/src/Page/About.elm diff --git a/src/Page/Contact.elm b/frontend/src/Page/Contact.elm similarity index 100% rename from src/Page/Contact.elm rename to frontend/src/Page/Contact.elm diff --git a/src/Page/Landing.elm b/frontend/src/Page/Landing.elm similarity index 100% rename from src/Page/Landing.elm rename to frontend/src/Page/Landing.elm diff --git a/frontend/src/Page/NotFound.elm b/frontend/src/Page/NotFound.elm new file mode 100644 index 0000000..2504e66 --- /dev/null +++ b/frontend/src/Page/NotFound.elm @@ -0,0 +1,16 @@ +module Page.NotFound exposing (Model, Msg, view, init, update) +import Element exposing (Element) + +type alias Model = {} +type alias Msg = {} + +init : () -> Model +init flags = {} + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = (model, Cmd.none) + +view : Model -> Element Msg +view model = + Element.el [] + <| Element.text "I'm sorry, it seems we don't have that page!" diff --git a/src/Page/Products.elm b/frontend/src/Page/Products.elm similarity index 100% rename from src/Page/Products.elm rename to frontend/src/Page/Products.elm diff --git a/src/Page/Resources.elm b/frontend/src/Page/Resources.elm similarity index 100% rename from src/Page/Resources.elm rename to frontend/src/Page/Resources.elm diff --git a/frontend/src/Ports.elm b/frontend/src/Ports.elm new file mode 100644 index 0000000..be70672 --- /dev/null +++ b/frontend/src/Ports.elm @@ -0,0 +1,13 @@ +port module Ports exposing (socket) + +import Websockets + +port webSocketCommand : Websockets.CommandPort msg +port webSocketEvent : Websockets.EventPort msg + +socket : Websockets.Methods msg +socket = + Websockets.withPorts + { command = webSocketCommand + , event = webSocketEvent + } diff --git a/frontend/src/ports.websocket.js b/frontend/src/ports.websocket.js new file mode 100644 index 0000000..f652ad3 --- /dev/null +++ b/frontend/src/ports.websocket.js @@ -0,0 +1,60 @@ +function initSockets(app) { + var sockets = new Map(); + app.ports.webSocketCommand.subscribe(function (command) { + switch (command.type) { + case "open": { + var name_1 = command.name, url = command.url, meta_1 = command.meta; + var oldSocket = sockets.get(name_1); + if (oldSocket) { + oldSocket.ws.close(-1, "New socket opened with the same name"); + sockets.delete(name_1); + } + var socket = { + ws: new WebSocket(url), + name: name_1, + meta: meta_1, + }; + sockets.set(name_1, socket); + socket.ws.addEventListener("open", function (ev) { + app.ports.webSocketEvent.send({ type: "opened", name: name_1, meta: meta_1 }); + }); + socket.ws.addEventListener("message", function (_a) { + var data = _a.data; + app.ports.webSocketEvent.send({ type: "message", name: name_1, meta: meta_1, data: data }); + }); + socket.ws.addEventListener("close", function (_a) { + var reason = _a.reason; + app.ports.webSocketEvent.send({ type: "closed", name: name_1, meta: meta_1, reason: reason }); + sockets.delete(name_1); + }); + socket.ws.addEventListener("error", function (ev) { + app.ports.webSocketEvent.send({ + type: "error", + name: name_1, + meta: meta_1, + error: null, + }); + sockets.delete(name_1); + }); + break; + } + case "send": { + var name_2 = command.name, data = command.data; + var socket = sockets.get(name_2); + if (socket) { + socket.ws.send(typeof data === "object" ? JSON.stringify(data) : data); + } + break; + } + case "close": { + var name_3 = command.name; + var socket = sockets.get(name_3); + if (socket) { + socket.ws.close(); + sockets.delete(name_3); + } + break; + } + } + }); +} -- 2.47.1 From 66b243a10733fe8a661add72e68f2d5cd2e779f3 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Tue, 31 Dec 2024 21:14:09 -0500 Subject: [PATCH 02/19] now supporting debug and release builds --- Makefile | 15 ++++++++++----- README.md | 9 +++++---- frontend/Makefile | 8 ++++++-- frontend/build.sh | 21 +++++++++++++++++++++ frontend/optimize.sh | 15 --------------- 5 files changed, 42 insertions(+), 26 deletions(-) create mode 100755 frontend/build.sh delete mode 100755 frontend/optimize.sh diff --git a/Makefile b/Makefile index d7a000b..5e2fcaf 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,15 @@ CARGO_RUN := cargo run # Targets .PHONY: all frontend backend clean serve -all: frontend backend +frontend_release: + make -C $(FRONTEND_DIR) release_build + cp $(FRONTEND_DIR)/index.html $(PUBLIC_DIR)/ + cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ + cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ + mkdir -p $(PUBLIC_DIR)/assets -frontend: - make -C $(FRONTEND_DIR) elm.min.js +frontend_debug: + make -C $(FRONTEND_DIR) debug_build cp $(FRONTEND_DIR)/index.html $(PUBLIC_DIR)/ cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ @@ -24,10 +29,10 @@ frontend: backend: $(CARGO_BUILD) --manifest-path=$(BACKEND_DIR)/Cargo.toml -serve: all +serve: frontend_release backend $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml -serve_debug: all +serve_debug: frontend_debug backend RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml clean: diff --git a/README.md b/README.md index f6ea50a..3a631b7 100644 --- a/README.md +++ b/README.md @@ -3,22 +3,23 @@ Example demonstrating how one might architect a single page application Elm app. # Dependencies MacOS +You will need to have rust and cargo installed on MacOS. + ```bash brew install node elm npm install -g uglify-js@2.4.11 ``` # Building -``` -make serve -``` +`make serve` or `make serve_debug` -Now open `http://localhost:8000` in your browser. +Now open `http://127.0.0.1:8080` in your browser. # TODO - [x] Add Makefile - [ ] Add GPLV3 License + - [ ] Add `make release` target that is nix ready... - [ ] Determine if `src/Body.elm` or pages in `src/Page` should have subscription functions - [ ] use actix backend that maps most root requests to serve `actix_file::Files` - [ ] Submit to slack for feedback... diff --git a/frontend/Makefile b/frontend/Makefile index c37a9c6..f3076cd 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -2,6 +2,10 @@ SRC_FILES := $(shell find src -name "*.elm") all: elm.min.js -elm.min.js: $(SRC_FILES) +debug_build: $(SRC_FILES) rm -f elm.min.js elm.js - ./optimize.sh src/Main.elm + ./build.sh --debug src/Main.elm + +release_build: $(SRC_FILES) + rm -f elm.min.js elm.js + ./build.sh --optimize src/Main.elm diff --git a/frontend/build.sh b/frontend/build.sh new file mode 100755 index 0000000..8dac734 --- /dev/null +++ b/frontend/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +js="elm.js" +min="elm.min.js" + +if [ "$1" = "--debug" ]; then + elm make --debug --output=$js "${@:2}" +elif [ "$1" = "--optimize" ]; then + elm make --optimize --output=$js "${@:2}" +else + echo "Error: You must specify either --debug or --optimize." + exit 1 +fi + +uglifyjs $js --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output $min + +echo "Compiled size: $(wc -c < $js) bytes ($js)" +echo "Minified size: $(wc -c < $min) bytes ($min)" +echo "Gzipped size: $(gzip -c $min | wc -c) bytes" diff --git a/frontend/optimize.sh b/frontend/optimize.sh deleted file mode 100755 index 7016792..0000000 --- a/frontend/optimize.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -set -e - -js="elm.js" -min="elm.min.js" - -elm make --debug --output=$js "$@" -# elm make --optimize --debug --output=$js "$@" - -uglifyjs $js --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output $min - -echo "Compiled size:$(wc $js -c) bytes ($js)" -echo "Minified size:$(wc $min -c) bytes ($min)" -echo "Gzipped size: $(gzip $min -c | wc -c) bytes" -- 2.47.1 From be408d89cd27fe9c3a2d9da5f3ac136e52e75cdf Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Tue, 31 Dec 2024 21:21:28 -0500 Subject: [PATCH 03/19] now seemingly properly handling back navigation --- README.md | 1 + frontend/src/Header.elm | 7 ++++++- frontend/src/Main.elm | 9 ++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3a631b7..d26dfa4 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Now open `http://127.0.0.1:8080` in your browser. # TODO - [x] Add Makefile + - [ ] Clicking in upper left should go to landing page. - [ ] Add GPLV3 License - [ ] Add `make release` target that is nix ready... - [ ] Determine if `src/Body.elm` or pages in `src/Page` should have subscription functions diff --git a/frontend/src/Header.elm b/frontend/src/Header.elm index 4f7dd33..9c7c8db 100644 --- a/frontend/src/Header.elm +++ b/frontend/src/Header.elm @@ -27,6 +27,11 @@ view model = { url = "/" ++ string , label = Element.text string } + title = Element.link + [Element.alignLeft] + { url = "/" + , label = Element.text "Elm Example App" + } products = headerButton "Products" resources = headerButton "Resources" @@ -37,7 +42,7 @@ view model = Element.spacing 15, Element.paddingXY 30 25, dropShadow] - [ Element.el [Element.alignLeft] (Element.text "Elm Example App") + [ title , products , resources , about diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index 9b556d7..70e403a 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -50,13 +50,12 @@ update msg model = in ( {model | page = newPage}, cmd |> Cmd.map Body ) UrlChanged url -> - ( {model | url = url}, Cmd.none ) - UrlRequest (Browser.Internal url) -> let - -- newModel = Body.handleRoute url.path - newModel = {model | page = Body.handleRoute url.path} + newModel = {model | page = Body.handleRoute url.path, url = url} in - ( newModel, Browser.Navigation.pushUrl model.key (Url.toString url) ) + ( newModel, Cmd.none ) + UrlRequest (Browser.Internal url) -> + ( model, Browser.Navigation.pushUrl model.key (Url.toString url) ) _ -> (model, Cmd.none) subscriptions : Model -> Sub Msg -- 2.47.1 From b9db2eaa637e8b8b270b9f21bd84a7387eb0b592 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 1 Jan 2025 01:48:55 -0500 Subject: [PATCH 04/19] added websocket dependencies --- frontend/elm.json | 1 + frontend/src/Main.elm | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/elm.json b/frontend/elm.json index 4217bec..c17e85d 100644 --- a/frontend/elm.json +++ b/frontend/elm.json @@ -10,6 +10,7 @@ "elm/core": "1.0.5", "elm/html": "1.0.0", "elm/url": "1.0.0", + "kageurufu/elm-websockets": "1.0.1", "mdgriffith/elm-ui": "1.1.8" }, "indirect": { diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index 70e403a..ca91721 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -9,6 +9,7 @@ import String exposing (right) import Browser.Navigation import Browser exposing (UrlRequest) import Html exposing (header) +import Ports -- internal imports import Body -- 2.47.1 From 18714713b6d3d79db8981e0196ca419b05ad15f0 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 1 Jan 2025 18:51:17 -0500 Subject: [PATCH 05/19] still compiles but probably don't need cleanupSubcriptions function --- README.md | 1 + frontend/elm.json | 2 +- frontend/src/Main.elm | 2 +- frontend/src/Page/About.elm | 16 +++++++++++++++- frontend/src/Page/Contact.elm | 17 ++++++++++++++++- frontend/src/Page/Landing.elm | 16 +++++++++++++++- frontend/src/Page/NotFound.elm | 16 +++++++++++++++- frontend/src/Page/Products.elm | 16 +++++++++++++++- frontend/src/Page/Resources.elm | 16 +++++++++++++++- frontend/src/Ports.elm | 20 +++++++++++++++++++- 10 files changed, 113 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d26dfa4..817e41c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Now open `http://127.0.0.1:8080` in your browser. # TODO - [x] Add Makefile + - [ ] Run `uglify` twice as per [this link](https://github.com/rtfeldman/elm-spa-example/tree/master?tab=readme-ov-file#production-build) - [ ] Clicking in upper left should go to landing page. - [ ] Add GPLV3 License - [ ] Add `make release` target that is nix ready... diff --git a/frontend/elm.json b/frontend/elm.json index c17e85d..9104ca4 100644 --- a/frontend/elm.json +++ b/frontend/elm.json @@ -9,12 +9,12 @@ "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", + "elm/json": "1.1.3", "elm/url": "1.0.0", "kageurufu/elm-websockets": "1.0.1", "mdgriffith/elm-ui": "1.1.8" }, "indirect": { - "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/virtual-dom": "1.0.3" } diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index ca91721..b99b58c 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -39,7 +39,7 @@ init flags url key = , header = header } in - (model, Cmd.none) + (model, Ports.socketOpen) update : Msg -> Model -> (Model, Cmd Msg) diff --git a/frontend/src/Page/About.elm b/frontend/src/Page/About.elm index 92c02a8..8b544e1 100644 --- a/frontend/src/Page/About.elm +++ b/frontend/src/Page/About.elm @@ -1,4 +1,12 @@ -module Page.About exposing (Model, Msg, view, init, update) +module Page.About exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,12 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Page/Contact.elm b/frontend/src/Page/Contact.elm index 3000024..eb0a201 100644 --- a/frontend/src/Page/Contact.elm +++ b/frontend/src/Page/Contact.elm @@ -1,4 +1,12 @@ -module Page.Contact exposing (Model, Msg, view, init, update) +module Page.Contact exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,13 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 3201232..47ddc9d 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -1,4 +1,12 @@ -module Page.Landing exposing (Model, Msg, view, init, update) +module Page.Landing exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,12 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Page/NotFound.elm b/frontend/src/Page/NotFound.elm index 2504e66..0b7badd 100644 --- a/frontend/src/Page/NotFound.elm +++ b/frontend/src/Page/NotFound.elm @@ -1,4 +1,12 @@ -module Page.NotFound exposing (Model, Msg, view, init, update) +module Page.NotFound exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,12 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Page/Products.elm b/frontend/src/Page/Products.elm index 125f98e..ea6d547 100644 --- a/frontend/src/Page/Products.elm +++ b/frontend/src/Page/Products.elm @@ -1,4 +1,12 @@ -module Page.Products exposing (Model, Msg, view, init, update) +module Page.Products exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,12 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Page/Resources.elm b/frontend/src/Page/Resources.elm index 2a7272c..84c32f9 100644 --- a/frontend/src/Page/Resources.elm +++ b/frontend/src/Page/Resources.elm @@ -1,4 +1,12 @@ -module Page.Resources exposing (Model, Msg, view, init, update) +module Page.Resources exposing + ( Model + , Msg + , view + , init + , update + , subscriptions + , cleanupSubcriptions + ) import Element exposing (Element) type alias Model = {} @@ -7,6 +15,12 @@ type alias Msg = {} init : () -> Model init flags = {} +cleanupSubcriptions : Model -> Cmd Msg +cleanupSubcriptions _ = Cmd.none + +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Ports.elm b/frontend/src/Ports.elm index be70672..a12d9ff 100644 --- a/frontend/src/Ports.elm +++ b/frontend/src/Ports.elm @@ -1,10 +1,28 @@ -port module Ports exposing (socket) +port module Ports exposing + ( socketOnEvent + , socketSend + , socketOpen + ) import Websockets +import Json.Encode as Encode port webSocketCommand : Websockets.CommandPort msg port webSocketEvent : Websockets.EventPort msg +socketName : String +socketName = "app" + +socketOnEvent : Websockets.EventHandlers msg -> Sub msg +socketOnEvent eventHandlers = + socket.onEvent eventHandlers + +socketSend : Encode.Value -> Cmd msg +socketSend data = socket.send socketName data + +socketOpen : Cmd msg +socketOpen = socket.open socketName "/ws/" [] + socket : Websockets.Methods msg socket = Websockets.withPorts -- 2.47.1 From 83d8196261fcc97b20c26fa3b2fd48628cbe25d8 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 1 Jan 2025 20:17:48 -0500 Subject: [PATCH 06/19] now routing subscriptions --- frontend/src/Body.elm | 24 +++++++++++++++++------- frontend/src/Header.elm | 12 +++++++++++- frontend/src/Main.elm | 6 ++++-- frontend/src/Page/About.elm | 4 ---- frontend/src/Page/Contact.elm | 4 ---- frontend/src/Page/Landing.elm | 4 ---- frontend/src/Page/NotFound.elm | 4 ---- frontend/src/Page/Products.elm | 4 ---- frontend/src/Page/Resources.elm | 4 ---- 9 files changed, 32 insertions(+), 34 deletions(-) diff --git a/frontend/src/Body.elm b/frontend/src/Body.elm index db7cadc..3cdc0f3 100644 --- a/frontend/src/Body.elm +++ b/frontend/src/Body.elm @@ -1,4 +1,4 @@ -module Body exposing (Msg(..), Model(..), init, update, view, handleRoute) +module Body exposing (Msg(..), Model(..), init, update, view, handleRoute, subscriptions) import Element @@ -28,6 +28,16 @@ type Model init : () -> Model init flags = ModelLanding (Page.Landing.init flags) +subscriptions : Model -> Sub Msg +subscriptions model = + case model of + ModelLanding m -> Page.Landing.subscriptions m |> Sub.map MsgLanding + ModelProducts m -> Page.Products.subscriptions m |> Sub.map MsgProducts + ModelResources m -> Page.Resources.subscriptions m |> Sub.map MsgResources + ModelAbout m -> Page.About.subscriptions m |> Sub.map MsgAbout + ModelContact m -> Page.Contact.subscriptions m |> Sub.map MsgContact + ModelNotFound m -> Page.NotFound.subscriptions m |> Sub.map MsgNotFound + handleRoute : String -> Model handleRoute path = let @@ -74,11 +84,11 @@ view : Model -> Element.Element Msg view model = let content = case model of - ModelLanding m -> Page.Landing.view m |> Element.map MsgLanding - ModelProducts m -> Page.Products.view m |> Element.map MsgProducts - ModelResources m -> Page.Resources.view m |> Element.map MsgResources - ModelAbout m -> Page.About.view m |> Element.map MsgAbout - ModelContact m -> Page.Contact.view m |> Element.map MsgContact - ModelNotFound m -> Page.NotFound.view m |> Element.map MsgNotFound + ModelLanding m -> Page.Landing.view m |> Element.map MsgLanding + ModelProducts m -> Page.Products.view m |> Element.map MsgProducts + ModelResources m -> Page.Resources.view m |> Element.map MsgResources + ModelAbout m -> Page.About.view m |> Element.map MsgAbout + ModelContact m -> Page.Contact.view m |> Element.map MsgContact + ModelNotFound m -> Page.NotFound.view m |> Element.map MsgNotFound in Element.el [Element.centerY ,Element.centerX] content diff --git a/frontend/src/Header.elm b/frontend/src/Header.elm index 9c7c8db..82e2e72 100644 --- a/frontend/src/Header.elm +++ b/frontend/src/Header.elm @@ -1,4 +1,11 @@ -module Header exposing (Model, Msg, view, init, update) +module Header exposing + ( Model + , Msg + , view + , init + , subscriptions + , update + ) import Element exposing (Element) import Element.Events import Element.Border @@ -9,6 +16,9 @@ type alias Msg = {} init : () -> Model init flags = {} +subscriptions : Model -> Sub Msg +subscriptions _ = Sub.none + update : Msg -> Model -> (Model, Cmd Msg) update msg model = (model, Cmd.none) diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index b99b58c..15057fd 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -60,8 +60,10 @@ update msg model = _ -> (model, Cmd.none) subscriptions : Model -> Sub Msg -subscriptions _ = - Sub.none +subscriptions model = Sub.batch + [ model.header |> Header.subscriptions |> Sub.map Header + , model.page |> Body.subscriptions |> Sub.map Body + ] view : Model -> Browser.Document Msg diff --git a/frontend/src/Page/About.elm b/frontend/src/Page/About.elm index 8b544e1..8df3cbc 100644 --- a/frontend/src/Page/About.elm +++ b/frontend/src/Page/About.elm @@ -5,7 +5,6 @@ module Page.About exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none diff --git a/frontend/src/Page/Contact.elm b/frontend/src/Page/Contact.elm index eb0a201..32fc41d 100644 --- a/frontend/src/Page/Contact.elm +++ b/frontend/src/Page/Contact.elm @@ -5,7 +5,6 @@ module Page.Contact exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 47ddc9d..d36ee12 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -5,7 +5,6 @@ module Page.Landing exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none diff --git a/frontend/src/Page/NotFound.elm b/frontend/src/Page/NotFound.elm index 0b7badd..c68f107 100644 --- a/frontend/src/Page/NotFound.elm +++ b/frontend/src/Page/NotFound.elm @@ -5,7 +5,6 @@ module Page.NotFound exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none diff --git a/frontend/src/Page/Products.elm b/frontend/src/Page/Products.elm index ea6d547..d7ebdc1 100644 --- a/frontend/src/Page/Products.elm +++ b/frontend/src/Page/Products.elm @@ -5,7 +5,6 @@ module Page.Products exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none diff --git a/frontend/src/Page/Resources.elm b/frontend/src/Page/Resources.elm index 84c32f9..93e8a48 100644 --- a/frontend/src/Page/Resources.elm +++ b/frontend/src/Page/Resources.elm @@ -5,7 +5,6 @@ module Page.Resources exposing , init , update , subscriptions - , cleanupSubcriptions ) import Element exposing (Element) @@ -15,9 +14,6 @@ type alias Msg = {} init : () -> Model init flags = {} -cleanupSubcriptions : Model -> Cmd Msg -cleanupSubcriptions _ = Cmd.none - subscriptions : Model -> Sub Msg subscriptions _ = Sub.none -- 2.47.1 From a691de6567512e70cca77f84ae6aa319f64898f5 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 1 Jan 2025 22:34:38 -0500 Subject: [PATCH 07/19] now successfully decoding time --- README.md | 2 ++ frontend/src/Main.elm | 1 - frontend/src/Page/Landing.elm | 51 ++++++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 817e41c..302d371 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Now open `http://127.0.0.1:8080` in your browser. # TODO - [x] Add Makefile + - [ ] `EventHandlers.onMessage` should inject successfully decoded message into + Msg type directly... - [ ] Run `uglify` twice as per [this link](https://github.com/rtfeldman/elm-spa-example/tree/master?tab=readme-ov-file#production-build) - [ ] Clicking in upper left should go to landing page. - [ ] Add GPLV3 License diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index 15057fd..38e8b90 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -65,7 +65,6 @@ subscriptions model = Sub.batch , model.page |> Body.subscriptions |> Sub.map Body ] - view : Model -> Browser.Document Msg view model = let diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index d36ee12..fbfbcd4 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -7,20 +7,57 @@ module Page.Landing exposing , subscriptions ) import Element exposing (Element) +import Websockets +import Ports +import Json.Decode as Decode -type alias Model = {} -type alias Msg = {} +type alias Model = { + time : String + } +type Msg + = SocketOpened + | SocketClosed + | SocketMessage String + | NoOp init : () -> Model -init flags = {} +init flags = { + time = "time not yet set" + } + +decodeMessage : String -> String +decodeMessage message = + let + decodedMessage = Decode.decodeString (Decode.field "time" Decode.string) message + in + case decodedMessage of + Ok decoded -> decoded + Err err -> "failed to decode" ++ message subscriptions : Model -> Sub Msg -subscriptions _ = Sub.none +subscriptions model = + Ports.socketOnEvent + (Websockets.EventHandlers + (\_ -> SocketOpened) + (\_ -> SocketClosed) + (\_ -> NoOp) + (\message -> SocketMessage message.data) + (\_ -> NoOp) + ) update : Msg -> Model -> (Model, Cmd Msg) -update msg model = (model, Cmd.none) +update msg model = + case msg of + SocketMessage message -> + let + decoded = decodeMessage message + in + ( {model | time = decoded}, Cmd.none ) + _ -> (model, Cmd.none) view : Model -> Element Msg view model = - Element.el [] - <| Element.text "Landing" + Element.column [] + [ Element.text "Landing", + Element.text <| "Time is : " ++ model.time + ] -- 2.47.1 From 5e00a8080d013c6449081fd4178c71df44d965b4 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Thu, 2 Jan 2025 10:57:13 -0500 Subject: [PATCH 08/19] things still work before major re-write --- README.md | 10 ++++------ backend/src/landing.rs | 7 +++++++ backend/src/main.rs | 19 +++++++++++++++---- frontend/src/Page/Landing.elm | 29 ++++++++++++++++++----------- 4 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 backend/src/landing.rs diff --git a/README.md b/README.md index 302d371..3c8868c 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,10 @@ Now open `http://127.0.0.1:8080` in your browser. - [ ] `EventHandlers.onMessage` should inject successfully decoded message into Msg type directly... - [ ] Run `uglify` twice as per [this link](https://github.com/rtfeldman/elm-spa-example/tree/master?tab=readme-ov-file#production-build) - - [ ] Clicking in upper left should go to landing page. + - [ ] JSONify backend code for all send/receive + - [ ] Backend should only communicate over websocket + - [ ] Close websocket after 15s of no response(from frontend) to websocket pings + - [ ] Implement dark mode - [ ] Add GPLV3 License - [ ] Add `make release` target that is nix ready... - - [ ] Determine if `src/Body.elm` or pages in `src/Page` should have subscription functions - - [ ] use actix backend that maps most root requests to serve `actix_file::Files` - - [ ] Submit to slack for feedback... - - [ ] Refactor into router page - - [ ] Handle back-navigation - [ ] Add `default.nix` diff --git a/backend/src/landing.rs b/backend/src/landing.rs new file mode 100644 index 0000000..eb7af31 --- /dev/null +++ b/backend/src/landing.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + + +#[derive(Serialize)] +pub struct Landing { + pub time : String +} diff --git a/backend/src/main.rs b/backend/src/main.rs index d69d44c..56bb0ed 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,10 +1,12 @@ +use actix::prelude::*; use actix_files::Files; -use actix_web::{web, App, HttpServer, Responder, HttpRequest, HttpResponse, Error}; +use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder}; use actix_web_actors::ws; -use log::{info}; +use log::info; use serde::{Deserialize, Serialize}; use std::{fs, time::Duration}; -use actix::prelude::*; + +mod landing; /// Greeting API structures #[derive(Serialize, Deserialize)] @@ -17,6 +19,11 @@ struct GreetingResponse { message: String, } +#[derive(Serialize)] +enum ToFrontend { + Landing(landing::Landing) +} + async fn greet(req: web::Json) -> impl Responder { info!("Received request to /api/greet with name: {}", req.name); let message = format!("Hello, {}!", req.name); @@ -41,7 +48,11 @@ impl Actor for MyWebSocket { } impl StreamHandler> for MyWebSocket { - fn handle(&mut self, msg: Result, ctx: &mut ws::WebsocketContext) { + fn handle( + &mut self, + msg: Result, + ctx: &mut ws::WebsocketContext, + ) { if let Ok(ws::Message::Ping(msg)) = msg { ctx.pong(&msg); } diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index fbfbcd4..1b2f048 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -14,10 +14,12 @@ import Json.Decode as Decode type alias Model = { time : String } +type alias Landing = { + time : String + } type Msg - = SocketOpened - | SocketClosed - | SocketMessage String + = ToFrontend Landing + | Time String | NoOp init : () -> Model @@ -34,25 +36,30 @@ decodeMessage message = Ok decoded -> decoded Err err -> "failed to decode" ++ message +decodeMessageOld : String -> String +decodeMessageOld message = + let + decodedMessage = Decode.decodeString (Decode.field "time" Decode.string) message + in + case decodedMessage of + Ok decoded -> decoded + Err err -> "failed to decode" ++ message + subscriptions : Model -> Sub Msg subscriptions model = Ports.socketOnEvent (Websockets.EventHandlers - (\_ -> SocketOpened) - (\_ -> SocketClosed) (\_ -> NoOp) - (\message -> SocketMessage message.data) + (\_ -> NoOp) + (\_ -> NoOp) + (\message -> (message.data |> decodeMessageOld |> Time)) (\_ -> NoOp) ) update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - SocketMessage message -> - let - decoded = decodeMessage message - in - ( {model | time = decoded}, Cmd.none ) + Time time -> ( {model | time = time}, Cmd.none ) _ -> (model, Cmd.none) view : Model -> Element Msg -- 2.47.1 From a6fd15c1b1907b2dcec7363e6e5789071ab8dbfb Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Thu, 2 Jan 2025 11:33:01 -0500 Subject: [PATCH 09/19] good stopping point - backend now pushes updates to frontend in consistent JSON format --- backend/src/main.rs | 21 +++++++++++++++++---- frontend/src/Page/Landing.elm | 26 +++++++++----------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 56bb0ed..929de35 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -5,8 +5,8 @@ use actix_web_actors::ws; use log::info; use serde::{Deserialize, Serialize}; use std::{fs, time::Duration}; +use chrono::Local; -mod landing; /// Greeting API structures #[derive(Serialize, Deserialize)] @@ -19,9 +19,14 @@ struct GreetingResponse { message: String, } +#[derive(Serialize)] +pub struct Landing { + pub time : String +} + #[derive(Serialize)] enum ToFrontend { - Landing(landing::Landing) + Landing(Landing) } async fn greet(req: web::Json) -> impl Responder { @@ -40,8 +45,16 @@ impl Actor for MyWebSocket { info!("WebSocket actor started"); // Send messages every second ctx.run_interval(Duration::from_secs(1), |_, ctx| { - let message = format!("{{\"time\" : \"{:?}\" }}", chrono::Local::now()); - ctx.text(message); + // Format the current time as HH:mm:ss + let current_time = Local::now().format("%H:%M:%S").to_string(); + let message = ToFrontend::Landing(Landing { time: current_time }); + + // Serialize the message to JSON + if let Ok(json_message) = serde_json::to_string(&message) { + ctx.text(json_message); + } else { + info!("Failed to serialize the message"); + } }); info!("Leaving started"); } diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 1b2f048..4ad6c19 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -19,7 +19,7 @@ type alias Landing = { } type Msg = ToFrontend Landing - | Time String + | DecodeError Decode.Error | NoOp init : () -> Model @@ -27,23 +27,15 @@ init flags = { time = "time not yet set" } -decodeMessage : String -> String -decodeMessage message = +decodeToFrontend message = let - decodedMessage = Decode.decodeString (Decode.field "time" Decode.string) message + decodeTime = Decode.field "time" Decode.string |> Decode.map Landing + decodeLanding = Decode.field "Landing" decodeTime |> Decode.map ToFrontend + decodedMessage = Decode.decodeString decodeLanding message in case decodedMessage of Ok decoded -> decoded - Err err -> "failed to decode" ++ message - -decodeMessageOld : String -> String -decodeMessageOld message = - let - decodedMessage = Decode.decodeString (Decode.field "time" Decode.string) message - in - case decodedMessage of - Ok decoded -> decoded - Err err -> "failed to decode" ++ message + Err err -> DecodeError err subscriptions : Model -> Sub Msg subscriptions model = @@ -52,19 +44,19 @@ subscriptions model = (\_ -> NoOp) (\_ -> NoOp) (\_ -> NoOp) - (\message -> (message.data |> decodeMessageOld |> Time)) + (\message -> (message.data |> decodeToFrontend)) (\_ -> NoOp) ) update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - Time time -> ( {model | time = time}, Cmd.none ) + ToFrontend landing -> ( {model | time = landing.time}, Cmd.none ) _ -> (model, Cmd.none) view : Model -> Element Msg view model = Element.column [] [ Element.text "Landing", - Element.text <| "Time is : " ++ model.time + Element.text <| "Current time is : " ++ model.time ] -- 2.47.1 From 3057c21f673ac63dfb1da14a1783d7e7974d7d4b Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Thu, 2 Jan 2025 23:24:10 -0500 Subject: [PATCH 10/19] now using mod landing as its own module --- backend/src/main.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 929de35..96a2df4 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize}; use std::{fs, time::Duration}; use chrono::Local; +mod landing; + /// Greeting API structures #[derive(Serialize, Deserialize)] @@ -19,20 +21,10 @@ struct GreetingResponse { message: String, } -#[derive(Serialize)] -pub struct Landing { - pub time : String -} #[derive(Serialize)] enum ToFrontend { - Landing(Landing) -} - -async fn greet(req: web::Json) -> impl Responder { - info!("Received request to /api/greet with name: {}", req.name); - let message = format!("Hello, {}!", req.name); - web::Json(GreetingResponse { message }) + Landing(landing::Landing) } /// WebSocket actor @@ -43,20 +35,14 @@ impl Actor for MyWebSocket { fn started(&mut self, ctx: &mut Self::Context) { info!("WebSocket actor started"); - // Send messages every second ctx.run_interval(Duration::from_secs(1), |_, ctx| { - // Format the current time as HH:mm:ss let current_time = Local::now().format("%H:%M:%S").to_string(); - let message = ToFrontend::Landing(Landing { time: current_time }); + let message = ToFrontend::Landing(landing::Landing { time: current_time }); - // Serialize the message to JSON if let Ok(json_message) = serde_json::to_string(&message) { ctx.text(json_message); - } else { - info!("Failed to serialize the message"); } }); - info!("Leaving started"); } } @@ -87,12 +73,10 @@ async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() - .route("/api/greet", web::post().to(greet)) // Greeting API .route("/ws/", web::get().to(websocket_handler)) // WebSocket endpoint .service(Files::new("/assets", "./public/assets")) .service(Files::new("/", "./public").index_file("index.html")) // Serve frontend .default_service(web::route().to(|| async { - // Serve the `index.html` file let index_html = fs::read_to_string("./public/index.html") .unwrap_or_else(|_| "404 Not Found".to_string()); HttpResponse::Ok() -- 2.47.1 From 7a59aab77dba1469a18a94310cc901780573856e Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Fri, 3 Jan 2025 08:07:44 -0500 Subject: [PATCH 11/19] hopefully building properly now --- .gitignore | 1 + Makefile | 41 ++++++++++++++++++++++++++++------------- frontend/Makefile | 18 +++++++++++++----- frontend/build.sh | 5 +++-- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index a550ae4..dc3b057 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ frontend/package-lock.json frontend/package.json backend/target public/* +elm-stuff/* diff --git a/Makefile b/Makefile index 5e2fcaf..a5a283a 100644 --- a/Makefile +++ b/Makefile @@ -9,19 +9,33 @@ ELM_MAKE := elm make CARGO_BUILD := cargo build CARGO_RUN := cargo run +ifeq ($(DEBUG)$(RELEASE),) # Both are empty +$(error You must set exactly one of DEBUG=1 or RELEASE=1) +endif + +ifeq ($(DEBUG)$(RELEASE),11) # Both are set +$(error Both DEBUG and RELEASE cannot be set at the same time) +endif + # Targets .PHONY: all frontend backend clean serve -frontend_release: - make -C $(FRONTEND_DIR) release_build - cp $(FRONTEND_DIR)/index.html $(PUBLIC_DIR)/ - cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ - cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ - mkdir -p $(PUBLIC_DIR)/assets +$(PUBLIC_DIR): + mkdir -p $(PUBLIC_DIR) -frontend_debug: - make -C $(FRONTEND_DIR) debug_build - cp $(FRONTEND_DIR)/index.html $(PUBLIC_DIR)/ +$(PUBLIC_DIR)/index.html: $(FRONTEND_DIR)/index.html $(PUBLIC_DIR) + cp $< $@ + +$(FRONTEND_DIR)/elm.min.js: +ifeq ($(DEBUG),1) + make -C $(FRONTEND_DIR) DEBUG=1 +else ifeq ($(RELEASE),1) + make -C $(FRONTEND_DIR) RELEASE=1 +endif + +.PHONY: frontend backend + +frontend: $(PUBLIC_DIR)/index.html $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR) cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ mkdir -p $(PUBLIC_DIR)/assets @@ -29,11 +43,12 @@ frontend_debug: backend: $(CARGO_BUILD) --manifest-path=$(BACKEND_DIR)/Cargo.toml -serve: frontend_release backend - $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml - -serve_debug: frontend_debug backend +serve: frontend backend +ifeq ($(DEBUG),1) RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml +else ifeq ($(RELEASE),1) + $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml +endif clean: rm -rf $(PUBLIC_DIR)/* diff --git a/frontend/Makefile b/frontend/Makefile index f3076cd..a7c0b10 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -2,10 +2,18 @@ SRC_FILES := $(shell find src -name "*.elm") all: elm.min.js -debug_build: $(SRC_FILES) - rm -f elm.min.js elm.js - ./build.sh --debug src/Main.elm +ifeq ($(DEBUG)$(RELEASE),) # Both are empty +$(error You must set exactly one of DEBUG=1 or RELEASE=1) +endif -release_build: $(SRC_FILES) - rm -f elm.min.js elm.js +ifeq ($(DEBUG)$(RELEASE),11) # Both are set +$(error Both DEBUG and RELEASE cannot be set at the same time) +endif + + +elm.min.js: $(SRC_FILES) +ifeq ($(DEBUG),1) + ./build.sh --debug src/Main.elm +else ifeq ($(RELEASE),1) ./build.sh --optimize src/Main.elm +endif \ No newline at end of file diff --git a/frontend/build.sh b/frontend/build.sh index 8dac734..e64570f 100755 --- a/frontend/build.sh +++ b/frontend/build.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -e +set -ex js="elm.js" min="elm.min.js" @@ -14,7 +14,8 @@ else exit 1 fi -uglifyjs $js --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output $min +uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters=true,keep_fargs=false,unsafe_comps=true,unsafe=true,passes=2' --output=elm.js +uglifyjs elm.js --mangle --output=elm.js echo "Compiled size: $(wc -c < $js) bytes ($js)" echo "Minified size: $(wc -c < $min) bytes ($min)" -- 2.47.1 From db0066980067ac0f1ecef76064389f7031115597 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Fri, 3 Jan 2025 13:29:56 +0000 Subject: [PATCH 12/19] drastically improve build scripts --- Makefile | 10 +++------- README.md | 12 ++++-------- frontend/Makefile | 5 ++++- frontend/build.sh | 4 ++-- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index a5a283a..7492640 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,7 @@ $(PUBLIC_DIR)/index.html: $(FRONTEND_DIR)/index.html $(PUBLIC_DIR) cp $< $@ $(FRONTEND_DIR)/elm.min.js: -ifeq ($(DEBUG),1) - make -C $(FRONTEND_DIR) DEBUG=1 -else ifeq ($(RELEASE),1) - make -C $(FRONTEND_DIR) RELEASE=1 -endif + make -C $(FRONTEND_DIR) .PHONY: frontend backend @@ -47,10 +43,10 @@ serve: frontend backend ifeq ($(DEBUG),1) RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml else ifeq ($(RELEASE),1) - $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml + $(CARGO_RUN) --release --manifest-path=$(BACKEND_DIR)/Cargo.toml endif clean: rm -rf $(PUBLIC_DIR)/* rm -rf $(BACKEND_DIR)/target - + make -C $(FRONTEND_DIR) clean diff --git a/README.md b/README.md index 3c8868c..007244b 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,12 @@ Example demonstrating how one might architect a single page application Elm app. -# Dependencies MacOS -You will need to have rust and cargo installed on MacOS. - -```bash -brew install node elm -npm install -g uglify-js@2.4.11 -``` # Building -`make serve` or `make serve_debug` +```bash +nix-shell -p elmPackages.elm cargo uglify-js +make serve RELEASE=1 # can also do DEBUG=1 instead +``` Now open `http://127.0.0.1:8080` in your browser. diff --git a/frontend/Makefile b/frontend/Makefile index a7c0b10..bd0b024 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -16,4 +16,7 @@ ifeq ($(DEBUG),1) ./build.sh --debug src/Main.elm else ifeq ($(RELEASE),1) ./build.sh --optimize src/Main.elm -endif \ No newline at end of file +endif + +clean: + rm elm.js elm.min.js diff --git a/frontend/build.sh b/frontend/build.sh index e64570f..e357801 100755 --- a/frontend/build.sh +++ b/frontend/build.sh @@ -14,8 +14,8 @@ else exit 1 fi -uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters=true,keep_fargs=false,unsafe_comps=true,unsafe=true,passes=2' --output=elm.js -uglifyjs elm.js --mangle --output=elm.js +uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters=true,keep_fargs=false,unsafe_comps=true,unsafe=true,passes=2' --output elm.js +uglifyjs elm.js --mangle --output $min echo "Compiled size: $(wc -c < $js) bytes ($js)" echo "Minified size: $(wc -c < $min) bytes ($min)" -- 2.47.1 From ccec612687137299c4c740449e9c8aa667865fbb Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Fri, 3 Jan 2025 10:58:40 -0500 Subject: [PATCH 13/19] now has text box --- Makefile | 6 ++---- frontend/src/Page/Landing.elm | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 7492640..3159b93 100644 --- a/Makefile +++ b/Makefile @@ -26,12 +26,9 @@ $(PUBLIC_DIR): $(PUBLIC_DIR)/index.html: $(FRONTEND_DIR)/index.html $(PUBLIC_DIR) cp $< $@ -$(FRONTEND_DIR)/elm.min.js: - make -C $(FRONTEND_DIR) - .PHONY: frontend backend -frontend: $(PUBLIC_DIR)/index.html $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR) +frontend: $(PUBLIC_DIR)/index.html $(PUBLIC_DIR) cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ mkdir -p $(PUBLIC_DIR)/assets @@ -40,6 +37,7 @@ backend: $(CARGO_BUILD) --manifest-path=$(BACKEND_DIR)/Cargo.toml serve: frontend backend + make -C $(FRONTEND_DIR) ifeq ($(DEBUG),1) RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml else ifeq ($(RELEASE),1) diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 4ad6c19..05340d0 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -10,6 +10,8 @@ import Element exposing (Element) import Websockets import Ports import Json.Decode as Decode +import Html.Attributes exposing (placeholder) +import Element.Input type alias Model = { time : String @@ -20,6 +22,7 @@ type alias Landing = { type Msg = ToFrontend Landing | DecodeError Decode.Error + | GreetWidgetText String | NoOp init : () -> Model @@ -54,9 +57,23 @@ update msg model = ToFrontend landing -> ( {model | time = landing.time}, Cmd.none ) _ -> (model, Cmd.none) +greetWidget = + let + textInput = + Element.Input.text [] + { onChange = GreetWidgetText + , text = "text" + , placeholder = Nothing + , label = Element.Input.labelAbove [] (Element.text "Greet") + } + in + Element.row [] + [ textInput ] + view : Model -> Element Msg view model = Element.column [] - [ Element.text "Landing", - Element.text <| "Current time is : " ++ model.time + [ Element.text "Landing" + -- , Element.text <| "Current time is : " ++ model.time + , greetWidget ] -- 2.47.1 From 45ca677c87a7a10a7c0e0e71340f4b3f75e5c4d7 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Fri, 3 Jan 2025 11:09:13 -0500 Subject: [PATCH 14/19] now text box label is hidden --- frontend/Makefile | 4 +++- frontend/src/Page/Landing.elm | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/Makefile b/frontend/Makefile index bd0b024..31d4a7f 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -2,6 +2,8 @@ SRC_FILES := $(shell find src -name "*.elm") all: elm.min.js +.PHONY: elm.min.js + ifeq ($(DEBUG)$(RELEASE),) # Both are empty $(error You must set exactly one of DEBUG=1 or RELEASE=1) endif @@ -11,7 +13,7 @@ $(error Both DEBUG and RELEASE cannot be set at the same time) endif -elm.min.js: $(SRC_FILES) +elm.min.js: ifeq ($(DEBUG),1) ./build.sh --debug src/Main.elm else ifeq ($(RELEASE),1) diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 05340d0..534c40c 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -64,7 +64,7 @@ greetWidget = { onChange = GreetWidgetText , text = "text" , placeholder = Nothing - , label = Element.Input.labelAbove [] (Element.text "Greet") + , label = Element.Input.labelHidden "Greet" } in Element.row [] @@ -74,6 +74,6 @@ view : Model -> Element Msg view model = Element.column [] [ Element.text "Landing" - -- , Element.text <| "Current time is : " ++ model.time + , Element.text <| "Current time is : " ++ model.time , greetWidget ] -- 2.47.1 From 2b49ed618d407d5449b8392bcd6e13bac8fe6fa2 Mon Sep 17 00:00:00 2001 From: Artturin Date: Sat, 4 Jan 2025 00:34:36 +0200 Subject: [PATCH 15/19] Add flake --- .gitignore | 1 + default.nix | 25 ++++++ flake.lock | 197 ++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 119 ++++++++++++++++++++++++++++ frontend/build.sh | 2 +- shell.nix | 25 ++++++ 6 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index dc3b057..83c9314 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ frontend/package.json backend/target public/* elm-stuff/* +result diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..2ba2a3e --- /dev/null +++ b/default.nix @@ -0,0 +1,25 @@ +{ + system ? builtins.currentSystem, +}: +let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + + root = lock.nodes.${lock.root}; + inherit (lock.nodes.${root.inputs.flake-compat}.locked) + owner + repo + rev + narHash + ; + + flake-compat = fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; + sha256 = narHash; + }; + + flake = import flake-compat { + inherit system; + src = ./.; + }; +in +flake.defaultNix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..2450164 --- /dev/null +++ b/flake.lock @@ -0,0 +1,197 @@ +{ + "nodes": { + "elm-spa": { + "inputs": { + "nixpkgs": [ + "mkElmDerivation", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1706301604, + "narHash": "sha256-n6LDjnPCTLbKTrRgeZhlLTfY6V45xNYcb4NYEMuO4jg=", + "owner": "jeslie0", + "repo": "elm-spa", + "rev": "4c82e18d5fcf9d4c027f0ef0e89204dd87584f7f", + "type": "github" + }, + "original": { + "owner": "jeslie0", + "repo": "elm-spa", + "type": "github" + } + }, + "elm-watch": { + "inputs": { + "nixpkgs": [ + "mkElmDerivation", + "nixpkgs" + ], + "npm-fix": "npm-fix", + "npmlock2nix": "npmlock2nix" + }, + "locked": { + "lastModified": 1706304401, + "narHash": "sha256-992cypnhoRbsGkDc5/X241rafBML4EP0EuT6cBcaY/8=", + "owner": "jeslie0", + "repo": "elm-watch", + "rev": "2f1c6c0e69b163c15e2ce66f543c38021b2a0ea3", + "type": "github" + }, + "original": { + "owner": "jeslie0", + "repo": "elm-watch", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "mkElmDerivation": { + "inputs": { + "elm-spa": "elm-spa", + "elm-watch": "elm-watch", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1735436157, + "narHash": "sha256-G7gF5z0HEQVaLJuygUoCSJs27wQdautyMsciFYdpNCo=", + "owner": "jeslie0", + "repo": "mkElmDerivation", + "rev": "f369fd6ff78205821c3f409814e840691be645e7", + "type": "github" + }, + "original": { + "owner": "jeslie0", + "repo": "mkElmDerivation", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1696757521, + "narHash": "sha256-cfgtLNCBLFx2qOzRLI6DHfqTdfWI+UbvsKYa3b3fvaA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2646b294a146df2781b1ca49092450e8a32814e1", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1735821806, + "narHash": "sha256-cuNapx/uQeCgeuhUhdck3JKbgpsml259sjUQnWM7zW8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d6973081434f88088e5321f83ebafe9a1167c367", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "npm-fix": { + "inputs": { + "nixpkgs": [ + "mkElmDerivation", + "elm-watch", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1706304213, + "narHash": "sha256-XN9ESRSOANR0iFbEMMY1C1jvgZlYJsXQYVAHxxRmn+c=", + "owner": "jeslie0", + "repo": "npm-lockfile-fix", + "rev": "e9851274afa12b04d98e694ed089aa9cde8d7349", + "type": "github" + }, + "original": { + "owner": "jeslie0", + "repo": "npm-lockfile-fix", + "type": "github" + } + }, + "npmlock2nix": { + "flake": false, + "locked": { + "lastModified": 1673447413, + "narHash": "sha256-sJM82Sj8yfQYs9axEmGZ9Evzdv/kDcI9sddqJ45frrU=", + "owner": "nix-community", + "repo": "npmlock2nix", + "rev": "9197bbf397d76059a76310523d45df10d2e4ca81", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "npmlock2nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "mkElmDerivation": "mkElmDerivation", + "nixpkgs": "nixpkgs_2", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..aa446c5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,119 @@ +{ + inputs = { + nixpkgs = { + url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + utils.url = "github:numtide/flake-utils"; + mkElmDerivation.url = "github:jeslie0/mkElmDerivation"; + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + }; + + outputs = + inputs: + inputs.utils.lib.eachDefaultSystem ( + system: + let + pkgs = import inputs.nixpkgs { + localSystem = system; + overlays = [ + inputs.mkElmDerivation.overlays.default + (final: prev: { + example-spa-elm-app = prev.callPackage ( + { + stdenv, + lib, + elmPackages, + uglify-js, + rustPlatform, + cargo, + rustc, + }: + stdenv.mkDerivation ( + let + allPackagesJsonPath = "${inputs.mkElmDerivation}/mkElmDerivation/all-packages.json"; + elmHashesJsonPath = "${inputs.mkElmDerivation}/mkElmDerivation/elm-hashes.json"; + snapshot = import "${inputs.mkElmDerivation}/src/snapshot/default.nix" ( + prev.haskellPackages // { inherit lib; } + ); + elmJson = ./frontend/elm.json; + in + { + pname = "example-spa-elm-app"; + version = "0.1.0"; + src = inputs.self; + + nativeBuildInputs = [ + elmPackages.elm + uglify-js + rustPlatform.cargoSetupHook + cargo + rustc + ]; + + cargoDeps = pkgs.rustPlatform.importCargoLock { + lockFile = ./backend/Cargo.lock; + allowBuiltinFetchGit = true; + }; + + cargoRoot = "backend"; + + makeFlags = [ + "RELEASE=1" + "frontend" + "backend" + ]; + + postPatch = '' + patchShebangs ./frontend/build.sh + ''; + + preBuild = '' + ( + cd frontend + ${ + (import "${inputs.mkElmDerivation}/nix/lib.nix" { + inherit + stdenv + lib + snapshot + allPackagesJsonPath + ; + }).mkDotElmCommand + elmHashesJsonPath + elmJson + } + ) + export ELM_HOME=$PWD/frontend/.elm + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out + cp -r ./public $out/ + cp ./backend/target/debug/backend $out/ + runHook postInstall + ''; + } + ) + ) { }; + }) + ]; + }; + in + { + packages = { + default = inputs.self.packages."${system}".example-spa-elm-app; + example-spa-elm-app = pkgs.example-spa-elm-app; + }; + + devShells.default = + with pkgs; + mkShell { + inputsFrom = [ example-spa-elm-app ]; + }; + } + ); +} diff --git a/frontend/build.sh b/frontend/build.sh index e357801..017fe35 100755 --- a/frontend/build.sh +++ b/frontend/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -ex diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..9332b75 --- /dev/null +++ b/shell.nix @@ -0,0 +1,25 @@ +{ + system ? builtins.currentSystem, +}: +let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + + root = lock.nodes.${lock.root}; + inherit (lock.nodes.${root.inputs.flake-compat}.locked) + owner + repo + rev + narHash + ; + + flake-compat = fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; + sha256 = narHash; + }; + + flake = import flake-compat { + inherit system; + src = ./.; + }; +in +flake.shellNix -- 2.47.1 From 9ebdbdb36a62ed9b82887a18df85d700da20dbf0 Mon Sep 17 00:00:00 2001 From: Artturin Date: Sat, 4 Jan 2025 01:01:58 +0200 Subject: [PATCH 16/19] Makefile: fix frontend build Its make was moved to `serve` target in ccec612687137299c4c740449e9c8aa667865fbb, but we can't use `serve` target in the nix build It's also run after `frontend` target is already built which probably causes the `frontend` target to build. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3159b93..ae86c3e 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ $(PUBLIC_DIR)/index.html: $(FRONTEND_DIR)/index.html $(PUBLIC_DIR) .PHONY: frontend backend frontend: $(PUBLIC_DIR)/index.html $(PUBLIC_DIR) + make -C $(FRONTEND_DIR) cp $(FRONTEND_DIR)/elm.min.js $(PUBLIC_DIR)/ cp $(FRONTEND_DIR)/src/ports.websocket.js $(PUBLIC_DIR)/ mkdir -p $(PUBLIC_DIR)/assets @@ -37,7 +38,6 @@ backend: $(CARGO_BUILD) --manifest-path=$(BACKEND_DIR)/Cargo.toml serve: frontend backend - make -C $(FRONTEND_DIR) ifeq ($(DEBUG),1) RUST_LOG=info,actix_web=debug $(CARGO_RUN) --manifest-path=$(BACKEND_DIR)/Cargo.toml else ifeq ($(RELEASE),1) -- 2.47.1 From 21627fb3b9345e9618a869b08d309e7b40ba2942 Mon Sep 17 00:00:00 2001 From: Artturin Date: Sat, 4 Jan 2025 01:20:22 +0200 Subject: [PATCH 17/19] Make the port configurable --- backend/src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 96a2df4..d9b1fe7 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -67,7 +67,10 @@ async fn main() -> std::io::Result<()> { env_logger::init(); let address = "127.0.0.1"; - let port = 8080; + let port = std::env::var("EXAMPLE_ELM_APP_PORT") + .unwrap_or("8080".to_string()) + .parse() + .unwrap(); info!("Starting server at http://{}:{}", address, port); -- 2.47.1 From bdaee5113002dc4979808ed294bbd99bce799805 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Sun, 5 Jan 2025 21:25:10 -0500 Subject: [PATCH 18/19] Saving work before I start a notable re-factor Going to make major changes to how messages are routed/handled. --- backend/src/landing.rs | 11 ++++- backend/src/main.rs | 19 ++------- frontend/src/Page/Landing.elm | 79 +++++++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/backend/src/landing.rs b/backend/src/landing.rs index eb7af31..22ae6f5 100644 --- a/backend/src/landing.rs +++ b/backend/src/landing.rs @@ -2,6 +2,13 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize)] -pub struct Landing { - pub time : String +pub enum DownMsg { + Greeting(String), + TimeUpdate(String), +} + + +#[derive(Serialize)] +pub enum UpMsg { + RequestGreet(String) } diff --git a/backend/src/main.rs b/backend/src/main.rs index d9b1fe7..afb4f56 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -9,22 +9,9 @@ use chrono::Local; mod landing; - -/// Greeting API structures -#[derive(Serialize, Deserialize)] -struct GreetingRequest { - name: String, -} - #[derive(Serialize)] -struct GreetingResponse { - message: String, -} - - -#[derive(Serialize)] -enum ToFrontend { - Landing(landing::Landing) +enum DownMsg { + Landing(landing::DownMsg), } /// WebSocket actor @@ -37,7 +24,7 @@ impl Actor for MyWebSocket { info!("WebSocket actor started"); ctx.run_interval(Duration::from_secs(1), |_, ctx| { let current_time = Local::now().format("%H:%M:%S").to_string(); - let message = ToFrontend::Landing(landing::Landing { time: current_time }); + let message = DownMsg::Landing(landing::DownMsg::TimeUpdate(current_time)); if let Ok(json_message) = serde_json::to_string(&message) { ctx.text(json_message); diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index 534c40c..a4e7aba 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -10,36 +10,52 @@ import Element exposing (Element) import Websockets import Ports import Json.Decode as Decode +import Json.Encode as Encode exposing (Value) import Html.Attributes exposing (placeholder) import Element.Input +import Element.Background type alias Model = { - time : String - } -type alias Landing = { - time : String + time : String, + greetWidgetText : String } +type DownMsgLanding + = Greeting String + | TimeUpdate String + +decodeDownMsgLanding : Decode.Decoder DownMsgLanding +decodeDownMsgLanding = + Decode.oneOf + [ Decode.map Greeting (Decode.field "Greeting" Decode.string) + , Decode.map TimeUpdate (Decode.field "TimeUpdate" Decode.string) + ] + +decodeDownMsg : Decode.Decoder Msg +decodeDownMsg = + Decode.map DownMsg decodeDownMsgLanding + +type UpMsgLanding = RequestGreet String + +encodeUpMsgLanding : UpMsgLanding -> Value +encodeUpMsgLanding msg = + case msg of + RequestGreet name -> + Encode.object + [ ( "RequestGreet", Encode.string name ) ] + type Msg - = ToFrontend Landing + = DownMsg DownMsgLanding + | UpMsg UpMsgLanding | DecodeError Decode.Error | GreetWidgetText String | NoOp init : () -> Model init flags = { - time = "time not yet set" + time = "time not yet set", + greetWidgetText = "" } -decodeToFrontend message = - let - decodeTime = Decode.field "time" Decode.string |> Decode.map Landing - decodeLanding = Decode.field "Landing" decodeTime |> Decode.map ToFrontend - decodedMessage = Decode.decodeString decodeLanding message - in - case decodedMessage of - Ok decoded -> decoded - Err err -> DecodeError err - subscriptions : Model -> Sub Msg subscriptions model = Ports.socketOnEvent @@ -47,23 +63,42 @@ subscriptions model = (\_ -> NoOp) (\_ -> NoOp) (\_ -> NoOp) - (\message -> (message.data |> decodeToFrontend)) + (\message -> + case Decode.decodeString decodeDownMsg message.data of + Ok msg -> msg + Err err -> DecodeError err + ) (\_ -> NoOp) ) update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - ToFrontend landing -> ( {model | time = landing.time}, Cmd.none ) + DownMsg (TimeUpdate time) -> ( {model | time = time}, Cmd.none ) + GreetWidgetText text -> ( {model | greetWidgetText = text}, Cmd.none ) _ -> (model, Cmd.none) -greetWidget = +greetWidget : Model -> Element Msg +greetWidget model = let + idleBlue = Element.Background.color <| Element.rgb255 18 147 217 + focusBlue = Element.Background.color <| Element.rgb255 18 125 184 + + myButton = Element.Input.button + [ idleBlue + , Element.focused [ focusBlue ] + ] + { onPress = Just <| DownMsg <| Greeting model.greetWidgetText + , label = Element.text "My Button" + } + placeholder = case model.greetWidgetText of + "" -> Just <| Element.Input.placeholder [] <| Element.text "Type Your Name" + _ -> Just <| Element.Input.placeholder [] <| Element.text "" textInput = Element.Input.text [] { onChange = GreetWidgetText - , text = "text" - , placeholder = Nothing + , text = model.greetWidgetText + , placeholder = placeholder , label = Element.Input.labelHidden "Greet" } in @@ -75,5 +110,5 @@ view model = Element.column [] [ Element.text "Landing" , Element.text <| "Current time is : " ++ model.time - , greetWidget + , greetWidget model ] -- 2.47.1 From ee818a553649e3a3217af20a386d518e1cedd3f0 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Mon, 6 Jan 2025 01:23:48 -0500 Subject: [PATCH 19/19] architecture basically done --- backend/src/landing.rs | 2 +- backend/src/main.rs | 28 ++++++++++++++-- frontend/src/Page/Landing.elm | 60 ++++++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/backend/src/landing.rs b/backend/src/landing.rs index 22ae6f5..163bbe2 100644 --- a/backend/src/landing.rs +++ b/backend/src/landing.rs @@ -8,7 +8,7 @@ pub enum DownMsg { } -#[derive(Serialize)] +#[derive(Deserialize)] pub enum UpMsg { RequestGreet(String) } diff --git a/backend/src/main.rs b/backend/src/main.rs index afb4f56..4e1f604 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -14,6 +14,11 @@ enum DownMsg { Landing(landing::DownMsg), } +#[derive(Deserialize)] +enum UpMsg { + Landing(landing::UpMsg), +} + /// WebSocket actor struct MyWebSocket; @@ -39,12 +44,31 @@ impl StreamHandler> for MyWebSocket { msg: Result, ctx: &mut ws::WebsocketContext, ) { - if let Ok(ws::Message::Ping(msg)) = msg { - ctx.pong(&msg); + match msg { + Ok(ws::Message::Text(text)) => { + // Attempt to decode the incoming message + if let Ok(up_msg) = serde_json::from_str::(&text) { + match up_msg { + UpMsg::Landing(landing::UpMsg::RequestGreet(name)) => { + let greeting = format!("Hello, {}!", name); + let response = DownMsg::Landing(landing::DownMsg::Greeting(greeting)); + + if let Ok(json_response) = serde_json::to_string(&response) { + ctx.text(json_response); + } + } + } + } + } + Ok(ws::Message::Ping(msg)) => { + ctx.pong(&msg); + } + _ => {} } } } + async fn websocket_handler(req: HttpRequest, stream: web::Payload) -> Result { ws::start(MyWebSocket {}, &req, stream) } diff --git a/frontend/src/Page/Landing.elm b/frontend/src/Page/Landing.elm index a4e7aba..8c039c0 100644 --- a/frontend/src/Page/Landing.elm +++ b/frontend/src/Page/Landing.elm @@ -17,7 +17,8 @@ import Element.Background type alias Model = { time : String, - greetWidgetText : String + greetWidgetText : String, + greeting : String } type DownMsgLanding = Greeting String @@ -25,14 +26,20 @@ type DownMsgLanding decodeDownMsgLanding : Decode.Decoder DownMsgLanding decodeDownMsgLanding = - Decode.oneOf - [ Decode.map Greeting (Decode.field "Greeting" Decode.string) - , Decode.map TimeUpdate (Decode.field "TimeUpdate" Decode.string) - ] + let + t = Decode.map Greeting (Decode.field "Greeting" Decode.string) + in + Decode.oneOf + [ Decode.map Greeting (Decode.field "Greeting" Decode.string) + , Decode.map TimeUpdate (Decode.field "TimeUpdate" Decode.string) + ] decodeDownMsg : Decode.Decoder Msg decodeDownMsg = - Decode.map DownMsg decodeDownMsgLanding + let + decoder = Decode.field "Landing" decodeDownMsgLanding + in + Decode.map DownMsg decoder type UpMsgLanding = RequestGreet String @@ -41,7 +48,10 @@ encodeUpMsgLanding msg = case msg of RequestGreet name -> Encode.object - [ ( "RequestGreet", Encode.string name ) ] + [ ( "Landing", Encode.object + [ ( "RequestGreet", Encode.string name ) ] + ) + ] type Msg = DownMsg DownMsgLanding @@ -53,7 +63,8 @@ type Msg init : () -> Model init flags = { time = "time not yet set", - greetWidgetText = "" + greetWidgetText = "", + greeting = "" } subscriptions : Model -> Sub Msg @@ -75,35 +86,48 @@ update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of DownMsg (TimeUpdate time) -> ( {model | time = time}, Cmd.none ) + DownMsg (Greeting greeting) -> ( {model | greeting = greeting}, Cmd.none) + UpMsg upMsgLanding -> ( + model, + upMsgLanding |> encodeUpMsgLanding |> Ports.socketSend + ) GreetWidgetText text -> ( {model | greetWidgetText = text}, Cmd.none ) _ -> (model, Cmd.none) greetWidget : Model -> Element Msg greetWidget model = let - idleBlue = Element.Background.color <| Element.rgb255 18 147 217 - focusBlue = Element.Background.color <| Element.rgb255 18 125 184 + idleBlue = Element.rgb255 18 147 217 + focusBlue = Element.rgb255 18 125 184 myButton = Element.Input.button - [ idleBlue - , Element.focused [ focusBlue ] + [ Element.Background.color idleBlue + , Element.mouseOver [Element.Background.color focusBlue] + , Element.width (Element.fill |> Element.maximum 100) + , Element.height (Element.fill |> Element.maximum 50) ] - { onPress = Just <| DownMsg <| Greeting model.greetWidgetText - , label = Element.text "My Button" + { onPress = Just <| UpMsg <| RequestGreet model.greetWidgetText + , label = Element.text "Greet" } placeholder = case model.greetWidgetText of "" -> Just <| Element.Input.placeholder [] <| Element.text "Type Your Name" _ -> Just <| Element.Input.placeholder [] <| Element.text "" textInput = - Element.Input.text [] + Element.Input.text + [ Element.width (Element.fill |> Element.maximum 400) + , Element.height Element.fill + ] { onChange = GreetWidgetText , text = model.greetWidgetText , placeholder = placeholder - , label = Element.Input.labelHidden "Greet" + , label = Element.Input.labelHidden "Enter your name" } + nameInput = Element.row [] [ textInput , myButton] in - Element.row [] - [ textInput ] + Element.column [] + [ nameInput + , Element.text model.greeting + ] view : Model -> Element Msg view model = -- 2.47.1