Using File IO and Configs
Reading contents from is a fundamental operation in any language, here are a few ways to do it in Rust.
std::fs
- Reference
std::fs::read_to_string
From my experience, this method works great with .txt
, .yaml
, and .json
, and can be further passed to Serde for easy deserialization.
rust
use std::fs;
fn main() {
// Example 1: Text file
let s = fs::read_to_string("path/to/file.txt").unwrap();
// Example 2: Yaml with serde reading a vector
let s = fs::read_to_string("path/to/file.yaml").unwrap();
let s_vec = serde_yaml::from_str::<Vec<String>>(&s).unwrap();
}
use std::fs;
fn main() {
// Example 1: Text file
let s = fs::read_to_string("path/to/file.txt").unwrap();
// Example 2: Yaml with serde reading a vector
let s = fs::read_to_string("path/to/file.yaml").unwrap();
let s_vec = serde_yaml::from_str::<Vec<String>>(&s).unwrap();
}
The .yaml
file from this example has only a list would look something like
yaml
---
- option_a
- option_b
- option_c
---
- option_a
- option_b
- option_c
For more complex usage you should create your own type and to deserialize into it.
config-rs
Reference https://github.com/mehcode/config-rs
This method is most suitable for reading configuration files into your program. A good practice is to couple this method with clap
to allow user-defined configuration paths.
rust
use clap::Parser;
use config::{Config, File};
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct ProgramConfig {
option_a: u64,
option_b: u64,
option_c: String,
}
#[derive(Parser, Debug)]
#[command(author = "Braden Stefanuk", version, about)]
struct Cli {
/// path to config file
#[arg(short, long)]
config: String,
}
fn main() {
let cli = Cli::parse();
let cfg: ProgramConfig = Config::builder()
.add_source(File::with_name(&cli.config))
.build()
.unwrap()
.try_deserialize()
.unwrap();
}
use clap::Parser;
use config::{Config, File};
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct ProgramConfig {
option_a: u64,
option_b: u64,
option_c: String,
}
#[derive(Parser, Debug)]
#[command(author = "Braden Stefanuk", version, about)]
struct Cli {
/// path to config file
#[arg(short, long)]
config: String,
}
fn main() {
let cli = Cli::parse();
let cfg: ProgramConfig = Config::builder()
.add_source(File::with_name(&cli.config))
.build()
.unwrap()
.try_deserialize()
.unwrap();
}
When writing raw bytes to a file use the following
rust
struct Controller;
impl Controller {
fn new() -> Self {
Self {}
}
fn write_to(&self, p: &str, c: &[u8]) -> Result<()> {
fs::write(p, c)?;
Ok(())
}
fn read_from(&self, p: &str) -> Result<Vec<u8>> {
let res = fs::read(p)?;
Ok(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_to_file_from() -> Result<()> {
let filename = "test_lsw_ctrl";
let fs = Controller::new();
fs.write_to(filename, b"1234")?;
let contents = fs.read_from(filename)?;
assert_eq!(contents, b"1234");
Ok(())
}
}
struct Controller;
impl Controller {
fn new() -> Self {
Self {}
}
fn write_to(&self, p: &str, c: &[u8]) -> Result<()> {
fs::write(p, c)?;
Ok(())
}
fn read_from(&self, p: &str) -> Result<Vec<u8>> {
let res = fs::read(p)?;
Ok(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_to_file_from() -> Result<()> {
let filename = "test_lsw_ctrl";
let fs = Controller::new();
fs.write_to(filename, b"1234")?;
let contents = fs.read_from(filename)?;
assert_eq!(contents, b"1234");
Ok(())
}
}