TOML v1.0.0
全称:Tom 的(语义)明显、(配置)最小化的语言。(Tom’s Obvious, Minimal Language)
作者:Tom Preston-Werner、Pradyun Gedam 等人。
宗旨:
- TOML 旨在成为一个语义明显且易于阅读的最小化配置文件格式。
- TOML 被设计成可以无歧义地映射为哈希表。
- TOML 应该能很容易地被解析成各种语言中的数据结构。
详细介绍参见:TOML: 简体中文 v1.0.0
安装依赖
cargo add toml #toml文件解析crate
cargo add serde #toml crate的依赖
cargo add chrono #用于解析配置文件中的时间
定义配置结构体
配置文件内容如下
[Boss."进化巨型丛林地虫"]
position = "None"
name = "进化巨型丛林地虫"
refresh-time-list = [00:00:00,04:00:00,08:00:00,12:00:00,16:00:00,20:00:00]
[Boss."莫迪尔沃尔格斯"]
position = "None"
name = "莫迪尔沃尔格斯"
refresh-time-list = [00:30:00,03:30:00,06:30:00,09:30:00,12:30:00,15:30:30,18:30:00,21:30:00]
根据配置文件的结构定义一个结构体
pub struct Boss {
pub name: String,
pub position: Option<String>,
pub refresh_time_list: Vec<base::value::Datetime>,
}
解析配置文件
贴代码参考
use std::{env, fs};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use base::Value;
use crate::boss::Boss;
use crate::error::ConfigError;
type Result<T> = std::result::Result<T, ConfigError>;
const BOSS_CONFIG_FILE: &str = "Boss.toml";
pub struct ConfigHandler {}
impl ConfigHandler {
/// 解析配置文件
pub fn parse_config() -> Result<HashMap<String,Boss>> {
let file = Self::get_config_file()?;
let content = Self::read_from(file)?;
let parse_result = content.parse::<Value>().map_err(|_| ConfigError::BindConfigError)?;
let count_and_keys = Self::get_config_count("Boss".to_string(), &parse_result)?;
let mut config = HashMap::new();
for key in count_and_keys.1.iter() {
if !config.contains_key(key) {
if let Ok(boss) = Self::bind_config(&parse_result,key.to_string()){
config.insert(key.to_string(),boss);
}
}
}
Ok(config)
}
/// 获取配置文件的root节点下的子节点及节点名称,便于后期配置文件子节点修改时能同步更新
///
/// # Arguments
///
/// * `root` 将root设置为要计算的根节点
/// * `parsed_content` 已解析的Value配置树
pub fn get_config_count(root: String, parsed_content: &Value) -> Result<(usize, Vec<String>)> {
let mut res = (0, vec![]);
if let Some(t) = parsed_content[root].as_table() {
for key in t.keys() {
res.1.insert(res.0, key.into());
res.0 += 1;
}
} else {
return Err(ConfigError::BindConfigError);
}
Ok(res)
}
/// 读取配置文件内容
pub fn read_from<P: AsRef<Path>>(path: P) -> Result<String> {
let config_file = path.as_ref();
let content = fs::read_to_string(config_file).map_err(|_| ConfigError::IOError)?;
Ok(content)
}
/// 获取配置文件路径,从执行路径向上查找
pub fn get_config_file() -> Result<PathBuf> {
let mut current_path = env::current_dir().map_err(|_| ConfigError::NotFound)?;
loop {
let file = current_path.join(BOSS_CONFIG_FILE);
if fs::metadata(&file).is_ok() {
return Ok(file);
} else {
match current_path.parent() {
Some(cwd) => {
current_path = cwd.to_path_buf()
}
None => break
}
}
}
Err(ConfigError::NotFound)
}
/// 将解析的Table对象绑定为配置对象
///
/// # Arguments
///
/// * `parsed_content` - 已解析的Value对象
/// * `key` - 具名Boss的节点名称
pub fn bind_config(parsed_content: &Value,key:String) -> Result<Boss> {
let mut boss = Boss::new();
if let Some(current_table) = parsed_content["Boss"][key].as_table(){
if let Some(name) = current_table["name"].as_str(){
boss.name = name.to_string();
}
if let Some(position) = current_table["position"].as_str(){
if position != "None"{
boss.position = Some(position.to_string());
}
}
if let Some(refresh_time_list) = current_table["refresh-time-list"].as_array(){
let mut index = 0;
for val in refresh_time_list{
if let Some(datetime) = val.as_datetime(){
boss.refresh_time_list.insert(index, datetime.clone());
index += 1;
}
}
}
}
Ok(boss)
}
}
小结
-
toml文件的解析结果其实就是一棵树
-
根据节点的类型我们需要做相应的转换
-
toml会将配置文件解析为一个Value枚举,然后根据配置节点实际情况做相应转换
pub enum Value { String(String), Integer(i64), Float(f64), Boolean(bool), Datetime(Datetime), Array(Array), Table(Table), }