使用 config-rs 将 TOML 字符串反序列化为枚举
Deserialize TOML string to enum using config-rs
我正在使用 config-rs 从 TOML 文件加载配置,我想将字符串反序列化为枚举。我尝试使用 serde_derive 的 deserialize_with
功能来解决它,但我不知道如何 return 一个合适的错误来满足函数签名。我怎样才能实现它?
我的依赖项:
config = "0.7"
serde_derive = "1.0"
serde = "1.0"
toml = "0.4"
用于反序列化枚举的示例代码RotationPolicyType
:
extern crate config;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate toml;
use std::env;
use config::{Config, Environment, File};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub enum RotationPolicyType {
ByDuration,
ByDay,
}
#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
pub rotations: i32,
#[serde(deserialize_with="deserialize_with")]
pub rotation_policy_type: RotationPolicyType,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
pub threads: usize,
pub file_writer: FileConfig,
}
impl Settings {
pub fn new() -> Self {
let mut s = Config::new();
s.merge(File::with_name("config/default")).unwrap();
s.merge(File::with_name("config/local").required(false))
.unwrap();
s.merge(Environment::with_prefix("app")).unwrap();
s.deserialize().unwrap()
}
}
fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
let s = String::deserialize(deserializer)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
let s = String::deserialize(deserializer)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
编译错误 deserialize_with:
error[E0106]: missing lifetime specifier
--> src/settings.rs:30:94
|
30 | fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
| ^^^^^^^^^^^^ expected lifetime parameter
error: aborting due to previous error
编译错误 deserialize_with2:
error[E0220]: associated type `Error` not found for `D`
--> src/settings.rs:42:90
|
42 | fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
| ^^^^^^^^ associated type `Error` not found
error: aborting due to previous error
首先,您的示例编译得不够深入,无法解决您所描述的错误。请注意生成 MCVE next time. Bonus points for getting it to work on https://play.rust-lang.org(这是可能的,extern crate config
在您的示例中完全没有必要)。
解决所有编译问题后,您的第一个错误只需更改函数 API 以匹配建议的 in the serde docs
-fn deserialize_with< D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer
+fn deserialize_with<'de, D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer<'de>
错误试图帮助你。它告诉您 Deserializer
缺少生命周期参数。
第二个函数告诉您 D
没有关联类型 Error
。它只有在 D
将实现 Deserializer<'de>
时才能拥有。但是您指定 &'de mut D
实现 Deserializer<'de>
。找到此问题的解决方案留作 reader.
的练习。
按照@oli-obk-ker 的建议,解决方案非常简单:
use std::env;
use config::{Config, File, Environment};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;
pub trait DeserializeWith: Sized {
fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
where D: Deserializer<'de>;
}
#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]
pub enum RotationPolicyType {
ByDuration,
ByDay
}
impl DeserializeWith for RotationPolicyType {
fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
let s = String::deserialize(de)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
pub rotations: i32,
#[serde(deserialize_with="RotationPolicyType::deserialize_with")]
pub rotation_policy_type: RotationPolicyType,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
pub threads: i32,
pub file_writer: FileConfig,
}
impl Settings {
pub fn new() -> Self {
let mut s = Config::new();
s.merge(File::with_name("config/default")).unwrap();
s.merge(File::with_name("config/local").required(false)).unwrap();
s.merge(Environment::with_prefix("app")).unwrap();
s.deserialize().unwrap()
}
}
我正在使用 config-rs 从 TOML 文件加载配置,我想将字符串反序列化为枚举。我尝试使用 serde_derive 的 deserialize_with
功能来解决它,但我不知道如何 return 一个合适的错误来满足函数签名。我怎样才能实现它?
我的依赖项:
config = "0.7"
serde_derive = "1.0"
serde = "1.0"
toml = "0.4"
用于反序列化枚举的示例代码RotationPolicyType
:
extern crate config;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate toml;
use std::env;
use config::{Config, Environment, File};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub enum RotationPolicyType {
ByDuration,
ByDay,
}
#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
pub rotations: i32,
#[serde(deserialize_with="deserialize_with")]
pub rotation_policy_type: RotationPolicyType,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
pub threads: usize,
pub file_writer: FileConfig,
}
impl Settings {
pub fn new() -> Self {
let mut s = Config::new();
s.merge(File::with_name("config/default")).unwrap();
s.merge(File::with_name("config/local").required(false))
.unwrap();
s.merge(Environment::with_prefix("app")).unwrap();
s.deserialize().unwrap()
}
}
fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
let s = String::deserialize(deserializer)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
let s = String::deserialize(deserializer)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
编译错误 deserialize_with:
error[E0106]: missing lifetime specifier
--> src/settings.rs:30:94
|
30 | fn deserialize_with<D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer {
| ^^^^^^^^^^^^ expected lifetime parameter
error: aborting due to previous error
编译错误 deserialize_with2:
error[E0220]: associated type `Error` not found for `D`
--> src/settings.rs:42:90
|
42 | fn deserialize_with2<'de, D>(deserializer: &'de mut D) -> Result<RotationPolicyType, D::Error> where &'de mut D: Deserializer<'de> {
| ^^^^^^^^ associated type `Error` not found
error: aborting due to previous error
首先,您的示例编译得不够深入,无法解决您所描述的错误。请注意生成 MCVE next time. Bonus points for getting it to work on https://play.rust-lang.org(这是可能的,extern crate config
在您的示例中完全没有必要)。
解决所有编译问题后,您的第一个错误只需更改函数 API 以匹配建议的 in the serde docs
-fn deserialize_with< D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer
+fn deserialize_with<'de, D>(deserializer: D) -> Result<RotationPolicyType, D::Error> where D: Deserializer<'de>
错误试图帮助你。它告诉您 Deserializer
缺少生命周期参数。
第二个函数告诉您 D
没有关联类型 Error
。它只有在 D
将实现 Deserializer<'de>
时才能拥有。但是您指定 &'de mut D
实现 Deserializer<'de>
。找到此问题的解决方案留作 reader.
按照@oli-obk-ker 的建议,解决方案非常简单:
use std::env;
use config::{Config, File, Environment};
use std::path::PathBuf;
use serde;
use serde::de::Deserializer;
use serde::Deserialize;
pub trait DeserializeWith: Sized {
fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
where D: Deserializer<'de>;
}
#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]
pub enum RotationPolicyType {
ByDuration,
ByDay
}
impl DeserializeWith for RotationPolicyType {
fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
let s = String::deserialize(de)?;
match s.as_ref() {
"ByDuration" => Ok(RotationPolicyType::ByDuration),
"ByDay" => Ok(RotationPolicyType::ByDay),
_ => Err(serde::de::Error::custom("error trying to deserialize rotation policy config"))
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct FileConfig {
pub rotations: i32,
#[serde(deserialize_with="RotationPolicyType::deserialize_with")]
pub rotation_policy_type: RotationPolicyType,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
pub threads: i32,
pub file_writer: FileConfig,
}
impl Settings {
pub fn new() -> Self {
let mut s = Config::new();
s.merge(File::with_name("config/default")).unwrap();
s.merge(File::with_name("config/local").required(false)).unwrap();
s.merge(Environment::with_prefix("app")).unwrap();
s.deserialize().unwrap()
}
}