I have made a script that writes to .toml files but, I think there is a better way of doing this:
pub fn write(p: i32, c: String) -> Result<(), std::io::Error> {
let proj_dirs = ProjectDirs::from("com", "m", "fli").unwrap();
let config_dir = proj_dirs.config_dir();
let mut config_file = fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(config_dir.join("config.toml"))?;
let config = Config{ p, c};
let t = toml::to_string(&config).unwrap();
let toml = t.as_bytes();
config_file.write_all(toml)?;
config_file.flush()?;
Ok(())
}
When I add some variable to my config.toml I will need to add it as a parameter of this function and then change this write function anywhere it is called.
Right now, I call this function like this
There is the toml crate you could use. It uses serde under the hood which is a very powerful tool when you need to work with most common serialization protocols like json or toml in your case.
Oh, you're absolutely right, my bad. I guess the question is more concerned with how to update any type of object and the interface changes that are part of it. I don't really see a way to not change any interface when you change your type. When you add fields to a struct, you will have to initialize them somewhere. You could pass the Config struct to write instead of constructing it inside, which seems more idiomatic to me. But this would only mitigate the problem that you have to change the constructor of your struct.
I'd probably write something like this in that case:
use std::io::Result;
use serde::Serialize;
#[derive(Serialize)]
struct Config {
p: i32,
c: String
}
impl Config {
fn new(p: i32, c: String) -> Self {
Self { p, c }
}
fn write(&self) -> Result<()> {
// write your Config struct to the the right file here
Ok(())
}
}
Then you have the two distinct operations "initialization" and "writing to file" split into two functions. Yes, you'd still need to update new every time your config changes. But the signature of the write method doesn't need any updates ever.