From 6b6f5aa48afcd3d0ab0e120bf53f1f133f9d568b Mon Sep 17 00:00:00 2001 From: finga Date: Mon, 30 Jan 2023 17:01:25 +0100 Subject: [PATCH] Spawn an axum server Concurrently spawn an axum server, which is not doing much yet, in order to prepare for the creation, etc. of reminders. --- Cargo.lock | 405 ++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 4 + config.toml | 4 + src/config.rs | 107 +++++++++---- src/main.rs | 83 +++++++---- 5 files changed, 519 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4473ddb..5b70df6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +[[package]] +name = "async-trait" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -15,10 +26,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "base64" -version = "0.13.1" +name = "axum" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -33,10 +94,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "cc" -version = "1.0.78" +name = "bytes" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -46,9 +113,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.1.3" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive", @@ -99,9 +166,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "diesel" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c186a7418a2aac330bb76cde82f16c36b03a66fb91db32d20214311f9f6545" +checksum = "4391a22b19c916e50bec4d6140f29bdda3e3bb187223fe6e3ea0b6e4d1021c04" dependencies = [ "bitflags", "byteorder", @@ -146,9 +213,9 @@ dependencies = [ [[package]] name = "email-encoding" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34dd14c63662e0206599796cd5e1ad0268ab2b9d19b868d6050d688eba2bbf98" +checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" dependencies = [ "base64", "memchr", @@ -190,6 +257,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foreign-types" version = "0.3.2" @@ -205,6 +278,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.25" @@ -281,6 +372,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[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.2" @@ -288,12 +413,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] -name = "idna" -version = "0.2.3" +name = "hyper" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -353,9 +500,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lettre" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eabca5e0b4d0e98e7f2243fb5b7520b6af2b65d8f87bcc86f2c75185a6ff243" +checksum = "dd84a055407850bcf4791baa77cb4818d37cbb79ad4e60b9b659727b920d2c65" dependencies = [ "base64", "email-encoding", @@ -371,6 +518,7 @@ dependencies = [ "once_cell", "quoted_printable", "socket2", + "tokio", "tracing", ] @@ -412,10 +560,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] -name = "matches" -version = "0.1.10" +name = "matchit" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "memchr" @@ -435,6 +583,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -482,6 +642,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.17.0" @@ -568,6 +738,32 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -697,6 +893,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + [[package]] name = "schannel" version = "0.1.21" @@ -723,9 +931,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645926f31b250a2dca3c232496c2d898d91036e45ca0e97e0e2390c54e11be36" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -764,6 +972,26 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.0" @@ -773,6 +1001,18 @@ dependencies = [ "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.4" @@ -782,6 +1022,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.7" @@ -824,6 +1073,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + [[package]] name = "tempfile" version = "3.3.0" @@ -918,6 +1173,37 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.6.0" @@ -941,9 +1227,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" dependencies = [ "indexmap", "nom8", @@ -952,6 +1238,54 @@ dependencies = [ "toml_datetime", ] +[[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.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "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.37" @@ -959,6 +1293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1010,6 +1345,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "unicode-bidi" version = "0.3.10" @@ -1049,6 +1390,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1060,12 +1411,16 @@ name = "whakarite" version = "0.1.0" dependencies = [ "anyhow", + "axum", "clap", "diesel", "lettre", "serde", "time", + "tokio", "toml", + "tower", + "tower-http", "tracing", "tracing-subscriber", "xdg", diff --git a/Cargo.toml b/Cargo.toml index f962407..b446843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,7 @@ serde = { version = "1", features = ["derive"] } toml = "0.6" clap = { version = "4", features = ["derive"] } xdg = "2" +tokio = { version = "1", features = ["full"] } +axum = "0.6" +tower = { version = "0.4", features = ["util"] } +tower-http = { version = "0.3", features = ["trace"] } diff --git a/config.toml b/config.toml index 25af680..287fc0a 100644 --- a/config.toml +++ b/config.toml @@ -4,3 +4,7 @@ # name = "whakarite" # user = "whakarite" pass = "whakarite" + +# [server] +# address = "::1" +# port = 8080 diff --git a/src/config.rs b/src/config.rs index d1e0eff..627a7be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,21 +1,25 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Error, Result}; use serde::Deserialize; -use std::{env, fs, path::PathBuf}; -use tracing::{error, info, trace}; +use std::{ + env, fs, + net::{IpAddr, Ipv6Addr}, + path::PathBuf, +}; +use tracing::{error, info, trace, warn}; -fn default_host() -> String { +fn default_database_host() -> String { "localhost".to_string() } -const fn default_port() -> u16 { +const fn default_database_port() -> u16 { 5432 } -fn default_name() -> String { +fn default_database_name() -> String { "whakarite".to_string() } -fn default_user() -> String { +fn default_database_user() -> String { "whakarite".to_string() } @@ -23,68 +27,105 @@ fn default_user() -> String { #[serde(deny_unknown_fields)] pub struct Database { /// Host of the database - #[serde(default = "default_host")] + #[serde(default = "default_database_host")] pub host: String, /// Port of the database - #[serde(default = "default_port")] + #[serde(default = "default_database_port")] pub port: u16, /// Name of the database - #[serde(default = "default_name")] + #[serde(default = "default_database_name")] pub name: String, /// Name of the user to connect to the database - #[serde(default = "default_user")] + #[serde(default = "default_database_user")] pub user: String, /// Password of the user to connect to the database pub pass: String, } +const fn default_server_address() -> IpAddr { + IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)) +} + +const fn default_server_port() -> u16 { + 8080 +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Server { + /// Listening address of the server + #[serde(default = "default_server_address")] + pub address: IpAddr, + + /// Port of the database + #[serde(default = "default_server_port")] + pub port: u16, +} + +impl Default for Server { + fn default() -> Self { + Self { + address: default_server_address(), + port: default_server_port(), + } + } +} + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct Config { /// Database configuration pub database: Database, + + /// server configuration + #[serde(default)] + pub server: Server, } impl Config { - fn try_paths() -> Result<(PathBuf, String)> { - let file = "config.toml"; + fn load_file(file: &PathBuf) -> Result { info!(?file, "loading configuration"); - if let Ok(config) = fs::read_to_string(&file) { - return Ok((file.into(), config)); + let config = toml::from_str(&fs::read_to_string(file)?)?; + trace!(?file, ?config, "loaded configuration"); + Ok(config) + } + + #[allow(clippy::cognitive_complexity)] + fn try_paths() -> Result { + let file = "config.toml"; + match Self::load_file(&file.into()) { + Ok(config) => return Ok(config), + Err(error) => warn!(?file, "cannot load configuration: {:#}", Error::msg(error)), } - let user_file = + let user_config = xdg::BaseDirectories::with_prefix(env!("CARGO_BIN_NAME"))?.get_config_file(file); - info!(file = ?user_file, "loading configuration"); - if let Ok(config) = fs::read_to_string(&user_file) { - return Ok((user_file, config)); + match Self::load_file(&user_config) { + Ok(config) => return Ok(config), + Err(error) => warn!(file = ?user_config, "cannot load configuration: {:#}", Error::msg(error)), } - let global_file = format!("/etc/{}/{}", env!("CARGO_BIN_NAME"), file); - info!(file = ?global_file, "loading configuration"); - if let Ok(config) = fs::read_to_string(&global_file) { - return Ok((global_file.into(), config)); + let global_config = format!("/etc/{}/{}", env!("CARGO_BIN_NAME"), file); + match Self::load_file(&global_config.clone().into()) { + Ok(config) => return Ok(config), + Err(error) => { + warn!(file = ?global_config, "cannot load configuration: {:#}", Error::msg(error)); + } } - error!("no configuration file found"); - bail!("no configuration file found"); + error!("no valid configuration file found"); + bail!("no valid configuration file found"); } pub fn load_config(file: Option) -> Result { if let Some(file) = file { - info!(?file, "loading configuration"); - let config: Config = toml::from_str(&fs::read_to_string(&file)?)?; - trace!(?file, ?config, "loaded configuration"); - Ok(config) + Ok(Self::load_file(&file)?) } else { - let (file, config) = Self::try_paths()?; - let config = toml::from_str(&config)?; - trace!(?file, ?config, "loaded configuration"); - Ok(config) + Ok(Self::try_paths()?) } } } diff --git a/src/main.rs b/src/main.rs index 9e13b5e..708e6cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,15 @@ use anyhow::Result; +use axum::{http::StatusCode, response::IntoResponse, Router}; use clap::Parser; use diesel::{ prelude::*, r2d2::{ConnectionManager, Pool, PooledConnection}, }; use lettre::{Message, SmtpTransport, Transport}; -use std::{env, thread::park_timeout}; +use std::{env, net::SocketAddr}; use time::{Duration, OffsetDateTime}; +use tower::ServiceBuilder; +use tower_http::trace::TraceLayer; use tracing::{debug, info, trace}; mod args; @@ -24,39 +27,51 @@ fn remind( ) -> Result<()> { info!("checking for reminders"); - for reminder in schema::reminders::dsl::reminders + let result = schema::reminders::dsl::reminders .filter(schema::reminders::executed.is_null()) .order(schema::reminders::planned.asc()) - .load::(db)? - { - trace!(?reminder, "checking reminder"); - if reminder.planned <= OffsetDateTime::now_utc() { - let email = Message::builder() - .from("whakarite@localhost".parse()?) - .to(reminder.receiver.parse()?) - .subject(&reminder.title) - .body(reminder.message.to_string())?; + .load::(db)?; - mailer.send(&email)?; + if result.is_empty() { + info!("no reminders present, parking indefinitely"); + std::thread::park(); + } else { + for reminder in result { + trace!(?reminder, "checking reminder"); + if reminder.planned <= OffsetDateTime::now_utc() { + let email = Message::builder() + .from("whakarite@localhost".parse()?) + .to(reminder.receiver.parse()?) + .subject(&reminder.title) + .body(reminder.message.to_string())?; - diesel::update(&reminder) - .set(schema::reminders::executed.eq(Some(OffsetDateTime::now_utc()))) - .execute(db)?; + mailer.send(&email)?; - debug!("email sent to {}", reminder.receiver); - } else { - let duration = reminder.planned - OffsetDateTime::now_utc(); - info!(?duration, "parking reminder"); + diesel::update(&reminder) + .set(schema::reminders::executed.eq(Some(OffsetDateTime::now_utc()))) + .execute(db)?; - park_timeout(::try_from(duration)?); + debug!("email sent to {}", reminder.receiver); + } else { + let duration = reminder.planned - OffsetDateTime::now_utc(); + info!(?duration, "parking reminder"); + + std::thread::park_timeout(::try_from(duration)?); + return Ok(()); + } } - // Check for another remind job if none dont loop forever } Ok(()) } -fn main() -> Result<()> { +#[allow(clippy::unused_async)] +async fn not_found() -> impl IntoResponse { + (StatusCode::NOT_FOUND, "Page not found") +} + +#[tokio::main] +async fn main() -> Result<()> { let args = Args::parse(); if env::var("RUST_LOG").is_err() { @@ -93,9 +108,25 @@ fn main() -> Result<()> { .values(&test_reminder) .execute(&mut db_pool.get()?)?; - let mailer = SmtpTransport::unencrypted_localhost(); + std::thread::spawn(move || { + let mailer = SmtpTransport::unencrypted_localhost(); - loop { - remind(&mut db_pool.get()?, &mailer)?; - } + loop { + remind(&mut db_pool.get().unwrap(), &mailer).unwrap(); + } + }); + + let app = Router::new() + .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())) + .fallback(not_found); + + let addr = SocketAddr::from((config.server.address, config.server.port)); + + info!("{} listening on {}", env!("CARGO_PKG_NAME"), addr); + + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await?; + + Ok(()) }