From a92dcfc8fdad2d3593d99eb90d6740036a5489c6 Mon Sep 17 00:00:00 2001 From: pierre Date: Sun, 18 Aug 2024 21:11:57 +0200 Subject: [PATCH] =?UTF-8?q?refactoring=20=E2=9A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 413 ++++++++++++++++++++++++++++++- Cargo.toml | 15 +- baru.yaml | 17 +- src/cli.rs | 17 ++ src/lib.rs | 57 ++--- src/main.rs | 89 +++---- src/module.rs | 22 +- src/{ => modules}/battery.rs | 3 + src/{ => modules}/brightness.rs | 6 +- src/{ => modules}/cpu.rs | 3 + src/{ => modules}/cpu_freq.rs | 6 +- src/{ => modules}/date_time.rs | 3 + src/{ => modules}/memory.rs | 6 +- src/{ => modules}/mic.rs | 3 + src/modules/mod.rs | 11 + src/{ => modules}/sound.rs | 3 + src/{ => modules}/temperature.rs | 169 ++++++++----- src/{ => modules}/wired.rs | 3 + src/{ => modules}/wireless.rs | 3 + src/pulse.rs | 3 + src/trace.rs | 82 ++++++ src/util.rs | 31 +++ 22 files changed, 793 insertions(+), 172 deletions(-) create mode 100644 src/cli.rs rename src/{ => modules}/battery.rs (99%) rename src/{ => modules}/brightness.rs (95%) rename src/{ => modules}/cpu.rs (98%) rename src/{ => modules}/cpu_freq.rs (97%) rename src/{ => modules}/date_time.rs (97%) rename src/{ => modules}/memory.rs (97%) rename src/{ => modules}/mic.rs (97%) create mode 100644 src/modules/mod.rs rename src/{ => modules}/sound.rs (97%) rename src/{ => modules}/temperature.rs (54%) rename src/{ => modules}/wired.rs (97%) rename src/{ => modules}/wireless.rs (98%) create mode 100644 src/trace.rs create mode 100644 src/util.rs diff --git a/Cargo.lock b/Cargo.lock index cfc5de0..b13ac96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,61 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "autocfg" version = "1.3.0" @@ -34,13 +89,19 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "baru" -version = "0.3.2" +version = "0.4.0" dependencies = [ + "anyhow", "chrono", + "clap", "cmake", "regex", "serde", "serde_yaml", + "thiserror", + "tracing", + "tracing-appender", + "tracing-subscriber", ] [[package]] @@ -75,6 +136,46 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clap" +version = "4.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cmake" version = "0.1.50" @@ -84,12 +185,42 @@ dependencies = [ "cc", ] +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -102,6 +233,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -135,6 +272,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.11" @@ -150,6 +293,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" @@ -162,12 +311,37 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -183,6 +357,24 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.86" @@ -209,8 +401,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -221,9 +422,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -269,6 +476,27 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.74" @@ -280,6 +508,140 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -292,6 +654,18 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -346,6 +720,28 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" @@ -355,6 +751,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 3d5d958..1731cb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,25 @@ [package] name = "baru" -version = "0.3.2" +version = "0.4.0" authors = ["pierre "] edition = "2021" links = "netlink,audio" build = "build.rs" [dependencies] -chrono = "0.4" -regex = "1" +anyhow = "1.0.86" +thiserror = "1.0" +clap = { version = "4.5", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" +tracing = "0.1" +tracing-subscriber = { version = "0.3.1", features = [ + "tracing-log", + "env-filter", +] } +tracing-appender = "0.2" +chrono = "0.4" +regex = "1" [build-dependencies] cmake = "0.1" diff --git a/baru.yaml b/baru.yaml index 8b011dd..33b0392 100644 --- a/baru.yaml +++ b/baru.yaml @@ -479,22 +479,21 @@ temperature: # core_inputs: u32 | List of u32 | String, default: 1 # # The average temperature is calculated from one or several input files. - # Input files are files named `tempx_input` where x is a number. + # Input files are located in the directory /sys/devices/platform/coretemp.0/hwmon/* + # and are named `tempn_input` where n is a number. # e.g. temp1_input temp2_input - # You can list them using `ls -l /sys/devices/platform/coretemp.0/hwmon/* ` # Input files can contain the temperature of a cpu core. # Based on the cpu and its number of cores you want to provide # the corresponding input file number(s). # Tips: see the label files, e.g. `temp1_label`, to identify # which input files reports cpu core temperature. # - # Can be a number to select one input file, eg. 1 for temp1_input. - # Can be a list of number to select multiple input files, eg. - # core_inputs: [ 2, 6 ] - # to select temp2_input and temp6_input - # Can be a inclusive range to select multiple input files, eg. - # core_inputs: 2..3 - # to select temp1_input temp2_input and temp3_input + # This option can be set either to: + # A number to select one input file, eg. 1 for temp1_input. + # A list of number to select multiple input files, eg. + # core_inputs: [ 2, 6 ] to select temp2_input and temp6_input + # A inclusive range to select multiple input files, eg. + # core_inputs: 2..3 to select temp1_input temp2_input and temp3_input # core_inputs: 1 diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..257b065 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,17 @@ +use clap::{Parser, ValueEnum}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, ValueEnum)] +pub enum Logs { + Off, + Stdout, + File, +} + +#[derive(Parser, Serialize, Deserialize, Debug, Clone)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + /// Enable app logs + #[arg(short, long)] + pub logs: Option, +} diff --git a/src/lib.rs b/src/lib.rs index db3583f..10828d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,40 +2,33 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -mod battery; -mod brightness; -mod cpu; -mod cpu_freq; -mod date_time; +pub mod cli; mod error; -mod memory; -mod mic; mod module; +mod modules; mod netlink; pub mod pulse; -mod sound; -mod temperature; -mod wired; -mod wireless; -use battery::Config as BatteryConfig; -use brightness::Config as BrightnessConfig; -use cpu::Config as CpuConfig; -use cpu_freq::Config as CpuFreqConfig; -use date_time::Config as DateTimeConfig; +pub mod trace; +pub mod util; + use error::Error; -use memory::Config as MemoryConfig; -use mic::Config as MicConfig; use module::{Bar, ModuleData}; +use modules::battery::Config as BatteryConfig; +use modules::brightness::Config as BrightnessConfig; +use modules::cpu::Config as CpuConfig; +use modules::cpu_freq::Config as CpuFreqConfig; +use modules::date_time::Config as DateTimeConfig; +use modules::memory::Config as MemoryConfig; +use modules::mic::Config as MicConfig; +use modules::sound::Config as SoundConfig; +use modules::temperature::Config as TemperatureConfig; +use modules::wired::Config as WiredConfig; +use modules::wireless::Config as WirelessConfig; use pulse::Pulse; use serde::{Deserialize, Serialize}; -use sound::Config as SoundConfig; -use std::fs; use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::thread; -use temperature::Config as TemperatureConfig; -use wired::Config as WiredConfig; -use wireless::Config as WirelessConfig; #[derive(Debug)] pub struct ModuleMsg(char, Option, Option); @@ -129,6 +122,10 @@ impl<'a> Baru<'a> { println!("{}", output); Ok(()) } + + pub fn modules(&self) -> Vec<&str> { + self.modules.iter().map(|m| m.module.name()).collect() + } } fn parse_format(format: &str) -> Vec { @@ -144,20 +141,6 @@ fn parse_format(format: &str) -> Vec { matches } -fn read_and_trim(file: &str) -> Result { - let content = fs::read_to_string(file) - .map_err(|err| format!("error while reading the file \"{}\": {}", file, err))?; - Ok(content.trim().to_string()) -} - -fn read_and_parse(file: &str) -> Result { - let content = read_and_trim(file)?; - let data = content - .parse::() - .map_err(|err| format!("error while parsing the file \"{}\": {}", file, err))?; - Ok(data) -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index 46b4d00..3e8690a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,68 +2,69 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use anyhow::{Context, Result}; +use baru::cli::Cli; use baru::pulse::Pulse; -use baru::{Baru, Config}; +use baru::{trace, util, Baru, Config}; +use clap::Parser; use std::env; use std::fs; -use std::io::Error; -use std::process; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, error, info}; +const XDG_CONFIG_HOME: &str = "XDG_CONFIG_HOME"; +const APP_DIR: &str = "baru"; +const CONFIG_FILE: &str = "baru.yaml"; const TICK_RATE: Duration = Duration::from_millis(50); -fn print_out_err(message: &str) { - println!("{}", message); - eprintln!("{}", message); -} +fn main() -> Result<()> { + let cli = Cli::parse(); + trace::init(cli.logs).context("failed to init tracing")?; + + let home = env::var("HOME")?; + let mut config_dir = env::var(XDG_CONFIG_HOME) + .map(PathBuf::from) + .unwrap_or_else(|_| Path::new(&home).join(".config")); + config_dir.push(APP_DIR); + util::check_dir(&config_dir)?; + + let config_file = config_dir.join(CONFIG_FILE); + info!("config file: {:?}", config_file); + let content = fs::read_to_string(config_file) + .inspect_err(|e| error!("failed to read config file: {}", e))?; + let config: Config = serde_yaml::from_str(&content) + .inspect_err(|e| error!("failed to parse config file: {}", e))?; + debug!("{:#?}", config); -fn main() -> Result<(), Error> { - let home = env::var("HOME").unwrap_or_else(|err| { - print_out_err(&format!("baru: environment variable HOME, {}", err)); - process::exit(1); - }); - let config_path = env::var("XDG_CONFIG_HOME").unwrap_or_else(|_| format!("{}/.config", home)); - let content = - fs::read_to_string(format!("{}/baru/baru.yaml", config_path)).unwrap_or_else(|err| { - print_out_err(&format!( - "baru: error while reading the config file, {}", - err - )); - process::exit(1); - }); - let config: Config = serde_yaml::from_str(&content).unwrap_or_else(|err| { - print_out_err(&format!( - "baru: error while deserializing the config file, {}", - err - )); - process::exit(1); - }); let tick = match config.tick { Some(ms) => Duration::from_millis(ms as u64), None => TICK_RATE, }; - let pulse = Arc::new(Mutex::new(Pulse::new(&config).unwrap_or_else(|err| { - print_out_err(&format!("baru: error while creating pulse module, {}", err)); - process::exit(1); - }))); - let mut baru = Baru::with_config(&config, &pulse).unwrap_or_else(|err| { - print_out_err(&format!("baru: {}", err)); - process::exit(1); - }); - baru.start().unwrap_or_else(|err| { - print_out_err(&format!("baru: {}", err)); - process::exit(1); - }); + let pulse = Arc::new(Mutex::new(Pulse::new(&config).inspect_err(|e| { + error!("baru: error while creating pulse module, {}", e); + })?)); + let mut baru = Baru::with_config(&config, &pulse) + .inspect_err(|e| error!("failed to create baru instance {}", e))?; + info!("baru instance initialized"); + + let modules = baru.modules(); + info!("modules registered: {}", modules.len()); + debug!("modules: {:?}", modules); + + baru.start() + .inspect_err(|e| error!("failed to start {}", e))?; + info!("started"); let mut iteration_start: Instant; let mut iteration_end: Duration; + + info!("launching main loop"); loop { iteration_start = Instant::now(); - baru.update().unwrap_or_else(|err| { - print_out_err(&format!("baru: {}", err)); - process::exit(1); - }); + baru.update() + .inspect_err(|e| error!("failed to update: {}", e))?; iteration_end = iteration_start.elapsed(); if iteration_end < tick { thread::sleep(tick - iteration_end); diff --git a/src/module.rs b/src/module.rs index f710bf4..ece0040 100644 --- a/src/module.rs +++ b/src/module.rs @@ -2,18 +2,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::battery::Battery; -use crate::brightness::Brightness; -use crate::cpu::Cpu; -use crate::cpu_freq::CpuFreq; -use crate::date_time::DateTime; use crate::error::Error; -use crate::memory::Memory; -use crate::mic::Mic; -use crate::sound::Sound; -use crate::temperature::Temperature; -use crate::wired::Wired; -use crate::wireless::Wireless; +use crate::modules::battery::Battery; +use crate::modules::brightness::Brightness; +use crate::modules::cpu::Cpu; +use crate::modules::cpu_freq::CpuFreq; +use crate::modules::date_time::DateTime; +use crate::modules::memory::Memory; +use crate::modules::mic::Mic; +use crate::modules::sound::Sound; +use crate::modules::temperature::Temperature; +use crate::modules::wired::Wired; +use crate::modules::wireless::Wireless; use crate::Config; use crate::ModuleMsg; use crate::Pulse; diff --git a/src/battery.rs b/src/modules/battery.rs similarity index 99% rename from src/battery.rs rename to src/modules/battery.rs index 3b405f9..464b3da 100644 --- a/src/battery.rs +++ b/src/modules/battery.rs @@ -14,6 +14,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const SYS_PATH: &str = "/sys/class/power_supply/"; @@ -173,6 +174,7 @@ impl<'a> Bar for Battery<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -180,6 +182,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::try_from(&main_config)?; + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/brightness.rs b/src/modules/brightness.rs similarity index 95% rename from src/brightness.rs rename to src/modules/brightness.rs index 1699926..da29c5c 100644 --- a/src/brightness.rs +++ b/src/modules/brightness.rs @@ -4,13 +4,15 @@ use crate::error::Error; use crate::module::{Bar, RunPtr}; +use crate::util::read_and_parse; use crate::Pulse; -use crate::{read_and_parse, Config as MainConfig, ModuleMsg}; +use crate::{Config as MainConfig, ModuleMsg}; use serde::{Deserialize, Serialize}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const SYS_PATH: &str = "/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/intel_backlight"; @@ -101,6 +103,7 @@ impl<'a> Bar for Brightness<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -108,6 +111,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/cpu.rs b/src/modules/cpu.rs similarity index 98% rename from src/cpu.rs rename to src/modules/cpu.rs index 59c2920..ee4a574 100644 --- a/src/cpu.rs +++ b/src/modules/cpu.rs @@ -14,6 +14,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const PROC_STAT: &str = "/proc/stat"; @@ -115,6 +116,7 @@ impl<'a> Bar for Cpu<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -122,6 +124,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut prev_idle = 0; let mut prev_total = 0; let mut iteration_start: Instant; diff --git a/src/cpu_freq.rs b/src/modules/cpu_freq.rs similarity index 97% rename from src/cpu_freq.rs rename to src/modules/cpu_freq.rs index 06b1535..dc35472 100644 --- a/src/cpu_freq.rs +++ b/src/modules/cpu_freq.rs @@ -4,8 +4,9 @@ use crate::error::Error; use crate::module::{Bar, RunPtr}; +use crate::util::read_and_parse; use crate::Pulse; -use crate::{read_and_parse, Config as MainConfig, ModuleMsg}; +use crate::{Config as MainConfig, ModuleMsg}; use serde::{Deserialize, Serialize}; use std::fs::{read_dir, DirEntry}; use std::sync::mpsc::Sender; @@ -13,6 +14,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; use std::{convert::TryFrom, path::Path}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const TICK_RATE: Duration = Duration::from_millis(100); @@ -176,6 +178,7 @@ impl<'a> Bar for CpuFreq<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -183,6 +186,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::try_from(&main_config)?; + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/date_time.rs b/src/modules/date_time.rs similarity index 97% rename from src/date_time.rs rename to src/modules/date_time.rs index a77603b..a8534c9 100644 --- a/src/date_time.rs +++ b/src/modules/date_time.rs @@ -12,6 +12,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const DATE_FORMAT: &str = "%a. %-e %B %Y, %-kh%M"; @@ -99,6 +100,7 @@ impl<'a> Bar for DateTime<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -106,6 +108,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/memory.rs b/src/modules/memory.rs similarity index 97% rename from src/memory.rs rename to src/modules/memory.rs index 10e1f22..ee5b802 100644 --- a/src/memory.rs +++ b/src/modules/memory.rs @@ -5,13 +5,15 @@ use crate::error::Error; use crate::module::{Bar, RunPtr}; use crate::pulse::Pulse; -use crate::{read_and_trim, Config as MainConfig, ModuleMsg}; +use crate::util::read_and_trim; +use crate::{Config as MainConfig, ModuleMsg}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const MEMINFO: &str = "/proc/meminfo"; @@ -149,6 +151,7 @@ impl MemRegex { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -156,6 +159,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mem_regex = MemRegex::new(); let mut iteration_start: Instant; let mut iteration_end: Duration; diff --git a/src/mic.rs b/src/modules/mic.rs similarity index 97% rename from src/mic.rs rename to src/modules/mic.rs index 64037b9..4589d94 100644 --- a/src/mic.rs +++ b/src/modules/mic.rs @@ -11,6 +11,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const TICK_RATE: Duration = Duration::from_millis(50); @@ -102,6 +103,7 @@ impl<'a> Bar for Mic<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -109,6 +111,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/modules/mod.rs b/src/modules/mod.rs new file mode 100644 index 0000000..c6cc1e9 --- /dev/null +++ b/src/modules/mod.rs @@ -0,0 +1,11 @@ +pub mod battery; +pub mod brightness; +pub mod cpu; +pub mod cpu_freq; +pub mod date_time; +pub mod memory; +pub mod mic; +pub mod sound; +pub mod temperature; +pub mod wired; +pub mod wireless; diff --git a/src/sound.rs b/src/modules/sound.rs similarity index 97% rename from src/sound.rs rename to src/modules/sound.rs index 87d27d3..34facd5 100644 --- a/src/sound.rs +++ b/src/modules/sound.rs @@ -7,6 +7,7 @@ use crate::module::{Bar, RunPtr}; use crate::pulse::Pulse; use crate::{Config as MainConfig, ModuleMsg}; use serde::{Deserialize, Serialize}; +use tracing::{debug, instrument}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; @@ -102,6 +103,7 @@ impl<'a> Bar for Sound<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -109,6 +111,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/temperature.rs b/src/modules/temperature.rs similarity index 54% rename from src/temperature.rs rename to src/modules/temperature.rs index db451d7..9c592ac 100644 --- a/src/temperature.rs +++ b/src/modules/temperature.rs @@ -5,15 +5,17 @@ use crate::error::Error; use crate::module::{Bar, RunPtr}; use crate::pulse::Pulse; -use crate::{read_and_parse, Config as MainConfig, ModuleMsg}; +use crate::util::read_and_parse; +use crate::{Config as MainConfig, ModuleMsg}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; -use std::fs; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use std::{fs, io}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const CORETEMP: &str = "/sys/devices/platform/coretemp.0/hwmon"; @@ -46,7 +48,7 @@ pub struct Config { #[derive(Debug)] pub struct InternalConfig<'a> { - coretemp: String, + coretemp: &'a str, high_level: u32, tick: Duration, inputs: Vec, @@ -54,66 +56,111 @@ pub struct InternalConfig<'a> { high_label: &'a str, } +impl<'a> Default for InternalConfig<'a> { + fn default() -> Self { + InternalConfig { + coretemp: CORETEMP, + high_level: HIGH_LEVEL, + tick: TICK_RATE, + inputs: vec![INPUT], + label: LABEL, + high_label: HIGH_LABEL, + } + } +} + +fn check_input_file(path: &str, n: u32) -> bool { + fs::metadata(format!("{path}/temp{n}_input")) + .map(|m| m.is_file()) + .inspect_err(|_e| { + // TODO log error + }) + .unwrap_or(false) +} + +fn check_dir(path: &str) -> Result { + let meta = fs::metadata(path)?; + Ok(meta.is_dir()) +} + +fn get_inputs(core_inputs: &CoreInputs, temp_dir: &str) -> Option> { + let re = Regex::new(r"^(\d+)\.\.(\d+)$").unwrap(); + + match core_inputs { + CoreInputs::Single(n) => { + if check_input_file(temp_dir, *n) { + Some(vec![*n]) + } else { + None + } + } + CoreInputs::Range(range) => { + if let Some(captured) = re.captures(range) { + let start = captured.get(1).unwrap().as_str().parse::().unwrap(); + let end = captured.get(2).unwrap().as_str().parse::().unwrap(); + if (start..end).is_empty() { + // TODO log error on wrong range values + return None; + } + let inputs = (start..end + 1) + .filter(|i| check_input_file(temp_dir, *i)) + .collect(); + return Some(inputs); + } + // TODO log error wrong range format + None + } + CoreInputs::List(list) => Some( + list.iter() + .filter(|i| check_input_file(temp_dir, **i)) + .copied() + .collect(), + ), + } +} + impl<'a> TryFrom<&'a MainConfig> for InternalConfig<'a> { type Error = Error; fn try_from(config: &'a MainConfig) -> Result { - let mut tick = TICK_RATE; - let mut coretemp = CORETEMP; - let mut high_level = HIGH_LEVEL; - let mut inputs = vec![]; - let error = "error when parsing temperature config, wrong core_inputs option, a digit or an inclusive range (eg. 2..4) expected"; - let re = Regex::new(r"^(\d+)\.\.(\d+)$").unwrap(); - let mut label = LABEL; - let mut high_label = HIGH_LABEL; - if let Some(c) = &config.temperature { - if let Some(v) = &c.coretemp { - coretemp = v; - } - if let Some(v) = c.high_level { - high_level = v; - } - if let Some(t) = c.tick { - tick = Duration::from_millis(t as u64) - } - if let Some(v) = &c.label { - label = v; - } - if let Some(v) = &c.high_label { - high_label = v; - } - if let Some(i) = &c.core_inputs { - match i { - CoreInputs::Single(n) => inputs.push(*n), - CoreInputs::Range(range) => { - if let Some(captured) = re.captures(range) { - let start = captured.get(1).unwrap().as_str().parse::().unwrap(); - let end = captured.get(2).unwrap().as_str().parse::().unwrap(); - if start > end { - return Err(Error::new(error)); - } - for i in start..end + 1 { - inputs.push(i) - } - } else { - return Err(Error::new(error)); + let coretemp = config + .temperature + .as_ref() + .and_then(|c| c.coretemp.as_deref()) + .unwrap_or(CORETEMP); + check_dir(coretemp)?; + let temp_dir = find_temp_dir(coretemp)?; + + let internal_cfg = config + .temperature + .as_ref() + .map(|c| { + let inputs = c + .core_inputs + .as_ref() + .and_then(|i| get_inputs(i, &temp_dir)) + .map(|mut i| { + if i.is_empty() { + i.push(INPUT); } - } - CoreInputs::List(list) => inputs = list.clone(), + i + }) + .unwrap_or(vec![INPUT]); + + InternalConfig { + coretemp, + high_level: c.high_level.unwrap_or(HIGH_LEVEL), + tick: c + .tick + .map_or(TICK_RATE, |t| Duration::from_millis(t as u64)), + inputs, + label: c.label.as_deref().unwrap_or(LABEL), + high_label: c.high_label.as_deref().unwrap_or(HIGH_LABEL), } - } - } - if inputs.is_empty() { - inputs.push(INPUT); - } - Ok(InternalConfig { - coretemp: find_temp_dir(coretemp)?, - high_level, - inputs, - tick, - label, - high_label, - }) + }) + .unwrap_or_default(); + + Ok(internal_cfg) } } @@ -160,6 +207,7 @@ impl<'a> Bar for Temperature<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -167,16 +215,15 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::try_from(&main_config)?; + debug!("{:#?}", config); + let temp_dir = find_temp_dir(config.coretemp)?; let mut iteration_start: Instant; let mut iteration_end: Duration; loop { iteration_start = Instant::now(); let mut inputs = vec![]; for i in &config.inputs { - inputs.push(read_and_parse(&format!( - "{}/temp{}_input", - config.coretemp, i - ))?) + inputs.push(read_and_parse(&format!("{}/temp{}_input", temp_dir, i))?) } let sum: i32 = inputs.iter().sum(); let average = ((sum as f32 / inputs.len() as f32) / 1000_f32).round() as i32; diff --git a/src/wired.rs b/src/modules/wired.rs similarity index 97% rename from src/wired.rs rename to src/modules/wired.rs index dc39833..5edd40a 100644 --- a/src/wired.rs +++ b/src/modules/wired.rs @@ -12,6 +12,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const TICK_RATE: Duration = Duration::from_millis(1000); @@ -118,6 +119,7 @@ impl<'a> Bar for Wired<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -125,6 +127,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/wireless.rs b/src/modules/wireless.rs similarity index 98% rename from src/wireless.rs rename to src/modules/wireless.rs index 694223f..d6da028 100644 --- a/src/wireless.rs +++ b/src/modules/wireless.rs @@ -12,6 +12,7 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; +use tracing::{debug, instrument}; const PLACEHOLDER: &str = "-"; const TICK_RATE: Duration = Duration::from_millis(500); @@ -132,6 +133,7 @@ impl<'a> Bar for Wireless<'a> { } } +#[instrument(skip_all)] pub fn run( key: char, main_config: MainConfig, @@ -139,6 +141,7 @@ pub fn run( tx: Sender, ) -> Result<(), Error> { let config = InternalConfig::from(&main_config); + debug!("{:#?}", config); let mut iteration_start: Instant; let mut iteration_end: Duration; loop { diff --git a/src/pulse.rs b/src/pulse.rs index 44f8cd0..b37f5e7 100644 --- a/src/pulse.rs +++ b/src/pulse.rs @@ -2,6 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use tracing::instrument; + use crate::error::Error; use crate::Config; use std::os::raw::c_char; @@ -27,6 +29,7 @@ pub struct Pulse( ); impl Pulse { + #[instrument(skip_all)] pub fn new(config: &Config) -> Result { let (sink_tx, sink_rx) = mpsc::channel(); let (source_tx, source_rx) = mpsc::channel(); diff --git a/src/trace.rs b/src/trace.rs new file mode 100644 index 0000000..db02766 --- /dev/null +++ b/src/trace.rs @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +use anyhow::Result; +use tracing_appender::{non_blocking::WorkerGuard, rolling}; +use tracing_subscriber::filter::LevelFilter; +use tracing_subscriber::EnvFilter; + +use crate::cli::Logs; +use crate::util; + +const XDG_CACHE_HOME: &str = "XDG_CACHE_HOME"; +const LOG_DIR: &str = "baru"; +const LOG_FILE: &str = "baru.log"; +const LOG_FILE_OLD: &str = "baru.old.log"; + +fn rotate_log_file(log_dir: PathBuf) -> Result<()> { + let log_file = log_dir.join(LOG_FILE); + if log_file.is_file() { + let old_file = log_dir.join(LOG_FILE_OLD); + let data = fs::read(&log_file).inspect_err(|e| { + eprintln!( + "failed to read log file during log rotation {}: {e}", + log_file.display() + ) + })?; + fs::write(&old_file, data).inspect_err(|e| { + eprintln!( + "failed to write log file during log rotation {}: {e}", + old_file.display() + ) + })?; + fs::remove_file(log_file)?; + } + Ok(()) +} + +pub fn init(logs: Option) -> Result> { + let Some(l) = logs else { + return Ok(None); + }; + + let filter = EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env() + .unwrap(); + + match l { + Logs::Stdout => { + tracing_subscriber::fmt() + .with_env_filter(filter) + .compact() + .init(); + Ok(None) + } + Logs::File => { + let home = env::var("HOME")?; + let cache_dir = env::var(XDG_CACHE_HOME) + .map(PathBuf::from) + .unwrap_or_else(|_| Path::new(&home).join(".cache")); + let log_dir = cache_dir.join(LOG_DIR); + util::check_dir(&log_dir)?; + rotate_log_file(log_dir.clone()).ok(); + + let appender = rolling::never(log_dir, LOG_FILE); + let (writer, guard) = tracing_appender::non_blocking(appender); + + tracing_subscriber::fmt() + .with_env_filter(filter) + .compact() + .with_ansi(false) + .with_writer(writer) + .init(); + Ok(Some(guard)) + } + _ => Ok(None), + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..107954d --- /dev/null +++ b/src/util.rs @@ -0,0 +1,31 @@ +use anyhow::{Context, Result}; +use std::{fs, path::PathBuf}; +use tracing::{debug, error}; + +use crate::error::Error; + +/// Check if a directory exists, if not create it including all +/// parent components +pub fn check_dir(path: &PathBuf) -> Result<()> { + if !path.is_dir() { + debug!("directory `{}` does not exist, creating it", path.display()); + return fs::create_dir_all(path) + .inspect_err(|e| error!("Failed to create directory `{}`: {e}", path.display())) + .context(format!("Failed to create directory `{}`", path.display())); + } + Ok(()) +} + +pub fn read_and_trim(file: &str) -> Result { + let content = fs::read_to_string(file) + .inspect_err(|e| error!("failed to read the file `{}`: {}", file, e))?; + Ok(content.trim().to_string()) +} + +pub fn read_and_parse(file: &str) -> Result { + let content = read_and_trim(file)?; + let data = content + .parse::() + .inspect_err(|e| error!("failed to parse the file `{}`: {}", file, e))?; + Ok(data) +}