M Cargo.lock => Cargo.lock +498 -0
@@ 75,6 75,17 @@ dependencies = [
]
[[package]]
+name = "async-trait"
+version = "0.1.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "atomic"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 90,6 101,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
+name = "axum"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "axum-macros",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 0.1.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "backtrace"
version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 129,6 208,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+
+[[package]]
name = "cc"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 308,6 393,7 @@ dependencies = [
name = "der_die_das"
version = "0.2.5"
dependencies = [
+ "axum",
"clap",
"clap_complete_command",
"color-eyre",
@@ 317,9 403,13 @@ dependencies = [
"figment",
"fs_extra",
"inquire",
+ "maud",
"serde",
"serde_json",
"time",
+ "tokio",
+ "tower",
+ "tower-http",
"tracing",
"tracing-error",
"tracing-subscriber",
@@ 393,12 483,66 @@ dependencies = [
]
[[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 = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 446,6 590,99 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range-header"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe"
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+]
+
+[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 528,12 765,58 @@ dependencies = [
]
[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "maud"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df518b75016b4289cdddffa1b01f2122f4a49802c93191f3133f6dc2472ebcaa"
+dependencies = [
+ "axum-core",
+ "http",
+ "itoa",
+ "maud_macros",
+]
+
+[[package]]
+name = "maud_macros"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa453238ec218da0af6b11fc5978d3b5c3a45ed97b722391a2a11f3306274e18"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 580,6 863,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 668,18 961,73 @@ dependencies = [
]
[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 830,6 1178,28 @@ dependencies = [
]
[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_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 = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 875,6 1245,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 911,6 1291,18 @@ dependencies = [
]
[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+
+[[package]]
name = "thiserror"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 974,11 1366,108 @@ dependencies = [
]
[[package]]
+name = "tokio"
+version = "1.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+dependencies = [
+ "bitflags 2.5.0",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "http-range-header",
+ "httpdate",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
+ "log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ 1054,6 1543,15 @@ dependencies = [
]
[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml => Cargo.toml +16 -0
@@ 15,12 15,16 @@ categories = ["command-line-utilities", "games"]
[[bin]]
name = "ddd"
path = "src/bin/ddd/main.rs"
+[[bin]]
+name = "homepage"
+path = "src/bin/homepage/main.rs"
[lib]
path = "src/lib.rs"
[dependencies]
+axum = { version = "0.7.5", optional = true, features = ["macros"] }
clap = { version = "4.5.4", features = ["derive"] }
clap_complete_command = "0.5.1"
color-eyre = "0.6.3"
@@ 30,12 34,24 @@ dirs = "5.0.1"
figment = { version = "0.10.18", features = ["env", "json"] }
fs_extra = "1.3.0"
inquire = "0.7.4"
+maud = { version = "0.26.0", features = ["axum"], optional = true }
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.116"
time = { version = "0.3.36", features = ["serde", "local-offset", "serde-well-known"] }
+tokio = { version = "1.37.0", features = ["full"], optional = true }
+tower = { version = "0.4.13", features = ["util"], optional = true }
+tower-http = { version = "0.5.2", features = ["fs", "trace"], optional = true }
tracing = "0.1.40"
tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[lints.clippy]
pedantic = "warn"
+
+[features]
+default = ["homepage"]
+homepage = ["dep:tokio", "dep:axum", "dep:maud", "dep:tower-http", "dep:tower"]
+
+
+[target.x86_64-pc-windows-gnu]
+linker = "x86_64-w64-mingw32-gcc"
A assets/logo.svg => assets/logo.svg +1 -0
@@ 0,0 1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><defs><style>.cls-1{fill:#dde9d4;}.cls-2{fill:#8b2a01;}.cls-3{fill:#ff7e47;}.cls-4{fill:#df581f;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M0,124A124,124,0,0,1,124,0H516A124,124,0,0,1,640,124V516A124,124,0,0,1,516,640H124A124,124,0,0,1,0,516Z"/><path class="cls-2" d="M549.3,400a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H558.3a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M518.8,431.8a9,9,0,0,1,9-9H538a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H527.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,528.2a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,496.5a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,463.5a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,431.8a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,400a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,432.1a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,496.8a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V507a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,432.1a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,203a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,137a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,71a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,203a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,137a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,104a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,71a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,104a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,237a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,237a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,237a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,237a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,269a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,269a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,301a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,301a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,301a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,301a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,334a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,334a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,366a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,366a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,366a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,366a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M208,98a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H228a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M129,98a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H149a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,98A20.06,20.06,0,0,1,70,78h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,178a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,178a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,258a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,258a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,338a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,338a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,418a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,418a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M208,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H228a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M129,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H149a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M214,90a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H234a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M135,90a20.06,20.06,0,0,1,20-20h32a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H155a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,90A20.06,20.06,0,0,1,77,70h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,169a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,169a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,249a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,249a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,329a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,329a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,408a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,408a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M214,488a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H234a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M135,488a20.06,20.06,0,0,1,20-20h32a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H155a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,488a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M221,82a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H241a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M142,82a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H162a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,82A20.06,20.06,0,0,1,85,62h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,161a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,161a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,240a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,240a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,318a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,318a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,397a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,397a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M221,476a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H241a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M142,476a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H162a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,476a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/></g></g></svg><
\ No newline at end of file
A shell.nix => shell.nix +13 -0
@@ 0,0 1,13 @@
+with import <nixpkgs> {
+ crossSystem = {
+ config = "x86_64-w64-mingw32";
+ };
+}; let
+ pkgs = import <nixpkgs> {crossSystem = {config = "x86_64-w64-mingw32";};};
+in
+ pkgs.mkShell
+ {
+ buildInputs = [
+ windows.mingw_w64_pthreads
+ ];
+ }
M src/attempt.rs => src/attempt.rs +1 -1
@@ 16,7 16,7 @@ pub enum Conclusion {
WrongArticle(Article),
}
-pub trait Repo: Clone {
+pub trait Repo: Clone + Send + Sync + std::fmt::Debug {
/// Saves an attempt
///
/// # Errors
M src/bin/ddd/main.rs => src/bin/ddd/main.rs +0 -3
@@ 15,9 15,6 @@ use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, Env
fn main() -> Result<()> {
trace_install()?;
- // // // error message management
- // color_eyre::install()?;
-
let config_path = dirs::config_dir()
.ok_or_eyre("could not file config folder!")?
.join("ddd")
A src/bin/homepage/arguments.rs => src/bin/homepage/arguments.rs +0 -0
A src/bin/homepage/commands.rs => src/bin/homepage/commands.rs +0 -0
A src/bin/homepage/config.rs => src/bin/homepage/config.rs +166 -0
@@ 0,0 1,166 @@
+use std::path::PathBuf;
+
+use color_eyre::eyre::OptionExt;
+use figment::{
+ providers::{Env, Format, Json},
+ Figment,
+};
+
+#[derive(
+ Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
+)]
+pub struct GroupConfigFile {
+ pub threshold: Option<u8>,
+ pub enable: Option<bool>,
+}
+impl Default for GroupConfigFile {
+ fn default() -> Self {
+ Self {
+ threshold: Some(100),
+ enable: Some(true),
+ }
+ }
+}
+
+#[derive(
+ Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
+)]
+pub struct WebConfigFile {
+ pub host: Option<String>,
+ pub port: Option<u16>,
+}
+impl Default for WebConfigFile {
+ fn default() -> Self {
+ Self {
+ host: Some("127.0.0.1".to_owned()),
+ port: Some(4949),
+ }
+ }
+}
+
+#[derive(
+ Clone,
+ Debug,
+ PartialEq,
+ Eq,
+ Hash,
+ PartialOrd,
+ Ord,
+ serde::Serialize,
+ serde::Deserialize,
+ Default,
+)]
+pub struct ConfigurationFile {
+ pub data_path: Option<PathBuf>,
+ pub group: Option<GroupConfigFile>,
+ pub web: Option<WebConfigFile>,
+}
+
+impl TryFrom<PathBuf> for ConfigurationFile {
+ type Error = color_eyre::Report;
+
+ fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
+ Ok(Figment::new()
+ .merge(Json::file(value))
+ .merge(Env::prefixed("DDD_"))
+ .extract()?)
+ }
+}
+
+impl TryFrom<&PathBuf> for ConfigurationFile {
+ type Error = color_eyre::Report;
+
+ fn try_from(value: &PathBuf) -> Result<Self, Self::Error> {
+ Ok(Figment::new()
+ .merge(Json::file(value))
+ .merge(Env::prefixed("DDD_"))
+ .extract()?)
+ }
+}
+
+#[derive(
+ Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
+)]
+pub struct GroupConfiguration {
+ pub threshold: u8,
+ pub enable: bool,
+}
+impl Default for GroupConfiguration {
+ fn default() -> Self {
+ Self {
+ threshold: 100,
+ enable: true,
+ }
+ }
+}
+
+#[derive(
+ Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
+)]
+pub struct WebConfiguration {
+ pub host: String,
+ pub port: u16,
+}
+impl Default for WebConfiguration {
+ fn default() -> Self {
+ Self {
+ host: "127.0.0.1".to_owned(),
+ port: 4949,
+ }
+ }
+}
+impl From<WebConfigFile> for WebConfiguration {
+ fn from(value: WebConfigFile) -> Self {
+ Self {
+ host: value.host.unwrap_or("127.0.0.1".to_owned()),
+ port: 4949,
+ }
+ }
+}
+
+impl From<GroupConfigFile> for GroupConfiguration {
+ fn from(value: GroupConfigFile) -> Self {
+ Self {
+ threshold: value.threshold.unwrap_or(100),
+ enable: value.enable.unwrap_or(true),
+ }
+ }
+}
+
+#[derive(
+ Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
+)]
+pub struct Config {
+ pub data_path: PathBuf,
+ pub group: GroupConfiguration,
+ pub web: WebConfiguration,
+}
+
+impl TryFrom<ConfigurationFile> for Config {
+ type Error = color_eyre::Report;
+
+ fn try_from(value: ConfigurationFile) -> color_eyre::Result<Self> {
+ let group = value.group.unwrap_or_default().into();
+ let web = value.web.unwrap_or_default().into();
+
+ if let Some(data_path) = value.data_path {
+ return Ok(Config {
+ data_path,
+ group,
+ web,
+ });
+ };
+ let p = dirs::data_dir()
+ .ok_or_eyre("Could not find any folder to put the data in.")?
+ .join("ddd");
+ if !p.try_exists()? {
+ fs_extra::dir::create_all(&p, false)?;
+ };
+
+ Ok(Config {
+ group,
+ data_path: p,
+ web,
+ })
+ }
+}
A src/bin/homepage/main.rs => src/bin/homepage/main.rs +93 -0
@@ 0,0 1,93 @@
+#![cfg(feature = "homepage")]
+
+mod arguments;
+mod commands;
+mod config;
+mod web;
+
+use std::{env, sync::Arc};
+
+use axum::{routing::get, Router};
+use color_eyre::{eyre::OptionExt as _, Result};
+use tracing::{debug, info, level_filters::LevelFilter};
+use tracing_error::ErrorLayer;
+use tracing_subscriber::{fmt, layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter};
+
+#[tokio::main(flavor = "current_thread")]
+async fn main() -> Result<()> {
+ trace_install()?;
+
+ let config_path = dirs::config_dir()
+ .ok_or_eyre("could not file config folder!")?
+ .join("ddd")
+ .join("config.json");
+
+ debug!("the config path is at: {:#?}", &config_path);
+
+ let configs_file = config::ConfigurationFile::try_from(config_path)?;
+
+ debug!("the configuration file is: {:#?}", &configs_file);
+ let configs = config::Config::try_from(configs_file)?;
+
+ debug!("the configuration is: {:#?}", &configs);
+
+ let storage = Arc::new(der_die_das::storage::Storage::try_from(configs.data_path)?);
+
+ let app_state = Arc::new(web::AppState {
+ nouns_repo: Arc::clone(&storage),
+ attempts_repo: Arc::clone(&storage),
+ group_config: configs.group,
+ });
+
+ let addr = format!("{}:{}", configs.web.host, configs.web.port);
+ let listener = tokio::net::TcpListener::bind(&addr).await?;
+
+ let router = Router::new()
+ .route("/", get(web::routes::home))
+ .route("/send-answer/:word/:attempt", get(web::routes::answer))
+ .route("/style.css", get(web::routes::style))
+ .route("/logo.svg", get(web::routes::logo))
+ .route("/favicon.ico", get(web::routes::logo))
+ .with_state(Arc::clone(&app_state));
+
+ info!("running at: {}", addr);
+ axum::serve(listener, router).await?;
+
+ Ok(())
+}
+
+fn trace_install() -> Result<()> {
+ let fmt_layer = fmt::layer().with_target(false);
+
+ let (filter_layer, current_filter) = match EnvFilter::try_from_default_env() {
+ Err(_) => (EnvFilter::try_new("warn").unwrap(), LevelFilter::OFF),
+ Ok(layer) => {
+ let level = layer.max_level_hint().unwrap_or(LevelFilter::OFF);
+ (layer, level)
+ }
+ };
+
+ let registry = tracing_subscriber::registry()
+ .with(filter_layer)
+ .with(ErrorLayer::default());
+
+ if current_filter == LevelFilter::OFF {
+ registry.with(fmt_layer.without_time()).init();
+ } else {
+ registry.with(fmt_layer).init();
+ }
+
+ if env::var("RUST_BACKTRACE").is_err() && current_filter == LevelFilter::TRACE {
+ env::set_var("RUST_BACKTRACE", "1");
+ }
+
+ let debug = current_filter >= LevelFilter::DEBUG;
+
+ color_eyre::config::HookBuilder::new()
+ .capture_span_trace_by_default(debug)
+ .display_location_section(debug)
+ .display_env_section(false)
+ .install()?;
+
+ Ok(())
+}
A src/bin/homepage/web.rs => src/bin/homepage/web.rs +11 -0
@@ 0,0 1,11 @@
+use der_die_das::{attempt, nouns};
+
+use crate::config::GroupConfiguration;
+pub(crate) mod routes;
+
+#[derive(Debug)]
+pub struct AppState<A: attempt::Repo, N: nouns::Repo> {
+ pub nouns_repo: N,
+ pub attempts_repo: A,
+ pub group_config: GroupConfiguration,
+}
A src/bin/homepage/web/logo.svg => src/bin/homepage/web/logo.svg +1 -0
@@ 0,0 1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><defs><style>.cls-1{fill:#dde9d4;}.cls-2{fill:#8b2a01;}.cls-3{fill:#ff7e47;}.cls-4{fill:#df581f;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M0,124A124,124,0,0,1,124,0H516A124,124,0,0,1,640,124V516A124,124,0,0,1,516,640H124A124,124,0,0,1,0,516Z"/><path class="cls-2" d="M549.3,400a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H558.3a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M518.8,431.8a9,9,0,0,1,9-9H538a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H527.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,528.2a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,496.5a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,463.5a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,431.8a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M487,400a9,9,0,0,1,9-9h10.2a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,432.1a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M445.5,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H454.5a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M413.8,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H422.8a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,528.6a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V540a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,496.8a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9V507a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,463.9a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,432.1a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v11.4a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M382,400.4a9,9,0,0,1,9-9h11.4a9,9,0,0,1,9,9v10.2a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M550,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,203a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,137a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M519,71a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,203a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,137a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,104a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M487,71a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M414,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H423a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,203a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,170a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,137a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M446,104a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H455a9,9,0,0,1-9-9Z"/><path class="cls-3" d="M382,71a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9V82a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,237a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,237a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,237a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,237a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v11a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,269a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,269a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,301a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,301a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,301a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,301a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,334a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,334a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M550,366a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H559a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M519,366a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H528a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M487,366a9,9,0,0,1,9-9h10a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H496a9,9,0,0,1-9-9Z"/><path class="cls-4" d="M382,366a9,9,0,0,1,9-9h11a9,9,0,0,1,9,9v10a9,9,0,0,1-9,9H391a9,9,0,0,1-9-9Z"/><path class="cls-2" d="M208,98a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H228a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M129,98a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H149a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,98A20.06,20.06,0,0,1,70,78h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,178a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,178a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,258a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,258a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,338a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,338a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M287,418a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H307a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,418a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M208,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H228a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M129,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H149a20.06,20.06,0,0,1-20-20Z"/><path class="cls-2" d="M50,498a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H70a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M214,90a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H234a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M135,90a20.06,20.06,0,0,1,20-20h32a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H155a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,90A20.06,20.06,0,0,1,77,70h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,169a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,169a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,249a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,249a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,329a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,329a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M292,408a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H312a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,408a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v32a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M214,488a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H234a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M135,488a20.06,20.06,0,0,1,20-20h32a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H155a20.06,20.06,0,0,1-20-20Z"/><path class="cls-4" d="M57,488a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H77a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M221,82a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H241a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M142,82a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H162a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,82A20.06,20.06,0,0,1,85,62h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,161a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,161a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,240a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,240a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,318a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,318a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M298,397a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H318a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,397a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v31a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M221,476a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H241a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M142,476a20.06,20.06,0,0,1,20-20h31a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H162a20.06,20.06,0,0,1-20-20Z"/><path class="cls-3" d="M65,476a20.06,20.06,0,0,1,20-20h30a20.06,20.06,0,0,1,20,20v30a20.06,20.06,0,0,1-20,20H85a20.06,20.06,0,0,1-20-20Z"/></g></g></svg>
A src/bin/homepage/web/routes.rs => src/bin/homepage/web/routes.rs +257 -0
@@ 0,0 1,257 @@
+use std::sync::Arc;
+
+use axum::{
+ extract::{Path, State},
+ http::{header, HeaderMap, StatusCode},
+ response::IntoResponse,
+};
+use color_eyre::eyre::OptionExt as _;
+use der_die_das::{
+ attempt,
+ history::History,
+ nouns::{self, Article, Noun},
+};
+use maud::{html, Markup, DOCTYPE};
+use tracing::{debug, instrument};
+
+use super::AppState;
+
+pub enum ErrorMessage {
+ Internal(color_eyre::Report),
+ NoWordsAvailable(color_eyre::Report),
+ WordNotFound(color_eyre::Report, i128),
+}
+
+fn error_message(input: &str) -> Markup {
+ html!(div class="message-error"{
+ p class="message" {
+ (input)
+ }
+ })
+}
+
+impl IntoResponse for ErrorMessage {
+ fn into_response(self) -> axum::response::Response {
+ match self {
+ ErrorMessage::Internal(e) => {
+ (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
+ }
+ ErrorMessage::NoWordsAvailable(e) => {
+ (StatusCode::NOT_FOUND, e.to_string()).into_response()
+ }
+ ErrorMessage::WordNotFound(e, id) => (
+ StatusCode::NOT_FOUND,
+ error_message(
+ &e.wrap_err(format!("word with id {id} not found"))
+ .to_string(),
+ ),
+ )
+ .into_response(),
+ }
+ }
+}
+
+#[instrument]
+pub async fn style() -> impl IntoResponse {
+ let content = include_str!("style.css");
+
+ let mut headers = HeaderMap::new();
+ headers.insert(header::CONTENT_TYPE, "text/css".parse().unwrap());
+ (headers, content)
+}
+
+#[instrument]
+pub async fn logo() -> impl IntoResponse {
+ let content = include_str!("logo.svg");
+
+ let mut headers = HeaderMap::new();
+ headers.insert(header::CONTENT_TYPE, "image/svg+xml".parse().unwrap());
+ (headers, content)
+}
+
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct Answer {
+ attempt: Article,
+ word: i128,
+}
+
+#[instrument]
+pub async fn answer<A: attempt::Repo, N: nouns::Repo>(
+ State(storage): State<Arc<AppState<A, N>>>,
+ Path(payload): Path<Answer>,
+) -> Result<impl IntoResponse, ErrorMessage> {
+ debug!("answer called: {:#?}", payload);
+ let noun = storage
+ .nouns_repo
+ .clone()
+ .find_noun_by_id(payload.word)
+ .map_err(|e| ErrorMessage::WordNotFound(e, payload.word))?;
+
+ debug!("found noun: {:#?}", noun);
+ debug!(
+ "here is the result of contains: {}, here is the articles:{:#?}, here is the article: {:#?}",
+ noun.articles.contains(&payload.attempt),
+ noun.articles,
+ payload.attempt,
+ );
+ if noun.articles.contains(&payload.attempt) {
+ debug!("the noun article contains: {:#}", payload.attempt);
+
+ let now = time::OffsetDateTime::now_utc();
+ storage
+ .attempts_repo
+ .clone()
+ .save_attempt(attempt::Attempt {
+ id: now.unix_timestamp_nanos(),
+ at: now,
+ for_word: noun.id,
+ what_happened: attempt::Conclusion::Success,
+ })
+ .map_err(ErrorMessage::Internal)?;
+
+ let h = History {
+ nouns: storage
+ .nouns_repo
+ .clone()
+ .all_nouns()
+ .map_err(ErrorMessage::Internal)?,
+ attempts: storage
+ .attempts_repo
+ .clone()
+ .all_attempts()
+ .map_err(ErrorMessage::Internal)?,
+ };
+
+ let n = h
+ .next_with_group(100)
+ .ok_or_eyre("no more words!")
+ .map_err(ErrorMessage::NoWordsAvailable)?;
+
+ debug!("found this word {:#?}", n);
+ return Ok(quize(&n.0, Some(true)));
+ }
+ Ok(quize(&noun, Some(false)))
+}
+#[instrument]
+pub async fn home<A: attempt::Repo, N: nouns::Repo>(
+ State(storage): State<Arc<AppState<A, N>>>,
+) -> Result<impl IntoResponse, ErrorMessage> {
+ let nouns = storage
+ .nouns_repo
+ .clone()
+ .all_nouns()
+ .map_err(ErrorMessage::Internal)?;
+
+ let attempts = storage
+ .attempts_repo
+ .clone()
+ .all_attempts()
+ .map_err(ErrorMessage::Internal)?;
+
+ let h = History { nouns, attempts };
+
+ let chosen = if storage.group_config.enable {
+ h.next_with_group(storage.group_config.threshold)
+ } else {
+ h.next()
+ }
+ .ok_or_eyre("there are no words available for asking. Have you added some?")
+ .map_err(ErrorMessage::NoWordsAvailable)?;
+ let header_generated = header();
+
+ Ok::<(StatusCode, Markup), _>((
+ StatusCode::OK,
+ html!(
+ (DOCTYPE)
+ html{
+ head{(title("DerDieDas")) (style_css())}
+ body {
+ (base_htmz())
+ header {(header_generated)}
+ main {(quize(&chosen.0, None))}
+ footer {}
+
+ (htmz())
+ }
+ }
+ ),
+ ))
+}
+
+#[instrument]
+fn header() -> Markup {
+ html!(
+ div id="header-wrapper" {
+ div id="logo-wrapper" {
+ img src="/logo.svg" width="100" alt="Der Die Das" {}
+
+ }
+ }
+ )
+}
+
+fn title(title: &str) -> Markup {
+ html!(title {(title)})
+}
+
+fn quize(noun: &Noun, res: Option<bool>) -> Markup {
+ let res_mark = match res {
+ Some(success) => {
+ if success {
+ html!(div id="super" {"SUPER!"})
+ } else {
+ html!(div id="sorry" {"sorry! :("})
+ }
+ }
+ None => {
+ html!( div id="new-day-new-word" {p id="new-day" {"new day"} p id="new-word" {"new word"}})
+ }
+ };
+
+ html!(
+ div id="quiz" {
+ (question(noun))
+ div id="res" {
+ (res_mark)
+
+ }
+ }
+
+
+ )
+}
+
+fn question(noun: &Noun) -> Markup {
+ html!(
+ form id="question-box"{
+ div class="buttons"{
+ button class="article-button" id="der-button" formaction=(format!("/send-answer/{}/der#quiz",noun.id)) {"der"}
+ button class="article-button" id="die-button" formaction=(format!("/send-answer/{}/die#quiz",noun.id)) {"die"}
+ button class="article-button" id="das-button" formaction=(format!("/send-answer/{}/das#quiz",noun.id)) {"das"}
+ }
+ div class="word-box"{
+ p id="word" {(noun.word)}
+ p id="meaning" {(noun.meaning)}
+ }
+ }
+ )
+}
+
+fn style_css() -> Markup {
+ html!(
+ link rel="stylesheet" href="/style.css" {}
+ )
+}
+
+fn htmz() -> Markup {
+ html!(
+ iframe hidden name="htmz" onload="setTimeout(()=>document.querySelector(contentWindow.location.hash||null)?.replaceWith(...contentDocument.body.childNodes))"{}
+
+ )
+}
+
+fn base_htmz() -> Markup {
+ html!(
+ base target="htmz"{}
+ )
+}
A src/bin/homepage/web/style.css => src/bin/homepage/web/style.css +161 -0
@@ 0,0 1,161 @@
+body {
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+header,
+body,
+footer {
+ padding: 1em;
+ width: 90%;
+}
+
+#header-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ padding: 1em;
+ border: 1px solid rgba(139, 42, 0, 1);
+ border-radius: 1em;
+}
+
+
+#logo {
+ display: flex;
+ max-width: 10%;
+ min-width: 5%;
+}
+
+#quiz {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ max-width: none;
+ justify-content: stretch;
+ align-content: center;
+ align-items: center;
+ transition: all ease 0.5s;
+}
+
+#new-day-new-word {
+ display: flex;
+ flex-direction: column;
+ justify-items: stretch;
+ align-items: center;
+
+
+ column-gap: 0px;
+ padding: 0.7em;
+ font-size: 3.5em;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ color: rgba(139, 42, 0, 1);
+}
+
+#new-day {
+ margin-block-start: 0em;
+ margin-block-end: 0em;
+}
+
+#new-word {
+ margin-block-start: 0em;
+ margin-block-end: 0em;
+ transition: all ease 0.5s;
+}
+
+#new-word:hover {
+ transition: all ease 0.5s;
+ transform: rotate(10deg);
+}
+
+#sorry,
+#super {
+ display: flex;
+ flex-direction: column;
+ justify-items: stretch;
+ align-items: center;
+ font-size: 4em;
+ padding: 1em;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ color: rgba(139, 42, 0, 1);
+}
+
+#question-box {
+ display: flex;
+ border: 1px solid rgba(139, 42, 0, 1);
+ border-radius: 1em;
+ align-content: center;
+ align-items: center;
+ max-width: none;
+ min-width: 50%;
+ justify-content: space-evenly;
+
+}
+
+.buttons {
+ display: flex;
+ flex-direction: column;
+ padding: 1em;
+ column-gap: 1em;
+ gap: 1em;
+}
+
+.article-button {
+ font-size: 1em;
+ padding: 1em;
+
+ border: 1px solid rgba(139, 42, 0, 1);
+ background-color: rgba(221, 233, 212, 1);
+ color: rgba(139, 42, 0, 1);
+
+ border-radius: 1em;
+ box-shadow: -0.1em 0.1em 0;
+ transition: all ease 0.5s;
+ min-width: 10em;
+
+}
+
+.article-button:hover {
+ font-weight: bold;
+ border-style: solid;
+ box-shadow: -0.5em 0.5em 0;
+ transition: all ease 0.5s;
+}
+
+.word-box {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ padding: 2em;
+ margin-block-end: 0;
+ margin-block-start: 0;
+}
+
+#word {
+ font-size: 4em;
+ font-weight: 700;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ color: rgba(139, 42, 0, 1);
+ margin-block-end: 0;
+ margin-block-start: 0;
+}
+
+#meaning {
+ font-size: 2em;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ color: rgba(139, 42, 0, 1);
+ margin-block-end: 0;
+ margin-block-start: 0;
+}
+
+#word:hover {
+ text-decoration-style: dashed;
+ text-decoration-line: underline;
+ transition: all ease 0.5s;
+}<
\ No newline at end of file
M src/nouns.rs => src/nouns.rs +5 -2
@@ 11,7 11,10 @@ pub struct Noun {
pub group: String,
}
-#[derive(Debug, Clone, Copy, clap::ValueEnum, PartialEq, Eq, Hash)]
+#[derive(
+ Debug, Clone, Copy, clap::ValueEnum, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize,
+)]
+#[serde(rename_all = "lowercase")]
pub enum Article {
Der,
Die,
@@ 32,7 35,7 @@ impl Display for Article {
}
}
-pub trait Repo: Clone {
+pub trait Repo: Clone + Send + Sync + std::fmt::Debug {
/// Saves one noun.
///
/// # Errors
M src/storage.rs => src/storage.rs +115 -2
@@ 1,4 1,4 @@
-use std::path::PathBuf;
+use std::{path::PathBuf, sync::Arc};
use color_eyre::{
eyre::{eyre, Result},
@@ 12,7 12,9 @@ use crate::{
nouns::{self, Article, Noun},
};
+#[derive(Debug, Clone)]
pub struct Storage(PathBuf);
+
impl Storage {
fn attempts_path(&self) -> PathBuf {
self.0.join("attempts")
@@ 62,6 64,63 @@ impl TryFrom<PathBuf> for Storage {
Ok(storage)
}
}
+impl nouns::Repo for Storage {
+ fn save_noun(self, noun: Noun) -> Result<()> {
+ (&self).save_noun(noun)
+ }
+
+ fn all_nouns(self) -> Result<Vec<Noun>> {
+ (&self).all_nouns()
+ }
+
+ fn find_noun_by_id(self, id: i128) -> Result<Noun> {
+ (&self).find_noun_by_id(id)
+ }
+
+ fn delete_noun_by_id(self, id: i128) -> Result<()> {
+ (&self).delete_noun_by_id(id)
+ }
+
+ fn edit_noun_by_id(
+ self,
+ id: i128,
+ article: Option<Vec<Article>>,
+ word: Option<String>,
+ meaning: Option<String>,
+ group: Option<String>,
+ ) -> Result<()> {
+ (&self).edit_noun_by_id(id, article, word, meaning, group)
+ }
+}
+impl nouns::Repo for Arc<Storage> {
+ fn save_noun(self, noun: Noun) -> Result<()> {
+ self.as_ref().save_noun(noun)
+ }
+
+ fn all_nouns(self) -> Result<Vec<Noun>> {
+ self.as_ref().all_nouns()
+ }
+
+ fn find_noun_by_id(self, id: i128) -> Result<Noun> {
+ self.as_ref().find_noun_by_id(id)
+ }
+
+ fn delete_noun_by_id(self, id: i128) -> Result<()> {
+ self.as_ref().delete_noun_by_id(id)
+ }
+
+ fn edit_noun_by_id(
+ self,
+ id: i128,
+ article: Option<Vec<Article>>,
+ word: Option<String>,
+ meaning: Option<String>,
+ group: Option<String>,
+ ) -> Result<()> {
+ self.as_ref()
+ .edit_noun_by_id(id, article, word, meaning, group)
+ }
+}
impl nouns::Repo for &Storage {
fn save_noun(self, noun: nouns::Noun) -> Result<()> {
@@ 131,7 190,7 @@ impl nouns::Repo for &Storage {
meaning: Option<String>,
group: Option<String>,
) -> Result<()> {
- let mut n = self.find_noun_by_id(id)?;
+ let mut n = self.clone().find_noun_by_id(id)?;
if let Some(x) = article {
debug!(
@@ 301,6 360,49 @@ impl From<Conclusion> for AttemptResultFileV1 {
}
}
+impl attempt::Repo for Arc<Storage> {
+ fn save_attempt(self, attempt: Attempt) -> Result<()> {
+ self.as_ref().save_attempt(attempt)
+ }
+
+ fn all_attempts(self) -> Result<Vec<Attempt>> {
+ self.as_ref().all_attempts()
+ }
+
+ fn find_attempt_by_id(self, id: i128) -> Result<Attempt> {
+ self.as_ref().find_attempt_by_id(id)
+ }
+
+ fn find_attempt_by_noun_id(self, id: i128) -> Result<Vec<Attempt>> {
+ self.as_ref().find_attempt_by_noun_id(id)
+ }
+
+ fn delete_attempt_by_id(self, id: i128) -> Result<()> {
+ self.as_ref().delete_attempt_by_id(id)
+ }
+}
+
+impl attempt::Repo for Storage {
+ fn save_attempt(self, attempt: Attempt) -> Result<()> {
+ (&self).save_attempt(attempt)
+ }
+
+ fn all_attempts(self) -> Result<Vec<Attempt>> {
+ (&self).all_attempts()
+ }
+
+ fn find_attempt_by_id(self, id: i128) -> Result<Attempt> {
+ (&self).find_attempt_by_id(id)
+ }
+
+ fn find_attempt_by_noun_id(self, id: i128) -> Result<Vec<Attempt>> {
+ (&self).find_attempt_by_noun_id(id)
+ }
+
+ fn delete_attempt_by_id(self, id: i128) -> Result<()> {
+ (&self).delete_attempt_by_id(id)
+ }
+}
impl attempt::Repo for &Storage {
fn save_attempt(self, a: Attempt) -> Result<()> {
let file_name = format!("{}.json", a.id);
@@ 367,3 469,14 @@ impl attempt::Repo for &Storage {
Ok(())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn is_normal<T: Sized + Send + Sync + Unpin>() {}
+ #[test]
+ fn normal_types() {
+ is_normal::<Storage>();
+ }
+}