other: clarify config file creation logic/code (#1518)

This commit is contained in:
Clement Tsang 2024-07-31 05:33:32 +00:00 committed by GitHub
parent 38c6e70c1e
commit 2c0fb2a723
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 28 deletions

View file

@ -12,8 +12,8 @@ If no config file argument is given, it will automatically look for a config fil
| Linux | `~/.config/bottom/bottom.toml` <br/> `$XDG_CONFIG_HOME/bottom/bottom.toml` |
| Windows | `C:\Users\<USER>\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

View file

@ -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("<failed locating>"))
.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)?;

View file

@ -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<PathBuf> {
fn get_config_path(override_config_path: Option<&Path>) -> Option<PathBuf> {
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<PathBuf> {
})
}
/// 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<Config> {
match &config_path {
Some(path) => {
if let Ok(config_string) = fs::read_to_string(path) {
Ok(toml_edit::de::from_str(&config_string)?)
} else {
fn create_config_at_path(path: &Path) -> anyhow::Result<Config> {
if let Some(parent_path) = path.parent() {
fs::create_dir_all(parent_path)?;
}
fs::File::create(path)?.write_all(CONFIG_TEXT.as_bytes())?;
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.
///
/// 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<Config> {
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 {
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()
);
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())
}
}