Skip to content

Commit

Permalink
More bacon config places (#270)
Browse files Browse the repository at this point in the history
* read bacon.toml in `workspace/.config/` and `package/.config`.
* read config embedded in Cargo.toml

Fix #268 
Fix #241
  • Loading branch information
Canop authored Dec 2, 2024
1 parent a4cc2c7 commit dbb6f70
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn run() -> anyhow::Result<()> {
}

let context = Context::new(&args)?;
info!("mission context: {:#?}", &context);
debug!("mission context: {:#?}", &context);

if args.init {
let package_config_path = context.package_config_path();
Expand Down
46 changes: 46 additions & 0 deletions src/conf/cargo_wrapped_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use {
super::Config,
anyhow::*,
serde::Deserialize,
std::path::Path,
};

#[derive(Deserialize)]
struct CargoWrappedConfig {
workspace: Option<WrappedMetadata>,
package: Option<WrappedMetadata>,
}
#[derive(Deserialize)]
struct WrappedMetadata {
metadata: Option<WrappedConfig>,
}
#[derive(Deserialize)]
struct WrappedConfig {
bacon: Option<Config>,
}

pub fn load_config_from_cargo_toml(cargo_file_path: &Path) -> Result<Vec<Config>> {
if !cargo_file_path.exists() {
return Ok(Vec::default());
}
let cargo_toml = std::fs::read_to_string(cargo_file_path)?;
let mut cargo: CargoWrappedConfig = toml::from_str(&cargo_toml)?;
let mut configs = Vec::new();
let worskpace_config = cargo
.workspace
.take()
.and_then(|workspace| workspace.metadata)
.and_then(|metadata| metadata.bacon);
if let Some(config) = worskpace_config {
configs.push(config);
}
let worskpace_config = cargo
.package
.take()
.and_then(|package| package.metadata)
.and_then(|metadata| metadata.bacon);
if let Some(config) = worskpace_config {
configs.push(config);
}
Ok(configs)
}
17 changes: 17 additions & 0 deletions src/conf/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ pub struct Config {
}

impl Config {
/// Load from zero to two configuration items from the provided path which
/// must be in TOML format but may not exist.
///
/// Expected structures are either bacon config or a cargo.toml file (which
/// may contain a workspace.metadata.bacon key and a package.metadata.bacon key).
pub fn from_path_detect(path: &Path) -> Result<Vec<Self>> {
if !path.exists() {
return Ok(Vec::default());
}
let file_name = path.file_name().and_then(|f| f.to_str());
if file_name == Some("Cargo.toml") {
load_config_from_cargo_toml(path)
} else {
Ok(vec![Self::from_path(path)?])
}
}
/// Load a configuration item filling the provided path in TOML
pub fn from_path(path: &Path) -> Result<Self> {
let conf = toml::from_str::<Self>(&fs::read_to_string(path)?)
.with_context(|| format!("Failed to parse configuration file at {:?}", path))?;
Expand Down
2 changes: 2 additions & 0 deletions src/conf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod action;
mod args;
mod cargo_wrapped_config;
mod config;
mod defaults;
mod keybindings;
Expand All @@ -9,6 +10,7 @@ mod settings;
pub use {
action::*,
args::*,
cargo_wrapped_config::*,
config::*,
defaults::*,
keybindings::*,
Expand Down
75 changes: 29 additions & 46 deletions src/conf/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ use {

/// The settings used in the application.
///
/// They're made from default, overriden (in order)
/// by the general prefs (global prefs.toml file), by
/// the package config (bacon.toml file in the project
/// directory) and by the launch arguments.
///
/// They're immutable during the execution of the missions.
#[derive(Debug, Clone)]
pub struct Settings {
Expand Down Expand Up @@ -83,10 +78,15 @@ impl Settings {
///
///
/// Hardcoded defaults are overriden by the following configuration elements, in order:
/// * the global `prefs.toml`
/// * the default `bacon.toml` file (embedded in the binary)
/// * the global `prefs.toml`, from user config directory
/// * the file whose path is in environment variable `BACON_PREFS`
/// * the workspace level `bacon.toml` file
/// * the package level `bacon.toml` file
/// * the workspace.metadata.bacon config in the workspace level `Cargo.toml` file
/// * the workspace level `bacon.toml` file in workspace-root/bacon.toml
/// * the workspace level `bacon.toml` file in workspace-root/.config/.bacon.toml
/// * the package.metadata.bacon config in the package level `Cargo.toml` file
/// * the package level `bacon.toml` file in package-root/.bacon.toml
/// * the package level `bacon.toml` file in package-root/.config/.bacon.toml
/// * the file whose path is in environment variable `BACON_CONFIG`
/// * args given as arguments, coming from the cli call
pub fn read(
Expand All @@ -98,47 +98,30 @@ impl Settings {
let default_package_config = Config::default_package_config();
settings.apply_config(&default_package_config);

if let Some(prefs_path) = bacon_prefs_path() {
if prefs_path.exists() {
let prefs = Config::from_path(&prefs_path)?;
info!("prefs: {:#?}", &prefs);
settings.register_config_file(prefs_path);
settings.apply_config(&prefs);
let paths = vec![
bacon_prefs_path(),
config_path_from_env("BACON_PREFS"),
context.workspace_cargo_path(),
context.workspace_config_path(),
context.workspace_dot_config_path(),
Some(context.package_cargo_path()),
Some(context.package_config_path()),
Some(context.package_dot_config_path()),
config_path_from_env("BACON_CONFIG"),
];
for path in paths.into_iter().flatten() {
if path.exists() {
let configs = Config::from_path_detect(&path)?;
if !configs.is_empty() {
info!("config loaded from {:?}", path);
settings.register_config_file(path.clone());
for config in configs {
settings.apply_config(&config);
}
}
}
}

if let Some(config_path) = config_path_from_env("BACON_PREFS") {
let config = Config::from_path(&config_path)?;
info!("config from env: {:#?}", &config);
settings.register_config_file(config_path);
settings.apply_config(&config);
}

let workspace_config_path = context.workspace_config_path();
let package_config_path = context.package_config_path();

if let Some(workspace_config_path) = workspace_config_path {
if workspace_config_path.exists() {
info!("loading workspace level bacon.toml");
let workspace_config = Config::from_path(&workspace_config_path)?;
settings.register_config_file(workspace_config_path);
settings.apply_config(&workspace_config);
}
}

if package_config_path.exists() {
let config = Config::from_path(&package_config_path)?;
settings.register_config_file(package_config_path);
settings.apply_config(&config);
}

if let Some(config_path) = config_path_from_env("BACON_CONFIG") {
let config = Config::from_path(&config_path)?;
info!("config from env: {:#?}", &config);
settings.register_config_file(config_path);
settings.apply_config(&config);
}

settings.apply_args(args);
settings.check()?;
info!("settings: {:#?}", &settings);
Expand Down
20 changes: 17 additions & 3 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,28 @@ impl Context {
settings,
})
}
pub fn package_config_path(&self) -> PathBuf {
self.package_directory.join("bacon.toml")
pub fn workspace_cargo_path(&self) -> Option<PathBuf> {
self.workspace_root.as_ref().map(|p| p.join("Cargo.toml"))
}
/// return the location of the workspace level bacon.toml file
/// (it may be the same path than the package config)
/// (if it's different from the package level bacon.toml file)
pub fn workspace_config_path(&self) -> Option<PathBuf> {
self.workspace_root.as_ref().map(|p| p.join("bacon.toml"))
}
pub fn workspace_dot_config_path(&self) -> Option<PathBuf> {
self.workspace_root
.as_ref()
.map(|p| p.join(".config/bacon.toml"))
}
pub fn package_cargo_path(&self) -> PathBuf {
self.package_directory.join("Cargo.toml")
}
pub fn package_config_path(&self) -> PathBuf {
self.package_directory.join("bacon.toml")
}
pub fn package_dot_config_path(&self) -> PathBuf {
self.package_directory.join(".config/bacon.toml")
}
}

fn cargo_manifest_not_found(err: &str) -> bool {
Expand Down
9 changes: 8 additions & 1 deletion website/docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ Bacon loads in order:
* its default internal configuration (which includes the default bacon.toml)
* the global `prefs.toml` ([global preferences](#global-preferences))
* the file whose path is in environment variable `BACON_PREFS`
* the `workspace.metadata.bacon` entry in the workspace's `Cargo.toml`
* the workspace level `bacon.toml` file ([project settings](#project-settings))
* the package level `bacon.toml` file
* the `bacon.toml` file in `workspace-root/.config/`
* the `package.metadata.bacon` entry in the package's `Cargo.toml`
* the `bacon.toml` file in `package-root/`
* the `bacon.toml` file in `package-root/.config/`
* the file whose path is in environment variable `BACON_CONFIG`

Each configuration file overrides the properties of previously loaded ones.

But you don't *need* so many files.
It's usually enough to have a global `prefs.toml` file and a project specific `bacon.toml`.

When you modified those files and bacon evolved since, you may want to have a look at the current default ones:

* [Current default prefs.toml](https://raw.githubusercontent.com/Canop/bacon/main/defaults/default-prefs.toml)
Expand Down

0 comments on commit dbb6f70

Please sign in to comment.