Skip to content

Commit

Permalink
Merge pull request #201 from solidiquis/multi-config
Browse files Browse the repository at this point in the history
Support multiple configs with `.erdtree.toml`
  • Loading branch information
solidiquis authored Jun 26, 2023

Unverified

No user is associated with the committer email.
2 parents ebf2dfc + 6cc476a commit c42784f
Showing 17 changed files with 905 additions and 206 deletions.
303 changes: 303 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ documentation = "https://github.com/solidiquis/erdtree"
homepage = "https://github.com/solidiquis/erdtree"
repository = "https://github.com/solidiquis/erdtree"
keywords = ["tree", "find", "ls", "du", "commandline"]
exclude = ["assets/*", "scripts/*"]
exclude = ["assets/*", "scripts/*", "example/*"]
readme = "README.md"
license = "MIT"
rust-version = "1.70.0"
@@ -28,6 +28,7 @@ ansi_term = "0.12.1"
chrono = "0.4.24"
clap = { version = "4.1.1", features = ["derive"] }
clap_complete = "4.1.1"
config = { version = "0.13.3", features = ["toml"] }
crossterm = "0.26.1"
dirs = "5.0"
errno = "0.3.1"
97 changes: 81 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ You can think of `erdtree` as a little bit of `du`, `tree`, `find`, `wc` and `ls
* [Installation](#installation)
* [Documentation](#documentation)
- [Configuration file](#configuration-file)
- [Toml file](#toml-file)
- [.erdtreerc](#erdtreerc)
- [Hardlinks](#hardlinks)
- [Symlinks](#symlinks)
- [Disk usage](#disk-usage)
@@ -316,6 +318,82 @@ Other means of installation to come.

If `erdtree`'s out-of-the-box defaults don't meet your specific requirements, you can set your own defaults using a configuration file.

The configuration file currently comes in two flavors: `.erdtreerc` (to be deprecated) and `.erdtree.toml`. If you have both,
`.erdtreerc` will take precedent and `.erdtree.toml` will be disregarded, but please **note that `.erdtreerc` will be deprecated in the near future.** There is
no reason to have both.

#### TOML file

`erdtree` will look for `.erdtree.toml in any of the following locations:

On Unix-systems:

```
$ERDTREE_TOML_PATH
$XDG_CONFIG_HOME/erdtree/.erdtree.toml
$XDG_CONFIG_HOME/.erdtree.toml
$HOME/.config/erdtree/.erdtree.toml
$HOME/.erdtree.toml
```

On Windows:

```
%APPDATA%\erdtree\.erdtree.toml
```

[Here](example/.erdtree.toml) and below is an example of a valid `.erdtree.toml`:

```toml
icons = true
human = true

# Compute file sizes like `du`
# e.g. `erd --config du`
[du]
disk_usage = "block"
icons = true
layout = "flat"
no-ignore = true
no-git = true
hidden = true
level = 1

# Do as `ls -l`
# e.g. `erd --config ls`
[ls]
icons = true
human = true
level = 1
suppress-size = true
long = true

# How many lines of Rust are in this code base?
# e.g. `erd --config rs`
[rs]
disk-usage = "word"
level = 1
pattern = "\\.rs$"
```

`.erdtree.toml` supports multiple configurations. The top-level table is the main config that will be applied without additional arguments.
If you wish to use a separate configuration, create a named table like `du` above, set your arguments, and invoke it like so:

```
$ erd --config du
# equivalent to
$ erd --disk-usage block --icons --layout flat --no-ignore --no-git --hidden --level 1
```

As far as the arguments go there are only three rules you need to be aware of:
1. `.erdtree.toml` only accepts long-named arguments without the preceding "--".
2. Types are enforced, so numbers are expected to be numbers, booleans are expected to be booleans, strings are expected to be strings, and so on and so forth.
3. `snake_case` and `kebap-case` works.

#### .erdtreerc

`erdtree` will look for a configuration file in any of the following locations:

On Linux/Mac/Unix-like:
@@ -333,24 +411,11 @@ The format of a config file is as follows:
- Every line is an `erdtree` option/argument.
- Lines starting with `#` are considered comments and are thus ignored.

Arguments passed to `erdtree` take precedence. If you have a config that you would like to ignore without deleting you can use `--no-config`.
Arguments passed to `erdtree` on the command-line will override those found in `.erdtreerc`.

Here is an example of a valid configuration file:

```
# Long argument
--icons
--human
[Click here](example/.erdtreerc) for an example `.erdtreerc`.

# or short argument
-l
# args can be passed like this
-d logical
# or like this
--unit=si
```
**If you have a config that you would like to ignore without deleting you can use `--no-config`.**

### Hardlinks

26 changes: 26 additions & 0 deletions example/.erdtree.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
icons = true
human = true

# Compute file sizes like `du`
[du]
disk_usage = "block"
icons = true
layout = "flat"
no-ignore = true
no-git = true
hidden = true
level = 1

# Do as `ls -l`
[ls]
icons = true
human = true
level = 1
suppress-size = true
long = true

# How many lines of Rust are in this code base?
[rs]
disk-usage = "word"
level = 1
pattern = "\\.rs$"
9 changes: 9 additions & 0 deletions example/.erdtreerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Long argument
--icons
--human

# or short argument
-l

# args can be passed like this
-d logical
22 changes: 22 additions & 0 deletions src/context/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const ERDTREE_CONFIG_TOML: &str = ".erdtree.toml";
const ERDTREE_TOML_PATH: &str = "ERDTREE_TOML_PATH";

const ERDTREE_CONFIG_NAME: &str = ".erdtreerc";
const ERDTREE_CONFIG_PATH: &str = "ERDTREE_CONFIG_PATH";

const ERDTREE_DIR: &str = "erdtree";

#[cfg(unix)]
const CONFIG_DIR: &str = ".config";

#[cfg(unix)]
const HOME: &str = "HOME";

#[cfg(unix)]
const XDG_CONFIG_HOME: &str = "XDG_CONFIG_HOME";

/// Concerned with loading `.erdtreerc`.
pub mod rc;

/// Concerned with loading `.erdtree.toml`.
pub mod toml;
58 changes: 20 additions & 38 deletions src/context/config.rs → src/context/config/rc.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
use std::{
env, fs,
path::{Path, PathBuf},
};

const ERDTREE_CONFIG_NAME: &str = ".erdtreerc";
const ERDTREE_CONFIG_PATH: &str = "ERDTREE_CONFIG_PATH";
const ERDTREE_DIR: &str = "erdtree";

#[cfg(unix)]
const CONFIG_DIR: &str = ".config";

#[cfg(unix)]
const HOME: &str = "HOME";

#[cfg(unix)]
const XDG_CONFIG_HOME: &str = "XDG_CONFIG_HOME";
use std::{env, fs, path::PathBuf};

/// Reads the config file into a `String` if there is one. When `None` is provided then the config
/// is looked for in the following locations in order:
@@ -25,25 +9,19 @@ const XDG_CONFIG_HOME: &str = "XDG_CONFIG_HOME";
/// - `$HOME/.config/erdtree/.erdtreerc`
/// - `$HOME/.erdtreerc`
#[cfg(unix)]
pub fn read_config_to_string<T: AsRef<Path>>(path: Option<T>) -> Option<String> {
path.map(fs::read_to_string)
.and_then(Result::ok)
.or_else(config_from_config_path)
pub fn read_config_to_string() -> Option<String> {
config_from_config_path()
.or_else(config_from_xdg_path)
.or_else(config_from_home)
.map(|e| prepend_arg_prefix(&e))
}

/// Reads the config file into a `String` if there is one. When `None` is provided then the config
/// is looked for in the following locations in order (Windows specific):
///
/// - `$ERDTREE_CONFIG_PATH`
/// - `%APPDATA%/erdtree/.erdtreerc`
#[cfg(windows)]
pub fn read_config_to_string<T: AsRef<Path>>(path: Option<T>) -> Option<String> {
path.map(fs::read_to_string)
.and_then(Result::ok)
.or_else(config_from_config_path)
pub fn read_config_to_string() -> Option<String> {
config_from_config_path()
.or_else(config_from_appdata)
.map(|e| prepend_arg_prefix(&e))
}
@@ -61,13 +39,13 @@ pub fn parse<'a>(config: &'a str) -> Vec<&'a str> {
.next()
.map_or(true, |ch| ch != '#')
})
.flat_map(str::split_ascii_whitespace)
.flat_map(str::split_whitespace)
.collect::<Vec<&'a str>>()
}

/// Try to read in config from `ERDTREE_CONFIG_PATH`.
fn config_from_config_path() -> Option<String> {
env::var_os(ERDTREE_CONFIG_PATH)
env::var_os(super::ERDTREE_CONFIG_PATH)
.map(PathBuf::from)
.map(fs::read_to_string)
.and_then(Result::ok)
@@ -78,15 +56,15 @@ fn config_from_config_path() -> Option<String> {
/// - `$HOME/.erdtreerc`
#[cfg(not(windows))]
fn config_from_home() -> Option<String> {
let home = env::var_os(HOME).map(PathBuf::from)?;
let home = env::var_os(super::HOME).map(PathBuf::from)?;

let config_path = home
.join(CONFIG_DIR)
.join(ERDTREE_DIR)
.join(ERDTREE_CONFIG_NAME);
.join(super::CONFIG_DIR)
.join(super::ERDTREE_DIR)
.join(super::ERDTREE_CONFIG_NAME);

fs::read_to_string(config_path).ok().or_else(|| {
let config_path = home.join(ERDTREE_CONFIG_NAME);
let config_path = home.join(super::ERDTREE_CONFIG_NAME);
fs::read_to_string(config_path).ok()
})
}
@@ -97,7 +75,9 @@ fn config_from_home() -> Option<String> {
fn config_from_appdata() -> Option<String> {
let app_data = dirs::config_dir()?;

let config_path = app_data.join(ERDTREE_DIR).join(ERDTREE_CONFIG_NAME);
let config_path = app_data
.join(super::ERDTREE_DIR)
.join(super::ERDTREE_CONFIG_NAME);

fs::read_to_string(config_path).ok()
}
@@ -107,12 +87,14 @@ fn config_from_appdata() -> Option<String> {
/// - `$XDG_CONFIG_HOME/.erdtreerc`
#[cfg(unix)]
fn config_from_xdg_path() -> Option<String> {
let xdg_config = env::var_os(XDG_CONFIG_HOME).map(PathBuf::from)?;
let xdg_config = env::var_os(super::XDG_CONFIG_HOME).map(PathBuf::from)?;

let config_path = xdg_config.join(ERDTREE_DIR).join(ERDTREE_CONFIG_NAME);
let config_path = xdg_config
.join(super::ERDTREE_DIR)
.join(super::ERDTREE_CONFIG_NAME);

fs::read_to_string(config_path).ok().or_else(|| {
let config_path = xdg_config.join(ERDTREE_CONFIG_NAME);
let config_path = xdg_config.join(super::ERDTREE_CONFIG_NAME);
fs::read_to_string(config_path).ok()
})
}
19 changes: 19 additions & 0 deletions src/context/config/toml/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use config::ConfigError;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to load .erdtree.toml")]
LoadConfig,

#[error("The configuration file is improperly formatted")]
InvalidFormat(#[from] ConfigError),

#[error("Named table '{0}' was not found in '.erdtree.toml'")]
MissingAltConfig(String),

#[error("'#{0}' is required to be a pointer-sized unsigned integer type")]
InvalidInteger(String),

#[error("'#{0}' has a type that is invalid")]
InvalidArgument(String),
}
Loading

0 comments on commit c42784f

Please sign in to comment.