Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple configs with .erdtree.toml #201

Merged
merged 8 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
97 changes: 81 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand All @@ -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

Expand Down
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:
Expand All @@ -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))
}
Expand All @@ -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)
Expand All @@ -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()
})
}
Expand All @@ -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()
}
Expand All @@ -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()
})
}
Expand Down
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