diff --git a/docs/content/configuration/config-file/index.md b/docs/content/configuration/config-file/index.md
index 8ccf31f4..433d4f3c 100644
--- a/docs/content/configuration/config-file/index.md
+++ b/docs/content/configuration/config-file/index.md
@@ -12,8 +12,8 @@ If no config file argument is given, it will automatically look for a config fil
| Linux | `~/.config/bottom/bottom.toml`
`$XDG_CONFIG_HOME/bottom/bottom.toml` |
| Windows | `C:\Users\\AppData\Roaming\bottom\bottom.toml` |
-Like if a path is passed with `-C`/`--config`, if a file doesn't exist at the path, bottom will automatically create a
-new, default config file at that location.
+If the config file doesn't exist at the path, bottom will automatically try to create a new config file at the location
+with default values.
## JSON Schema
diff --git a/src/main.rs b/src/main.rs
index b6e703ab..c7c979ce 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -27,7 +27,6 @@ use std::{
boxed::Box,
io::{stderr, stdout, Write},
panic::{self, PanicInfo},
- path::Path,
sync::{
mpsc::{self, Receiver, Sender},
Arc, Condvar, Mutex,
@@ -36,7 +35,6 @@ use std::{
time::{Duration, Instant},
};
-use anyhow::Context;
use app::{layout_manager::UsedWidgets, App, AppConfigFields, DataFilters};
use crossterm::{
event::{
@@ -49,7 +47,7 @@ use crossterm::{
};
use data_conversion::*;
use event::{handle_key_event_or_break, handle_mouse_event, BottomEvent, CollectionThreadEvent};
-use options::{args, get_config_path, get_or_create_config, init_app};
+use options::{args, get_or_create_config, init_app};
use tui::{backend::CrosstermBackend, Terminal};
#[allow(unused_imports)]
use utils::logging::*;
@@ -330,18 +328,8 @@ fn main() -> anyhow::Result<()> {
}
// Read from config file.
- let config = {
- let config_path = get_config_path(args.general.config_location.as_deref());
- get_or_create_config(config_path.as_deref()).with_context(|| {
- format!(
- "Unable to parse or create the config file at: {}",
- config_path
- .as_deref()
- .unwrap_or_else(|| Path::new(""))
- .display()
- )
- })?
- };
+
+ let config = get_or_create_config(args.general.config_location.as_deref())?;
// Create the "app" and initialize a bunch of stuff.
let (mut app, widget_layout, styling) = init_app(args, config)?;
diff --git a/src/options.rs b/src/options.rs
index b1500329..b770b347 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -68,7 +68,7 @@ macro_rules! is_flag_enabled {
///
/// For more details on this, see [dirs](https://docs.rs/dirs/latest/dirs/fn.config_dir.html)'
/// documentation.
-pub fn get_config_path(override_config_path: Option<&Path>) -> Option {
+fn get_config_path(override_config_path: Option<&Path>) -> Option {
const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
if let Some(conf_loc) = override_config_path {
@@ -92,29 +92,73 @@ pub fn get_config_path(override_config_path: Option<&Path>) -> Option {
})
}
+fn create_config_at_path(path: &Path) -> anyhow::Result {
+ if let Some(parent_path) = path.parent() {
+ fs::create_dir_all(parent_path)?;
+ }
+
+ let mut file = fs::File::create(path)?;
+ file.write_all(CONFIG_TEXT.as_bytes())?;
+
+ Ok(Config::default())
+}
+
/// Get the config at `config_path`. If there is no config file at the specified
/// path, it will try to create a new file with the default settings, and return
-/// the default config. If bottom fails to write a new config, it will silently
-/// just return the default config.
-pub fn get_or_create_config(config_path: Option<&Path>) -> OptionResult {
- match &config_path {
+/// the default config.
+///
+/// We're going to use the following behaviour on when we'll return an error rather
+/// than just "silently" continuing on:
+/// - If the user passed in a path explicitly, then we will be loud and error out.
+/// - If the user does NOT pass in a path explicitly, then just show a warning,
+/// but continue. This is in case they do not want to write a default config file at
+/// the XDG locations, for example.
+pub fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result {
+ let adjusted_config_path = get_config_path(config_path);
+
+ match &adjusted_config_path {
Some(path) => {
if let Ok(config_string) = fs::read_to_string(path) {
Ok(toml_edit::de::from_str(&config_string)?)
} else {
- if let Some(parent_path) = path.parent() {
- fs::create_dir_all(parent_path)?;
- }
+ match create_config_at_path(path) {
+ Ok(cfg) => Ok(cfg),
+ Err(err) => {
+ if config_path.is_some() {
+ Err(err.context(format!(
+ "bottom could not create a new config file at '{}'.",
+ path.display()
+ )))
+ } else {
+ indoc::eprintdoc!(
+ "Note: bottom couldn't create a default config file at '{}', and the \
+ application has fallen back to the default configuration.
+
+ Caused by:
+ {err}
+ ",
+ path.display()
+ );
- fs::File::create(path)?.write_all(CONFIG_TEXT.as_bytes())?;
- Ok(Config::default())
+ Ok(Config::default())
+ }
+ }
+ }
}
}
None => {
// If we somehow don't have any config path, then just assume the default config
// but don't write to any file.
//
- // TODO: Maybe make this "show" an error, but don't crash.
+ // TODO: For now, just print a message to stderr indicating this. In the future,
+ // probably show in-app (too).
+
+ eprintln!(
+ "Note: bottom couldn't find a location to create or read a config file, so \
+ the application has fallen back to the default configuration. \
+ This could be for a variety of reasons, such as issues with file permissions."
+ );
+
Ok(Config::default())
}
}