mirror of https://github.com/kdl-org/kdl-rs.git
feat(serde): allow defining data shape
This commit is contained in:
parent
4b577cf084
commit
a4146e8284
201
src/de.rs
201
src/de.rs
|
|
@ -37,9 +37,11 @@
|
||||||
//! assert_eq!(config, Config { name: "my-app".into(), port: 8080 });
|
//! assert_eq!(config, Config { name: "my-app".into(), port: 8080 });
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::de::{self, DeserializeSeed, Deserializer as _, MapAccess, SeqAccess, Visitor};
|
use serde::de::value::StringDeserializer;
|
||||||
|
use serde::de::{self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{KdlDocument, KdlEntry, KdlNode, KdlValue};
|
use crate::{KdlDocument, KdlEntry, KdlNode, KdlValue};
|
||||||
|
|
@ -691,22 +693,21 @@ impl<'de, 'a> SeqAccess<'de> for NodeGroupSeqAccess<'a> {
|
||||||
|
|
||||||
struct NodeMapAccess<'a> {
|
struct NodeMapAccess<'a> {
|
||||||
/// Properties and children combined as (key, value_source).
|
/// Properties and children combined as (key, value_source).
|
||||||
entries: Vec<(&'a str, NodeMapValue<'a>)>,
|
entries: Vec<(Cow<'a, str>, NodeMapValue<'a>)>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NodeMapValue<'a> {
|
enum NodeMapValue<'a> {
|
||||||
Arg(&'a KdlValue),
|
Arg(&'a KdlValue),
|
||||||
|
Args(Vec<&'a KdlEntry>),
|
||||||
SingleNode(&'a KdlNode),
|
SingleNode(&'a KdlNode),
|
||||||
MultiNode(Vec<&'a KdlNode>),
|
MultiNode(Vec<&'a KdlNode>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeMapAccess<'a> {
|
impl<'a> NodeMapAccess<'a> {
|
||||||
fn new(node: &'a KdlNode) -> Self {
|
fn new(node: &'a KdlNode) -> Self {
|
||||||
let mut entries: Vec<(&'a str, NodeMapValue<'a>)> = Vec::new();
|
let mut entries: Vec<(Cow<'a, str>, NodeMapValue<'a>)> = Vec::new();
|
||||||
|
|
||||||
// Add the first argument as "-" if there are args and also props/children
|
|
||||||
// (for mixed nodes). Otherwise arguments are not included in map mode.
|
|
||||||
let args: Vec<_> = node
|
let args: Vec<_> = node
|
||||||
.entries()
|
.entries()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -718,18 +719,21 @@ impl<'a> NodeMapAccess<'a> {
|
||||||
.filter(|e| e.name().is_some())
|
.filter(|e| e.name().is_some())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// If there are only args and no props/children, we shouldn't be in map mode.
|
for (i, arg) in args.iter().enumerate() {
|
||||||
// But if we are (struct requested), put args as "-" entries.
|
entries.push((
|
||||||
if !args.is_empty() && (props.is_empty() && node.children().is_none()) {
|
format!("$argument{}", i + 1).into(),
|
||||||
for arg in args.iter() {
|
NodeMapValue::Arg(arg.value()),
|
||||||
entries.push(("-", NodeMapValue::Arg(arg.value())));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !args.is_empty() {
|
||||||
|
entries.push(("$arguments".into(), NodeMapValue::Args(args.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add properties
|
|
||||||
for prop in &props {
|
for prop in &props {
|
||||||
let name = prop.name().unwrap().value();
|
let name = prop.name().unwrap().value();
|
||||||
entries.push((name, NodeMapValue::Arg(prop.value())));
|
entries.push((format!("@{}", name).into(), NodeMapValue::Arg(prop.value())));
|
||||||
|
entries.push((name.into(), NodeMapValue::Arg(prop.value())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add children (grouped by node name)
|
// Add children (grouped by node name)
|
||||||
|
|
@ -747,9 +751,9 @@ impl<'a> NodeMapAccess<'a> {
|
||||||
for key in child_keys {
|
for key in child_keys {
|
||||||
let group = child_groups.remove(key).unwrap();
|
let group = child_groups.remove(key).unwrap();
|
||||||
if group.len() == 1 {
|
if group.len() == 1 {
|
||||||
entries.push((key, NodeMapValue::SingleNode(group[0])));
|
entries.push((key.into(), NodeMapValue::SingleNode(group[0])));
|
||||||
} else {
|
} else {
|
||||||
entries.push((key, NodeMapValue::MultiNode(group)));
|
entries.push((key.into(), NodeMapValue::MultiNode(group)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -768,8 +772,11 @@ impl<'de, 'a> MapAccess<'de> for NodeMapAccess<'a> {
|
||||||
if self.idx >= self.entries.len() {
|
if self.idx >= self.entries.len() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let key = self.entries[self.idx].0;
|
match &self.entries[self.idx].0 {
|
||||||
seed.deserialize(str_deserializer(key)).map(Some)
|
Cow::Borrowed(s) => seed.deserialize(str_deserializer(s)),
|
||||||
|
Cow::Owned(s) => seed.deserialize(StringDeserializer::new(s.clone())),
|
||||||
|
}
|
||||||
|
.map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_value_seed<V: DeserializeSeed<'de>>(
|
fn next_value_seed<V: DeserializeSeed<'de>>(
|
||||||
|
|
@ -780,12 +787,57 @@ impl<'de, 'a> MapAccess<'de> for NodeMapAccess<'a> {
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
match value {
|
match value {
|
||||||
NodeMapValue::Arg(v) => seed.deserialize(ValueDeserializer { value: v }),
|
NodeMapValue::Arg(v) => seed.deserialize(ValueDeserializer { value: v }),
|
||||||
|
NodeMapValue::Args(entries) => seed.deserialize(ArgsSeqDeserializer {
|
||||||
|
entries: entries.clone(),
|
||||||
|
}),
|
||||||
NodeMapValue::SingleNode(node) => seed.deserialize(NodeDeserializer { node }),
|
NodeMapValue::SingleNode(node) => seed.deserialize(NodeDeserializer { node }),
|
||||||
NodeMapValue::MultiNode(nodes) => seed.deserialize(NodeGroupDeserializer { nodes }),
|
NodeMapValue::MultiNode(nodes) => seed.deserialize(NodeGroupDeserializer { nodes }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ArgsSeqDeserializer<'a> {
|
||||||
|
entries: Vec<&'a KdlEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a> Deserializer<'de> for ArgsSeqDeserializer<'a> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
|
||||||
|
visitor.visit_seq(ArgSeqAccess {
|
||||||
|
iter: self.entries.into_iter(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
|
||||||
|
visitor.visit_seq(ArgSeqAccess {
|
||||||
|
iter: self.entries.into_iter(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
|
||||||
|
if self.entries.is_empty() {
|
||||||
|
visitor.visit_none()
|
||||||
|
} else {
|
||||||
|
visitor.visit_some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_newtype_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error> {
|
||||||
|
visitor.visit_newtype_struct(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
serde::forward_to_deserialize_any! {
|
||||||
|
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
|
||||||
|
bytes byte_buf unit unit_struct tuple
|
||||||
|
tuple_struct map struct enum identifier ignored_any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct NodeEnumAccess<'a> {
|
struct NodeEnumAccess<'a> {
|
||||||
node: &'a KdlNode,
|
node: &'a KdlNode,
|
||||||
}
|
}
|
||||||
|
|
@ -1289,4 +1341,119 @@ h 8
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_props() {
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Server {
|
||||||
|
#[serde(rename = "@host")]
|
||||||
|
host: String,
|
||||||
|
#[serde(rename = "@port")]
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Config {
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
let kdl = r#"server host="localhost" port=8080"#;
|
||||||
|
let config: Config = from_str(kdl).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
config,
|
||||||
|
Config {
|
||||||
|
server: Server {
|
||||||
|
host: "localhost".into(),
|
||||||
|
port: 8080,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_args() {
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Server {
|
||||||
|
#[serde(rename = "$argument1")]
|
||||||
|
host: String,
|
||||||
|
#[serde(rename = "@port")]
|
||||||
|
port: u16,
|
||||||
|
#[serde(rename = "$arguments")]
|
||||||
|
all: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Config {
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
let kdl = r#"server "localhost" port=8080 "remote""#;
|
||||||
|
let config: Config = from_str(kdl).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
config,
|
||||||
|
Config {
|
||||||
|
server: Server {
|
||||||
|
host: "localhost".into(),
|
||||||
|
port: 8080,
|
||||||
|
all: Vec::from(["localhost".into(), "remote".into()])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_all_args() {
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Command {
|
||||||
|
#[serde(rename = "@name")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "$arguments")]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Config {
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
let kdl = r#"command name="run" "--verbose" "--output" "result.txt""#;
|
||||||
|
let config: Config = from_str(kdl).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
config,
|
||||||
|
Config {
|
||||||
|
command: Command {
|
||||||
|
name: "run".into(),
|
||||||
|
args: vec!["--verbose".into(), "--output".into(), "result.txt".into(),],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_children_args() {
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Server {
|
||||||
|
#[serde(rename = "@host")]
|
||||||
|
host: String,
|
||||||
|
#[serde(rename = "$arguments")]
|
||||||
|
ports: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
struct Config {
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
let kdl = r#"server host="localhost" 8080 8081 8082"#;
|
||||||
|
let config: Config = from_str(kdl).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
config,
|
||||||
|
Config {
|
||||||
|
server: Server {
|
||||||
|
host: "localhost".into(),
|
||||||
|
ports: vec![8080, 8081, 8082],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
334
src/se.rs
334
src/se.rs
|
|
@ -761,11 +761,24 @@ impl<'a> ser::SerializeStruct for NodeChildMapSerializer<'a> {
|
||||||
key: &'static str,
|
key: &'static str,
|
||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut child = KdlNode::new(key);
|
if let Some(attr_name) = key.strip_prefix('@') {
|
||||||
let mut child_ser = NodeValueSerializer { node: &mut child };
|
let kdl_val = to_kdl_value(value)?;
|
||||||
value.serialize(&mut child_ser)?;
|
self.node
|
||||||
let children = self.node.ensure_children();
|
.entries_mut()
|
||||||
children.nodes_mut().push(child);
|
.push(KdlEntry::new_prop(attr_name, kdl_val));
|
||||||
|
} else if key == "$arguments" {
|
||||||
|
let mut ser = ArgsSerializer { node: self.node };
|
||||||
|
value.serialize(&mut ser)?;
|
||||||
|
} else if key.starts_with("$argument") {
|
||||||
|
let kdl_val = to_kdl_value(value)?;
|
||||||
|
self.node.entries_mut().push(KdlEntry::new(kdl_val));
|
||||||
|
} else {
|
||||||
|
let mut child = KdlNode::new(key);
|
||||||
|
let mut child_ser = NodeValueSerializer { node: &mut child };
|
||||||
|
value.serialize(&mut child_ser)?;
|
||||||
|
let children = self.node.ensure_children();
|
||||||
|
children.nodes_mut().push(child);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1170,6 +1183,235 @@ impl ser::Serializer for KdlValueSerializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ArgsSerializer<'a> {
|
||||||
|
node: &'a mut KdlNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::Serializer for &'a mut ArgsSerializer<'a> {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
type SerializeSeq = ArgsSeqSerializer<'a>;
|
||||||
|
type SerializeTuple = ArgsSeqSerializer<'a>;
|
||||||
|
type SerializeTupleStruct = ArgsSeqSerializer<'a>;
|
||||||
|
type SerializeTupleVariant = ser::Impossible<(), Error>;
|
||||||
|
type SerializeMap = ser::Impossible<(), Error>;
|
||||||
|
type SerializeStruct = ser::Impossible<(), Error>;
|
||||||
|
type SerializeStructVariant = ser::Impossible<(), Error>;
|
||||||
|
|
||||||
|
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||||
|
Ok(ArgsSeqSerializer { node: self.node })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||||
|
self.serialize_seq(Some(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple_struct(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||||
|
self.serialize_seq(Some(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node.entries_mut().push(KdlEntry::new(v));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_i64(v as i64)
|
||||||
|
}
|
||||||
|
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_i64(v as i64)
|
||||||
|
}
|
||||||
|
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_i64(v as i64)
|
||||||
|
}
|
||||||
|
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node
|
||||||
|
.entries_mut()
|
||||||
|
.push(KdlEntry::new(KdlValue::Integer(v as i128)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_u64(v as u64)
|
||||||
|
}
|
||||||
|
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_u64(v as u64)
|
||||||
|
}
|
||||||
|
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_u64(v as u64)
|
||||||
|
}
|
||||||
|
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node
|
||||||
|
.entries_mut()
|
||||||
|
.push(KdlEntry::new(KdlValue::Integer(v as i128)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_f64(v as f64)
|
||||||
|
}
|
||||||
|
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node
|
||||||
|
.entries_mut()
|
||||||
|
.push(KdlEntry::new(KdlValue::Float(v)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_str(&v.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node
|
||||||
|
.entries_mut()
|
||||||
|
.push(KdlEntry::new(KdlValue::String(v.to_string())));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"bytes cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node.entries_mut().push(KdlEntry::new(KdlValue::Null));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
|
||||||
|
value.serialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.node.entries_mut().push(KdlEntry::new(KdlValue::Null));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit_variant(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
_: u32,
|
||||||
|
variant: &'static str,
|
||||||
|
) -> Result<Self::Ok, Self::Error> {
|
||||||
|
self.serialize_str(variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
value: &T,
|
||||||
|
) -> Result<Self::Ok, Self::Error> {
|
||||||
|
value.serialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
_: u32,
|
||||||
|
_: &'static str,
|
||||||
|
_: &T,
|
||||||
|
) -> Result<Self::Ok, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"newtype variants cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"maps are cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_struct(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"structs are cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_struct_variant(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
_: u32,
|
||||||
|
_: &'static str,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"struct variants cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple_variant(
|
||||||
|
self,
|
||||||
|
_: &'static str,
|
||||||
|
_: u32,
|
||||||
|
_: &'static str,
|
||||||
|
_: usize,
|
||||||
|
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||||
|
Err(ser::Error::custom(
|
||||||
|
"tuple variants cannot be represented as KDL arguments",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArgsSeqSerializer<'a> {
|
||||||
|
node: &'a mut KdlNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeSeq for ArgsSeqSerializer<'a> {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
||||||
|
let kdl_val = to_kdl_value(value)?;
|
||||||
|
self.node.entries_mut().push(KdlEntry::new(kdl_val));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeTuple for ArgsSeqSerializer<'a> {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
||||||
|
ser::SerializeSeq::serialize_element(self, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
ser::SerializeSeq::end(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeTupleStruct for ArgsSeqSerializer<'a> {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
||||||
|
ser::SerializeSeq::serialize_element(self, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
ser::SerializeSeq::end(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -1323,4 +1565,86 @@ mod tests {
|
||||||
assert_eq!(doc.get("name").unwrap().get(0), Some(&"test".into()));
|
assert_eq!(doc.get("name").unwrap().get(0), Some(&"test".into()));
|
||||||
assert_eq!(doc.get("port").unwrap().get(0), Some(&3000.into()));
|
assert_eq!(doc.get("port").unwrap().get(0), Some(&3000.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_props() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Server {
|
||||||
|
#[serde(rename = "@host")]
|
||||||
|
host: String,
|
||||||
|
#[serde(rename = "@port")]
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Config {
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
server: Server {
|
||||||
|
host: "localhost".into(),
|
||||||
|
port: 8080,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let kdl = to_string(&config).unwrap();
|
||||||
|
assert!(kdl.contains("server"));
|
||||||
|
assert!(kdl.contains("host=localhost"));
|
||||||
|
assert!(kdl.contains("port=8080"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_args() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Server {
|
||||||
|
#[serde(rename = "$argument1")]
|
||||||
|
host: String,
|
||||||
|
#[serde(rename = "@port")]
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Config {
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
server: Server {
|
||||||
|
host: "localhost".into(),
|
||||||
|
port: 8080,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let kdl = to_string(&config).unwrap();
|
||||||
|
assert!(kdl.contains("server"));
|
||||||
|
assert!(kdl.contains("localhost"));
|
||||||
|
assert!(kdl.contains("port=8080"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_all_args() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Command {
|
||||||
|
#[serde(rename = "@name")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "$arguments")]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Config {
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
command: Command {
|
||||||
|
name: "run".into(),
|
||||||
|
args: vec!["--verbose".into(), "--output".into()],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let kdl = to_string(&config).unwrap();
|
||||||
|
assert!(kdl.contains("command"));
|
||||||
|
assert!(kdl.contains("name=run"));
|
||||||
|
assert!(kdl.contains("--verbose"));
|
||||||
|
assert!(kdl.contains("--output"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue