diff --git a/Cargo.lock b/Cargo.lock index 2183192..336bf43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.6" @@ -53,6 +68,12 @@ dependencies = [ "syn", ] +[[package]] +name = "async_once" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" + [[package]] name = "atty" version = "0.2.14" @@ -133,6 +154,21 @@ dependencies = [ "syn", ] +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -145,6 +181,25 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "biteme" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "color-eyre", + "domain", + "eyre", + "inquire", + "regex", + "serde", + "serde_yaml", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -188,6 +243,44 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "cached" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" +dependencies = [ + "async-trait", + "async_once", + "cached_proc_macro", + "cached_proc_macro_types", + "futures 0.3.26", + "hashbrown 0.13.2", + "instant", + "lazy_static", + "once_cell", + "thiserror", + "tokio", +] + +[[package]] +name = "cached_proc_macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" +dependencies = [ + "cached_proc_macro_types", + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + [[package]] name = "cc" version = "1.0.79" @@ -243,6 +336,28 @@ dependencies = [ "half", ] +[[package]] +name = "clap" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags", + "clap_lex", + "is-terminal", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -253,6 +368,33 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colored" version = "2.0.0" @@ -333,6 +475,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -387,6 +554,41 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.6" @@ -418,6 +620,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + [[package]] name = "educe" version = "0.4.20" @@ -450,6 +658,27 @@ dependencies = [ "syn", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "eyre" version = "0.6.8" @@ -460,6 +689,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" @@ -614,6 +852,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "gloo-net" version = "0.2.6" @@ -662,6 +906,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "heck" version = "0.4.1" @@ -686,6 +936,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "html-escape" version = "0.2.13" @@ -782,6 +1038,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.3.0" @@ -805,7 +1067,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "inquire" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd079157ad94a32f7511b2e13037f3ae417ad80a6a9b0de29154d48b86f5d6c8" +dependencies = [ + "bitflags", + "chrono", + "crossterm", + "dyn-clone", + "lazy_static", + "newline-converter", + "tempfile", + "thiserror", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", ] [[package]] @@ -817,6 +1116,18 @@ dependencies = [ "libc", ] +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1081,6 +1392,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -1134,6 +1451,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -1146,6 +1472,15 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "newline-converter" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "nom" version = "7.1.3" @@ -1156,6 +1491,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -1205,6 +1550,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -1218,9 +1572,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.12.3", ] +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "pad-adapter" version = "0.1.1" @@ -1443,6 +1815,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1458,6 +1836,20 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -1568,6 +1960,7 @@ dependencies = [ name = "services" version = "0.1.0" dependencies = [ + "cached", "chrono", "domain", "eyre", @@ -1589,6 +1982,36 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1673,6 +2096,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.109" @@ -1702,6 +2131,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1731,6 +2173,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.1.45" @@ -1923,6 +2375,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -2030,6 +2518,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 9e8a509..84837ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,14 @@ [workspace] -members = [".", "crates/services", "crates/domain"] +members = [".", "crates/services", "crates/domain", "crates/biteme"] +[workspace.dependencies] +domain = { path = "crates/domain" } +services = { path = "crates/services" } +biteme = { path = "crates/biteme" } + +uuid = { version = "1.3.0", features = ["v4", "serde"] } +chrono = { version = "0.4.23", features = ["serde"] } +serde = { version = "1", features = ["derive"] } [package] name = "ssr_modes_axum" @@ -20,7 +28,6 @@ leptos_meta = { version = "*", default-features = false } leptos_axum = { version = "*", default-features = false, optional = true } leptos_router = { version = "*", default-features = false } log = "0.4" -serde = { version = "1", features = ["derive"] } simple_logger = "4" thiserror = "1" axum = { version = "0.6.1", optional = true } @@ -28,11 +35,13 @@ tower = { version = "0.4.13", optional = true } tower-http = { version = "0.3.4", features = ["fs"], optional = true } tokio = { version = "1", features = ["time"], optional = true } wasm-bindgen = "0.2" -chrono = { version = "0.4.23", features = ["serde"] } -uuid = { version = "1.3.0", features = ["v4", "wasm-bindgen", "js", "serde"] } -domain = { path = "crates/domain" } -services = { path = "crates/services", optional = true } +serde = { workspace = true } +chrono = { workspace = true } +uuid = { workspace = true, features = ["v4", "wasm-bindgen", "js", "serde"] } + +domain = { workspace = true } +services = { workspace = true, optional = true } [features] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] diff --git a/Makefile.toml b/Makefile.toml index 6122747..5b9e15f 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -18,7 +18,15 @@ args = [ "./style/output.css", "--watch", ] +workspace = false [tasks.build_tailwind] command = "npx" args = ["tailwindcss", "-i", "./input.css", "-o", "./style/output.css"] +workspace = false + +[tasks.install_biteme] +command = "cargo" +args = ["install", "--path", "crates/biteme"] +workspace = false +install_crate = "cargo-all-features" diff --git a/crates/biteme/Cargo.toml b/crates/biteme/Cargo.toml new file mode 100644 index 0000000..2a30d46 --- /dev/null +++ b/crates/biteme/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "biteme" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +domain = { workspace = true } + +serde = { workspace = true } +uuid = { workspace = true } +chrono = { workspace = true } + +clap = "4.1.8" +color-eyre = "0.6.2" +eyre = "0.6.8" +inquire = { version = "0.6.0", features = ["editor", "chrono", "date"] } +tokio = { version = "1.26.0", features = ["full"] } +tracing = { version = "0.1.37", features = ["log"] } +serde_yaml = "0.9.19" +tracing-subscriber = "0.3.16" +regex = "1.7.1" diff --git a/crates/biteme/src/main.rs b/crates/biteme/src/main.rs new file mode 100644 index 0000000..ff6c377 --- /dev/null +++ b/crates/biteme/src/main.rs @@ -0,0 +1,95 @@ +use domain::{Event, Image}; +use inquire::validator::ValueRequiredValidator; +use regex::Regex; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + tracing_subscriber::fmt::init(); + + color_eyre::install()?; + + let cli = clap::Command::new("biteme") + .subcommand(clap::Command::new("generate").subcommand(clap::Command::new("article"))); + + let args = std::env::args(); + + let matches = cli.get_matches_from(args); + match matches.subcommand() { + Some(("generate", subm)) => match subm.subcommand() { + Some(("article", _subm)) => { + generate_article().await?; + } + _ => panic!("command not valid"), + }, + _ => panic!("command not valid"), + } + + Ok(()) +} + +async fn generate_article() -> eyre::Result<()> { + let name = inquire::Text::new("What are you going to eat?") + .with_validator(ValueRequiredValidator::default()) + .prompt()?; + let description = inquire::Editor::new("Do you want to provide a description?") + .prompt_skippable()? + .and_then(|ci| if ci == "" { None } else { Some(ci) }); + let time = inquire::DateSelect::new("When is the event?") + .with_min_date(chrono::Local::now().date_naive()) + .prompt()?; + let cover_image = inquire::Text::new("Do you have a picture for it?") + .prompt_skippable()? + .and_then(|ci| if ci == "" { None } else { Some(ci) }); + let cover_alt = if let Some(_) = cover_image { + Some( + inquire::Text::new("Do you have a description for the image?") + .with_validator(ValueRequiredValidator::default()) + .prompt()?, + ) + } else { + None + }; + + let prepared_name = name.replace(" ", "-"); + let prepared_name = prepared_name.replace("--", "-"); + let prepared_name = prepared_name.trim_matches('-'); + + let re = Regex::new(r"[a-zA-Z-_0-9]*")?; + let name_slug = re + .find_iter(&prepared_name) + .map(|n| n.as_str()) + .collect::>(); + let name_slug = name_slug.join(""); + + let slug = format!("{}-{}", time.format("%Y-%m-%d"), name_slug.to_lowercase()); + + let event = Event { + id: uuid::Uuid::new_v4(), + cover_image: cover_image.zip(cover_alt).map(|(image, alt)| Image { + id: uuid::Uuid::new_v4(), + url: image, + alt, + metadata: None, + }), + name, + description: description.clone(), + time, + recipe_id: None, + images: Vec::new(), + metadata: None, + }; + + let contents = serde_yaml::to_string(&event)?; + let contents = format!( + "--- +{}--- + +{}", + contents, + description.unwrap_or("".into()) + ); + + tokio::fs::write(format!("articles/events/{}.md", slug), contents).await?; + + Ok(()) +} diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index 3765010..f0ff1f7 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -6,12 +6,14 @@ use serde::{Deserialize, Serialize}; pub struct Metadata(HashMap); #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct Recipe { pub id: uuid::Uuid, pub metadata: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct Image { pub id: uuid::Uuid, pub url: String, @@ -20,6 +22,7 @@ pub struct Image { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct Event { pub id: uuid::Uuid, pub cover_image: Option, @@ -32,6 +35,7 @@ pub struct Event { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct EventOverview { pub id: uuid::Uuid, pub cover_image: Option, diff --git a/crates/services/Cargo.toml b/crates/services/Cargo.toml index c465fae..58b5848 100644 --- a/crates/services/Cargo.toml +++ b/crates/services/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cached = "0.42.0" chrono = { version = "0.4.23", features = ["serde"] } domain = { path = "../domain" } eyre = "0.6.8" diff --git a/crates/services/src/lib.rs b/crates/services/src/lib.rs index 96040f8..3246ae2 100644 --- a/crates/services/src/lib.rs +++ b/crates/services/src/lib.rs @@ -1,10 +1,13 @@ use std::path::PathBuf; +use std::sync::Arc; +use cached::proc_macro::{cached, once}; use domain::{Event, Image, Metadata}; use serde::{Deserialize, Serialize}; pub struct EventStore { pub path: PathBuf, + events: Arc>>, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -16,6 +19,7 @@ pub struct RawImage { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RawEvent { + #[serde(alias = "coverImage")] pub cover_image: Option, pub name: String, pub description: Option, @@ -29,7 +33,7 @@ pub struct RawEvent { } mod short_time_stamp { - use chrono::{DateTime, NaiveDate, TimeZone, Utc}; + use chrono::NaiveDate; use serde::{self, Deserialize, Deserializer, Serializer}; const FORMAT: &'static str = "%Y-%m-%d"; @@ -79,42 +83,65 @@ impl From for Image { impl EventStore { pub fn new(path: PathBuf) -> Self { - Self { path } + Self { + path, + events: Default::default(), + } } pub async fn get_upcoming_events(&self) -> eyre::Result> { let mut event_path = self.path.clone(); event_path.push("events"); - let mut dir = tokio::fs::read_dir(event_path).await?; - let mut events = vec![]; + let events = fetch_events(event_path).await?; - while let Ok(Some(entry)) = dir.next_entry().await { - let metadata = entry.metadata().await?; - if metadata.is_file() { - let file = tokio::fs::read(entry.path()).await?; - let content = std::str::from_utf8(&file)?; - if content.starts_with("---\n") { - let after_marker = &content[4..]; - if let Some(marker_end) = after_marker.find("---\n") { - let raw_front_matter = &content[4..marker_end + 4]; - let mut raw_event: RawEvent = serde_yaml::from_str(raw_front_matter)?; - raw_event.content = content[marker_end + 4..].to_string(); - - events.push(raw_event.into()) - } - } - } - } + let mut e = self.events.write().await; + *e = events.clone(); Ok(events) } + + pub async fn get_event(&self, event_id: uuid::Uuid) -> eyre::Result> { + let events = self.events.read().await; + + let event = events.iter().find(|e| e.id == event_id); + + Ok(event.map(|e| e.clone())) + } +} + +#[once(time = 60, result = true, sync_writes = true)] +pub async fn fetch_events(event_path: PathBuf) -> eyre::Result> { + let mut dir = tokio::fs::read_dir(event_path).await?; + + let mut events = vec![]; + + while let Ok(Some(entry)) = dir.next_entry().await { + let metadata = entry.metadata().await?; + if metadata.is_file() { + let file = tokio::fs::read(entry.path()).await?; + let content = std::str::from_utf8(&file)?; + if content.starts_with("---\n") { + let after_marker = &content[4..]; + if let Some(marker_end) = after_marker.find("---\n") { + let raw_front_matter = &content[4..marker_end + 4]; + let mut raw_event: RawEvent = serde_yaml::from_str(raw_front_matter)?; + raw_event.content = content[marker_end + 4..].to_string(); + + events.push(raw_event.into()) + } + } + } + } + + Ok(events) } impl Default for EventStore { fn default() -> Self { Self { path: PathBuf::from("articles"), + events: Default::default(), } } } diff --git a/src/api/events.rs b/src/api/events.rs index fdf6d5f..1365458 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -1,66 +1,42 @@ -use lazy_static::lazy_static; +use cfg_if::cfg_if; use leptos::*; use serde::{Deserialize, Serialize}; -use domain::{Event, EventOverview, Image}; +use domain::{Event, EventOverview}; -lazy_static! { - static ref EVENTS: Vec = vec![ - Event { - cover_image: Some(Image { - id: uuid::Uuid::new_v4(), - url: "https://images.unsplash.com/photo-1513104890138-7c749659a591?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80".into(), - alt: "some-alt".into(), - metadata: None, - }), - id: uuid::Uuid::new_v4(), - name: "Pizza".into(), - description: Some("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.".into()), - time: chrono::Utc::now() - .checked_add_days(chrono::Days::new(1)) - .unwrap() - .date_naive(), - recipe_id: None, - images: vec![], - metadata: None, - }, - Event { - cover_image: Some(Image { - id: uuid::Uuid::new_v4(), - url: "https://images.unsplash.com/photo-1513104890138-7c749659a591?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80".into(), - alt: "some-alt".into(), - metadata: None, - }), - id: uuid::Uuid::new_v4(), - name: "Kød boller".into(), - description: Some("Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.".into()), - time: chrono::Utc::now() - .checked_add_days(chrono::Days::new(4)) - .unwrap() - .date_naive(), - recipe_id: None, - images: vec![], - metadata: None, - }, - Event { - cover_image: Some(Image { - id: uuid::Uuid::new_v4(), - url: "https://images.unsplash.com/photo-1513104890138-7c749659a591?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80".into(), - alt: "some-alt".into(), - metadata: None, - }), - id: uuid::Uuid::new_v4(), - name: "Pizza".into(), - description: Some("description".into()), - time: chrono::Utc::now() - .checked_sub_days(chrono::Days::new(2)) - .unwrap() - .date_naive(), - recipe_id: None, - images: vec![], - metadata: None, - }, - ]; +cfg_if! { + if #[cfg(feature = "ssr")] { + use services::EventStore; + use lazy_static::lazy_static; + + lazy_static! { + static ref EVENTSTORE: EventStore = EventStore::default(); + } + async fn get_upcoming_events_fn() -> Result { + let current_time = chrono::Utc::now(); + + let mut events: Vec = EVENTSTORE + .get_upcoming_events() + .await + .map_err(|e| ServerFnError::ServerError(e.to_string()))? + .iter() + .filter(|d| d.time.gt(&chrono::Utc::now().date_naive())) + .map(|data| data.clone().into()) + .collect(); + + events.sort_by(|a, b| a.time.cmp(&b.time)); + + Ok(UpcomingEventsOverview { events }) + } + async fn get_full_event_fn(event_id: uuid::Uuid) -> Result, ServerFnError> { + let event = EVENTSTORE + .get_event(event_id) + .await + .map_err(|e| ServerFnError::ServerError(e.to_string()))?; + + Ok(event) + } + } } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -70,40 +46,10 @@ pub struct UpcomingEventsOverview { #[server(GetUpcomingEvents, "/api")] pub async fn get_upcoming_events() -> Result { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; get_upcoming_events_fn().await } -#[cfg(feature = "ssr")] -async fn get_upcoming_events_fn() -> Result { - let current_time = chrono::Utc::now(); - - let es = services::EventStore::default(); - - let events = es - .get_upcoming_events() - .await - .map_err(|e| ServerFnError::ServerError(e.to_string()))? - .iter() - .map(|data| data.clone().into()) - .collect(); - - Ok(UpcomingEventsOverview { events }) -} - #[server(GetFullEvent, "/api")] pub async fn get_full_event(event_id: uuid::Uuid) -> Result, ServerFnError> { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - get_full_event_fn(event_id).await } - -#[cfg(feature = "ssr")] -async fn get_full_event_fn(event_id: uuid::Uuid) -> Result, ServerFnError> { - let event = EVENTS - .iter() - .find(|data| data.id == event_id) - .map(|d| d.clone()); - - Ok(event) -} diff --git a/src/components/day.rs b/src/components/day.rs index 13dfd9e..a51e81e 100644 --- a/src/components/day.rs +++ b/src/components/day.rs @@ -51,6 +51,12 @@ pub fn Day( }.into_view(cx) }} + { + last.filter(|l| !l).map(|_l| view! { + cx, +
@@ -188,8 +194,8 @@ fn DayContentCollapsed( }}
diff --git a/style/output.css b/style/output.css index 0a4fc33..106ecc0 100644 --- a/style/output.css +++ b/style/output.css @@ -655,6 +655,32 @@ video { flex-grow: 1; } +@keyframes bounce { + 0%, 100% { + transform: translateY(-25%); + animation-timing-function: cubic-bezier(0.8,0,1,1); + } + + 50% { + transform: none; + animation-timing-function: cubic-bezier(0,0,0.2,1); + } +} + +.animate-bounce { + animation: bounce 1s infinite; +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + .list-decimal { list-style-type: decimal; } @@ -743,6 +769,20 @@ video { border-top-right-radius: 1rem; } +.border { + border-width: 1px; +} + +.border-gray-400 { + --tw-border-opacity: 1; + border-color: rgb(156 163 175 / var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + .bg-gray-200 { --tw-bg-opacity: 1; background-color: rgb(229 231 235 / var(--tw-bg-opacity));