kixunil / configure_me Goto Github PK
View Code? Open in Web Editor NEWA Rust library for processing application configuration easily
A Rust library for processing application configuration easily
Maybe we can add this attribute to the code to avoid warnings:
https://stackoverflow.com/questions/51481551/what-does-automatically-derived-mean
Document default values in help and man pages. This will need doc_default
to work with man pages.
doc_default
Unused Result warnings when testing.
It'd be neat if template files and scripts could be generated from the configuration.
When file list is implemented in build script, then it could auto-generate the config file too. That'd help a lot.
By splitting data validation and putting it into a separate library, it'd be possible to share the validation code between codegen and generated code. It might be also useful for other projects.
Generate shell completions. Possibly steal them from clap. :)
xtask pattern could improve compilation times for libraries. However it's not entirely suited for this kind of optimization. So instead we can put codegen into cfg_me
and if it's installed just run it, if it's not run xtask which will do the same thing.
And make sure cfg_me
is versioned similarly to cargo +version ...
(but maybe scan the project file for version instead?) so that we can make sure codegen matches.
Support subcommands like e.g. commit
is a subcommand of git
.
Example input:
[general]
# global options
# This is a global param - given to program in the form of cmd --foo subcmd
# We could later support cmd subcmd --foo
[[param]]
name = "foo"
type = "String"
# ...
# same as above an be done with switch
# We use subcmd instead of subcommand to keep it reasonably short because it will be repeated
[subcmd.subcmdname.general]
# Only support these two for now, other things available above are mostly non-sensical here
summary = "This command does something"
doc = "Long explanation of what this command is all about..."
# LATER we could also support include = "file-relative-to-current"
# included files should probably only support params and switches
# works exactly the same as in top-level
[[subcmd.subcmdname.param]]
name = "bar"
type = "String"
# switch is supported here as well
# nested subcommand is also supported
The generated code will look something like this:
enum Subcommand {
Subcmdname(subcmdname::Config),
}
// recursively generates almost the same thing as top-level
mod subcmdname {
struct Config {
// no _program_name here, it's pointless
bar: String,
}
// also has raw module like the top-level
mod raw {
}
}
Specification parsing code will get subcmd: HashMap<Ident, Subcommand>
field.
struct Subcommand {
global: SubcommandGlobal,
param: Vec<Param>,
switch: Vec<Switch>,
}
Difficult stuff:
Config
, or outside? The advantage of being outside is it can get global config without self-reference but maybe that's not useful as immutable references will most likely be used?clap
does? If there are multiple nested subcommands and mid-commmands don't have any options, then avoiding the struct would lead to easier-to-write code, although harder-to-refactor. Maybe instead have Subcmdname { config: subcmdname::Config, subcmd: subcmdname::Subcommand }
, then matching code can just ignore empty struct. This obviously implies "outside" for the question about.cannot bind by-move and by-ref in the same pattern
Merging of conf files is illogical, error-prone, confusing and possibly buggy. Tests need to be written and merge_in
modified to override &mut self
.
Currently man page doesn't include FILES
section, so config files aren't documented.
config_files
option to General
FILES
section is in man
crateFILES
section to man pageConfig::including_default_config_files()
to make use of the information provided in config specman
with description sectionConsider changing sequence to map in specification Toml input. That means:
[[param]]
name = "foo"
type = "String"
would become:
[param.foo]
type = "String"
Alternative that makes Toml input map more directly to Rust code, catching duplicates much more early:
[field.foo]
cli = "param" # other possibilities: switch, disabled
type = "String"
So far I think I prefer the less verbose option, duplicate detection can be written differently.
@dr-orlovsky @romanz would appreciate your thoughts.
Sorry if I missed something, but looking in the docs I have not found possibility to use multiple configuration sections, like
[networking]
ip = "192.168.0.1"
port = 8000
[cli]
enable = true
It should be possible to provide custom merge function for cases when value of a field comes from two sources.
set out_dir like pb builder's style:Builder::out_dir.
fn path_in_out_dir<P: AsRef<Path>>(file_name: P) -> Result<PathBuf, Error> {
//TODO pass the out_dir config
let mut out: PathBuf = std::env::var_os("OUT_DIR").ok_or(ErrorData::MissingOutDir)?.into();
out.push(file_name);
Ok(out)
}
Pros:
Cons:
It's currently possible to collect multiple arguments by using custom merge_fn
, but that would require a newtype wrapper around the collection that implements ParseArg
by inserting it into the collection. It might be nicer to avoid forcing everyone into implementing it by either:
interface files should be implemented in another crate, but configure_me
could improve working with them if it's integrated.
Add a special argument that allows to specify config file to load.
Since a new version of Toml came out, it creates duplicates in crates that use the new version.
This will remove the necessity to add serde_derive
into Cargo.toml
when using configure_me
Currently help wraps at 80 chars, which is a hard-coded value. It'd be nicer to detect the size of the terminal run-time and use that.
Support env vars. Abusing parse_arg
for parsing should be fine.
It should be possible to define verbosity by specifying --verbose
argument multiple times.
Just would like to check that I am using this lib efficiently with cases we have in different LNP/BP nodes (LNP Node, BP Node, RGB Node).
Each node is represented by
The main service should be configured with either:
The rest of services are never intended to be launched by human from command-line (they are started as threads or processes by the main service, or automatically by something like Kubernetes), but they also should be configured with
What I plan is:
Code generated by configure_me triggers a clippy warning:
used binding `_program_path` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
(pedantic)
I think the underscore was to prevent clashes with user-defined fields. Maybe there's a workaround?
Clippy with -- -W clippy::pedantic
produces $subj error on generated code if no env_prefix
is configured
Clippy output:
Checking configure_me_user v0.1.0 (<path>/configure_me_user)
warning: enum with no variants
--> <path>/configure_me_user/target/debug/build/configure_me_user-72c791d01889789c/out/configure_me_config.rs:46:1
|
46 | pub enum EnvParseError {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-W clippy::empty-enum` implied by `-W clippy::pedantic`
help: consider using the uninhabited type `!` or a wrapper around it
--> <path>/configure_me_user/target/debug/build/configure_me_user-72c791d01889789c/out/configure_me_config.rs:46:1
|
46 | pub enum EnvParseError {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/master/index.html#empty_enum
Maybe it can be changed to be buildable? I'm not a fan of dinosaur software, but 1.34 is fairly recent.
It'd be nice to be able to condition switches/params on features being enabled or based on platform etc.
Suggested design:
After implementing #4 there could be an additional option to implement trigger file and scripts.
Create a new struct File
, which will contain only fields that are enabled to load from file. This will be groundwork for subcommands etc.
I am trying to use configure_me for a crate with two executables each with their own config files.
This section in the documentation seems wrong:
# config for binary foo
[package.metadata.configure_me.bin]
foo = "foo_config_spec.toml"
# config for binary bar
[package.metadata.configure_me.bin]
bar = "bar_config_spec.toml"
I believe it should be
# config for binary foo
[package.metadata.configure_me.bin]
foo = "foo_config_spec.toml"
bar = "bar_config_spec.toml"
I can open a PR for this later.
How is it possible to use per-binary configuration and code generation (when there are multiple bins in src/bin
section and each one needs a separate config)?
This causes loss of the error message when stderr is redirected, e.g. when used with systemd
Arguments should override everything else, not the other way around.
help: use the applicable keyword: `Self`
Make use of parse_arg::ParseArg::describe_type()
to describe what's expected as the value of parameter.
It might be nice to be able to convert parameter type to a different type. This would be useful for scenarios when newtype is used to implement ParseArg
.
New version of configure_me
with updated README should be released.
It'd be great to add an option to enable skipping default config files. It'd resolve romanz/electrs#217 as well as provide some robustness for other interesting cases. Programs that don't have such options are sometimes painful to work with (pkg-config comes to mind).
Proposed solution: make it possible to add skip_default_configs
to a boolean flag. After processing arguments, this is checked and configs are not loaded if it's set to true.
Pros:
Cons:
Alternatives:
EPERM
) - lot of hair pulling in cases where the user forgot didn't want this behavior.configure_me
- would remove configuration handling, but be inflexible. There's a precedent with --help
, but that is extremely widespread - basically every program understands -h
and --help
. This isn't true for skipping default config.Add standard_config_files
bool option to General
. After enabling, it should pull from /etc/name
and ~/.config/name
This will be implemented after #4.
It's missing cargo:
prefix.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.