Merge pull request #12 from teohhanhui/riir

Parse config
This commit is contained in:
Teoh Han Hui 2024-07-01 04:08:10 +08:00 committed by GitHub
commit 3c355f54f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 491 additions and 54 deletions

243
Cargo.lock generated
View file

@ -38,6 +38,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]] [[package]]
name = "bpaf" name = "bpaf"
version = "0.9.12" version = "0.9.12"
@ -75,7 +81,7 @@ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
"num-traits", "num-traits",
"windows-targets", "windows-targets 0.52.5",
] ]
[[package]] [[package]]
@ -84,12 +90,53 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"serde",
]
[[package]]
name = "directories"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
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",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
@ -109,9 +156,14 @@ dependencies = [
"anyhow", "anyhow",
"bpaf", "bpaf",
"chrono", "chrono",
"deranged",
"directories",
"indexmap", "indexmap",
"regex", "regex",
"rgb", "rgb",
"serde",
"serde_json",
"serde_path_to_error",
"shell-words", "shell-words",
"strum", "strum",
"tracing", "tracing",
@ -150,6 +202,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -158,6 +211,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.69" version = "0.3.69"
@ -179,6 +238,16 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.21"
@ -216,6 +285,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -252,6 +327,17 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.5" version = "1.10.5"
@ -293,6 +379,53 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
dependencies = [
"itoa",
"serde",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.7" version = "0.1.7"
@ -356,6 +489,26 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.8" version = "1.1.8"
@ -459,6 +612,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.92" version = "0.2.92"
@ -541,7 +700,31 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.5",
]
[[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-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]] [[package]]
@ -550,28 +733,46 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.52.5",
"windows_i686_gnu", "windows_i686_gnu 0.52.5",
"windows_i686_gnullvm", "windows_i686_gnullvm",
"windows_i686_msvc", "windows_i686_msvc 0.52.5",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc", "windows_x86_64_msvc 0.52.5",
] ]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.5" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.5" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.5" version = "0.52.5"
@ -584,24 +785,48 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.5" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.5" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.5" version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.5" version = "0.52.5"

View file

@ -16,10 +16,15 @@ ansi_colours = { version = "1.2.2", default-features = false }
anyhow = { version = "1.0.86", default-features = false } anyhow = { version = "1.0.86", default-features = false }
bpaf = { version = "0.9.12", default-features = false } bpaf = { version = "0.9.12", default-features = false }
chrono = { version = "0.4.38", default-features = false } chrono = { version = "0.4.38", default-features = false }
deranged = { version = "0.3.11", default-features = false }
derive_more = { version = "1.0.0-beta.6", default-features = false } derive_more = { version = "1.0.0-beta.6", default-features = false }
directories = { version = "5.0.1", default-features = false }
indexmap = { version = "2.2.6", default-features = false } indexmap = { version = "2.2.6", default-features = false }
regex = { version = "1.10.5", default-features = false } regex = { version = "1.10.5", default-features = false }
rgb = { version = "0.8.37", default-features = false } rgb = { version = "0.8.37", default-features = false }
serde = { version = "1.0.203", default-features = false }
serde_json = { version = "1.0.118", default-features = false }
serde_path_to_error = { version = "0.1.16", default-features = false }
shell-words = { version = "1.1.0", default-features = false } shell-words = { version = "1.1.0", default-features = false }
strum = { version = "0.26.3", default-features = false } strum = { version = "0.26.3", default-features = false }
tracing = { version = "0.1.40", default-features = false } tracing = { version = "0.1.40", default-features = false }

View file

@ -14,10 +14,15 @@ default-run = "hyfetch"
anyhow = { workspace = true, features = ["std"] } anyhow = { workspace = true, features = ["std"] }
bpaf = { workspace = true, features = [] } bpaf = { workspace = true, features = [] }
chrono = { workspace = true, features = ["clock", "std"] } chrono = { workspace = true, features = ["clock", "std"] }
deranged = { workspace = true, features = ["serde", "std"] }
# derive_more = { workspace = true, features = ["std"] } # derive_more = { workspace = true, features = ["std"] }
indexmap = { workspace = true, features = ["std"] } directories = { workspace = true, features = [] }
indexmap = { workspace = true, features = ["serde", "std"] }
regex = { workspace = true, features = ["perf", "std", "unicode"] } regex = { workspace = true, features = ["perf", "std", "unicode"] }
rgb = { workspace = true, features = [] } rgb = { workspace = true, features = [] }
serde = { workspace = true, features = ["derive", "std"] }
serde_json = { workspace = true, features = ["std"] }
serde_path_to_error = { workspace = true, features = [] }
shell-words = { workspace = true, features = ["std"] } shell-words = { workspace = true, features = ["std"] }
strum = { workspace = true, features = ["derive", "std"] } strum = { workspace = true, features = ["derive", "std"] }
tracing = { workspace = true, features = ["attributes", "std"] } tracing = { workspace = true, features = ["attributes", "std"] }

View file

@ -1,13 +1,18 @@
use std::io::{self, IsTerminal}; use std::fmt;
use std::fs::{self, File};
use std::io::{self, ErrorKind, IsTerminal, Read};
use std::path::Path;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use chrono::Datelike; use chrono::Datelike;
use directories::ProjectDirs;
use hyfetch::cli_options::options; use hyfetch::cli_options::options;
use hyfetch::models::Config;
use hyfetch::neofetch_util::get_distro_ascii; use hyfetch::neofetch_util::get_distro_ascii;
use tracing::debug; use tracing::debug;
fn main() -> Result<()> { fn main() -> Result<()> {
let options = options().fallback_to_usage().run(); let options = options().run();
init_tracing_subsriber(options.debug).context("Failed to init tracing subscriber")?; init_tracing_subsriber(options.debug).context("Failed to init tracing subscriber")?;
@ -25,25 +30,26 @@ fn main() -> Result<()> {
// TODO // TODO
// TODO let config = if options.config {
// let config = if options.config { create_config(options.config_file).context("Failed to create config")?
// create_config() } else if let Some(config) =
// } else { read_config(&options.config_file).context("Failed to read config")?
// check_config(options.config_file) {
// }; config
} else {
create_config(options.config_file).context("Failed to create config")?
};
let now = chrono::Local::now(); let now = chrono::Local::now();
let show_pride_month = options.june let cache_path = ProjectDirs::from("", "", "hyfetch")
|| now.month() == 6 .context("Failed to get base dirs")?
// TODO .cache_dir()
// && !config.pride_month_shown.contains(now.year()) .to_owned();
// && !june_path.is_file() let june_path = cache_path.join(format!("animation-displayed-{}", now.year()));
&& io::stdout().is_terminal(); let show_pride_month =
options.june || now.month() == 6 && !june_path.is_file() && io::stdout().is_terminal();
if show_pride_month if show_pride_month && !config.pride_month_disable {
// TODO
// && !config.pride_month_disable
{
// TODO // TODO
// pride_month.start_animation(); // pride_month.start_animation();
println!(); println!();
@ -51,7 +57,11 @@ fn main() -> Result<()> {
println!("(You can always view the animation again with `hyfetch --june`)"); println!("(You can always view the animation again with `hyfetch --june`)");
println!(); println!();
// TODO if !june_path.is_file() {
fs::create_dir_all(cache_path).context("Failed to create cache dir")?;
File::create(&june_path)
.with_context(|| format!("Failed to create file {june_path:?}"))?;
}
} }
// TODO // TODO
@ -59,6 +69,51 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
/// Reads config from file.
///
/// Returns `None` if the config file does not exist.
#[tracing::instrument(level = "debug")]
fn read_config<P>(path: P) -> Result<Option<Config>>
where
P: AsRef<Path> + fmt::Debug,
{
let path = path.as_ref();
let mut file = match File::open(path) {
Ok(file) => file,
Err(err) if err.kind() == ErrorKind::NotFound => {
return Ok(None);
},
Err(err) => {
return Err(err).with_context(|| format!("Failed to open {path:?}"));
},
};
let mut buf = String::new();
file.read_to_string(&mut buf)
.with_context(|| format!("Failed to read {path:?}"))?;
let deserializer = &mut serde_json::Deserializer::from_str(&buf);
let config: Config = serde_path_to_error::deserialize(deserializer)
.with_context(|| format!("Failed to parse {path:?}"))?;
debug!(?config, "read config");
Ok(Some(config))
}
/// Creates config interactively.
///
/// The config is automatically stored to file.
#[tracing::instrument(level = "debug")]
fn create_config<P>(path: P) -> Result<Config>
where
P: AsRef<Path> + fmt::Debug,
{
todo!()
}
fn init_tracing_subsriber(debug: bool) -> Result<()> { fn init_tracing_subsriber(debug: bool) -> Result<()> {
use std::env; use std::env;
use std::str::FromStr; use std::str::FromStr;

View file

@ -5,6 +5,7 @@ use anyhow::Context;
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
use bpaf::ShellComp; use bpaf::ShellComp;
use bpaf::{construct, long, OptionParser, Parser}; use bpaf::{construct, long, OptionParser, Parser};
use directories::BaseDirs;
use strum::VariantNames; use strum::VariantNames;
use crate::presets::Preset; use crate::presets::Preset;
@ -13,11 +14,11 @@ use crate::types::{AnsiMode, Backend};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Options { pub struct Options {
pub config: bool, pub config: bool,
pub config_file: Option<PathBuf>, pub config_file: PathBuf,
pub preset: Option<Preset>, pub preset: Option<Preset>,
pub mode: Option<AnsiMode>, pub mode: Option<AnsiMode>,
pub backend: Option<Backend>, pub backend: Option<Backend>,
pub backend_args: Vec<String>, pub args: Vec<String>,
pub colors_scale: Option<f32>, pub colors_scale: Option<f32>,
pub colors_set_lightness: Option<f32>, pub colors_set_lightness: Option<f32>,
pub colors_use_overlay: bool, pub colors_use_overlay: bool,
@ -37,7 +38,16 @@ pub fn options() -> OptionParser<Options> {
.argument("CONFIG_FILE"); .argument("CONFIG_FILE");
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
let config_file = config_file.complete_shell(ShellComp::Nothing); let config_file = config_file.complete_shell(ShellComp::Nothing);
let config_file = config_file.optional(); let config_file = config_file
.fallback_with(|| {
Ok::<_, anyhow::Error>(
BaseDirs::new()
.context("Failed to get base dirs")?
.config_dir()
.join("hyfetch.json"),
)
})
.debug_fallback();
let preset = long("preset") let preset = long("preset")
.short('p') .short('p')
.help(&*format!( .help(&*format!(
@ -49,7 +59,11 @@ PRESET={{{}}}",
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
let preset = preset.complete(complete_preset); let preset = preset.complete(complete_preset);
let preset = preset let preset = preset
.parse(|s| Preset::from_str(&s).with_context(|| format!("Failed to parse preset `{s}`"))) .parse(|s| {
Preset::from_str(&s).with_context(|| {
format!("PRESET should be one of {{{}}}", Preset::VARIANTS.join(","))
})
})
.optional(); .optional();
let mode = long("mode") let mode = long("mode")
.short('m') .short('m')
@ -62,7 +76,11 @@ MODE={{{}}}",
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
let mode = mode.complete(complete_mode); let mode = mode.complete(complete_mode);
let mode = mode let mode = mode
.parse(|s| AnsiMode::from_str(&s).with_context(|| format!("Failed to parse mode `{s}`"))) .parse(|s| {
AnsiMode::from_str(&s).with_context(|| {
format!("MODE should be one of {{{}}}", AnsiMode::VARIANTS.join(","))
})
})
.optional(); .optional();
let backend = long("backend") let backend = long("backend")
.short('b') .short('b')
@ -75,12 +93,19 @@ BACKEND={{{}}}",
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
let backend = backend.complete(complete_backend); let backend = backend.complete(complete_backend);
let backend = backend let backend = backend
.parse(|s| Backend::from_str(&s).with_context(|| format!("Failed to parse backend `{s}`"))) .parse(|s| {
Backend::from_str(&s).with_context(|| {
format!(
"BACKEND should be one of {{{}}}",
Backend::VARIANTS.join(",")
)
})
})
.optional(); .optional();
let backend_args = long("args") let args = long("args")
.help("Additional arguments pass-through to backend") .help("Additional arguments pass-through to backend")
.argument::<String>("ARGS") .argument::<String>("ARGS")
.parse(|s| shell_words::split(&s).context("Failed to split args for shell")) .parse(|s| shell_words::split(&s).context("ARGS should be valid command-line arguments"))
.fallback(vec![]); .fallback(vec![]);
let colors_scale = long("c-scale") let colors_scale = long("c-scale")
.help("Lighten colors by a multiplier") .help("Lighten colors by a multiplier")
@ -125,7 +150,7 @@ BACKEND={{{}}}",
preset, preset,
mode, mode,
backend, backend,
backend_args, args,
colors_scale, colors_scale,
colors_set_lightness, colors_set_lightness,
colors_use_overlay, colors_use_overlay,

View file

@ -1,5 +1,22 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use deranged::RangedU8;
use rgb::RGB8; use rgb::RGB8;
use serde::{Deserialize, Serialize};
/// An indexed color where the color palette is the set of colors used in
/// neofetch ascii art.
///
/// The range of valid values as supported in neofetch is `1`-`6`.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
pub struct NeofetchAsciiIndexedColor(RangedU8<1, 6>);
/// An indexed color where the color palette is the set of unique colors in a
/// preset.
///
/// The range of valid values depends on the number of unique colors in a
/// certain preset.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
pub struct PresetIndexedColor(usize);
pub trait FromHex { pub trait FromHex {
/// Creates color from hex code. /// Creates color from hex code.

View file

@ -1,6 +1,7 @@
pub mod cli_options; pub mod cli_options;
pub mod color_util; pub mod color_util;
pub mod distros; pub mod distros;
pub mod models;
pub mod neofetch_util; pub mod neofetch_util;
pub mod presets; pub mod presets;
pub mod types; pub mod types;

View file

@ -0,0 +1,71 @@
use serde::{Deserialize, Serialize};
use crate::neofetch_util::ColorAlignment;
use crate::presets::Preset;
use crate::types::{AnsiMode, Backend, LightDark};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config {
pub preset: Preset,
pub mode: AnsiMode,
pub light_dark: LightDark,
pub lightness: Option<f32>,
pub color_align: ColorAlignment,
pub backend: Backend,
#[serde(with = "self::args_serde_with")]
pub args: Vec<String>,
pub distro: Option<String>,
pub pride_month_disable: bool,
}
mod args_serde_with {
use std::fmt;
use serde::de::{self, value, Deserialize, Deserializer, SeqAccess, Visitor};
use serde::ser::Serializer;
pub(super) fn serialize<S>(value: &Vec<String>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&shell_words::join(value))
}
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrVec;
impl<'de> Visitor<'de> for StringOrVec {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or list of strings")
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![])
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
shell_words::split(s).map_err(de::Error::custom)
}
fn visit_seq<S>(self, seq: S) -> Result<Self::Value, S::Error>
where
S: SeqAccess<'de>,
{
Deserialize::deserialize(value::SeqAccessDeserializer::new(seq))
}
}
deserializer.deserialize_any(StringOrVec)
}
}

View file

@ -4,14 +4,35 @@ use std::ffi::OsStr;
use std::os::unix::process::ExitStatusExt as _; use std::os::unix::process::ExitStatusExt as _;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::sync::OnceLock;
use std::{env, fmt}; use std::{env, fmt};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use indexmap::IndexMap;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize};
use tracing::debug; use tracing::debug;
use crate::color_util::{NeofetchAsciiIndexedColor, PresetIndexedColor};
use crate::distros::Distro; use crate::distros::Distro;
static NEOFETCH_COLOR_RE: OnceLock<Regex> = OnceLock::new();
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase", tag = "mode")]
pub enum ColorAlignment {
Horizontal {
fore_back: Option<(NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor)>,
},
Vertical {
fore_back: Option<(NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor)>,
},
Custom {
#[serde(rename = "custom_colors")]
colors: IndexMap<NeofetchAsciiIndexedColor, PresetIndexedColor>,
},
}
/// Gets the absolute path of the neofetch command. /// Gets the absolute path of the neofetch command.
pub fn get_command_path() -> Result<PathBuf> { pub fn get_command_path() -> Result<PathBuf> {
if let Ok(workspace_dir) = env::var("CARGO_WORKSPACE_DIR") { if let Ok(workspace_dir) = env::var("CARGO_WORKSPACE_DIR") {
@ -72,13 +93,16 @@ where
} }
/// Gets distro ascii width and height, ignoring color code. /// Gets distro ascii width and height, ignoring color code.
pub fn ascii_size<S>(asc: S, neofetch_color_re: &Regex) -> (u8, u8) pub fn ascii_size<S>(asc: S) -> (u8, u8)
where where
S: AsRef<str>, S: AsRef<str>,
{ {
let asc = asc.as_ref(); let asc = asc.as_ref();
let Some(width) = neofetch_color_re let Some(width) = NEOFETCH_COLOR_RE
.get_or_init(|| {
Regex::new(r"\$\{c[0-9]\}").expect("neofetch color regex should not be invalid")
})
.replace_all(asc, "") .replace_all(asc, "")
.split('\n') .split('\n')
.map(|line| line.len()) .map(|line| line.len())
@ -98,14 +122,11 @@ where
{ {
let asc = asc.as_ref(); let asc = asc.as_ref();
let neofetch_color_re = let (w, _) = ascii_size(asc);
Regex::new(r"\$\{c[0-9]\}").expect("neofetch color regex should not be invalid");
let (w, _) = ascii_size(asc, &neofetch_color_re); let mut buf = String::new();
let mut buf = "".to_owned();
for line in asc.split('\n') { for line in asc.split('\n') {
let (line_w, _) = ascii_size(line, &neofetch_color_re); let (line_w, _) = ascii_size(line);
let pad = " ".repeat((w - line_w) as usize); let pad = " ".repeat((w - line_w) as usize);
buf.push_str(&format!("{line}{pad}\n")) buf.push_str(&format!("{line}{pad}\n"))
} }

View file

@ -3,11 +3,13 @@ use std::iter;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use indexmap::IndexSet; use indexmap::IndexSet;
use rgb::RGB8; use rgb::RGB8;
use serde::{Deserialize, Serialize};
use strum::{EnumString, VariantNames}; use strum::{EnumString, VariantNames};
use crate::color_util::FromHex; use crate::color_util::FromHex;
#[derive(Clone, Hash, Debug, EnumString, VariantNames)] #[derive(Clone, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]
pub enum Preset { pub enum Preset {
Abrosexual, Abrosexual,
@ -44,13 +46,16 @@ pub enum Preset {
Genderfaun, Genderfaun,
Genderfluid, Genderfluid,
Genderflux, Genderflux,
#[serde(rename = "gendernonconforming1")]
#[strum(serialize = "gendernonconforming1")] #[strum(serialize = "gendernonconforming1")]
GenderNonconforming1, GenderNonconforming1,
#[serde(rename = "gendernonconforming2")]
#[strum(serialize = "gendernonconforming2")] #[strum(serialize = "gendernonconforming2")]
GenderNonconforming2, GenderNonconforming2,
Gendervoid, Gendervoid,
Girlflux, Girlflux,
Greygender, Greygender,
#[serde(alias = "biromantic2")]
Greysexual, Greysexual,
Gynesexual, Gynesexual,
Intergender, Intergender,
@ -79,7 +84,7 @@ pub enum Preset {
#[derive(Clone, Eq, PartialEq, Hash, Debug)] #[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct ColorProfile { pub struct ColorProfile {
colors: Vec<RGB8>, pub colors: Vec<RGB8>,
} }
impl Preset { impl Preset {
@ -409,10 +414,6 @@ impl ColorProfile {
Ok(Self::new(weighted_colors)) Ok(Self::new(weighted_colors))
} }
pub fn colors(&self) -> &[RGB8] {
&self.colors
}
/// Creates another color profile with only the unique colors. /// Creates another color profile with only the unique colors.
pub fn unique_colors(&self) -> Self { pub fn unique_colors(&self) -> Self {
let unique_colors = self.colors.iter().collect::<IndexSet<_>>(); let unique_colors = self.colors.iter().collect::<IndexSet<_>>();

View file

@ -1,14 +1,25 @@
use serde::{Deserialize, Serialize};
use strum::{EnumString, VariantNames}; use strum::{EnumString, VariantNames};
#[derive(Clone, Eq, PartialEq, Hash, Debug, EnumString, VariantNames)] #[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")] #[strum(serialize_all = "lowercase")]
pub enum AnsiMode { pub enum AnsiMode {
#[serde(rename = "8bit")]
#[strum(serialize = "8bit")] #[strum(serialize = "8bit")]
Ansi256, Ansi256,
Rgb, Rgb,
} }
#[derive(Clone, Eq, PartialEq, Hash, Debug, EnumString, VariantNames)] #[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum LightDark {
Light,
Dark,
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]
pub enum Backend { pub enum Backend {
Qwqfetch, Qwqfetch,