diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs
index 66fe052b..35853243 100644
--- a/serde_macros/src/de.rs
+++ b/serde_macros/src/de.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use syntax::ast::{
Ident,
MetaItem,
@@ -353,7 +355,8 @@ fn deserialize_item_enum(
cx,
builder,
enum_def.variants.iter()
- .map(|variant| builder.expr().str(variant.node.name))
+ .map(|variant|
+ field::FieldLit::Global(builder.expr().str(variant.node.name)))
.collect()
);
@@ -531,7 +534,7 @@ fn deserialize_struct_variant(
fn deserialize_field_visitor(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
- field_exprs: Vec
>,
+ field_exprs: Vec,
) -> Vec> {
// Create the field names for the fields.
let field_idents: Vec = (0 .. field_exprs.len())
@@ -548,14 +551,80 @@ fn deserialize_field_visitor(
)
.build();
+
+ let fmts = field_exprs.iter()
+ .fold(HashSet::new(), |mut set, field_expr|
+ match field_expr {
+ &field::FieldLit::Format{ref formats, default: _} => {
+ for (fmt, _) in formats.iter() {
+ set.insert(fmt.clone());
+ };
+ set
+ },
+ _ => set
+ });
+
// Match arms to extract a field from a string
- let field_arms: Vec<_> = field_idents.iter()
- .zip(field_exprs.into_iter())
+ let default_field_arms: Vec<_> = field_idents.iter()
+ .zip(field_exprs.iter())
.map(|(field_ident, field_expr)| {
- quote_arm!(cx, $field_expr => { Ok(__Field::$field_ident) })
+ match field_expr {
+ &field::FieldLit::Global(ref expr) =>
+ quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }),
+ &field::FieldLit::Format{formats: _, ref default} =>
+ quote_arm!(cx, $default => { Ok(__Field::$field_ident)})
+ }
})
.collect();
+ let body = if fmts.is_empty() {
+ quote_expr!(cx,
+ match value {
+ $default_field_arms,
+ _ => Err(::serde::de::Error::unknown_field_error(value)),
+ })
+ } else {
+ let field_arms : Vec<_> = fmts.iter()
+ .map(|fmt| {
+ field_idents.iter()
+ .zip(field_exprs.iter())
+ .map(|(field_ident, field_expr)| {
+ match field_expr {
+ &field::FieldLit::Global(ref expr) =>
+ quote_arm!(cx,
+ $expr => { Ok(__Field::$field_ident) }),
+ &field::FieldLit::Format{ref formats, ref default} => {
+ let expr = formats.get(fmt).unwrap_or(default);
+ quote_arm!(cx,
+ $expr => { Ok(__Field::$field_ident) })}}
+ })
+ .collect::>()
+ })
+ .collect();
+
+ let fmt_matches : Vec<_> = fmts.iter()
+ .zip(field_arms.iter())
+ .map(|(ref fmt, ref arms)| {
+ quote_arm!(cx, $fmt => {
+ match value {
+ $arms,
+ _ => {
+ Err(::serde::de::Error::unknown_field_error(value))
+ }
+ }})
+ })
+ .collect();
+
+ quote_expr!(cx,
+ match D::fmt() {
+ $fmt_matches,
+ _ => match value {
+ $default_field_arms,
+ _ => Err(::serde::de::Error::unknown_field_error(value)),
+ }
+ })
+ };
+
vec![
field_enum,
@@ -565,26 +634,30 @@ fn deserialize_field_visitor(
fn deserialize(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error>
where D: ::serde::de::Deserializer,
{
- struct __FieldVisitor;
+ use std::marker::PhantomData;
- impl ::serde::de::Visitor for __FieldVisitor {
+ struct __FieldVisitor {
+ phantom: PhantomData
+ }
+
+ impl ::serde::de::Visitor for __FieldVisitor
+ where D: ::serde::de::Deserializer
+ {
type Value = __Field;
fn visit_str(&mut self, value: &str) -> ::std::result::Result<__Field, E>
where E: ::serde::de::Error,
{
- match value {
- $field_arms
- _ => Err(::serde::de::Error::unknown_field_error(value)),
- }
+ $body
}
}
- deserializer.visit(__FieldVisitor)
+ deserializer.visit(
+ __FieldVisitor::{ phantom: PhantomData })
}
}
).unwrap(),
- ]
+ ]
}
fn deserialize_struct_visitor(
@@ -596,7 +669,7 @@ fn deserialize_struct_visitor(
let field_visitor = deserialize_field_visitor(
cx,
builder,
- field::struct_field_strs(cx, builder, struct_def, field::Direction::Deserialize),
+ field::struct_field_strs(cx, builder, struct_def),
);
let visit_map_expr = deserialize_map(
@@ -639,11 +712,16 @@ fn deserialize_map(
let extract_values: Vec> = field_names.iter()
.zip(struct_def.fields.iter())
.map(|(field_name, field)| {
- let rename = field::field_rename(field, &field::Direction::Deserialize);
+ let rename = field::field_rename(builder, field);
let name_str = match (rename, field.node.kind) {
- (Some(rename), _) => builder.expr().build_lit(P(rename.clone())),
- (None, ast::NamedField(name, _)) => builder.expr().str(name),
- (None, ast::UnnamedField(_)) => panic!("struct contains unnamed fields"),
+ (field::Rename::Global(rename), _)
+ => builder.expr().build_lit(P(rename.clone())),
+ (field::Rename::None, ast::NamedField(name, _))
+ => builder.expr().str(name),
+ (field::Rename::None, ast::UnnamedField(_))
+ => panic!("struct contains unnamed fields"),
+ (field::Rename::Format(renames), _)
+ => builder.expr().str("fixme"),
};
let missing_expr = if field::default_value(field) {
diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs
index c35e694b..95ae8d6b 100644
--- a/serde_macros/src/field.rs
+++ b/serde_macros/src/field.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
@@ -5,19 +7,17 @@ use syntax::ptr::P;
use aster;
-pub enum Direction {
- Serialize,
- Deserialize,
+pub enum Rename<'a> {
+ None,
+ Global(&'a ast::Lit),
+ // Format(HashMap)
+ Format(HashMap, &'a ast::Lit>)
}
pub fn field_rename<'a>(
+ builder: &aster::AstBuilder,
field: &'a ast::StructField,
- direction: &Direction,
-) -> Option<&'a ast::Lit> {
- let dir_attr = match *direction {
- Direction::Serialize => "rename_serialize",
- Direction::Deserialize => "rename_deserialize",
- };
+) -> Rename<'a> {
field.node.attrs.iter()
.find(|sa| {
if let ast::MetaList(ref n, _) = sa.node.value.node {
@@ -30,47 +30,99 @@ pub fn field_rename<'a>(
if let ast::MetaList(_, ref vals) = sa.node.value.node {
attr::mark_used(&sa);
vals.iter().fold(None, |v, mi| {
- if let ast::MetaNameValue(ref n, ref lit) = mi.node {
- if n == &"rename" || n == &dir_attr {
- Some(lit)
- } else {
- v
- }
- } else {
- v
+ match mi.node {
+ ast::MetaNameValue(ref n, ref lit) => {
+ if n == &"rename" {
+ Some(Rename::Global(lit))
+ } else {
+ v
+ }
+ },
+ ast::MetaList(ref n, ref items) => {
+ if n == &"rename" {
+ let mut m = HashMap::new();
+ m.extend(
+ items.iter()
+ .filter_map(
+ |item|
+ match item.node {
+ ast::MetaNameValue(ref n, ref lit) =>
+ Some((// (n.to_owned(), lit)
+ builder.expr().str(n),
+ lit
+ )),
+ _ => None
+ }));
+ Some(Rename::Format(m))
+ } else {
+ v
+ }
+ },
+ _ => {v}
}
})
} else {
None
}
})
+ .unwrap_or(Rename::None)
+}
+
+pub enum FieldLit {
+ Global(P),
+ Format{
+ formats: HashMap, P>,
+ default: P,
+ }
}
pub fn struct_field_strs(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_def: &ast::StructDef,
- direction: Direction,
-) -> Vec> {
+) -> Vec {
struct_def.fields.iter()
.map(|field| {
- match field_rename(field, &direction) {
- Some(rename) => builder.expr().build_lit(P(rename.clone())),
- None => {
- match field.node.kind {
- ast::NamedField(name, _) => {
- builder.expr().str(name)
- }
- ast::UnnamedField(_) => {
- cx.bug("struct has named and unnamed fields")
- }
+ match field_rename(builder, field) {
+ Rename::Global(rename) =>
+ FieldLit::Global(
+ builder.expr().build_lit(P(rename.clone()))),
+ Rename::Format(renames) => {
+ let mut res = HashMap::new();
+ res.extend(
+ renames.into_iter()
+ .map(|(k,v)|
+ (k, builder.expr().build_lit(P(v.clone())))));
+ FieldLit::Format{
+ formats: res,
+ default: default_field(cx, builder, field.node.kind),
}
+ },
+ Rename::None => {
+ FieldLit::Global(
+ default_field(cx, builder, field.node.kind))
}
}
})
.collect()
}
+fn default_field(
+ cx: &ExtCtxt,
+ builder: &aster::AstBuilder,
+ kind: ast::StructFieldKind,
+) -> P {
+ match kind {
+ ast::NamedField(name, _) => {
+ builder.expr().str(name)
+ }
+ ast::UnnamedField(_) => {
+ cx.bug("struct has named and unnamed fields")
+ }
+ }
+}
+
+
pub fn default_value(field: &ast::StructField) -> bool {
field.node.attrs.iter()
.any(|sa| {
diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs
index d9c58d02..be0896d7 100644
--- a/serde_macros/src/ser.rs
+++ b/serde_macros/src/ser.rs
@@ -13,7 +13,7 @@ use syntax::ptr::P;
use aster;
-use field::{Direction, struct_field_strs};
+use field::{FieldLit, struct_field_strs};
pub fn expand_derive_serialize(
cx: &mut ExtCtxt,
@@ -517,12 +517,29 @@ fn serialize_struct_visitor(
{
let len = struct_def.fields.len();
- let key_exprs = struct_field_strs(cx, builder, struct_def, Direction::Serialize);
+ let key_exprs = struct_field_strs(cx, builder, struct_def);
- let arms: Vec = key_exprs.iter()
+ let arms: Vec = key_exprs.into_iter()
.zip(value_exprs)
.enumerate()
- .map(|(i, (key_expr, value_expr))| {
+ .map(|(i, (field, value_expr))| {
+ let key_expr = match field {
+ FieldLit::Global(x) => x,
+ FieldLit::Format{formats, default} => {
+ let arms = formats.iter()
+ .map(|(fmt, lit)| {
+ quote_arm!(cx, $fmt => { $lit })
+ })
+ .collect::>();
+ quote_expr!(cx,
+ {
+ match S::fmt() {
+ $arms,
+ _ => $default
+ }
+ })
+ },
+ };
quote_arm!(cx,
$i => {
self.state += 1;
diff --git a/src/de/mod.rs b/src/de/mod.rs
index b10f12c0..091adbc6 100644
--- a/src/de/mod.rs
+++ b/src/de/mod.rs
@@ -113,6 +113,10 @@ pub trait Deserializer {
{
self.visit(visitor)
}
+
+ fn fmt() -> &'static str {
+ ""
+ }
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/json/de.rs b/src/json/de.rs
index 532d46e3..dde68ed2 100644
--- a/src/json/de.rs
+++ b/src/json/de.rs
@@ -463,6 +463,11 @@ impl de::Deserializer for Deserializer
Err(self.error(ErrorCode::ExpectedSomeValue))
}
}
+
+ #[inline]
+ fn fmt() -> &'static str {
+ "json"
+ }
}
struct SeqVisitor<'a, Iter: 'a + Iterator- >> {
diff --git a/src/json/ser.rs b/src/json/ser.rs
index 824af96a..5537859e 100644
--- a/src/json/ser.rs
+++ b/src/json/ser.rs
@@ -256,6 +256,11 @@ impl ser::Serializer for Serializer
try!(self.formatter.colon(&mut self.writer));
value.serialize(self)
}
+
+ #[inline]
+ fn fmt() -> &'static str {
+ "json"
+ }
}
pub trait Formatter {
diff --git a/src/json/value.rs b/src/json/value.rs
index 6d041343..c9b8afc0 100644
--- a/src/json/value.rs
+++ b/src/json/value.rs
@@ -571,6 +571,11 @@ impl ser::Serializer for Serializer {
Ok(())
}
+
+ #[inline]
+ fn fmt() -> &'static str {
+ "value"
+ }
}
pub struct Deserializer {
@@ -677,6 +682,11 @@ impl de::Deserializer for Deserializer {
None => Ok(value)
}
}
+
+ #[inline]
+ fn fmt() -> &'static str {
+ "value"
+ }
}
struct SeqDeserializer<'a> {
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 5c317398..432d2535 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -179,6 +179,10 @@ pub trait Serializer {
fn visit_map_elt(&mut self, key: K, value: V) -> Result<(), Self::Error>
where K: Serialize,
V: Serialize;
+
+ fn fmt() -> &'static str {
+ ""
+ }
}
pub trait SeqVisitor {
diff --git a/tests/test_annotations.rs b/tests/test_annotations.rs
index 2bc9a336..5f3d37b6 100644
--- a/tests/test_annotations.rs
+++ b/tests/test_annotations.rs
@@ -21,9 +21,9 @@ struct Rename {
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
-struct DirectionRename {
+struct FormatRename {
a1: i32,
- #[serde(rename_serialize="a3", rename_deserialize="a4")]
+ #[serde(rename(xml= "a4", json="a5"))]
a2: i32,
}
@@ -47,11 +47,11 @@ fn test_rename() {
}
#[test]
-fn test_direction_rename() {
- let value = DirectionRename { a1: 1, a2: 2 };
+fn test_format_rename() {
+ let value = FormatRename { a1: 1, a2: 2 };
let serialized_value = json::to_string(&value).unwrap();
- assert_eq!(serialized_value, "{\"a1\":1,\"a3\":2}");
+ assert_eq!(serialized_value, "{\"a1\":1,\"a5\":2}");
- let deserialized_value = json::from_str("{\"a1\":1,\"a4\":2}").unwrap();
+ let deserialized_value = json::from_str("{\"a1\":1,\"a5\":2}").unwrap();
assert_eq!(value, deserialized_value);
}
diff --git a/tests/test_json.rs b/tests/test_json.rs
index ba45d974..bbead941 100644
--- a/tests/test_json.rs
+++ b/tests/test_json.rs
@@ -1024,7 +1024,7 @@ fn test_missing_field() {
fn test_missing_renamed_field() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo {
- #[serde(rename_deserialize="y")]
+ #[serde(rename="y")]
x: Option,
}
@@ -1042,3 +1042,26 @@ fn test_missing_renamed_field() {
))).unwrap();
assert_eq!(value, Foo { x: Some(5) });
}
+
+#[test]
+fn test_missing_fmt_renamed_field() {
+ #[derive(Debug, PartialEq, Deserialize)]
+ struct Foo {
+ #[serde(rename(json="y", value="z"))]
+ x: Option,
+ }
+
+ let value: Foo = from_str("{}").unwrap();
+ assert_eq!(value, Foo { x: None });
+
+ let value: Foo = from_str("{\"y\": 5}").unwrap();
+ assert_eq!(value, Foo { x: Some(5) });
+
+ let value: Foo = from_value(Value::Object(treemap!())).unwrap();
+ assert_eq!(value, Foo { x: None });
+
+ let value : Foo = from_value(Value::Object(treemap!(
+ "z".to_string() => Value::I64(5)
+ ))).unwrap();
+ assert_eq!(value, Foo { x: Some(5) });
+}