From e7db9c38f390c86d1c7f61a59400b329e8a36b28 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 11 Jan 2024 10:44:12 +0100 Subject: [PATCH 01/12] cli: work on basic cli and serde error hanlding --- Cargo.lock | 56 ++++++++++++++++++++++++++++++++ Cargo.toml | 5 ++- crates/app/Cargo.toml | 13 ++++++++ crates/app/src/main.rs | 73 ++++++++++++++++++++++++++++++++++++++++++ crates/rpl/Cargo.toml | 4 +-- 5 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 crates/app/Cargo.toml create mode 100644 crates/app/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index b0b723c..7e1aaf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "app" +version = "0.1.0" +dependencies = [ + "ariadne", + "clap", + "ron", + "serde", + "serde_json", +] + +[[package]] +name = "ariadne" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd002a6223f12c7a95cdd4b1cb3a0149d22d37f7a9ecdb2cb691a071fe236c29" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -308,6 +329,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -462,6 +489,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + [[package]] name = "scopeguard" version = "1.2.0" @@ -488,6 +521,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -543,6 +587,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "utf8parse" version = "0.2.1" @@ -621,6 +671,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/Cargo.toml b/Cargo.toml index 6aa4943..4e17091 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ +members = [ "crates/app", "crates/executor", "crates/pl-cli", "crates/rpl" @@ -8,6 +8,9 @@ resolver = "2" [workspace.dependencies] clap = { version = "4", features = [ "derive" ] } +serde = { version = "1.0", features = [ "derive" ] } +ron = "0.8" +serde_json = "1.0" [lints.rust] unsafe_code = "deny" diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml new file mode 100644 index 0000000..4f05ebc --- /dev/null +++ b/crates/app/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { workspace = true, features = [ "derive" ] } +serde = { workspace = true, features = [ "derive" ] } +ron = { workspace = true } +serde_json = { workspace = true } +ariadne = "0.4" diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs new file mode 100644 index 0000000..a96ef3f --- /dev/null +++ b/crates/app/src/main.rs @@ -0,0 +1,73 @@ +use crate::{config::Configs, error_reporting::report_serde_json_err}; + +mod cli { + use clap::{Parser, Subcommand}; + + #[derive(Parser)] + struct Args { + #[command(subcommand)] + command: Command, + } + + #[derive(Clone, Subcommand)] + enum Command {} +} + +mod config { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Serialize, Deserialize)] + pub struct Configs<'a> { + example_value: i32, + example_string: &'a str, + } + + impl Configs<'_> { + pub fn read_json(config_text: &str) -> serde_json::Result> { + serde_json::from_str(config_text) + } + } +} + +mod error_reporting { + use std::{ops::Range, process}; + + use ariadne::{Report, Source}; + + pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { + use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source}; + let offset = try_reconstruct_loc(src, err.line(), err.column()); + + Report::build(ariadne::ReportKind::Error, "test", offset) + .with_label(Label::new(("test", offset..offset))) + .with_message(err.to_string()) + .finish() + .print(("test", Source::from(src))) + .unwrap(); + process::exit(1); + } + fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { + let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); + + src.lines().enumerate().fold(0, |acc, (i, line)| { + if i < line_nr { + acc + line.len() + } else if i == line_nr { + acc + col_nr + } else { + acc + } + }) + } +} + +fn main() { + const TEST_JSON_CONFIG: &str = "{ \"example_value\": 42, \"example_string\": \"meow\" }"; + const TEST_JSON_CONFIG_BROKEN: &str = "{ + \"example_value\": \"42\", + \"example_string\": \"meow\" +}"; + + dbg!(Configs::read_json(TEST_JSON_CONFIG_BROKEN) + .map_err(|e| report_serde_json_err(TEST_JSON_CONFIG_BROKEN, e))); +} diff --git a/crates/rpl/Cargo.toml b/crates/rpl/Cargo.toml index e406f11..d125c78 100644 --- a/crates/rpl/Cargo.toml +++ b/crates/rpl/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = { version = "1.0.193", features = [ "derive" ] } -ron = "0.8" +serde = { workspace = true, features = [ "derive" ] } +ron = { workspace = true } -- 2.46.0 From 33aa131b95281a377dd0d7081acb60feaef3f0f3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 11 Jan 2024 16:54:55 +0100 Subject: [PATCH 02/12] app: improve errors a bit more added a message to the label so the position of the error gets shown and a note to make clear we don't have enough info for better error messages thanks to serde --- crates/app/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index a96ef3f..b5de824 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -39,8 +39,13 @@ mod error_reporting { let offset = try_reconstruct_loc(src, err.line(), err.column()); Report::build(ariadne::ReportKind::Error, "test", offset) - .with_label(Label::new(("test", offset..offset))) + .with_label( + Label::new(("test", offset..offset)).with_message("Something went wrong here!"), + ) .with_message(err.to_string()) + .with_note( + "We'd like to give better errors, but serde errors are horrible to work with...", + ) .finish() .print(("test", Source::from(src))) .unwrap(); -- 2.46.0 From 4788278d86314e35ae13b3eaa8e32090eecc4c5c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Jan 2024 09:31:48 +0100 Subject: [PATCH 03/12] more basic features, config gets read and startup messages --- Cargo.lock | 240 +++++++++++++++++++++++++++++++++++++++-- crates/app/Cargo.toml | 5 +- crates/app/src/main.rs | 158 +++++++++++++++++++++------ 3 files changed, 357 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e1aaf8..091f8a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -62,9 +62,12 @@ version = "0.1.0" dependencies = [ "ariadne", "clap", + "dirs", + "owo-colors", "ron", "serde", "serde_json", + "time", ] [[package]] @@ -226,6 +229,36 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.9.0" @@ -285,6 +318,17 @@ dependencies = [ "spin", ] +[[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 = "gif" version = "0.12.0" @@ -350,6 +394,23 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -400,6 +461,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + [[package]] name = "pl-cli" version = "0.1.0" @@ -422,6 +504,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.70" @@ -469,6 +557,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "ron" version = "0.8.1" @@ -570,6 +678,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiff" version = "0.9.0" @@ -581,6 +709,26 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "libc", + "num_threads", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -599,19 +747,49 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "weezl" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[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", + "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]] @@ -620,51 +798,93 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "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" diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 4f05ebc..0325fc1 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -6,8 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { workspace = true, features = [ "derive" ] } +clap = { workspace = true, features = [ "derive", "env" ] } serde = { workspace = true, features = [ "derive" ] } ron = { workspace = true } serde_json = { workspace = true } ariadne = "0.4" +time = { version = "0.3", features = [ "local-offset" ] } +dirs = "5" +owo-colors = "4" diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index b5de824..6fec037 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -1,48 +1,124 @@ -use crate::{config::Configs, error_reporting::report_serde_json_err}; +use clap::Parser; +use cli::Args; +use welcome_msg::print_startup_msg; + +use crate::config::Configs; mod cli { - use clap::{Parser, Subcommand}; + use std::path::PathBuf; + + use clap::Parser; #[derive(Parser)] - struct Args { - #[command(subcommand)] - command: Command, + pub(crate) struct Args { + /// Read this config file. + #[arg(short, long)] + pub config_file: Option, + #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] + pub no_startup_message: bool, } - - #[derive(Clone, Subcommand)] - enum Command {} } mod config { + use std::{ + fs, + path::{Path, PathBuf}, + process, + }; + use serde::{Deserialize, Serialize}; + use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; + #[derive(Debug, Serialize, Deserialize)] - pub struct Configs<'a> { - example_value: i32, - example_string: &'a str, + pub struct Configs { + #[serde(default = "default_example_value")] + pub example_value: i32, + #[serde(default = "default_no_startup_msg")] + pub no_startup_message: bool, } - impl Configs<'_> { - pub fn read_json(config_text: &str) -> serde_json::Result> { - serde_json::from_str(config_text) + /// what the fuck serde why do i need this + fn default_example_value() -> i32 { + 43 + } + fn default_no_startup_msg() -> bool { + false + } + + impl Configs { + pub fn read(custom_path: Option) -> Self { + use owo_colors::OwoColorize; + let p = match custom_path { + Some(p) => p, + None => { + let config_path = dirs::config_dir().expect("config dir should exist"); + + let ron_path = config_path.with_file_name("config.ron"); + let json_path = config_path.with_file_name("config.json"); + + if Path::new(&ron_path).exists() { + ron_path + } else if Path::new(&json_path).exists() { + json_path + } else { + eprintln!("{}: couldn't find config file", "Fatal error".red()); + process::exit(1) + } + } + }; + + match p.extension().map(|v| v.to_str().unwrap()) { + Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), + Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), + None | Some(_) => { + eprintln!( + "{}: couldn't determine config file type", + "Fatal error".red() + ); + process::exit(1) + } + } + } + pub fn read_json(config_text: &str) -> Configs { + match serde_json::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_json_err(config_text, e), + } + } + + pub fn read_ron(config_text: &str) -> Configs { + match ron::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_ron_err(config_text, e), + } } } } mod error_reporting { - use std::{ops::Range, process}; + use std::process; - use ariadne::{Report, Source}; + use ron::error::Position; pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { - use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source}; - let offset = try_reconstruct_loc(src, err.line(), err.column()); + report_serde_err(src, err.line(), err.column(), err.to_string()) + } + + pub fn report_serde_ron_err(src: &str, err: ron::error::SpannedError) -> ! { + let Position { line, col } = err.position; + report_serde_err(src, line, col, err.to_string()) + } + + pub fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { + use ariadne::{Label, Report, Source}; + let offset = try_reconstruct_loc(src, line, col); Report::build(ariadne::ReportKind::Error, "test", offset) .with_label( Label::new(("test", offset..offset)).with_message("Something went wrong here!"), ) - .with_message(err.to_string()) + .with_message(msg) .with_note( "We'd like to give better errors, but serde errors are horrible to work with...", ) @@ -54,25 +130,37 @@ mod error_reporting { fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); - src.lines().enumerate().fold(0, |acc, (i, line)| { - if i < line_nr { - acc + line.len() - } else if i == line_nr { - acc + col_nr - } else { - acc - } - }) + src.lines() + .enumerate() + .fold(0, |acc, (i, line)| match i.cmp(&line_nr) { + std::cmp::Ordering::Less => acc + line.len() + 1, + std::cmp::Ordering::Equal => acc + col_nr, + std::cmp::Ordering::Greater => acc, + }) + } +} + +mod welcome_msg { + use time::{Month, OffsetDateTime}; + + pub fn print_startup_msg() { + // now or fallback to utc + let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); + + if now.month() == Month::June { + println!("Hello, thanks for using iOwO and happy pride month!"); + println!("Why pride month is important in {}", now.year()); + } else { + println!("Hello, thanks for using iOwO! :3"); + } } } fn main() { - const TEST_JSON_CONFIG: &str = "{ \"example_value\": 42, \"example_string\": \"meow\" }"; - const TEST_JSON_CONFIG_BROKEN: &str = "{ - \"example_value\": \"42\", - \"example_string\": \"meow\" -}"; + let args = Args::parse(); + let cfg = Configs::read(args.config_file); - dbg!(Configs::read_json(TEST_JSON_CONFIG_BROKEN) - .map_err(|e| report_serde_json_err(TEST_JSON_CONFIG_BROKEN, e))); + if !(args.no_startup_message || cfg.no_startup_message) { + print_startup_msg(); + } } -- 2.46.0 From 96374b6491a17412b7eef284759e4fd29b1797f5 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Jan 2024 09:36:30 +0100 Subject: [PATCH 04/12] split up modules into subfiles --- crates/app/src/cli.rs | 12 +++ crates/app/src/config.rs | 74 ++++++++++++++ crates/app/src/error_reporting.rs | 37 +++++++ crates/app/src/main.rs | 155 +----------------------------- crates/app/src/welcome_msg.rs | 13 +++ 5 files changed, 140 insertions(+), 151 deletions(-) create mode 100644 crates/app/src/cli.rs create mode 100644 crates/app/src/config.rs create mode 100644 crates/app/src/error_reporting.rs create mode 100644 crates/app/src/welcome_msg.rs diff --git a/crates/app/src/cli.rs b/crates/app/src/cli.rs new file mode 100644 index 0000000..30754c5 --- /dev/null +++ b/crates/app/src/cli.rs @@ -0,0 +1,12 @@ +use std::path::PathBuf; + +use clap::Parser; + +#[derive(Parser)] +pub(crate) struct Args { + /// Read this config file. + #[arg(short, long)] + pub config_file: Option, + #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] + pub no_startup_message: bool, +} diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs new file mode 100644 index 0000000..86f6763 --- /dev/null +++ b/crates/app/src/config.rs @@ -0,0 +1,74 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process, +}; + +use serde::{Deserialize, Serialize}; + +use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Configs { + #[serde(default = "default_example_value")] + pub example_value: i32, + #[serde(default = "default_no_startup_msg")] + pub no_startup_message: bool, +} + +/// what the fuck serde why do i need this +fn default_example_value() -> i32 { + 43 +} +fn default_no_startup_msg() -> bool { + false +} + +impl Configs { + pub fn read(custom_path: Option) -> Self { + use owo_colors::OwoColorize; + let p = match custom_path { + Some(p) => p, + None => { + let config_path = dirs::config_dir().expect("config dir should exist"); + + let ron_path = config_path.with_file_name("config.ron"); + let json_path = config_path.with_file_name("config.json"); + + if Path::new(&ron_path).exists() { + ron_path + } else if Path::new(&json_path).exists() { + json_path + } else { + eprintln!("{}: couldn't find config file", "Fatal error".red()); + process::exit(1) + } + } + }; + + match p.extension().map(|v| v.to_str().unwrap()) { + Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), + Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), + None | Some(_) => { + eprintln!( + "{}: couldn't determine config file type", + "Fatal error".red() + ); + process::exit(1) + } + } + } + pub fn read_json(config_text: &str) -> Configs { + match serde_json::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_json_err(config_text, e), + } + } + + pub fn read_ron(config_text: &str) -> Configs { + match ron::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_ron_err(config_text, e), + } + } +} diff --git a/crates/app/src/error_reporting.rs b/crates/app/src/error_reporting.rs new file mode 100644 index 0000000..f71e10d --- /dev/null +++ b/crates/app/src/error_reporting.rs @@ -0,0 +1,37 @@ +use std::process; + +use ron::error::Position; + +pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { + report_serde_err(src, err.line(), err.column(), err.to_string()) +} + +pub fn report_serde_ron_err(src: &str, err: ron::error::SpannedError) -> ! { + let Position { line, col } = err.position; + report_serde_err(src, line, col, err.to_string()) +} + +pub fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { + use ariadne::{Label, Report, Source}; + let offset = try_reconstruct_loc(src, line, col); + + Report::build(ariadne::ReportKind::Error, "test", offset) + .with_label(Label::new(("test", offset..offset)).with_message("Something went wrong here!")) + .with_message(msg) + .with_note("We'd like to give better errors, but serde errors are horrible to work with...") + .finish() + .print(("test", Source::from(src))) + .unwrap(); + process::exit(1); +} +fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { + let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); + + src.lines() + .enumerate() + .fold(0, |acc, (i, line)| match i.cmp(&line_nr) { + std::cmp::Ordering::Less => acc + line.len() + 1, + std::cmp::Ordering::Equal => acc + col_nr, + std::cmp::Ordering::Greater => acc, + }) +} diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 6fec037..491e8b4 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -4,157 +4,10 @@ use welcome_msg::print_startup_msg; use crate::config::Configs; -mod cli { - use std::path::PathBuf; - - use clap::Parser; - - #[derive(Parser)] - pub(crate) struct Args { - /// Read this config file. - #[arg(short, long)] - pub config_file: Option, - #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] - pub no_startup_message: bool, - } -} - -mod config { - use std::{ - fs, - path::{Path, PathBuf}, - process, - }; - - use serde::{Deserialize, Serialize}; - - use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; - - #[derive(Debug, Serialize, Deserialize)] - pub struct Configs { - #[serde(default = "default_example_value")] - pub example_value: i32, - #[serde(default = "default_no_startup_msg")] - pub no_startup_message: bool, - } - - /// what the fuck serde why do i need this - fn default_example_value() -> i32 { - 43 - } - fn default_no_startup_msg() -> bool { - false - } - - impl Configs { - pub fn read(custom_path: Option) -> Self { - use owo_colors::OwoColorize; - let p = match custom_path { - Some(p) => p, - None => { - let config_path = dirs::config_dir().expect("config dir should exist"); - - let ron_path = config_path.with_file_name("config.ron"); - let json_path = config_path.with_file_name("config.json"); - - if Path::new(&ron_path).exists() { - ron_path - } else if Path::new(&json_path).exists() { - json_path - } else { - eprintln!("{}: couldn't find config file", "Fatal error".red()); - process::exit(1) - } - } - }; - - match p.extension().map(|v| v.to_str().unwrap()) { - Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), - Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), - None | Some(_) => { - eprintln!( - "{}: couldn't determine config file type", - "Fatal error".red() - ); - process::exit(1) - } - } - } - pub fn read_json(config_text: &str) -> Configs { - match serde_json::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_json_err(config_text, e), - } - } - - pub fn read_ron(config_text: &str) -> Configs { - match ron::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_ron_err(config_text, e), - } - } - } -} - -mod error_reporting { - use std::process; - - use ron::error::Position; - - pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { - report_serde_err(src, err.line(), err.column(), err.to_string()) - } - - pub fn report_serde_ron_err(src: &str, err: ron::error::SpannedError) -> ! { - let Position { line, col } = err.position; - report_serde_err(src, line, col, err.to_string()) - } - - pub fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { - use ariadne::{Label, Report, Source}; - let offset = try_reconstruct_loc(src, line, col); - - Report::build(ariadne::ReportKind::Error, "test", offset) - .with_label( - Label::new(("test", offset..offset)).with_message("Something went wrong here!"), - ) - .with_message(msg) - .with_note( - "We'd like to give better errors, but serde errors are horrible to work with...", - ) - .finish() - .print(("test", Source::from(src))) - .unwrap(); - process::exit(1); - } - fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { - let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); - - src.lines() - .enumerate() - .fold(0, |acc, (i, line)| match i.cmp(&line_nr) { - std::cmp::Ordering::Less => acc + line.len() + 1, - std::cmp::Ordering::Equal => acc + col_nr, - std::cmp::Ordering::Greater => acc, - }) - } -} - -mod welcome_msg { - use time::{Month, OffsetDateTime}; - - pub fn print_startup_msg() { - // now or fallback to utc - let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); - - if now.month() == Month::June { - println!("Hello, thanks for using iOwO and happy pride month!"); - println!("Why pride month is important in {}", now.year()); - } else { - println!("Hello, thanks for using iOwO! :3"); - } - } -} +mod cli; +mod config; +mod error_reporting; +mod welcome_msg; fn main() { let args = Args::parse(); diff --git a/crates/app/src/welcome_msg.rs b/crates/app/src/welcome_msg.rs new file mode 100644 index 0000000..c03c543 --- /dev/null +++ b/crates/app/src/welcome_msg.rs @@ -0,0 +1,13 @@ +use time::{Month, OffsetDateTime}; + +pub fn print_startup_msg() { + // now or fallback to utc + let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); + + if now.month() == Month::June { + println!("Hello, thanks for using iOwO and happy pride month!"); + println!("Why pride month is important in {}", now.year()); + } else { + println!("Hello, thanks for using iOwO! :3"); + } +} -- 2.46.0 From e67c80a6a9d2ad76da661cd8e075dad6f16750c2 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Jan 2024 22:19:37 +0100 Subject: [PATCH 05/12] move ron to be specific to `app` --- Cargo.toml | 2 -- crates/app/Cargo.toml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e17091..3fff7c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,6 @@ resolver = "2" [workspace.dependencies] clap = { version = "4", features = [ "derive" ] } serde = { version = "1.0", features = [ "derive" ] } -ron = "0.8" -serde_json = "1.0" [lints.rust] unsafe_code = "deny" diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 0325fc1..a0043ee 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] clap = { workspace = true, features = [ "derive", "env" ] } serde = { workspace = true, features = [ "derive" ] } -ron = { workspace = true } -serde_json = { workspace = true } +ron = "0.8" +serde_json = "1.0" ariadne = "0.4" time = { version = "0.3", features = [ "local-offset" ] } dirs = "5" -- 2.46.0 From 6ccfaedb13a3c8ce412e8d40d542f93290725403 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Jan 2024 22:21:49 +0100 Subject: [PATCH 06/12] fix cursed workspace member formatting done by cargo --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3fff7c3..bc41d16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = [ "crates/app", +members = [ + "crates/app", "crates/executor", "crates/pl-cli", "crates/rpl" -- 2.46.0 From ea2e5d6075e6d48d15b204c7a666126dcbc069de Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 15 Jan 2024 09:03:55 +0100 Subject: [PATCH 07/12] app: unify config handling stuff a bit --- crates/app/src/config.rs | 80 ++++++---------------------- crates/app/src/{ => config}/cli.rs | 0 crates/app/src/config/config_file.rs | 75 ++++++++++++++++++++++++++ crates/app/src/main.rs | 11 ++-- crates/rpl/Cargo.toml | 2 +- 5 files changed, 94 insertions(+), 74 deletions(-) rename crates/app/src/{ => config}/cli.rs (100%) create mode 100644 crates/app/src/config/config_file.rs diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index 86f6763..c19b94a 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -1,74 +1,24 @@ -use std::{ - fs, - path::{Path, PathBuf}, - process, -}; +use clap::Parser; -use serde::{Deserialize, Serialize}; +use self::{cli::Args, config_file::Configs}; -use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; +mod cli; +mod config_file; -#[derive(Debug, Serialize, Deserialize)] -pub struct Configs { - #[serde(default = "default_example_value")] - pub example_value: i32, - #[serde(default = "default_no_startup_msg")] - pub no_startup_message: bool, +/// this struct may hold all configuration +pub struct Config { + pub startup_msg: bool, } -/// what the fuck serde why do i need this -fn default_example_value() -> i32 { - 43 -} -fn default_no_startup_msg() -> bool { - false -} +impl Config { + pub fn read() -> Self { + let args = Args::parse(); + let cfg = Configs::read(args.config_file); -impl Configs { - pub fn read(custom_path: Option) -> Self { - use owo_colors::OwoColorize; - let p = match custom_path { - Some(p) => p, - None => { - let config_path = dirs::config_dir().expect("config dir should exist"); - - let ron_path = config_path.with_file_name("config.ron"); - let json_path = config_path.with_file_name("config.json"); - - if Path::new(&ron_path).exists() { - ron_path - } else if Path::new(&json_path).exists() { - json_path - } else { - eprintln!("{}: couldn't find config file", "Fatal error".red()); - process::exit(1) - } - } - }; - - match p.extension().map(|v| v.to_str().unwrap()) { - Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), - Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), - None | Some(_) => { - eprintln!( - "{}: couldn't determine config file type", - "Fatal error".red() - ); - process::exit(1) - } - } - } - pub fn read_json(config_text: &str) -> Configs { - match serde_json::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_json_err(config_text, e), - } - } - - pub fn read_ron(config_text: &str) -> Configs { - match ron::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_ron_err(config_text, e), + Self { + // this is negated because to an outward api, the negative is more intuitive, + // while in the source the other way around is more intuitive + startup_msg: !(args.no_startup_message || cfg.no_startup_message), } } } diff --git a/crates/app/src/cli.rs b/crates/app/src/config/cli.rs similarity index 100% rename from crates/app/src/cli.rs rename to crates/app/src/config/cli.rs diff --git a/crates/app/src/config/config_file.rs b/crates/app/src/config/config_file.rs new file mode 100644 index 0000000..fc96675 --- /dev/null +++ b/crates/app/src/config/config_file.rs @@ -0,0 +1,75 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process, +}; + +use serde::{Deserialize, Serialize}; + +use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Configs { + #[serde(default = "default_example_value")] + pub example_value: i32, + #[serde(default = "default_no_startup_msg")] + pub no_startup_message: bool, +} + +/// what the fuck serde why do i need this +pub(crate) fn default_example_value() -> i32 { + 43 +} + +pub(crate) fn default_no_startup_msg() -> bool { + false +} + +impl Configs { + pub fn read(custom_path: Option) -> Self { + use owo_colors::OwoColorize; + let p = match custom_path { + Some(p) => p, + None => { + let config_path = dirs::config_dir().expect("config dir should exist"); + + let ron_path = config_path.with_file_name("config.ron"); + let json_path = config_path.with_file_name("config.json"); + + if Path::new(&ron_path).exists() { + ron_path + } else if Path::new(&json_path).exists() { + json_path + } else { + eprintln!("{}: couldn't find config file", "Fatal error".red()); + process::exit(1) + } + } + }; + + match p.extension().map(|v| v.to_str().unwrap()) { + Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), + Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), + None | Some(_) => { + eprintln!( + "{}: couldn't determine config file type", + "Fatal error".red() + ); + process::exit(1) + } + } + } + pub fn read_json(config_text: &str) -> Configs { + match serde_json::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_json_err(config_text, e), + } + } + + pub fn read_ron(config_text: &str) -> Configs { + match ron::from_str(config_text) { + Ok(c) => c, + Err(e) => report_serde_ron_err(config_text, e), + } + } +} diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 491e8b4..deab5b4 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -1,19 +1,14 @@ -use clap::Parser; -use cli::Args; +use config::Config; use welcome_msg::print_startup_msg; -use crate::config::Configs; - -mod cli; mod config; mod error_reporting; mod welcome_msg; fn main() { - let args = Args::parse(); - let cfg = Configs::read(args.config_file); + let cfg = Config::read(); - if !(args.no_startup_message || cfg.no_startup_message) { + if cfg.startup_msg { print_startup_msg(); } } diff --git a/crates/rpl/Cargo.toml b/crates/rpl/Cargo.toml index d125c78..b40a46e 100644 --- a/crates/rpl/Cargo.toml +++ b/crates/rpl/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] serde = { workspace = true, features = [ "derive" ] } -ron = { workspace = true } +ron = "0.8" -- 2.46.0 From 7c9dca0ae21a0754ffb9e9899e4dda53d97dd776 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 15 Jan 2024 10:43:35 +0100 Subject: [PATCH 08/12] app: improve config loading and merging --- crates/app/src/config.rs | 49 +++++++++++++++++-- crates/app/src/config/cli.rs | 2 +- crates/app/src/config/config_file.rs | 71 ++++++++++------------------ crates/app/src/main.rs | 5 +- 4 files changed, 73 insertions(+), 54 deletions(-) diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index c19b94a..a0196fe 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -1,6 +1,10 @@ use clap::Parser; -use self::{cli::Args, config_file::Configs}; +use self::{ + cli::Args, + config_file::{find_config_file, Configs}, + error::ConfigError, +}; mod cli; mod config_file; @@ -11,14 +15,49 @@ pub struct Config { } impl Config { - pub fn read() -> Self { + pub fn read() -> Result { let args = Args::parse(); - let cfg = Configs::read(args.config_file); + let config_path = if let Some(config_path) = args.config_path { + config_path + } else { + find_config_file()? + }; + let file_config = Configs::read(config_path)?; - Self { + Ok(Self { // this is negated because to an outward api, the negative is more intuitive, // while in the source the other way around is more intuitive - startup_msg: !(args.no_startup_message || cfg.no_startup_message), + startup_msg: !(args.no_startup_message || file_config.no_startup_message), + }) + } +} + +pub mod error { + #[derive(Debug)] + pub enum ConfigError { + NoConfigDir, + NoConfigFileFound, + IoError(std::io::Error), + UnknownExtension(Option), + SerdeJsonError(serde_json::Error), + SerdeRonError(ron::error::SpannedError), + } + + impl From for ConfigError { + fn from(value: std::io::Error) -> Self { + Self::IoError(value) + } + } + + impl From for ConfigError { + fn from(value: serde_json::Error) -> Self { + Self::SerdeJsonError(value) + } + } + + impl From for ConfigError { + fn from(value: ron::error::SpannedError) -> Self { + Self::SerdeRonError(value) } } } diff --git a/crates/app/src/config/cli.rs b/crates/app/src/config/cli.rs index 30754c5..23f8ef1 100644 --- a/crates/app/src/config/cli.rs +++ b/crates/app/src/config/cli.rs @@ -6,7 +6,7 @@ use clap::Parser; pub(crate) struct Args { /// Read this config file. #[arg(short, long)] - pub config_file: Option, + pub config_path: Option, #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] pub no_startup_message: bool, } diff --git a/crates/app/src/config/config_file.rs b/crates/app/src/config/config_file.rs index fc96675..d454223 100644 --- a/crates/app/src/config/config_file.rs +++ b/crates/app/src/config/config_file.rs @@ -1,12 +1,11 @@ use std::{ fs, path::{Path, PathBuf}, - process, }; use serde::{Deserialize, Serialize}; -use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; +use super::error::ConfigError; #[derive(Debug, Serialize, Deserialize)] pub struct Configs { @@ -17,59 +16,37 @@ pub struct Configs { } /// what the fuck serde why do i need this -pub(crate) fn default_example_value() -> i32 { +fn default_example_value() -> i32 { 43 } -pub(crate) fn default_no_startup_msg() -> bool { +fn default_no_startup_msg() -> bool { false } +pub(super) fn find_config_file() -> Result { + let Some(config_path) = dirs::config_dir() else { + return Err(ConfigError::NoConfigDir); + }; + + let ron_path = config_path.with_file_name("config.ron"); + let json_path = config_path.with_file_name("config.json"); + + if Path::new(&ron_path).exists() { + Ok(ron_path) + } else if Path::new(&json_path).exists() { + Ok(json_path) + } else { + Err(ConfigError::NoConfigFileFound) + } +} + impl Configs { - pub fn read(custom_path: Option) -> Self { - use owo_colors::OwoColorize; - let p = match custom_path { - Some(p) => p, - None => { - let config_path = dirs::config_dir().expect("config dir should exist"); - - let ron_path = config_path.with_file_name("config.ron"); - let json_path = config_path.with_file_name("config.json"); - - if Path::new(&ron_path).exists() { - ron_path - } else if Path::new(&json_path).exists() { - json_path - } else { - eprintln!("{}: couldn't find config file", "Fatal error".red()); - process::exit(1) - } - } - }; - + pub fn read(p: PathBuf) -> Result { match p.extension().map(|v| v.to_str().unwrap()) { - Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), - Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), - None | Some(_) => { - eprintln!( - "{}: couldn't determine config file type", - "Fatal error".red() - ); - process::exit(1) - } - } - } - pub fn read_json(config_text: &str) -> Configs { - match serde_json::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_json_err(config_text, e), - } - } - - pub fn read_ron(config_text: &str) -> Configs { - match ron::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_ron_err(config_text, e), + Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?), + Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?), + e => Err(ConfigError::UnknownExtension(e.map(|v| v.to_owned()))), } } } diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index deab5b4..dd7f3e4 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -2,11 +2,14 @@ use config::Config; use welcome_msg::print_startup_msg; mod config; + +#[allow(unused)] mod error_reporting; mod welcome_msg; fn main() { - let cfg = Config::read(); + // TODO: proper error handling + let cfg = Config::read().unwrap(); if cfg.startup_msg { print_startup_msg(); -- 2.46.0 From 6006f92d9c9a16a729067820bbb315aba0a45060 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 19 Jan 2024 08:54:36 +0100 Subject: [PATCH 09/12] app: better? config error handling --- crates/app/src/config.rs | 39 +++++++++++++++++++++++++++++---------- crates/app/src/main.rs | 2 +- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index a0196fe..f68fa34 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -3,7 +3,6 @@ use clap::Parser; use self::{ cli::Args, config_file::{find_config_file, Configs}, - error::ConfigError, }; mod cli; @@ -15,20 +14,40 @@ pub struct Config { } impl Config { - pub fn read() -> Result { + pub fn read() -> Self { let args = Args::parse(); let config_path = if let Some(config_path) = args.config_path { - config_path + Ok(config_path) } else { - find_config_file()? + find_config_file() }; - let file_config = Configs::read(config_path)?; - Ok(Self { - // this is negated because to an outward api, the negative is more intuitive, - // while in the source the other way around is more intuitive - startup_msg: !(args.no_startup_message || file_config.no_startup_message), - }) + let file_config = if let Ok(config_path) = config_path { + let file_config = Configs::read(config_path); + + match file_config { + Ok(c) => Some(c), + Err(e) => { + eprintln!("Config error: {e:?}"); + eprintln!("Proceeding with defaults or cli args..."); + None + } + } + } else { + None + }; + + if let Some(file_config) = file_config { + Self { + // this is negated because to an outward api, the negative is more intuitive, + // while in the source the other way around is more intuitive + startup_msg: !(args.no_startup_message || file_config.no_startup_message), + } + } else { + Self { + startup_msg: !args.no_startup_message, + } + } } } diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index dd7f3e4..1347572 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -9,7 +9,7 @@ mod welcome_msg; fn main() { // TODO: proper error handling - let cfg = Config::read().unwrap(); + let cfg = Config::read(); if cfg.startup_msg { print_startup_msg(); -- 2.46.0 From 0615ea653c27a9e15ed4cb0d40fd00ae3f36048a Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 19 Jan 2024 09:51:48 +0100 Subject: [PATCH 10/12] app(doc): well... write the docs --- crates/app/src/config.rs | 11 +++++++++++ crates/app/src/config/cli.rs | 3 +++ crates/app/src/config/config_file.rs | 7 ++----- crates/app/src/error_reporting.rs | 7 ++++++- crates/app/src/welcome_msg.rs | 3 +++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index f68fa34..9f2b15a 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -14,6 +14,7 @@ pub struct Config { } impl Config { + /// Get the configs from all possible places (args, file, env...) pub fn read() -> Self { let args = Args::parse(); let config_path = if let Some(config_path) = args.config_path { @@ -22,6 +23,7 @@ impl Config { find_config_file() }; + // try to read a maybe existing config file let file_config = if let Ok(config_path) = config_path { let file_config = Configs::read(config_path); @@ -52,13 +54,22 @@ impl Config { } pub mod error { + /// Errors that can occur when reading configs #[derive(Debug)] pub enum ConfigError { + /// The config dir doesn't exist NoConfigDir, + /// We didn't find a config file in the config dir NoConfigFileFound, + /// An io error happened while reading/opening it! IoError(std::io::Error), + /// The given extension (via an argument) isn't known. + /// + /// Occurs if the extension is neither `.json` nor `.ron` (including if there is no extension, in which case this will be `None`). UnknownExtension(Option), + /// Wrapper around an `Error` from `serde_json` SerdeJsonError(serde_json::Error), + /// Wrapper around a `SpannedError` from `ron` SerdeRonError(ron::error::SpannedError), } diff --git a/crates/app/src/config/cli.rs b/crates/app/src/config/cli.rs index 23f8ef1..92026a2 100644 --- a/crates/app/src/config/cli.rs +++ b/crates/app/src/config/cli.rs @@ -7,6 +7,9 @@ pub(crate) struct Args { /// Read this config file. #[arg(short, long)] pub config_path: Option, + /// Turn off the startup message. + /// + /// The startup message is not constant and depends on the time. #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] pub no_startup_message: bool, } diff --git a/crates/app/src/config/config_file.rs b/crates/app/src/config/config_file.rs index d454223..a0c4db1 100644 --- a/crates/app/src/config/config_file.rs +++ b/crates/app/src/config/config_file.rs @@ -11,7 +11,7 @@ use super::error::ConfigError; pub struct Configs { #[serde(default = "default_example_value")] pub example_value: i32, - #[serde(default = "default_no_startup_msg")] + #[serde(default)] pub no_startup_message: bool, } @@ -20,10 +20,7 @@ fn default_example_value() -> i32 { 43 } -fn default_no_startup_msg() -> bool { - false -} - +/// Find the location of a config file and check if there is, in fact, a file pub(super) fn find_config_file() -> Result { let Some(config_path) = dirs::config_dir() else { return Err(ConfigError::NoConfigDir); diff --git a/crates/app/src/error_reporting.rs b/crates/app/src/error_reporting.rs index f71e10d..33ffff6 100644 --- a/crates/app/src/error_reporting.rs +++ b/crates/app/src/error_reporting.rs @@ -2,16 +2,19 @@ use std::process; use ron::error::Position; +/// Report an `Error` from the `serde_json` crate pub fn report_serde_json_err(src: &str, err: serde_json::Error) -> ! { report_serde_err(src, err.line(), err.column(), err.to_string()) } +/// Report a `SpannedError` from the `ron` crate pub fn report_serde_ron_err(src: &str, err: ron::error::SpannedError) -> ! { let Position { line, col } = err.position; report_serde_err(src, line, col, err.to_string()) } -pub fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { +/// Basic function for reporting serde type of errors +fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { use ariadne::{Label, Report, Source}; let offset = try_reconstruct_loc(src, line, col); @@ -24,6 +27,8 @@ pub fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { .unwrap(); process::exit(1); } + +/// Reconstruct a byte offset from the line + column numbers typical from serde crates fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize { let (line_nr, col_nr) = (line_nr - 1, col_nr - 1); diff --git a/crates/app/src/welcome_msg.rs b/crates/app/src/welcome_msg.rs index c03c543..a3e15a3 100644 --- a/crates/app/src/welcome_msg.rs +++ b/crates/app/src/welcome_msg.rs @@ -1,11 +1,14 @@ use time::{Month, OffsetDateTime}; +/// Print the startup message which changes depending on system time. pub fn print_startup_msg() { // now or fallback to utc let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); if now.month() == Month::June { + // pride startup message println!("Hello, thanks for using iOwO and happy pride month!"); + // TODO: proper link with explaination println!("Why pride month is important in {}", now.year()); } else { println!("Hello, thanks for using iOwO! :3"); -- 2.46.0 From 746c81ab60afb168060cd8247734d19f91ce20ca Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 19 Jan 2024 18:50:51 +0100 Subject: [PATCH 11/12] app(cli): apply review --- crates/app/src/config/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/app/src/config/cli.rs b/crates/app/src/config/cli.rs index 92026a2..bdf769e 100644 --- a/crates/app/src/config/cli.rs +++ b/crates/app/src/config/cli.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use clap::Parser; +use clap::{builder::BoolishValueParser, ArgAction, Parser}; #[derive(Parser)] pub(crate) struct Args { @@ -10,6 +10,6 @@ pub(crate) struct Args { /// Turn off the startup message. /// /// The startup message is not constant and depends on the time. - #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] + #[arg(long, env = "NO_STARTUP_MESSAGE", action = ArgAction::SetTrue, value_parser = BoolishValueParser::new())] pub no_startup_message: bool, } -- 2.46.0 From 48458fd1c966573023c4b1e966c2a916d10d157f Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 20 Jan 2024 20:07:55 +0100 Subject: [PATCH 12/12] remove old cli --- Cargo.toml | 1 - crates/pl-cli/Cargo.toml | 11 ----------- crates/pl-cli/src/main.rs | 19 ------------------- 3 files changed, 31 deletions(-) delete mode 100644 crates/pl-cli/Cargo.toml delete mode 100644 crates/pl-cli/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index bc41d16..1fc1618 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "crates/app", "crates/executor", - "crates/pl-cli", "crates/rpl" ] resolver = "2" diff --git a/crates/pl-cli/Cargo.toml b/crates/pl-cli/Cargo.toml deleted file mode 100644 index e6f8cc8..0000000 --- a/crates/pl-cli/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "pl-cli" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap = { workspace = true, features = [ "derive" ] } -rpl = { path = "../rpl" } -executor = { path = "../executor" } diff --git a/crates/pl-cli/src/main.rs b/crates/pl-cli/src/main.rs deleted file mode 100644 index 9e84d33..0000000 --- a/crates/pl-cli/src/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::{fs, path::PathBuf}; - -use clap::Parser; -use executor::{execute_all, Executors}; - -#[derive(Parser, Debug)] -pub struct Args { - file: PathBuf, - primary_executor: Executors, -} - -fn main() { - let args = dbg!(Args::parse()); - - let f = fs::read_to_string(args.file).unwrap(); - let pl = rpl::from_ron(&f); - - execute_all(pl.0); -} -- 2.46.0