Add support for configuration files

With this patch, we load defaults for the command-line arguments from
the configuration file located in the user configuration directory
according to the XDG Base Directory Specification.
This commit is contained in:
Robin Krahl 2020-08-10 23:37:26 +02:00
parent 3a20d662c5
commit e5ee4df73b
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8
6 changed files with 97 additions and 9 deletions

View File

@ -12,6 +12,12 @@ SPDX-License-Identifier: MIT
the rich text viewer.
- Add the `--no-syntax-highlight` option to disable syntax highlighting.
- Add the `--theme [theme]` option to select the syntax highlighting theme.
- Add support for configuration files:
- Load the `config.toml` file from the config directory according to the XDG
Base Directory Specification `${XDG_CONFIG_HOME}/rusty-man/config.toml`,
where `${XDG_CONFIG_HOME}` defaults to `${HOME}/.config`. The
configuration file can be used to set defaults for the command-line
options.
## v0.1.3 (2020-07-28)

17
Cargo.lock generated
View File

@ -726,6 +726,8 @@ dependencies = [
"serde_tuple 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syntect 4.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -998,6 +1000,14 @@ dependencies = [
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
@ -1065,6 +1075,11 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "xdg"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "xml-rs"
version = "0.8.3"
@ -1197,6 +1212,7 @@ dependencies = [
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
@ -1208,5 +1224,6 @@ dependencies = [
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
"checksum xml-rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
"checksum xml5ever 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"

View File

@ -26,6 +26,8 @@ pager = "0.15.0"
serde_json = "1.0.56"
serde_repr = "0.1.6"
serde_tuple = "0.5.0"
toml = "0.5.6"
xdg = "2.2.0"
[dependencies.env_logger]
version = "0.7.1"

View File

@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
use std::fs;
use serde::Deserialize;
use structopt::StructOpt;
use crate::doc;
@ -18,9 +21,11 @@ use crate::viewer;
/// rusty-man tries to find an item that exactly matches the given keyword. If it doesnt find an
/// exact match, it reads the search indexes of all available sources and tries to find a partial
/// match.
#[derive(Debug, StructOpt)]
#[derive(Debug, Default, Deserialize, StructOpt)]
#[serde(default)]
pub struct Args {
/// The keyword to open the documentation for, e. g. `rand_core::RngCore`
#[serde(skip)]
pub keyword: doc::Name,
/// The sources to check for documentation generated by rustdoc
@ -32,6 +37,7 @@ pub struct Args {
/// The viewer for the rustdoc documentation (one of: plain, rich)
#[structopt(long, parse(try_from_str = viewer::get_viewer))]
#[serde(deserialize_with = "deserialize_viewer")]
pub viewer: Option<Box<dyn viewer::Viewer>>,
/// Do not search the default documentation sources
@ -55,10 +61,12 @@ pub struct Args {
pub examples: bool,
#[structopt(flatten)]
#[serde(flatten)]
pub viewer_args: ViewerArgs,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Default, Deserialize, StructOpt)]
#[serde(default)]
pub struct ViewerArgs {
/// Disable syntax highlighting.
///
@ -71,12 +79,66 @@ pub struct ViewerArgs {
///
/// rusty-man includes these color themes: base16-ocean.dark, base16-eighties.dark,
/// base16-mocha.dark, base16-ocean.light, InspiredGitHub, Solarized (dark), Solarized (light).
#[structopt(long, default_value = "base16-eighties.dark")]
pub theme: String,
/// Default value: base16-eighties.dark.
#[structopt(long)]
pub theme: Option<String>,
}
impl Args {
pub fn load() -> Args {
Args::from_args()
pub fn load() -> anyhow::Result<Args> {
let mut args = Args::from_args();
if let Some(config) = Args::load_config()? {
args.merge(config);
}
Ok(args)
}
fn load_config() -> anyhow::Result<Option<Args>> {
let dirs = xdg::BaseDirectories::with_prefix("rusty-man")?;
if let Some(path) = dirs.find_config_file("config.toml") {
log::info!("Loading configuration file '{}'", path.display());
let s = fs::read_to_string(path)?;
toml::from_str(&s).map(Some).map_err(From::from)
} else {
Ok(None)
}
}
fn merge(&mut self, mut args: Args) {
if !args.source_paths.is_empty() {
args.source_paths.append(&mut self.source_paths);
self.source_paths = args.source_paths;
}
if self.viewer.is_none() {
self.viewer = args.viewer;
}
if !self.no_default_sources {
self.no_default_sources = args.no_default_sources;
}
if !self.no_search {
self.no_search = args.no_search;
}
if !self.examples {
self.examples = args.examples;
}
if !self.viewer_args.no_syntax_highlight {
self.viewer_args.no_syntax_highlight = args.viewer_args.no_syntax_highlight;
}
if self.viewer_args.theme.is_none() {
self.viewer_args.theme = args.viewer_args.theme;
}
}
}
fn deserialize_viewer<'de, D>(d: D) -> Result<Option<Box<dyn viewer::Viewer>>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let s: Option<&str> = Deserialize::deserialize(d)?;
s.map(|s| viewer::get_viewer(s).map_err(D::Error::custom))
.transpose()
}

View File

@ -48,7 +48,7 @@ use std::path;
fn main() -> anyhow::Result<()> {
env_logger::init();
let args = args::Args::load();
let args = args::Args::load()?;
let sources = load_sources(&args.source_paths, !args.no_default_sources)?;
let doc = if let Some(doc) = find_doc(&sources, &args.keyword)? {
Some(doc)

View File

@ -59,10 +59,11 @@ impl super::Printer for RichTextRenderer {
use anyhow::Context;
let mut theme_set = syntect::highlighting::ThemeSet::load_defaults();
let theme_name = args.theme.as_deref().unwrap_or("base16-eighties.dark");
let theme = theme_set
.themes
.remove(&args.theme)
.with_context(|| format!("Could not find theme {}", &args.theme))?;
.remove(theme_name)
.with_context(|| format!("Could not find theme {}", theme_name))?;
Ok(Self {
line_length: viewer::get_line_length(),
highlight: !args.no_syntax_highlight,