[英]Rust: How to read a config file at runtime and store in a global struct, accessible across threads?
This is my first project in Rust, and I think I'm missing something simple.这是我在 Rust 中的第一个项目,我想我错过了一些简单的东西。
I'm attempting to create a simple Web API daemon that will receive POSTs of JSON, parse the JSON, and send an email using credentials provided in a config file.我正在尝试创建一个简单的 Web API 守护程序,它将接收 JSON 的 POST,解析 JSON,并使用配置文件中提供的凭据发送电子邮件。 90% of that problem has been easy. 90% 的问题都很简单。 I am struggling with "parse the config file at runtime".我正在努力解决“在运行时解析配置文件”。
I'm successfully use hyper and letter to receive JSON and send emails.我成功地使用 hyper 和 letter 来接收 JSON 并发送电子邮件。 But I'd like this daemon to be configurable on the server, not at build-time (like most Linux / Unix daemons).但是我希望这个守护进程可以在服务器上配置,而不是在构建时(像大多数 Linux / Unix 守护进程)。 I've diligently followed through here .我一直在努力地跟进这里。
I've created a config module, declared a struct, and used lazy_static!{}
to store an initial version of the configuration struct.我创建了一个配置模块,声明了一个结构,并使用了lazy_static!{}
来存储配置结构的初始版本。
I think I've boiled my problem down to one core question: How do I read and parse a config file, then clone the values into my struct?我想我已经将我的问题归结为一个核心问题:如何读取和解析配置文件,然后将值克隆到我的结构中? Especially considering the fact that the size of those values can't be known at runtime...特别是考虑到这些值的大小在运行时无法知道的事实......
eg src/config.rs
例如src/config.rs
use std::sync::RwLock;
use serde::Deserialize;
use std::fs;
use std::io::prelude::*;
#[derive(Debug, Deserialize, Clone, Copy)]
pub struct RimfireCfg {
pub verbose: u8,
/* web api server config */
pub listen_address: &'static str,
/* mail server config */
pub mailserver: &'static str,
pub port: u16,
pub user: &'static str,
pub password: &'static str,
}
lazy_static! {
pub static ref CONFIG: RwLock<RimfireCfg> = RwLock::new(
RimfireCfg {
verbose: 0,
listen_address: "127.0.0.1:3000",
mailserver: "smtp-mail.outlook.com",
port: 587,
user: "",
password: "",
}
);
}
impl RimfireCfg {
pub fn init() -> Result<(), i32> {
let mut w = CONFIG.write().unwrap();
/* read the config file */
let _lcfg: RimfireCfg =
toml::from_slice(&fs::read("rimfire.toml").unwrap()).unwrap();
// this is clearly wrong ...
*w.listen_address = _lcfg.listen_address.clone();
dbg!(*w);
Ok(())
}
pub fn clone_config() -> RimfireCfg {
let m = CONFIG.read().unwrap();
*m
}
}
and src/main.rs
:和src/main.rs
:
#[macro_use]
extern crate lazy_static;
mod config;
use config::RimfireCfg;
fn main() {
let a = RimfireCfg::clone_config();
dbg!(a);
RimfireCfg::init().unwrap();
let a = RimfireCfg::clone_config();
dbg!(a);
}
Any thoughts?有什么想法吗? suggestions?建议?
Your struct should store String
s.你的结构应该存储String
s。 String
s are mutable and growable -- you can read data from a file into them, unlike &'static str
s. String
s 是可变的和可增长的——你可以从文件中读取数据到它们中,不像&'static str
s。 &'static str
s are usually only for constant string literals. &'static str
通常仅用于常量字符串文字。 I think using String
s should get your example working with minimal tweaks.我认为使用String
s 应该让您的示例以最少的调整工作。
Also, you can use std::mem::swap()
to write the whole config at once.此外,您可以使用std::mem::swap()
一次编写整个配置。 This is also more efficient because it doesn't require cloning strings.这也更有效,因为它不需要克隆字符串。
I think the global static RwLock
is reasonable if you want the config to be a global variable.如果您希望配置成为全局变量,我认为全局静态RwLock
是合理的。 Alternatively, as in Sahsahae's comment, you could avoid global state and pass an Arc<RimfireCfg>
into anything that uses it.或者,如 Sahsahae 的评论中所示,您可以避免全局状态并将Arc<RimfireCfg>
传递给使用它的任何内容。 This is often cleaner.这通常更清洁。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.