Feature request: Examples of how to use this to configure a rust program.
cstorey opened this issue · comments
I've an application where i might need to update small parts of th configuration for different environments (credentials, which messaging topics to subscribe to, etc). I'm currently using dhall for this, but it seems like the current implementation is no longer maintained.
i've been thinking that nickel might be a good replacement for this, but I can't find clear guidance on how to integrate this for configuration (eg: being able to pass an io::Reader into nickle function, and get back either a deserialized data type or a useful error).
I'd love to see a clear examples of how to do this, along with helper functions like serde_dhall::from_file
that make it easy to use for common cases, while preservinvg the existing usage for less common cases.
It's entirely possible for me to pick through the types (eg: it looks like various types in the crate implement serde's Deserialize/r, but I've never quite had the time to properly sit through it and implement it.
Thanks for your consideration.
Hey, thanks for the question! We've been definitely more focused on the CLI usage than the rust APIs, so most things here are under-documented and subject to change. But you should be able to get started by using Program::new_from_source
to load from a std::io::Read
(or see the other Program::new_from...
methods). Then Program::eval_full_for_export
will give you back a RichTerm
that implements serde::Serialize
. You can either serialize it to some format, or use serde-transcode
to convert it to something else.
For a full example, you could look at the code for the export
cli command
Oh, amazing. Thanks for the pointers!
Additionally, the tests of the deserialize
module are also a good inspiration on how to just eval a Nickel string and get back an arbitrary Rust structure that derives Deserialize
:
nickel/core/src/deserialize.rs
Line 584 in ebbb4f2
FWIW, here's what seemed to work for me (with Nickel 0.4), based on the hints above - please note it can probably be improved, it's just in the hacked-together state in which I first got it to work:
https://github.com/akavel/mana/blob/01736bebe81d355335368992e6b19e543f025cbf/src/main.rs#L254-L272
use nickel_lang_core::error::report::ErrorFormat;
use nickel_lang_core::eval::cache::lazy::CBNCache;
use nickel_lang_core::program::Program as Prog;
let err = std::io::stderr();
let mut prog = Prog::<CBNCache>::new_from_file(&path, err)?;
let res_field = prog.parse_field_path(field_path.clone());
let Ok(field) = res_field else {
prog.report(res_field.unwrap_err(), ErrorFormat::Text);
bail!("failed to parse {field_path:?} as Nickel path");
};
prog.field = field;
let res_term = prog.eval_full_for_export();
let Ok(term) = res_term else {
prog.report(res_term.unwrap_err(), ErrorFormat::Text);
bail!("script {path:?} failed");
};
// FIXME: shorten to: toml::Table::deserialize(term)
let toml = toml::to_string(&term).unwrap();
parse_input_toml(&toml)