mirror of
https://github.com/orhun/systeroid
synced 2024-07-05 17:08:36 +00:00
feat(config): add a configuration file (#12)
This commit adds a configuration file (`systeroid.conf`) for configuring the CLI/TUI settings. Consult README.md or the file itself for more information.
This commit is contained in:
parent
570fdf9c2d
commit
907b337158
|
@ -4,6 +4,7 @@
|
||||||
/target/
|
/target/
|
||||||
/.cargo/
|
/.cargo/
|
||||||
/assets/
|
/assets/
|
||||||
|
/config/
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
.editorconfig
|
.editorconfig
|
||||||
|
|
74
Cargo.lock
generated
74
Cargo.lock
generated
|
@ -8,6 +8,17 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
|
@ -186,6 +197,15 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-next"
|
name = "dirs-next"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -196,6 +216,17 @@ dependencies = [
|
||||||
"dirs-sys-next",
|
"dirs-sys-next",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys-next"
|
name = "dirs-sys-next"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -216,6 +247,12 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlv-list"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "downcast-rs"
|
name = "downcast-rs"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -288,6 +325,15 @@ dependencies = [
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -491,6 +537,16 @@ version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-multimap"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
||||||
|
dependencies = [
|
||||||
|
"dlv-list",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parseit"
|
name = "parseit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -606,6 +662,16 @@ version = "0.6.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-ini"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ordered-multimap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
|
@ -735,10 +801,12 @@ name = "systeroid-core"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
|
"dirs",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"parseit",
|
"parseit",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"rust-ini",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysctl",
|
"sysctl",
|
||||||
|
@ -831,6 +899,12 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
|
25
README.md
25
README.md
|
@ -93,6 +93,7 @@ Although **systeroid** does not need the parameter section to be specified expli
|
||||||
- [Changing the colors](#changing-the-colors)
|
- [Changing the colors](#changing-the-colors)
|
||||||
- [Viewing the parameter documentation](#viewing-the-parameter-documentation)
|
- [Viewing the parameter documentation](#viewing-the-parameter-documentation)
|
||||||
- [Setting the refresh rate](#setting-the-refresh-rate)
|
- [Setting the refresh rate](#setting-the-refresh-rate)
|
||||||
|
- [Configuration](#configuration)
|
||||||
- [Resources](#resources)
|
- [Resources](#resources)
|
||||||
- [References](#references)
|
- [References](#references)
|
||||||
- [Logo](#logo)
|
- [Logo](#logo)
|
||||||
|
@ -215,6 +216,7 @@ systeroid [options] [variable[=value] ...] --load[=<file>]
|
||||||
-P, --no-pager do not pipe output into a pager
|
-P, --no-pager do not pipe output into a pager
|
||||||
-v, --verbose enable verbose logging
|
-v, --verbose enable verbose logging
|
||||||
--tui show terminal user interface
|
--tui show terminal user interface
|
||||||
|
-c, --config <path> set the path of the configuration file
|
||||||
-h, --help display this help and exit (-d)
|
-h, --help display this help and exit (-d)
|
||||||
-V, --version output version information and exit
|
-V, --version output version information and exit
|
||||||
```
|
```
|
||||||
|
@ -416,6 +418,7 @@ systeroid-tui [options]
|
||||||
set the foreground color [default: white]
|
set the foreground color [default: white]
|
||||||
-n, --no-docs do not show the kernel documentation
|
-n, --no-docs do not show the kernel documentation
|
||||||
--deprecated include deprecated variables while listing
|
--deprecated include deprecated variables while listing
|
||||||
|
-c, --config <path> set the path of the configuration file
|
||||||
-h, --help display this help and exit
|
-h, --help display this help and exit
|
||||||
-V, --version output version information and exit
|
-V, --version output version information and exit
|
||||||
```
|
```
|
||||||
|
@ -548,6 +551,28 @@ It is possible to specify a value in milliseconds via `--tick-rate` argument for
|
||||||
systeroid-tui --tick-rate 500
|
systeroid-tui --tick-rate 500
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
**systeroid** can be configured with a configuration file that uses the [INI format](https://en.wikipedia.org/wiki/INI_file). It can be specified via `--config` or `SYSTEROID_CONFIG` environment variable. It can also be placed in one of the following global locations:
|
||||||
|
|
||||||
|
- `$HOME/.config/systeroid/systeroid.conf`
|
||||||
|
- `$HOME/.systeroid/systeroid.conf`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# set the config path via argument
|
||||||
|
systeroid --config config/systeroid.conf
|
||||||
|
|
||||||
|
# set the config path via env
|
||||||
|
SYSTEROID_CONFIG=config/systeroid.conf systeroid
|
||||||
|
|
||||||
|
# use a global path
|
||||||
|
mkdir -p "$HOME/.config/systeroid"
|
||||||
|
cp config/systeroid.conf "$HOME/.config/systeroid"
|
||||||
|
systeroid
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example [systeroid.conf](./config/systeroid.conf) for the configuration options.
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
### References
|
### References
|
||||||
|
|
66
config/systeroid.conf
Normal file
66
config/systeroid.conf
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
; systeroid ~ configuration file
|
||||||
|
; https://github.com/orhun/systeroid
|
||||||
|
;
|
||||||
|
; Each line either contains a comment or a command line argument grouped under a section.
|
||||||
|
; Run "systeroid --help" or "systeroid-tui --help" to get a list of all possible configuration options.
|
||||||
|
|
||||||
|
[general]
|
||||||
|
; display the deprecated parameters such as base_reachable_time and retrans_time while listing
|
||||||
|
; See https://bugzilla.redhat.com/show_bug.cgi?id=152435
|
||||||
|
display_deprecated = false
|
||||||
|
; path of the Linux kernel documentation
|
||||||
|
; this is distro dependent, systeroid checks the following locations as default:
|
||||||
|
; - /usr/share/doc/linux/
|
||||||
|
; - /usr/share/doc/linux-doc/
|
||||||
|
; - /usr/share/doc/linux-docs/
|
||||||
|
; - /usr/share/doc/kernel-doc-*/Documentation/
|
||||||
|
kernel_docs = "/usr/share/doc/linux"
|
||||||
|
|
||||||
|
[cli]
|
||||||
|
; enable verbose logging
|
||||||
|
verbose = false
|
||||||
|
; ignore unknown variable errors
|
||||||
|
ignore_errors = true
|
||||||
|
; do not print variable after the value is set
|
||||||
|
quiet = false
|
||||||
|
; do not pipe output into a pager
|
||||||
|
; note that the default pager is less(1) and you can change it by using `PAGER` environment variable
|
||||||
|
no_pager = false
|
||||||
|
; display type for the parameter, available options are:
|
||||||
|
; - default: print the parameter name along with its value
|
||||||
|
; - name: print only the name of the parameter
|
||||||
|
; - value: print only the value of the parameter
|
||||||
|
; - binary: print only the value of the parameter without new line
|
||||||
|
display_type = "default"
|
||||||
|
; output type for the list, available options are:
|
||||||
|
; - default: print the output as is
|
||||||
|
; - tree: print the output in a tree-like format
|
||||||
|
; - json: print the output in JSON format
|
||||||
|
output_type = "default"
|
||||||
|
|
||||||
|
[cli.colors]
|
||||||
|
; available colors are defined in https://docs.rs/colored/latest/colored/enum.Color.html
|
||||||
|
; default color for the symbols
|
||||||
|
default_color = "bright black"
|
||||||
|
; section colors
|
||||||
|
section_abi = "red"
|
||||||
|
section_fs = "green"
|
||||||
|
section_kernel = "magenta"
|
||||||
|
section_net = "blue"
|
||||||
|
section_sunrpc = "yellow"
|
||||||
|
section_user = "cyan"
|
||||||
|
section_vm = "bright red"
|
||||||
|
section_unknown = "white"
|
||||||
|
|
||||||
|
[tui]
|
||||||
|
; tick rate of the terminal
|
||||||
|
tick_rate = 250
|
||||||
|
; disable showing the parameter documentation
|
||||||
|
no_docs = true
|
||||||
|
|
||||||
|
[tui.colors]
|
||||||
|
; available colors are defined in https://docs.rs/tui/latest/tui/style/enum.Color.html
|
||||||
|
; terminal foreground color
|
||||||
|
fg_color = "white"
|
||||||
|
; terminal background color
|
||||||
|
bg_color = "black"
|
|
@ -21,3 +21,5 @@ serde = { version = "1.0.140", features = ["derive"] }
|
||||||
serde_json = "1.0.82"
|
serde_json = "1.0.82"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
parseit = { version = "0.1.0", features = ["gzip"] }
|
parseit = { version = "0.1.0", features = ["gzip"] }
|
||||||
|
rust-ini = "0.18.0"
|
||||||
|
dirs = "4.0.0"
|
||||||
|
|
|
@ -1,9 +1,26 @@
|
||||||
use crate::sysctl::display::DisplayType;
|
use crate::error::Result;
|
||||||
|
use crate::sysctl::r#type::{DisplayType, OutputType};
|
||||||
use crate::sysctl::section::Section;
|
use crate::sysctl::section::Section;
|
||||||
use colored::Color;
|
use colored::Color;
|
||||||
|
use ini::Ini;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/* Macro for the concise initialization of HashMap */
|
/// Default configuration file.
|
||||||
|
pub const DEFAULT_CONFIG: &str = "systeroid.conf";
|
||||||
|
|
||||||
|
/// Environment variable for setting the path of the configuration file.
|
||||||
|
pub const CONFIG_ENV: &str = "SYSTEROID_CONFIG";
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
/// Default locations for the configuration file.
|
||||||
|
pub static ref DEFAULT_CONFIG_PATHS: Vec<Option<PathBuf>> = vec![
|
||||||
|
dirs::config_dir().map(|p| p.join("systeroid").join(DEFAULT_CONFIG)),
|
||||||
|
dirs_next::home_dir().map(|p| p.join(".systeroid").join(DEFAULT_CONFIG)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro for the concise initialization of HashMap
|
||||||
macro_rules! map {
|
macro_rules! map {
|
||||||
($( $key: expr => $val: expr ),*) => {{
|
($( $key: expr => $val: expr ),*) => {{
|
||||||
let mut map = ::std::collections::HashMap::new();
|
let mut map = ::std::collections::HashMap::new();
|
||||||
|
@ -12,47 +29,227 @@ macro_rules! map {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Macro for parsing a boolean value from INI format
|
||||||
|
macro_rules! parse_ini_flag {
|
||||||
|
($self: ident, $config: ident, $section: ident, $name: ident) => {
|
||||||
|
if let Some($name) = $section.get(stringify!($name)) {
|
||||||
|
$self.$config.$name = $name == "true";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration.
|
/// Configuration.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// Whether if the deprecated variables should be included while listing.
|
||||||
|
pub display_deprecated: bool,
|
||||||
|
/// Path of the Linux kernel documentation.
|
||||||
|
pub kernel_docs: Option<PathBuf>,
|
||||||
|
/// CLI configuration.
|
||||||
|
pub cli: CliConfig,
|
||||||
|
/// TUI configuration.
|
||||||
|
pub tui: TuiConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CLI configuration.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CliConfig {
|
||||||
/// Whether if the verbose logging is enabled.
|
/// Whether if the verbose logging is enabled.
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
/// Whether if the errors should be ignored.
|
/// Whether if the errors should be ignored.
|
||||||
pub ignore_errors: bool,
|
pub ignore_errors: bool,
|
||||||
/// Whether if the deprecated variables should be included while listing.
|
|
||||||
pub display_deprecated: bool,
|
|
||||||
/// Whether if the quiet mode is enabled.
|
/// Whether if the quiet mode is enabled.
|
||||||
pub quiet: bool,
|
pub quiet: bool,
|
||||||
/// Whether if the pager is disabled.
|
/// Whether if the pager is disabled.
|
||||||
pub no_pager: bool,
|
pub no_pager: bool,
|
||||||
/// Sections and the corresponding colors.
|
|
||||||
pub section_colors: HashMap<Section, Color>,
|
|
||||||
/// Default color for the output
|
|
||||||
pub default_color: Color,
|
|
||||||
/// Display type of the kernel parameters.
|
/// Display type of the kernel parameters.
|
||||||
pub display_type: DisplayType,
|
pub display_type: DisplayType,
|
||||||
|
/// Output type of the application.
|
||||||
|
pub output_type: OutputType,
|
||||||
|
/// Color configuration.
|
||||||
|
pub color: CliColorConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CLI color configuration.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CliColorConfig {
|
||||||
|
/// Default color for the output
|
||||||
|
pub default_color: Color,
|
||||||
|
/// Sections and the corresponding colors.
|
||||||
|
pub section_colors: HashMap<Section, Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TUI configuration.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TuiConfig {
|
||||||
|
/// Refresh rate of the terminal.
|
||||||
|
pub tick_rate: u64,
|
||||||
|
/// Do not parse/show Linux kernel documentation.
|
||||||
|
pub no_docs: bool,
|
||||||
|
/// Color configuration.
|
||||||
|
pub color: TuiColorConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TUI color configuration.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TuiColorConfig {
|
||||||
|
/// Foreground color.
|
||||||
|
pub fg_color: String,
|
||||||
|
/// Background color.
|
||||||
|
pub bg_color: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Parses the configuration file and overrides values.
|
||||||
|
pub fn parse(&mut self, path: Option<PathBuf>) -> Result<()> {
|
||||||
|
let mut config_paths = DEFAULT_CONFIG_PATHS.clone();
|
||||||
|
if path.is_some() {
|
||||||
|
config_paths.insert(0, path);
|
||||||
|
}
|
||||||
|
let mut config_path = None;
|
||||||
|
for path in config_paths.into_iter().flatten() {
|
||||||
|
if path.exists() {
|
||||||
|
config_path = Some(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(path) = config_path {
|
||||||
|
let ini = Ini::load_from_file(path)?;
|
||||||
|
if let Some(general_section) = ini.section(Some("general")) {
|
||||||
|
if let Some(display_deprecated) = general_section.get("display_deprecated") {
|
||||||
|
self.display_deprecated = display_deprecated == "true";
|
||||||
|
}
|
||||||
|
if let Some(kernel_docs) = general_section.get("kernel_docs") {
|
||||||
|
self.kernel_docs = Some(PathBuf::from(kernel_docs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(section) = ini.section(Some("cli")) {
|
||||||
|
parse_ini_flag!(self, cli, section, verbose);
|
||||||
|
parse_ini_flag!(self, cli, section, ignore_errors);
|
||||||
|
parse_ini_flag!(self, cli, section, quiet);
|
||||||
|
parse_ini_flag!(self, cli, section, no_pager);
|
||||||
|
if let Some(display_type) = section.get("display_type").map(DisplayType::from) {
|
||||||
|
self.cli.display_type = display_type;
|
||||||
|
}
|
||||||
|
if let Some(output_type) = section.get("output_type").map(OutputType::from) {
|
||||||
|
self.cli.output_type = output_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(section) = ini.section(Some("cli.colors")) {
|
||||||
|
if let Some(default_color) = section
|
||||||
|
.get("default_color")
|
||||||
|
.and_then(|v| Color::try_from(v).ok())
|
||||||
|
{
|
||||||
|
self.cli.color.default_color = default_color;
|
||||||
|
}
|
||||||
|
for (key, value) in section.iter() {
|
||||||
|
if key.starts_with("section_") {
|
||||||
|
if let (sysctl_section, Some(color)) = (
|
||||||
|
Section::from(key.trim_start_matches("section_").to_string()),
|
||||||
|
Color::try_from(value).ok(),
|
||||||
|
) {
|
||||||
|
self.cli.color.section_colors.insert(sysctl_section, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(section) = ini.section(Some("tui")) {
|
||||||
|
if let Some(tick_rate) = section.get("tick_rate").and_then(|v| v.parse().ok()) {
|
||||||
|
self.tui.tick_rate = tick_rate;
|
||||||
|
}
|
||||||
|
parse_ini_flag!(self, tui, section, no_docs);
|
||||||
|
}
|
||||||
|
if let Some(section) = ini.section(Some("tui.colors")) {
|
||||||
|
if let Some(fg_color) = section.get("fg_color") {
|
||||||
|
self.tui.color.fg_color = fg_color.to_string();
|
||||||
|
}
|
||||||
|
if let Some(bg_color) = section.get("bg_color") {
|
||||||
|
self.tui.color.bg_color = bg_color.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
verbose: false,
|
|
||||||
ignore_errors: false,
|
|
||||||
display_deprecated: false,
|
display_deprecated: false,
|
||||||
quiet: false,
|
kernel_docs: None,
|
||||||
no_pager: false,
|
cli: CliConfig {
|
||||||
section_colors: map! {
|
verbose: false,
|
||||||
Section::Abi => Color::Red,
|
ignore_errors: false,
|
||||||
Section::Fs => Color::Green,
|
quiet: false,
|
||||||
Section::Kernel => Color::Magenta,
|
no_pager: false,
|
||||||
Section::Net => Color::Blue,
|
display_type: DisplayType::Default,
|
||||||
Section::Sunrpc => Color::Yellow,
|
output_type: OutputType::Default,
|
||||||
Section::User => Color::Cyan,
|
color: CliColorConfig {
|
||||||
Section::Vm => Color::BrightRed,
|
default_color: Color::BrightBlack,
|
||||||
Section::Unknown => Color::White
|
section_colors: map! {
|
||||||
|
Section::Abi => Color::Red,
|
||||||
|
Section::Fs => Color::Green,
|
||||||
|
Section::Kernel => Color::Magenta,
|
||||||
|
Section::Net => Color::Blue,
|
||||||
|
Section::Sunrpc => Color::Yellow,
|
||||||
|
Section::User => Color::Cyan,
|
||||||
|
Section::Vm => Color::BrightRed,
|
||||||
|
Section::Unknown => Color::White
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tui: TuiConfig {
|
||||||
|
tick_rate: 250,
|
||||||
|
no_docs: false,
|
||||||
|
color: TuiColorConfig {
|
||||||
|
fg_color: String::from("white"),
|
||||||
|
bg_color: String::from("black"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
default_color: Color::BrightBlack,
|
|
||||||
display_type: DisplayType::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_config() -> Result<()> {
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.display_deprecated = true;
|
||||||
|
config.cli.display_type = DisplayType::Value;
|
||||||
|
config.cli.color.default_color = Color::Blue;
|
||||||
|
config.cli.color.section_colors = HashMap::new();
|
||||||
|
config.tui.tick_rate = 3000;
|
||||||
|
config.tui.color.fg_color = String::new();
|
||||||
|
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("config")
|
||||||
|
.join(DEFAULT_CONFIG);
|
||||||
|
config.parse(Some(path))?;
|
||||||
|
assert_eq!(
|
||||||
|
Config::default().display_deprecated,
|
||||||
|
config.display_deprecated
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Some(PathBuf::from("/usr/share/doc/linux")),
|
||||||
|
config.kernel_docs
|
||||||
|
);
|
||||||
|
assert_eq!(Config::default().cli.display_type, config.cli.display_type);
|
||||||
|
assert_eq!(
|
||||||
|
Config::default().cli.color.default_color,
|
||||||
|
config.cli.color.default_color
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Config::default().cli.color.section_colors,
|
||||||
|
config.cli.color.section_colors
|
||||||
|
);
|
||||||
|
assert_eq!(Config::default().tui.tick_rate, config.tui.tick_rate);
|
||||||
|
assert_eq!(
|
||||||
|
Config::default().tui.color.fg_color,
|
||||||
|
config.tui.color.fg_color
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ pub enum Error {
|
||||||
/// Error that may occur while handling sysctl operations.
|
/// Error that may occur while handling sysctl operations.
|
||||||
#[error("sysctl error: `{0}`")]
|
#[error("sysctl error: `{0}`")]
|
||||||
SysctlError(#[from] sysctl::SysctlError),
|
SysctlError(#[from] sysctl::SysctlError),
|
||||||
|
/// Error that may occur while parsing an INI document.
|
||||||
|
#[error("INI parsing error: `{0}`")]
|
||||||
|
IniError(#[from] ini::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type alias for the standard [`Result`] type.
|
/// Type alias for the standard [`Result`] type.
|
||||||
|
|
|
@ -10,7 +10,7 @@ use parseit::globwalk;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use sysctl::{CtlFlags, CtlIter, Sysctl as SysctlImpl};
|
use sysctl::{CtlFlags, CtlIter, Sysctl as SysctlImpl};
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ impl Sysctl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if config.verbose {
|
if config.cli.verbose {
|
||||||
eprintln!("{} ({})", e, ctl.name()?);
|
eprintln!("{} ({})", e, ctl.name()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ impl Sysctl {
|
||||||
|| param.get_absolute_name() == Some(&query.replace('/', "."))
|
|| param.get_absolute_name() == Some(&query.replace('/', "."))
|
||||||
})
|
})
|
||||||
.collect::<Vec<&Parameter>>();
|
.collect::<Vec<&Parameter>>();
|
||||||
if parameters.is_empty() && !self.config.ignore_errors {
|
if parameters.is_empty() && !self.config.cli.ignore_errors {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}: cannot stat {}{}: No such file or directory",
|
"{}: cannot stat {}{}: No such file or directory",
|
||||||
env!("CARGO_PKG_NAME").split('-').collect::<Vec<_>>()[0],
|
env!("CARGO_PKG_NAME").split('-').collect::<Vec<_>>()[0],
|
||||||
|
@ -85,12 +85,8 @@ impl Sysctl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the descriptions of the kernel parameters using the given cached data.
|
/// Updates the descriptions of the kernel parameters using the given cached data.
|
||||||
pub fn update_docs_from_cache(
|
pub fn update_docs_from_cache(&mut self, cache: &Cache) -> Result<()> {
|
||||||
&mut self,
|
let mut kernel_docs_path = if let Some(path) = &self.config.kernel_docs {
|
||||||
kernel_docs: Option<&PathBuf>,
|
|
||||||
cache: &Cache,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut kernel_docs_path = if let Some(path) = kernel_docs {
|
|
||||||
vec![path.to_path_buf()]
|
vec![path.to_path_buf()]
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
|
@ -197,7 +193,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert!(sysctl.get_parameters("---").is_empty());
|
assert!(sysctl.get_parameters("---").is_empty());
|
||||||
|
|
||||||
sysctl.update_docs_from_cache(None, &Cache::init()?)?;
|
sysctl.update_docs_from_cache(&Cache::init()?)?;
|
||||||
|
|
||||||
let parameter = sysctl
|
let parameter = sysctl
|
||||||
.get_parameter("kernel.hostname")
|
.get_parameter("kernel.hostname")
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
/// Possible ways of displaying the kernel parameters.
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum DisplayType {
|
|
||||||
/// Print the kernel parameter name along with its value.
|
|
||||||
Default,
|
|
||||||
/// Print only the name of the parameter.
|
|
||||||
Name,
|
|
||||||
/// Print only the value of the parameter.
|
|
||||||
Value,
|
|
||||||
/// Print only the value of the parameter without new line.
|
|
||||||
Binary,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DisplayType {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Default
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,8 +4,8 @@ pub mod controller;
|
||||||
/// Sysctl section.
|
/// Sysctl section.
|
||||||
pub mod section;
|
pub mod section;
|
||||||
|
|
||||||
/// Sysctl display options.
|
/// Sysctl display/output options.
|
||||||
pub mod display;
|
pub mod r#type;
|
||||||
|
|
||||||
/// Kernel parameter.
|
/// Kernel parameter.
|
||||||
pub mod parameter;
|
pub mod parameter;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::sysctl::display::DisplayType;
|
use crate::sysctl::r#type::DisplayType;
|
||||||
use crate::sysctl::section::Section;
|
use crate::sysctl::section::Section;
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -53,9 +53,11 @@ impl Parameter {
|
||||||
/// Returns the parameter name with corresponding section colors.
|
/// Returns the parameter name with corresponding section colors.
|
||||||
pub fn get_colored_name(&self, config: &Config) -> String {
|
pub fn get_colored_name(&self, config: &Config) -> String {
|
||||||
let section_color = *(config
|
let section_color = *(config
|
||||||
|
.cli
|
||||||
|
.color
|
||||||
.section_colors
|
.section_colors
|
||||||
.get(&self.section)
|
.get(&self.section)
|
||||||
.unwrap_or(&config.default_color));
|
.unwrap_or(&config.cli.color.default_color));
|
||||||
let fields = self.name.split('.').collect::<Vec<&str>>();
|
let fields = self.name.split('.').collect::<Vec<&str>>();
|
||||||
fields
|
fields
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -66,7 +68,7 @@ impl Parameter {
|
||||||
result,
|
result,
|
||||||
"{}{}",
|
"{}{}",
|
||||||
v.color(section_color),
|
v.color(section_color),
|
||||||
".".color(config.default_color)
|
".".color(config.cli.color.default_color)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result += v;
|
result += v;
|
||||||
|
@ -75,14 +77,16 @@ impl Parameter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the components of the parameter to contruct a [`Tree`].
|
/// Returns the components of the parameter to construct a [`Tree`].
|
||||||
///
|
///
|
||||||
/// [`Tree`]: crate::tree::Tree
|
/// [`Tree`]: crate::tree::Tree
|
||||||
pub fn get_tree_components(&self, config: &Config) -> Vec<String> {
|
pub fn get_tree_components(&self, config: &Config) -> Vec<String> {
|
||||||
let section_color = *(config
|
let section_color = *(config
|
||||||
|
.cli
|
||||||
|
.color
|
||||||
.section_colors
|
.section_colors
|
||||||
.get(&self.section)
|
.get(&self.section)
|
||||||
.unwrap_or(&config.default_color));
|
.unwrap_or(&config.cli.color.default_color));
|
||||||
let mut components = self
|
let mut components = self
|
||||||
.name
|
.name
|
||||||
.split('.')
|
.split('.')
|
||||||
|
@ -95,11 +99,11 @@ impl Parameter {
|
||||||
.for_each(|(i, component)| {
|
.for_each(|(i, component)| {
|
||||||
if i != total_components - 1 {
|
if i != total_components - 1 {
|
||||||
*component = component.color(section_color).to_string();
|
*component = component.color(section_color).to_string();
|
||||||
} else if config.display_type != DisplayType::Name {
|
} else if config.cli.display_type != DisplayType::Name {
|
||||||
*component = format!(
|
*component = format!(
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
component,
|
component,
|
||||||
"=".color(config.default_color),
|
"=".color(config.cli.color.default_color),
|
||||||
self.value.replace('\n', " ").bold()
|
self.value.replace('\n', " ").bold()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +113,7 @@ impl Parameter {
|
||||||
|
|
||||||
/// Prints the kernel parameter to given output.
|
/// Prints the kernel parameter to given output.
|
||||||
pub fn display_value<Output: Write>(&self, config: &Config, output: &mut Output) -> Result<()> {
|
pub fn display_value<Output: Write>(&self, config: &Config, output: &mut Output) -> Result<()> {
|
||||||
match config.display_type {
|
match config.cli.display_type {
|
||||||
DisplayType::Name => {
|
DisplayType::Name => {
|
||||||
writeln!(output, "{}", self.get_colored_name(config))?;
|
writeln!(output, "{}", self.get_colored_name(config))?;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +129,7 @@ impl Parameter {
|
||||||
output,
|
output,
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
self.get_colored_name(config),
|
self.get_colored_name(config),
|
||||||
"=".color(config.default_color),
|
"=".color(config.cli.color.default_color),
|
||||||
value.bold(),
|
value.bold(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +191,7 @@ impl Parameter {
|
||||||
let ctl = Ctl::new(&self.name)?;
|
let ctl = Ctl::new(&self.name)?;
|
||||||
let new_value = ctl.set_value_string(new_value)?;
|
let new_value = ctl.set_value_string(new_value)?;
|
||||||
self.value = new_value;
|
self.value = new_value;
|
||||||
if !config.quiet {
|
if !config.cli.quiet {
|
||||||
self.display_value(config, output)?;
|
self.display_value(config, output)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -211,10 +215,12 @@ mod tests {
|
||||||
assert_eq!(Some("test_param"), parameter.get_absolute_name());
|
assert_eq!(Some("test_param"), parameter.get_absolute_name());
|
||||||
|
|
||||||
let mut config = Config {
|
let mut config = Config {
|
||||||
default_color: Color::White,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
config.cli.color.default_color = Color::White;
|
||||||
*(config
|
*(config
|
||||||
|
.cli
|
||||||
|
.color
|
||||||
.section_colors
|
.section_colors
|
||||||
.get_mut(&Section::Kernel)
|
.get_mut(&Section::Kernel)
|
||||||
.expect("failed to get color")) = Color::Yellow;
|
.expect("failed to get color")) = Color::Yellow;
|
||||||
|
@ -237,7 +243,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
output.clear();
|
output.clear();
|
||||||
config.display_type = DisplayType::Name;
|
config.cli.display_type = DisplayType::Name;
|
||||||
parameter.display_value(&config, &mut output)?;
|
parameter.display_value(&config, &mut output)?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"kernel.fictional.test_param\n",
|
"kernel.fictional.test_param\n",
|
||||||
|
@ -245,12 +251,12 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
output.clear();
|
output.clear();
|
||||||
config.display_type = DisplayType::Value;
|
config.cli.display_type = DisplayType::Value;
|
||||||
parameter.display_value(&config, &mut output)?;
|
parameter.display_value(&config, &mut output)?;
|
||||||
assert_eq!("1\n", String::from_utf8_lossy(&output));
|
assert_eq!("1\n", String::from_utf8_lossy(&output));
|
||||||
|
|
||||||
output.clear();
|
output.clear();
|
||||||
config.display_type = DisplayType::Binary;
|
config.cli.display_type = DisplayType::Binary;
|
||||||
parameter.display_value(&config, &mut output)?;
|
parameter.display_value(&config, &mut output)?;
|
||||||
assert_eq!("1", String::from_utf8_lossy(&output));
|
assert_eq!("1", String::from_utf8_lossy(&output));
|
||||||
|
|
||||||
|
|
73
systeroid-core/src/sysctl/type.rs
Normal file
73
systeroid-core/src/sysctl/type.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/// Macro for generating enum type with a Default variant and common implementations.
|
||||||
|
macro_rules! gen_type_property {
|
||||||
|
($name: ident,
|
||||||
|
$($variant: ident,)+
|
||||||
|
) => {
|
||||||
|
/// Enum containing variants.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum $name {
|
||||||
|
/// Default variant.
|
||||||
|
Default,
|
||||||
|
$(
|
||||||
|
/// Variant.
|
||||||
|
$variant
|
||||||
|
),+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for $name {
|
||||||
|
fn from(value: &'a str) -> Self {
|
||||||
|
for section in Self::variants() {
|
||||||
|
if value.to_lowercase() == section.to_string() {
|
||||||
|
return *section;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for $name {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
write!(f, "{}", format!("{:?}", self).to_lowercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
/// Returns the variants.
|
||||||
|
pub fn variants() -> &'static [Self] {
|
||||||
|
&[Self::Default, $(Self::$variant),+]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_type_property!(DisplayType, Name, Value, Binary,);
|
||||||
|
gen_type_property!(OutputType, Tree, Json,);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_gen_type() {
|
||||||
|
gen_type_property!(TestType, One, Two, Three,);
|
||||||
|
assert_eq!(TestType::Two, TestType::from("two"));
|
||||||
|
assert_eq!(TestType::Two, TestType::from("TwO"));
|
||||||
|
assert_eq!(TestType::Default, TestType::from("tw0"));
|
||||||
|
assert_eq!(TestType::Default, TestType::default());
|
||||||
|
assert_eq!("three", &TestType::Three.to_string());
|
||||||
|
assert_eq!("one", &TestType::One.to_string());
|
||||||
|
assert_eq!(
|
||||||
|
&[
|
||||||
|
TestType::Default,
|
||||||
|
TestType::One,
|
||||||
|
TestType::Two,
|
||||||
|
TestType::Three
|
||||||
|
],
|
||||||
|
TestType::variants()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::style::Colors;
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use systeroid_core::config::CONFIG_ENV;
|
||||||
use systeroid_core::sysctl::section::Section;
|
use systeroid_core::sysctl::section::Section;
|
||||||
use systeroid_core::sysctl::KERNEL_DOCS_ENV;
|
use systeroid_core::sysctl::KERNEL_DOCS_ENV;
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ For more details see {bin}(8)."#;
|
||||||
/// Command-line arguments.
|
/// Command-line arguments.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
/// Location of the configuration file.
|
||||||
|
pub config: Option<PathBuf>,
|
||||||
/// Refresh rate of the terminal.
|
/// Refresh rate of the terminal.
|
||||||
pub tick_rate: u64,
|
pub tick_rate: u64,
|
||||||
/// Path of the Linux kernel documentation.
|
/// Path of the Linux kernel documentation.
|
||||||
|
@ -26,8 +28,10 @@ pub struct Args {
|
||||||
pub section: Option<Section>,
|
pub section: Option<Section>,
|
||||||
/// Query to search on startup.
|
/// Query to search on startup.
|
||||||
pub search_query: Option<String>,
|
pub search_query: Option<String>,
|
||||||
/// Background/foreground colors.
|
/// Foreground color.
|
||||||
pub colors: Colors,
|
pub fg_color: String,
|
||||||
|
/// Background color.
|
||||||
|
pub bg_color: String,
|
||||||
/// Do not parse/show Linux kernel documentation.
|
/// Do not parse/show Linux kernel documentation.
|
||||||
pub no_docs: bool,
|
pub no_docs: bool,
|
||||||
/// Whether if the deprecated variables should be included while listing.
|
/// Whether if the deprecated variables should be included while listing.
|
||||||
|
@ -70,6 +74,12 @@ impl Args {
|
||||||
"deprecated",
|
"deprecated",
|
||||||
"include deprecated variables while listing",
|
"include deprecated variables while listing",
|
||||||
);
|
);
|
||||||
|
opts.optopt(
|
||||||
|
"c",
|
||||||
|
"config",
|
||||||
|
"set the path of the configuration file",
|
||||||
|
"<path>",
|
||||||
|
);
|
||||||
opts.optflag("h", "help", "display this help and exit");
|
opts.optflag("h", "help", "display this help and exit");
|
||||||
opts.optflag("V", "version", "output version information and exit");
|
opts.optflag("V", "version", "output version information and exit");
|
||||||
opts
|
opts
|
||||||
|
@ -106,14 +116,18 @@ impl Args {
|
||||||
.map(PathBuf::from),
|
.map(PathBuf::from),
|
||||||
section: matches.opt_str("s").map(Section::from),
|
section: matches.opt_str("s").map(Section::from),
|
||||||
search_query: matches.opt_str("q"),
|
search_query: matches.opt_str("q"),
|
||||||
colors: Colors::new(
|
fg_color: matches
|
||||||
matches.opt_str("bg-color").as_deref().unwrap_or("black"),
|
.opt_str("fg-color")
|
||||||
matches.opt_str("fg-color").as_deref().unwrap_or("white"),
|
.unwrap_or_else(|| String::from("white")),
|
||||||
)
|
bg_color: matches
|
||||||
.map_err(|e| eprintln!("error: `{}`", e))
|
.opt_str("bg-color")
|
||||||
.ok()?,
|
.unwrap_or_else(|| String::from("black")),
|
||||||
no_docs: matches.opt_present("n"),
|
no_docs: matches.opt_present("n"),
|
||||||
display_deprecated: matches.opt_present("deprecated"),
|
display_deprecated: matches.opt_present("deprecated"),
|
||||||
|
config: matches
|
||||||
|
.opt_str("c")
|
||||||
|
.or_else(|| env::var(CONFIG_ENV).ok())
|
||||||
|
.map(PathBuf::from),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use crate::args::Args;
|
||||||
use crate::command::Command;
|
use crate::command::Command;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::event::{Event, EventHandler};
|
use crate::event::{Event, EventHandler};
|
||||||
|
use crate::style::Colors;
|
||||||
use systeroid_core::cache::Cache;
|
use systeroid_core::cache::Cache;
|
||||||
use systeroid_core::config::Config;
|
use systeroid_core::config::Config;
|
||||||
use systeroid_core::sysctl::controller::Sysctl;
|
use systeroid_core::sysctl::controller::Sysctl;
|
||||||
|
@ -34,18 +35,25 @@ use tui::terminal::Terminal;
|
||||||
|
|
||||||
/// Runs `systeroid-tui`.
|
/// Runs `systeroid-tui`.
|
||||||
pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
|
pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
|
||||||
let config = Config {
|
let mut config = Config {
|
||||||
display_deprecated: args.display_deprecated,
|
display_deprecated: args.display_deprecated,
|
||||||
|
kernel_docs: args.kernel_docs,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
config.tui.tick_rate = args.tick_rate;
|
||||||
|
config.tui.no_docs = args.no_docs;
|
||||||
|
config.tui.color.fg_color = args.fg_color;
|
||||||
|
config.tui.color.bg_color = args.bg_color;
|
||||||
|
config.parse(args.config)?;
|
||||||
|
let colors = Colors::new(&config.tui.color.bg_color, &config.tui.color.fg_color)?;
|
||||||
let mut sysctl = Sysctl::init(config)?;
|
let mut sysctl = Sysctl::init(config)?;
|
||||||
if !args.no_docs {
|
if !sysctl.config.tui.no_docs {
|
||||||
sysctl.update_docs_from_cache(args.kernel_docs.as_ref(), &Cache::init()?)?;
|
sysctl.update_docs_from_cache(&Cache::init()?)?;
|
||||||
}
|
}
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
terminal.hide_cursor()?;
|
terminal.hide_cursor()?;
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
let event_handler = EventHandler::new(args.tick_rate);
|
let event_handler = EventHandler::new(sysctl.config.tui.tick_rate);
|
||||||
let mut app = App::new(&mut sysctl);
|
let mut app = App::new(&mut sysctl);
|
||||||
if let Some(section) = args.section {
|
if let Some(section) = args.section {
|
||||||
app.section_list.state.select(Some(
|
app.section_list.state.select(Some(
|
||||||
|
@ -63,7 +71,7 @@ pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
|
||||||
app.input = None;
|
app.input = None;
|
||||||
}
|
}
|
||||||
while app.running {
|
while app.running {
|
||||||
terminal.draw(|frame| ui::render(frame, &mut app, &args.colors))?;
|
terminal.draw(|frame| ui::render(frame, &mut app, &colors))?;
|
||||||
match event_handler.next()? {
|
match event_handler.next()? {
|
||||||
Event::KeyPress(key) => {
|
Event::KeyPress(key) => {
|
||||||
let command = Command::parse(key, app.is_input_mode());
|
let command = Command::parse(key, app.is_input_mode());
|
||||||
|
@ -91,6 +99,8 @@ mod tests {
|
||||||
fn test_systeroid_tui() -> Result<()> {
|
fn test_systeroid_tui() -> Result<()> {
|
||||||
let args = Args {
|
let args = Args {
|
||||||
tick_rate: 1000,
|
tick_rate: 1000,
|
||||||
|
fg_color: String::from("white"),
|
||||||
|
bg_color: String::from("black"),
|
||||||
..Args::default()
|
..Args::default()
|
||||||
};
|
};
|
||||||
let backend = TestBackend::new(40, 10);
|
let backend = TestBackend::new(40, 10);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::output::OutputType;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -9,6 +8,7 @@ use systeroid_core::parseit::reader;
|
||||||
use systeroid_core::parseit::regex::Regex;
|
use systeroid_core::parseit::regex::Regex;
|
||||||
use systeroid_core::sysctl::controller::Sysctl;
|
use systeroid_core::sysctl::controller::Sysctl;
|
||||||
use systeroid_core::sysctl::parameter::Parameter;
|
use systeroid_core::sysctl::parameter::Parameter;
|
||||||
|
use systeroid_core::sysctl::r#type::OutputType;
|
||||||
use systeroid_core::sysctl::{DEPRECATED_PARAMS, SYSTEM_PRELOAD};
|
use systeroid_core::sysctl::{DEPRECATED_PARAMS, SYSTEM_PRELOAD};
|
||||||
use systeroid_core::tree::{Tree, TreeNode};
|
use systeroid_core::tree::{Tree, TreeNode};
|
||||||
|
|
||||||
|
@ -19,18 +19,12 @@ pub struct App<'a, Output: Write> {
|
||||||
sysctl: &'a mut Sysctl,
|
sysctl: &'a mut Sysctl,
|
||||||
/// Standard output.
|
/// Standard output.
|
||||||
output: &'a mut Output,
|
output: &'a mut Output,
|
||||||
/// Output type.
|
|
||||||
output_type: OutputType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Output: Write> App<'a, Output> {
|
impl<'a, Output: Write> App<'a, Output> {
|
||||||
/// Constructs a new instance.
|
/// Constructs a new instance.
|
||||||
pub fn new(sysctl: &'a mut Sysctl, output: &'a mut Output, output_type: OutputType) -> Self {
|
pub fn new(sysctl: &'a mut Sysctl, output: &'a mut Output) -> Self {
|
||||||
Self {
|
Self { sysctl, output }
|
||||||
sysctl,
|
|
||||||
output,
|
|
||||||
output_type,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints the given parameters to stdout.
|
/// Prints the given parameters to stdout.
|
||||||
|
@ -38,7 +32,7 @@ impl<'a, Output: Write> App<'a, Output> {
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'b Parameter>,
|
I: Iterator<Item = &'b Parameter>,
|
||||||
{
|
{
|
||||||
match self.output_type {
|
match self.sysctl.config.cli.output_type {
|
||||||
OutputType::Default => {
|
OutputType::Default => {
|
||||||
parameters.try_for_each(|parameter| {
|
parameters.try_for_each(|parameter| {
|
||||||
parameter.display_value(&self.sysctl.config, self.output)
|
parameter.display_value(&self.sysctl.config, self.output)
|
||||||
|
@ -54,7 +48,8 @@ impl<'a, Output: Write> App<'a, Output> {
|
||||||
.map(|v| v.as_ref()),
|
.map(|v| v.as_ref()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Tree::new(root_node.childs).print(self.output, self.sysctl.config.default_color)?;
|
Tree::new(root_node.childs)
|
||||||
|
.print(self.output, self.sysctl.config.cli.color.default_color)?;
|
||||||
}
|
}
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
Parameter::display_bulk_json(parameters.collect(), self.output)?;
|
Parameter::display_bulk_json(parameters.collect(), self.output)?;
|
||||||
|
@ -81,7 +76,7 @@ impl<'a, Output: Write> App<'a, Output> {
|
||||||
|
|
||||||
/// Displays the documentation of a parameter.
|
/// Displays the documentation of a parameter.
|
||||||
pub fn display_documentation(&mut self, param_name: &str) -> Result<()> {
|
pub fn display_documentation(&mut self, param_name: &str) -> Result<()> {
|
||||||
let no_pager = self.sysctl.config.no_pager;
|
let no_pager = self.sysctl.config.cli.no_pager;
|
||||||
for parameter in self.sysctl.get_parameters(param_name) {
|
for parameter in self.sysctl.get_parameters(param_name) {
|
||||||
let mut fallback_to_default = false;
|
let mut fallback_to_default = false;
|
||||||
if no_pager {
|
if no_pager {
|
||||||
|
@ -231,13 +226,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_app() -> Result<()> {
|
fn test_app() -> Result<()> {
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
let mut sysctl = Sysctl::init(Config {
|
let mut config = Config::default();
|
||||||
no_pager: true,
|
config.cli.no_pager = true;
|
||||||
..Config::default()
|
let mut sysctl = Sysctl::init(config)?;
|
||||||
})?;
|
sysctl.update_docs_from_cache(&Cache::init()?)?;
|
||||||
sysctl.update_docs_from_cache(None, &Cache::init()?)?;
|
|
||||||
|
|
||||||
let mut app = App::new(&mut sysctl, &mut output, OutputType::Default);
|
let mut app = App::new(&mut sysctl, &mut output);
|
||||||
|
|
||||||
app.display_parameters(Regex::new("kernel|vm").ok(), false)?;
|
app.display_parameters(Regex::new("kernel|vm").ok(), false)?;
|
||||||
let result = String::from_utf8_lossy(app.output);
|
let result = String::from_utf8_lossy(app.output);
|
||||||
|
@ -245,7 +239,7 @@ mod tests {
|
||||||
assert!(result.contains("kernel.version ="));
|
assert!(result.contains("kernel.version ="));
|
||||||
app.output.clear();
|
app.output.clear();
|
||||||
|
|
||||||
app.output_type = OutputType::Tree;
|
app.sysctl.config.cli.output_type = OutputType::Tree;
|
||||||
app.display_parameters(None, false)?;
|
app.display_parameters(None, false)?;
|
||||||
assert!(String::from_utf8_lossy(app.output).contains("─ osrelease ="));
|
assert!(String::from_utf8_lossy(app.output).contains("─ osrelease ="));
|
||||||
app.output.clear();
|
app.output.clear();
|
||||||
|
@ -255,7 +249,7 @@ mod tests {
|
||||||
app.output.clear();
|
app.output.clear();
|
||||||
|
|
||||||
let param_name = String::from("kernel.version");
|
let param_name = String::from("kernel.version");
|
||||||
app.output_type = OutputType::Default;
|
app.sysctl.config.cli.output_type = OutputType::Default;
|
||||||
app.process_parameter(param_name.clone(), true, false)?;
|
app.process_parameter(param_name.clone(), true, false)?;
|
||||||
let result = String::from_utf8_lossy(app.output);
|
let result = String::from_utf8_lossy(app.output);
|
||||||
assert_eq!(1, result.lines().count());
|
assert_eq!(1, result.lines().count());
|
||||||
|
@ -263,7 +257,7 @@ mod tests {
|
||||||
app.output.clear();
|
app.output.clear();
|
||||||
|
|
||||||
let param_name = String::from("kernel.version");
|
let param_name = String::from("kernel.version");
|
||||||
app.output_type = OutputType::Json;
|
app.sysctl.config.cli.output_type = OutputType::Json;
|
||||||
app.process_parameter(param_name.clone(), true, false)?;
|
app.process_parameter(param_name.clone(), true, false)?;
|
||||||
let result = String::from_utf8_lossy(app.output);
|
let result = String::from_utf8_lossy(app.output);
|
||||||
assert!(result.contains("\"section\":\"kernel\""));
|
assert!(result.contains("\"section\":\"kernel\""));
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::output::OutputType;
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use systeroid_core::config::CONFIG_ENV;
|
||||||
use systeroid_core::parseit::regex::Regex;
|
use systeroid_core::parseit::regex::Regex;
|
||||||
use systeroid_core::sysctl::display::DisplayType;
|
use systeroid_core::sysctl::r#type::DisplayType;
|
||||||
|
use systeroid_core::sysctl::r#type::OutputType;
|
||||||
use systeroid_core::sysctl::{DEFAULT_PRELOAD, KERNEL_DOCS_ENV};
|
use systeroid_core::sysctl::{DEFAULT_PRELOAD, KERNEL_DOCS_ENV};
|
||||||
|
|
||||||
/// Help message for the arguments.
|
/// Help message for the arguments.
|
||||||
|
@ -19,6 +20,8 @@ For more details see {bin}(8)."#;
|
||||||
/// Command-line arguments.
|
/// Command-line arguments.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
/// Location of the configuration file.
|
||||||
|
pub config: Option<PathBuf>,
|
||||||
/// Whether if the verbose logging is enabled.
|
/// Whether if the verbose logging is enabled.
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
/// Whether if the quiet mode is enabled.
|
/// Whether if the quiet mode is enabled.
|
||||||
|
@ -97,6 +100,12 @@ impl Args {
|
||||||
opts.optflag("P", "no-pager", "do not pipe output into a pager");
|
opts.optflag("P", "no-pager", "do not pipe output into a pager");
|
||||||
opts.optflag("v", "verbose", "enable verbose logging");
|
opts.optflag("v", "verbose", "enable verbose logging");
|
||||||
opts.optflag("", "tui", "show terminal user interface");
|
opts.optflag("", "tui", "show terminal user interface");
|
||||||
|
opts.optopt(
|
||||||
|
"c",
|
||||||
|
"config",
|
||||||
|
"set the path of the configuration file",
|
||||||
|
"<path>",
|
||||||
|
);
|
||||||
opts.optflag("h", "help", "display this help and exit (-d)");
|
opts.optflag("h", "help", "display this help and exit (-d)");
|
||||||
opts.optflag("V", "version", "output version information and exit");
|
opts.optflag("V", "version", "output version information and exit");
|
||||||
opts
|
opts
|
||||||
|
@ -195,6 +204,10 @@ impl Args {
|
||||||
explain: matches.opt_present("E"),
|
explain: matches.opt_present("E"),
|
||||||
output_type,
|
output_type,
|
||||||
show_tui: matches.opt_present("tui"),
|
show_tui: matches.opt_present("tui"),
|
||||||
|
config: matches
|
||||||
|
.opt_str("c")
|
||||||
|
.or_else(|| env::var(CONFIG_ENV).ok())
|
||||||
|
.map(PathBuf::from),
|
||||||
values: matches.free,
|
values: matches.free,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
pub mod app;
|
pub mod app;
|
||||||
/// Command-line argument parser.
|
/// Command-line argument parser.
|
||||||
pub mod args;
|
pub mod args;
|
||||||
/// Application output types.
|
|
||||||
pub mod output;
|
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
|
@ -20,20 +18,23 @@ use systeroid_core::sysctl::controller::Sysctl;
|
||||||
|
|
||||||
/// Runs `systeroid`.
|
/// Runs `systeroid`.
|
||||||
pub fn run<Output: Write>(args: Args, output: &mut Output) -> Result<()> {
|
pub fn run<Output: Write>(args: Args, output: &mut Output) -> Result<()> {
|
||||||
let config = Config {
|
let mut config = Config {
|
||||||
verbose: args.verbose,
|
|
||||||
ignore_errors: args.ignore_errors,
|
|
||||||
display_deprecated: args.display_deprecated,
|
display_deprecated: args.display_deprecated,
|
||||||
quiet: args.quiet,
|
kernel_docs: args.kernel_docs,
|
||||||
no_pager: args.no_pager,
|
|
||||||
display_type: args.display_type,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
config.cli.verbose = args.verbose;
|
||||||
|
config.cli.ignore_errors = args.ignore_errors;
|
||||||
|
config.cli.quiet = args.quiet;
|
||||||
|
config.cli.no_pager = args.no_pager;
|
||||||
|
config.cli.display_type = args.display_type;
|
||||||
|
config.cli.output_type = args.output_type;
|
||||||
|
config.parse(args.config)?;
|
||||||
let mut sysctl = Sysctl::init(config)?;
|
let mut sysctl = Sysctl::init(config)?;
|
||||||
if args.explain {
|
if args.explain {
|
||||||
sysctl.update_docs_from_cache(args.kernel_docs.as_ref(), &Cache::init()?)?;
|
sysctl.update_docs_from_cache(&Cache::init()?)?;
|
||||||
}
|
}
|
||||||
let mut app = App::new(&mut sysctl, output, args.output_type);
|
let mut app = App::new(&mut sysctl, output);
|
||||||
|
|
||||||
if args.preload_system_files {
|
if args.preload_system_files {
|
||||||
app.preload_from_system()?;
|
app.preload_from_system()?;
|
||||||
|
|
|
@ -8,6 +8,9 @@ fn main() {
|
||||||
if args.show_tui {
|
if args.show_tui {
|
||||||
let bin = format!("{}-tui", env!("CARGO_PKG_NAME"));
|
let bin = format!("{}-tui", env!("CARGO_PKG_NAME"));
|
||||||
let mut command = Command::new(&bin);
|
let mut command = Command::new(&bin);
|
||||||
|
if let Some(config) = args.config {
|
||||||
|
command.arg("--config").arg(config);
|
||||||
|
}
|
||||||
if let Some(kernel_docs) = args.kernel_docs {
|
if let Some(kernel_docs) = args.kernel_docs {
|
||||||
command.arg("--docs").arg(kernel_docs);
|
command.arg("--docs").arg(kernel_docs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
/// Possible output types for the [`App`].
|
|
||||||
///
|
|
||||||
/// [`App`]: crate::app::App
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum OutputType {
|
|
||||||
/// Print the output as is.
|
|
||||||
Default,
|
|
||||||
/// Print the output in a tree-like format.
|
|
||||||
Tree,
|
|
||||||
/// Print the output in JSON format.
|
|
||||||
Json,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OutputType {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Default
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
use {
|
use systeroid::args::Args;
|
||||||
systeroid::args::Args, systeroid_core::error::Result,
|
use systeroid_core::error::Result;
|
||||||
systeroid_core::sysctl::display::DisplayType,
|
use systeroid_core::sysctl::r#type::DisplayType;
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "live-tests"), ignore)]
|
#[cfg_attr(not(feature = "live-tests"), ignore)]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user