-
Notifications
You must be signed in to change notification settings - Fork 10
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
Configuration file #100
Comments
The thought has crossed my mind. It's probably a good idea. I hope it can be implemented easily such that command line arguments take precedence over what is in the config. Things may get tricky with the I am not familiar with |
I hope it can be implemented easily such that command line arguments
take precedence over what is in the config.
Yes, I agree. We should prefer arguments over environment variables
over user configuration over system configuration.
Perhaps we can layer this functionality before argument parsing (it's
what I've done elsewhere and it has served me well so far)?
Yes, I agree. I thought of adding the data to the {Run,Exec}Ctx,
similar to the environment variables.
I suspect we will also need one of the backends, presumably `toml` (or
`yaml`?).
I’d prefer toml. It is simple enough for non-Rust users to get used to,
and it is quite similar the common INI-style configuration files.
I am actually not quite sure what value-add the crate provides over just
using `serde` with `toml` and [a custom
derive](https://docs.rs/toml/0.5.6/toml/#deserialization-and-serialization).
It would make it easier to merge multiple configuration sources (e. g.
system configuration in /etc/xdg/nitrocli/config(.toml?), user
configuration in ~/.config/nitrocli/config(.toml?) and environment
variables).
I think the major decision is which arguments to support. We could
support all arguments, with subgroups for subcommands, e. g.:
model = "pro"
[otp.get]
algorithm = "hotp"
This would be the most elegant and complete approach, but honestly, I
can’t think of a scenario where one would like to set defaults for any
of the subcommand arguments. So to keep things simple, and until a user
requests anything else, I would limit the supported arguments to:
- serial number (once implemented)
- USB path (if implemented)
- model
- verbose
- no_cache
These should also be read from the environment as NITROKEY_SERIAL_NUMBER
etc.
We could also add the other environment variables (*_ADMIN_PIN etc.) but
I’m reluctant to allowing the user to store their passwords and PINs in
a configuration file.
|
Yep, seems reasonable.
Sounds good.
Ah, interesting. That seems reasonable then.
Yeah, sounds good to me. I'd say it would be good if we could rule out the "supporting all arguments" approach based on reasons of complexity of the implementation and not on the basis of whether the feature will be used. If it's easy and elegant to implement, I'd prefer that approach.
Agreed. |
That’s true. I omitted that part of my argument – I would only consider the complex implementation if there is a use case.
I did not yet write any code, but I see the following problems:
So it might be possible to implement this using our |
I did some experiments and noticed another problem: The commands and subcommands are enums. Even if everything else works, config could only give us an |
What I meant with my initial comment "Perhaps we can layer this functionality before argument parsing" is basically the following: when the program starts up we read the config file(s). We also get the program arguments. Then we translate the config-file provided configuration into program arguments (as the user would supply them on the command line) and insert them into the argument vector we ultimately pass to How can we infer the option to use from the configuration? In the past I literally just put the config options into the config file; hypothetical example (using - 'status': [
'--verbose',
] This configuration would result in [otp.get]
algorithm = "hotp" work as well (just prepend two dashes :P) As indicated above, though, things may get interesting with nested sub commands. I don't claim to have thought that through in its entirety. |
Hm. You basically would want
Yeah, sucks a bit, but I don't see a solution to that either.
That, I would say should be covered by the approach I outlined above. But let me know if I am misunderstanding. |
Ah, I see. I’ll have to think a bit more about that. My first concern is that it would make error reporting a bit confusing (if there is an error in the configuration file, the user will get an error message about the arguments).
No. I was thinking about this: Consider the subcommand I started implementing the simple version of my initial approach (parsing the config file for selected arguments and storing them in *Ctx). It is working fine, but it turns out that the |
Good point. I'd hope the user sees the connection between the two. Obviously, that would be more apparent if we actually added the options into the config file directly (but I understand that is not necessarily the preferred way for other reasons).
Understood. Yeah, I am fine with not overloading the feature.
Nice!
Not so nice :-| If you think it adds value okay, as long as it does not permeate into every part of the program and could be swapped it out with reasonable effort (which, I would say, hasn't really been the case with |
Yes, it’s pretty isolated. In my example implementation in #101, only |
Okay, I looked at your pull request. Thanks for working on it! Looks pretty good to me. I can't say much more, I guess, without seeing an alternative approach :D But this seems isolated enough that I think it would be reasonable to merge. The only thing that annoys me is the multitude of new dependencies, but I guess we crossed that line already when we switched to I guess we get for free a new match Args::from_iter_safe(args.iter()) {
Ok(args) => {
+ let mut config = ctx.config;
+ config.update(&args);
let mut ctx = ExecCtx {
- model: args.model,
stdout: ctx.stdout,
stderr: ctx.stderr,
admin_pin: ctx.admin_pin.take(),
user_pin: ctx.user_pin.take(),
new_admin_pin: ctx.new_admin_pin.take(),
new_user_pin: ctx.new_user_pin.take(),
password: ctx.password.take(),
- no_cache: ctx.no_cache,
+ config,
verbosity: args.verbose.into(),
};
args.cmd.execute(&mut ctx)
} can work. If we have a mutable reference to a |
If we have a mutable reference to a `RunCtx`, how can we move the `config` out of it?
Config derives Copy, so I guess we just create a new copy. Does that
make sense?
|
Yep, thank you (I did check for that but must have missed it...) |
Implemented in #111. |
What do you think about having defaults for at least some of the arguments in a configuration file? For example, once we have a
--serial-number
option, I’d like to be able to set the serial number of my production device for allnitrocli
calls.An implementation could use
config
for the file parsing andapp_dirs
to get XDG-compliant paths. (This would pull inserde
andnom
.)The text was updated successfully, but these errors were encountered: