Last active
October 17, 2024 18:56
-
-
Save ptdecker/d80df0a5220178d2e7a0be3e8b563600 to your computer and use it in GitHub Desktop.
TOML Crate Demonstration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! TOML Value Manipulation Demonstration | |
//! | |
//! This code is to supplement this excellent article: | |
//! | |
//! [Rust: Read, Write, and Modify .toml Files — Another way to handle User Set Environment Variables](https://medium.com/@itsuki.enjoy/rust-read-write-and-modify-toml-files-another-way-to-handle-user-set-environment-variables-27e1baf1a65f) | |
//! | |
//! This demo code assumes you have added additional entries to your ~/.cargo/config.toml file. Although, I used | |
//! `foo` and `bar` instead of the `password` and `username` that the article uses. | |
//! | |
//! Put this code into `main.rs`. And, here is the corresponding `Cargo.toml` needed for this demo. | |
//! | |
//! ```toml | |
//! [package] | |
//! name = "toml_demo" | |
//! version = "0.1.0" | |
//! edition = "2021" | |
//! | |
//! [dependencies] | |
//! toml = "0.8.19" | |
//! ``` | |
//! | |
use std::{fs::{OpenOptions, File}, io::{Write, Read}, path::{Path, PathBuf}, str::FromStr}; | |
use toml::{Table, Value}; | |
/// Type alias for errors. Can evolved as the need for more sophisticated error handling progresses. | |
/// See Jeremy Chone's excellent YouTube tutorial: | |
/// | |
/// [Rust Error Handling - Best Practices](https://www.youtube.com/watch?v=j-VQCYP7wyw) | |
type Error = Box<dyn std::error::Error + Send + Sync>; | |
/// Checks for the existence of a TOML config file and create it if it doesn't exist | |
fn init_toml(name: &str) -> Result<(), Error> { | |
let cargo_config_path = PathBuf::from_str(&format!("{}/{}", env!("CARGO_HOME"), name))?; | |
if !Path::new(&cargo_config_path).exists() { | |
println!("config.toml does not exist, creating it!"); | |
let _ = File::create(cargo_config_path)?; | |
} | |
Ok(()) | |
} | |
/// Reads a TOML file as a toml::Table | |
fn read_toml(name: &str) -> Result<Table, Error> { | |
let mut file = File::open(&format!("{}/{}", env!("CARGO_HOME"), name))?; | |
let mut contents = String::new(); | |
file.read_to_string(&mut contents)?; | |
Ok(contents.parse::<Table>()?) | |
} | |
/// Reads a specific attribute value from a section of a TOML file and return it as a toml::Value | |
fn read_toml_value(name: &str, section: &str, attribute: &str) -> Result<Value, Error> { | |
read_toml(name)? | |
.get(section) | |
.ok_or_else(|| format!("Environment section {section} was not found").into()) | |
.and_then(|env| { | |
env.as_table() | |
.ok_or_else(|| format!("Environment section {section} is not a table").into()) | |
}) | |
.and_then(|table| { | |
table | |
.get(attribute) | |
.ok_or_else(|| { | |
format!("Attribute {attribute} is missing from section {section}").into() | |
}) | |
.cloned() | |
}) | |
} | |
/// Write a value to a specific attribute in a specified section of a TOML file. If the section does | |
/// not already exist, it will be inserted into the TOML. If the value already exists in the section, | |
/// it will be updated. If it does not exist, it will be inserted. All other values in the section | |
/// are preserved. | |
fn write_toml_value( | |
name: &str, | |
section: &str, | |
attribute: &str, | |
value: &Value, | |
) -> Result<(), Error> { | |
let mut main_table = read_toml(name)?; | |
if let Value::Table(ref mut section) = main_table | |
.entry(section) | |
.or_insert_with(|| Value::Table(Table::new())) | |
{ | |
section.entry(attribute).or_insert(value.clone()); | |
} | |
OpenOptions::new() | |
.write(true) | |
.create(true) | |
.truncate(true) | |
.open(&format!("{}/{}", env!("CARGO_HOME"), name))? | |
.write_all(&main_table.to_string().as_bytes())?; | |
Ok(()) | |
} | |
fn main() -> Result<(), Error> { | |
// Demo reading from config.toml | |
println!("{}", read_toml("config.toml")?); | |
// Demo reading a specific value from config.toml | |
println!( | |
"foo: {}, bar: {}", | |
read_toml_value("config.toml", "env", "foo")?, | |
read_toml_value("config.toml", "env", "bar")? | |
); | |
// Demo creating a new TOML file if it doesn't exist | |
init_toml("config2.toml")?; | |
// Demo writing to the new TOML file | |
write_toml_value("config2.toml", "env", "baz", &"fizz".into())?; | |
// Demo reading from second demo TOML file | |
println!("{}", read_toml("config2.toml")?); | |
// Demo reading a specific value from second demo TOML file | |
println!("baz: {}", read_toml_value("config2.toml", "env", "baz")?,); | |
// Demo writing another value to the new TOML file | |
write_toml_value("config2.toml", "env", "bar", &"buzz".into())?; | |
// Demo reading from second demo TOML file | |
println!("{}", read_toml("config2.toml")?); | |
// Demo reading a specific value from second demo TOML file | |
println!("bar: {}", read_toml_value("config2.toml", "env", "bar")?,); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment