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

make configuration handling a lot better #141

Merged
merged 19 commits into from
Apr 26, 2024
Merged

make configuration handling a lot better #141

merged 19 commits into from
Apr 26, 2024

Conversation

albertotirla
Copy link
Member

this makes the configuration system far more resiliant to problems, as well as introduces some composability to it. This is how configuration is sourced at the moment, feedback welcome:

  • default: the configuration struct has a default value, which is computed with a specific implementation of the default trait
  • env vars: environment variables for the options available in the configuration, prefixed with ODILIA_
  • configuration file sent via a cli argument: if that argument is present, it will be converted to a path and then merged in with the rest
  • XDG_CONFIG_HOME: the file at $XDG_CONFIG_HOME/odilia/config.toml will be used
  • system wide: configuration from /etc/odilia/config.toml will be used

if any of those files don't exist, nothing will happen, because a good known default set is provided at startup, anything else is in addition to that.
if a file in XDG_CONFIG_HOME doesn't exist, it will be created with the default configuration. I may move it to later in the chain, to populate it with values from all other config sources listed above instead of only the defaults

Copy link

codecov bot commented Mar 16, 2024

Codecov Report

Attention: Patch coverage is 12.82051% with 34 lines in your changes are missing coverage. Please review.

Project coverage is 18.04%. Comparing base (fa09dab) to head (be121fb).
Report is 1 commits behind head on main.

❗ Current head be121fb differs from pull request most recent head 949a72d. Consider uploading reports for the commit 949a72d to get more accurate results

Files Patch % Lines
odilia/src/main.rs 0.00% 30 Missing ⚠️
common/src/errors.rs 0.00% 3 Missing ⚠️
common/src/settings/mod.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #141      +/-   ##
==========================================
- Coverage   18.90%   18.04%   -0.86%     
==========================================
  Files          20       20              
  Lines         984     1003      +19     
==========================================
- Hits          186      181       -5     
- Misses        798      822      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -80,6 +85,42 @@ async fn main() -> eyre::Result<()> {
//initialize a task tracker, which will allow us to wait for all tasks to finish
let tracker = TaskTracker::new();

tracing::debug!("Reading configuration");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the creation of the application config to a new function in config? Seems more fitting. In fact, you could even implement this as ApplicationConfigBuilder::default() or something like that, as it constructs a config from various steps.

Copy link
Member Author

@albertotirla albertotirla Mar 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's a very good idea. As I see it, the configuration is supposed to be plane objects, models as in an mvc pattern for example, essentially just data.

Adding methods to that object would imply that the configuration knows how to build itself, in other words, has additional responsabilities. This is contrary to the principles I go by writing code, because essentially config is indeed just a typed data store, and nothing more than that. That's why it should live in state, with no mutability possibilities, passing around bits of it to the various services needing those. We aren't working in an object oriented programming environment, but rust has some traits from that system as well, so I think we should use everything we can to make our code better and easier to follow, and that includes the sound parts of OOP principles

That's why I think this should be done in main, if not the main function, at least the main module. You see, the configuration should be initialised first, because it initialises everything else, including logging. State::new already needs logging, for various things, especially because it then goes on and initialises lots of the core components of the screenreader, which in turn need logging, and orphan spans will be weird in the visualisers or even if we inspect the logs by hand, so that whole chain requires that logging is initialized before it.

odilia/src/state.rs Show resolved Hide resolved
odilia/src/state.rs Show resolved Hide resolved
@TTWNO
Copy link
Member

TTWNO commented Mar 16, 2024

Really liking this! Thank you for your time and effort!

@albertotirla
Copy link
Member Author

no problem at all! someone has to do these things as well

…ilia

this is currently using figment as the configuration library, other options include config and simply serde-toml
this is not yet complete, as huge refactorings are about to take place
…ke state::new accept the configuration structure as a parameter

this makes it easier to make, for example, speech have the desired rate from startup, logging be initialised from the config with the desired filter and perhaps a path location, stuff like that
the method join replaced the previously used merge. Apparently, with merge, when conflicts are encountered, it preferes keeping the current value, instead of replacing it. So, config was taken from the default values supplied with the modules, but the values in there were never replaced by those who should have a greatter priority.
as a consequence, user defined configuration files wouldn't be applied, and the user would understandably be confused by the results
up to now, configuration was read, but never actually used. This begins a series of changes, perhaps across multiple fronts, to do so.
This makes `state::new` send a message on the ssip channel, with the request to set the rate to a user defined value
warning: if somehow the ssip task isn't initialized by the point we get there, the change will be lost in the channel, or may be picked up later than intended
we use nested toml tables in our configuration, one table per what we think to be a logical section. However, because we want to add environment variables as configuration sources, we have to be able to parse nested dictionaries.
In order to do that, beside just using .prefix to filter out variables which don't concern us, we must also split the string of the variable name in keys, and for that we try to use the .split method
@TTWNO TTWNO merged commit 7a91a84 into main Apr 26, 2024
@albertotirla albertotirla deleted the configs branch September 7, 2024 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants