commit 938dffc63dfeeb85b55ee081bb3d72384cd80d50 Author: LastExceed Date: Sun Feb 11 11:54:02 2024 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..30e3300 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1019 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[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 = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rumqttc" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8941c6791801b667d52bfe9ff4fc7c968d4f3f9ae8ae7abdaaa1c966feafc8" +dependencies = [ + "bytes", + "flume", + "futures-util", + "log", + "rustls-native-certs", + "rustls-pemfile", + "rustls-webpki", + "thiserror", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spacermake" +version = "0.1.0" +dependencies = [ + "boolinator", + "chrono", + "csv", + "futures", + "itertools", + "json", + "lazy_static", + "rumqttc", + "serde", + "tap", + "tokio", + "toml", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "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-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0061108 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "spacermake" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# paho-mqtt = { version = "0.12.3", default-features=false, features = ["bundled"] } +tokio = { version = "1.35.1", features = ["rt-multi-thread", "fs", "sync"] } +rumqttc = "0.23.0" +json = "0.12.4" +boolinator = "2.4.0" +itertools = "0.12.1" +csv = "1.3.0" +chrono = "0.4.33" +serde = { version = "1.0.196", features = ["derive"] } +tap = "1.0.1" +lazy_static = "1.4.0" +toml = "0.8.10" +futures = "0.3.30" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..421534a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,53 @@ +use std::time::Duration; +use std::collections::{HashMap, HashSet}; + +use lazy_static::*; +use rumqttc::{AsyncClient, EventLoop, MqttOptions, QoS}; +use state::{Announcer, Listener, State}; + +use utils::parse_toml_file; + +mod state; +mod utils; + +pub const BOOKING_TOPIC: &str = "fabaccess/log"; + +lazy_static! { + static ref SLAVES_BY_MASTER: HashMap> = parse_toml_file("master-slave_relations.toml"); + static ref SLAVE_PROPERTIES: HashMap = parse_toml_file("slave_properties.toml"); + static ref MACHINE_IDS: HashMap = parse_toml_file::("/root/fabfire/config.toml") + ["readers"] + .as_table() + .unwrap() + .iter() + .map(|(_key, value)| { + let entry = value.as_table().unwrap(); + ( + entry["machine"].as_str().unwrap().replace("urn:fabaccess:resource:", ""), + entry["id"].as_str().unwrap().into() + ) + }) + .collect(); +} + +#[tokio::main] +async fn main() { + let (client, event_loop) = create_client().await; + + let listener = State::new(Listener, client); + let announcer = listener.duplicate_as(Announcer); + + tokio::spawn(announcer.run()); + listener.run(event_loop).await; +} + +async fn create_client() -> (AsyncClient, EventLoop) { + let mut mqttoptions = MqttOptions::new("spacermake", "mqtt.makerspace-bocholt.local", 1883); + mqttoptions.set_keep_alive(Duration::from_secs(5)); + + let (client, event_loop) = AsyncClient::new(mqttoptions, 10); + client.subscribe("tele/+/MARGINS", QoS::AtMostOnce).await.expect("failed to subscribe"); + client.subscribe(BOOKING_TOPIC, QoS::AtMostOnce).await.expect("failed to subscribe"); + + (client, event_loop) +} \ No newline at end of file diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..c089b8e --- /dev/null +++ b/src/state.rs @@ -0,0 +1,66 @@ +use std::time::Instant; +use std::sync::Arc; +use std::collections::{HashMap, VecDeque}; + +use rumqttc::{AsyncClient, QoS}; +use tokio::sync::RwLock; + +use crate::utils::booking::Booking; +use crate::SLAVE_PROPERTIES; + +mod announcer; +mod listener; + +//markers +pub struct Listener; +pub struct Announcer; + +pub struct State { + pub kind: Kind, + pub client: Arc>, + pub bookings: Arc>>, + pub scheduled_shutdowns: Arc>> +} + +impl State { + pub fn new(kind: Kind, client: AsyncClient) -> Self { + Self { + kind, + client: Arc::new(RwLock::new(client)), + bookings: Default::default(), + scheduled_shutdowns: Default::default() + } + } + + pub fn duplicate_as(&self, kind: NewKind) -> State { + State { + kind, + client: Arc::clone(&self.client), + bookings: Arc::clone(&self.bookings), + scheduled_shutdowns: Arc::clone(&self.scheduled_shutdowns) + } + } + + //probably doesn't belong here, dunno where else to put it + async fn update_power_state(&self, machine: &str, new_state: bool) { + self.client + .read() + .await + .publish( + get_slave_topic(machine), + QoS::AtMostOnce, + false, + if new_state { b"ON".as_slice() } else { b"OFF".as_slice() } + ) + .await + .expect("failed to publish"); + } +} + +fn get_slave_topic(machine: &str) -> String { + if SLAVE_PROPERTIES[machine][2] { + format!("cmnd/{machine}/Power") + } else { + format!("shellies/{machine}/relay/0") + } +} \ No newline at end of file diff --git a/src/state/announcer.rs b/src/state/announcer.rs new file mode 100644 index 0000000..24a455b --- /dev/null +++ b/src/state/announcer.rs @@ -0,0 +1,73 @@ +use std::time::{Duration, Instant}; + +use futures::join; +use futures::future::join_all; +use rumqttc::QoS; +use tap::Pipe; +use tokio::time::sleep; + +use crate::MACHINE_IDS; +use crate::utils::{create_display_update_message, minute_mark}; +use crate::{Announcer, State}; + +impl State { + pub async fn run(self) { + loop { + join!( + self.update_all_runtime_displays(), + self.perform_scheduled_shutdowns() + ); + + sleep(Duration::from_secs(1)).await; + } + } + + async fn update_all_runtime_displays(&self) { + self.bookings + .read() + .await + .iter() + .filter_map(|(machine, booking)| { + if !booking.is_running() || !minute_mark(booking.total_runtime()) { + return None; + } + + let Some(id) = MACHINE_IDS.get(machine) else { + println!("error: no ID found for {machine}"); + return None; + }; + + let future = self.update_runtime_display(id, booking.total_runtime()); + Some(future) + }) + .pipe(join_all) + .await; + } + + async fn update_runtime_display(&self, machine_id: &str, runtime: Duration) { + self.client + .read() + .await + .publish( + format!("/cmnd/reader/{machine_id}"), + QoS::AtMostOnce, + false, + create_display_update_message(runtime) + ) + .await + .expect("failed to publish display update"); + } + + async fn perform_scheduled_shutdowns(&self) { + let now = Instant::now(); + let mut schedule = self.scheduled_shutdowns.write().await; + while let Some((time, machine)) = schedule.front() { + if time > &now { + break; + } + + self.update_power_state(&machine, false).await; + schedule.pop_front(); + } + } +} \ No newline at end of file diff --git a/src/state/listener.rs b/src/state/listener.rs new file mode 100644 index 0000000..172170c --- /dev/null +++ b/src/state/listener.rs @@ -0,0 +1,163 @@ +use std::ops::Sub; +use std::time::{Duration, Instant}; + +use boolinator::Boolinator; +use rumqttc::EventLoop; +use rumqttc::Event::Incoming; +use rumqttc::Packet::Publish; + +use crate::{State, Listener, BOOKING_TOPIC, SLAVES_BY_MASTER, SLAVE_PROPERTIES}; +use crate::utils::get_power_state; +use crate::utils::logs::{log_debug, machinelog}; +use crate::utils::booking::Booking; + +impl State { + pub async fn run(mut self, mut event_loop: EventLoop) { + loop { + let Incoming(Publish(publish)) = event_loop + .poll() + .await + .expect("notification error") + else { continue }; + + self.on_publish(publish).await; + } + } + + async fn on_publish(&mut self, publish: rumqttc::Publish) { + //pretty ugly, cant figure out a clean way to do this + let Ok(payload) = String::from_utf8(publish.payload.clone().into()) + else { + log_debug(&publish.topic, &format!("{:?}", &publish.payload), Err("non-utf8 payload")) + .expect("debug log failed"); + + return; + }; + //end of ugly + + let result = self.handle_payload(&publish.topic, &payload).await; + + log_debug(&publish.topic, &payload, result) + .expect("debug log failed") + } + + async fn handle_payload(&mut self, topic: &str, payload: &str) -> Result<(), &str> { + let splits: Result<[_; 3], _> = topic + .split('/') + .collect::>() + .try_into(); + + match splits { + Ok(["tele", machine_name, "MARGINS"]) + => self.on_machine_activity(payload, &machine_name.into()).await, + + _ if topic == BOOKING_TOPIC + => self.on_booking_change(payload).await, + + _ => Err("unknown topic") + } + } + + async fn on_booking_change(&mut self, payload: &str) -> Result<(), &'static str> { + let [machine, user, status] = payload + .split(';') + .map(String::from) + .collect::>() + .try_into() + .map_err(|_| "unexpected data count in payload")?; + + match status.as_str() { + "booked" => self.try_book(&machine, &user).await?, + "released" => self.try_release(&machine).await?, + _ => return Ok(()) //ignore other statuses + } + + println!("info: {user} {status} {machine}"); + + Ok(()) + } + + #[allow(clippy::ptr_arg)] //false positive + async fn try_book(&mut self, machine: &String, user: &String) -> Result<(), &'static str> { + let mut bookings = self.bookings.write().await; + if bookings.contains_key(machine) { + return Err("machine got double-booked"); + } + bookings.insert(machine.clone(), Booking::new(user.clone())); + drop(bookings); + self.update_slaves(machine, false, true, true).await + } + + async fn try_release(&mut self, machine: &String) -> Result<(), &'static str> { + let mut booking = self + .bookings + .write() + .await + .remove(machine) + .ok_or("released unbooked machine")?; + + let was_running = booking.track(false); + self.update_slaves(machine, was_running, true, false).await?; + + machinelog(machine, &booking) + .expect("machine log failed"); + + Ok(()) + } + + async fn on_machine_activity(&mut self, payload: &str, machine: &String) -> Result<(), &'static str> { + let power_string = get_power_state(payload)?; + + let (power, err) = + match power_string.as_str() { + "ON" => (true, "machine was turned on while already running"), + "OFF" => (false, "machine was turned off without running in the first place"), + _ => return Err("unknown power state") + }; + + self.bookings + .write() + .await + .get_mut(machine) + .ok_or("received activity from unbooked machine")? + .track(power) + .as_result((), err)?; + + self.update_slaves(machine, true, false, power).await?; + + println!("info: {machine} got turned {power_string}"); + + Ok(()) + } + + pub async fn update_slaves(&mut self, master: &String, short_slaves: bool, long_slaves: bool, power: bool) -> Result<(), &'static str> { + let slaves_used_by_others = self + .bookings + .read() + .await + .iter() + .filter(|(_, booking)| booking.is_running()) + .flat_map(|(machine, _)| &SLAVES_BY_MASTER[machine]) //todo: error handing + .cloned() + .collect(); + + let slaves_to_update = SLAVES_BY_MASTER + .get(master) + .ok_or("unknown master")? + .sub(&slaves_used_by_others) + .into_iter() + .filter(|slave| if SLAVE_PROPERTIES[slave][0] { long_slaves } else { short_slaves }); + + for slave in slaves_to_update { + if !power && SLAVE_PROPERTIES[&slave][1] { + let shutdown_timestamp = Instant::now() + Duration::from_secs(30); + self.scheduled_shutdowns.write().await.push_back((shutdown_timestamp, slave)); + continue; + } + + self.update_power_state(&slave, power).await; + } + + Ok(()) + } +} \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..b13f7d6 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,46 @@ +use std::fs; +use std::time::Duration; + +use serde::de::DeserializeOwned; + +pub mod logs; +pub mod booking; + +pub fn parse_toml_file(path: &str) -> T { + let file_content = fs::read_to_string(path).expect("failed to read .toml file"); + toml::from_str(&file_content).expect("failed to parse toml") +} + +pub fn get_power_state(payload: &str) -> Result { + //todo: there gotta be an easier way to do this + json::parse(payload) + .map_err(|_| "payload is not a valid json string")? + .entries() + .find(|(key, _value)| *key == "MARGINS") + .ok_or("no MARGINS data present in payload")? + .1 + .entries() + .find(|(key, _value)| *key == "PowerHigh") + .ok_or("no powerHigh information")? + .1 + .as_str() + .ok_or("powerHigh state was not a string") + .map(str::to_string) +} + +///whether this duration crossed a minute boundary within the last second +pub fn minute_mark(duration: Duration) -> bool { + duration.as_secs() % 60 == 0 +} + +pub fn create_display_update_message(runtime: Duration) -> String { + let hours = runtime.as_secs() / 3600; + let minutes = runtime.as_secs() / 60 % 60; + + json::object! { + Cmd: "message", + MssgID: 12, + ClrTxt: "Nutzungsdauer", + AddnTxt: format!("{hours:.0}:{minutes:0>2.0}") + }.to_string() +} \ No newline at end of file diff --git a/src/utils/booking.rs b/src/utils/booking.rs new file mode 100644 index 0000000..a587b1f --- /dev/null +++ b/src/utils/booking.rs @@ -0,0 +1,55 @@ +use std::time::{Duration, Instant}; + +use chrono::{DateTime, Local}; + +pub struct Booking { + pub user: String, + pub creation_datetime: DateTime, + pub creation_instant: Instant, + pub currently_running_since: Option, + runtime_accumulator: Duration +} + +impl Booking { + pub fn new(user: String) -> Self { + Self { + user, + creation_datetime: Local::now(), + creation_instant: Instant::now(), + currently_running_since: None, + runtime_accumulator: Duration::ZERO + } + } + + pub fn track(&mut self, power: bool) -> bool { + if self.is_running() == power { + return false; + } + + if power { + self.currently_running_since = Some(Instant::now()); + } else { + self.runtime_accumulator += self + .currently_running_since + .take() + .unwrap() + .elapsed(); + } + + true + } + + pub fn is_running(&self) -> bool { + self.currently_running_since.is_some() + } + + pub fn total_runtime(&self) -> Duration { + let mut total = self.runtime_accumulator; + + if let Some(startup) = self.currently_running_since { + total += startup.elapsed(); + } + + total + } +} \ No newline at end of file diff --git a/src/utils/logs.rs b/src/utils/logs.rs new file mode 100644 index 0000000..40dad82 --- /dev/null +++ b/src/utils/logs.rs @@ -0,0 +1,64 @@ +use std::time::Duration; +use std::ops::Div; +use std::io::{self, Write}; +use std::fs::File; + +use chrono::Local; +use serde::Serialize; +use tap::Pipe; + +use crate::utils::booking::Booking; + +#[derive(Serialize)] +struct Record<'s> { + machine: &'s str, + date: String, + time_booked: String, + time_released: String, + booking_duration: i32, //minutes + runtime: Duration, //minutes + user: &'s str +} + +pub fn machinelog(machine: &str, booking: &Booking) -> io::Result<()> { + let record = Record { + machine, + date: booking.creation_datetime.date_naive().to_string(), + time_booked: booking.creation_datetime.time().to_string(), + time_released: Local::now().time().to_string(), + booking_duration: booking.creation_instant.elapsed().as_secs_f32().div(60.0).ceil() as _, + runtime: booking.total_runtime(), + user: &booking.user + }; + + File::options() + .append(true) + .open("/root/machinelog")? + .pipe(csv::Writer::from_writer) + .serialize(record) + .map_err(|_| io::ErrorKind::Other.into()) +} + +pub fn log_debug(topic: &str, payload: &str, result: Result<(), &str>) -> io::Result<()> { + if let Err(error) = result { + println!("error: {error}"); + println!(" topic: {topic}"); + println!(" payload: {payload}"); + println!() + } + + let time = Local::now().to_string(); + let result = result.err().unwrap_or("ok"); + + let record = format!(" +time: {time} +topic: {topic} +payload: {payload} +result: {result}", + ); + + File::options() + .append(true) + .open("/root/machinelog_debug.csv")? + .write_all(record.as_bytes()) +} \ No newline at end of file