Skip to content

Commit 5e6d787

Browse files
committed
auto merge of #4840 : jbclements/rust/add-json-enum-encoding, r=catamorphism
r? I added code to the JSON encoder to support the serialization of enums. Before this, the JSON serializer only handled Option, and encoded None as 'null'. Following this change, all enums are encoded as arrays containing the enum name followed by the encoded fields. This appears consistent with the unstated invariant that the resulting output can be mapped back to the input *if* there's a decoder around that knows the types that were in existence when the serialization occurred. Also, added test cases.
2 parents 7393fde + 9df11ae commit 5e6d787

File tree

2 files changed

+269
-19
lines changed

2 files changed

+269
-19
lines changed

src/libstd/json.rs

+107-8
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,57 @@ pub impl Encoder: serialize::Encoder {
121121
fn emit_owned(&self, f: fn()) { f() }
122122
fn emit_managed(&self, f: fn()) { f() }
123123

124-
fn emit_enum(&self, name: &str, f: fn()) {
125-
if name != "option" { die!(~"only supports option enum") }
124+
fn emit_enum(&self, _name: &str, f: fn()) {
126125
f()
127126
}
128-
fn emit_enum_variant(&self, _name: &str, id: uint, _cnt: uint, f: fn()) {
129-
if id == 0 {
130-
self.emit_nil();
127+
128+
fn emit_enum_variant(&self, name: &str, _id: uint, _cnt: uint, f: fn()) {
129+
// encoding of enums is special-cased for Option. Specifically:
130+
// Some(34) => 34
131+
// None => null
132+
133+
// other enums are encoded as vectors:
134+
// Kangaroo(34,"William") => ["Kangaroo",[34,"William"]]
135+
136+
// the default expansion for enums is more verbose than I'd like;
137+
// specifically, the inner pair of brackets seems superfluous,
138+
// BUT the design of the enumeration framework and the requirements
139+
// of the special-case for Option mean that a first argument must
140+
// be encoded "naked"--with no commas--and that the option name
141+
// can't be followed by just a comma, because there might not
142+
// be any elements in the tuple.
143+
144+
// FIXME #4872: this would be more precise and less frightening
145+
// with fully-qualified option names. To get that information,
146+
// we'd have to change the expansion of auto-encode to pass
147+
// those along.
148+
149+
if (name == ~"Some") {
150+
f();
151+
} else if (name == ~"None") {
152+
self.wr.write_str(~"null");
131153
} else {
132-
f()
154+
self.wr.write_char('[');
155+
self.wr.write_str(escape_str(name));
156+
self.wr.write_char(',');
157+
self.wr.write_char('[');
158+
f();
159+
self.wr.write_char(']');
160+
self.wr.write_char(']');
133161
}
134162
}
135-
fn emit_enum_variant_arg(&self, _idx: uint, f: fn()) {
136-
f()
163+
164+
fn emit_enum_variant_arg(&self, idx: uint, f: fn()) {
165+
if (idx != 0) {self.wr.write_char(',');}
166+
f();
137167
}
138168

139169
fn emit_borrowed_vec(&self, _len: uint, f: fn()) {
140170
self.wr.write_char('[');
141171
f();
142172
self.wr.write_char(']');
143173
}
174+
144175
fn emit_owned_vec(&self, len: uint, f: fn()) {
145176
self.emit_borrowed_vec(len, f)
146177
}
@@ -1180,6 +1211,8 @@ mod tests {
11801211
11811212
use core::result;
11821213
use core::hashmap::linear::LinearMap;
1214+
use core::cmp;
1215+
11831216
11841217
fn mk_object(items: &[(~str, Json)]) -> Json {
11851218
let mut d = ~LinearMap::new();
@@ -1247,6 +1280,72 @@ mod tests {
12471280
assert a == b;
12481281
}
12491282

1283+
// two fns copied from libsyntax/util/testing.rs.
1284+
// Should they be in their own crate?
1285+
pub pure fn check_equal_ptr<T : cmp::Eq> (given : &T, expected: &T) {
1286+
if !((given == expected) && (expected == given )) {
1287+
die!(fmt!("given %?, expected %?",given,expected));
1288+
}
1289+
}
1290+
1291+
pub pure fn check_equal<T : cmp::Eq> (given : T, expected: T) {
1292+
if !((given == expected) && (expected == given )) {
1293+
die!(fmt!("given %?, expected %?",given,expected));
1294+
}
1295+
}
1296+
1297+
// testing both auto_encode's calling patterns
1298+
// and json... not sure where to put these tests.
1299+
#[test]
1300+
fn test_write_enum () {
1301+
let bw = @io::BytesWriter {bytes: dvec::DVec(), pos: 0};
1302+
let bww : @io::Writer = (bw as @io::Writer);
1303+
let encoder = (@Encoder(bww) as @serialize::Encoder);
1304+
do encoder.emit_enum(~"animal") {
1305+
do encoder.emit_enum_variant (~"frog",37,1242) {
1306+
// name of frog:
1307+
do encoder.emit_enum_variant_arg (0) {
1308+
encoder.emit_owned_str(~"Henry")
1309+
}
1310+
// mass of frog in grams:
1311+
do encoder.emit_enum_variant_arg (1) {
1312+
encoder.emit_int(349);
1313+
}
1314+
}
1315+
}
1316+
check_equal(str::from_bytes(bw.bytes.data),
1317+
~"[\"frog\",[\"Henry\",349]]");
1318+
}
1319+
1320+
#[test]
1321+
fn test_write_some () {
1322+
let bw = @io::BytesWriter {bytes: dvec::DVec(), pos: 0};
1323+
let bww : @io::Writer = (bw as @io::Writer);
1324+
let encoder = (@Encoder(bww) as @serialize::Encoder);
1325+
do encoder.emit_enum(~"Option") {
1326+
do encoder.emit_enum_variant (~"Some",37,1242) {
1327+
do encoder.emit_enum_variant_arg (0) {
1328+
encoder.emit_owned_str(~"jodhpurs")
1329+
}
1330+
}
1331+
}
1332+
check_equal(str::from_bytes(bw.bytes.data),
1333+
~"\"jodhpurs\"");
1334+
}
1335+
1336+
#[test]
1337+
fn test_write_none () {
1338+
let bw = @io::BytesWriter {bytes: dvec::DVec(), pos: 0};
1339+
let bww : @io::Writer = (bw as @io::Writer);
1340+
let encoder = (@Encoder(bww) as @serialize::Encoder);
1341+
do encoder.emit_enum(~"Option") {
1342+
do encoder.emit_enum_variant (~"None",37,1242) {
1343+
}
1344+
}
1345+
check_equal(str::from_bytes(bw.bytes.data),
1346+
~"null");
1347+
}
1348+
12501349
#[test]
12511350
fn test_trailing_characters() {
12521351
assert from_str(~"nulla") ==

src/libsyntax/ext/auto_encode.rs

+162-11
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@ For example, a type like:
2323
2424
would generate two implementations like:
2525
26-
impl<S: Encoder> node_id: Encodable<S> {
27-
fn encode(s: &S) {
28-
do s.emit_struct("Node", 1) {
29-
s.emit_field("id", 0, || s.emit_uint(self))
30-
}
26+
impl<S: std::serialize::Encoder> Node: Encodable<S> {
27+
fn encode(&self, s: &S) {
28+
do s.emit_struct("Node", 1) {
29+
s.emit_field("id", 0, || s.emit_uint(self.id))
3130
}
3231
}
32+
}
3333
34-
impl<D: Decoder> node_id: Decodable {
35-
static fn decode(d: &D) -> Node {
36-
do d.read_struct("Node", 1) {
37-
Node {
38-
id: d.read_field(~"x", 0, || decode(d))
39-
}
34+
impl<D: Decoder> node_id: Decodable {
35+
static fn decode(d: &D) -> Node {
36+
do d.read_struct("Node", 1) {
37+
Node {
38+
id: d.read_field(~"x", 0, || decode(d))
4039
}
4140
}
4241
}
42+
}
4343
4444
Other interesting scenarios are whe the item has type parameters or
4545
references other non-built-in types. A type definition like:
@@ -1150,3 +1150,154 @@ fn mk_enum_deser_body(
11501150
]
11511151
)
11521152
}
1153+
1154+
1155+
#[cfg(test)]
1156+
mod test {
1157+
use std::serialize::Encodable;
1158+
use std::serialize::Encoder;
1159+
use core::dvec::*;
1160+
use util::testing::*;
1161+
use core::io;
1162+
use core::str;
1163+
use core::option::Option;
1164+
use core::option::Some;
1165+
use core::option::None;
1166+
use std;
1167+
1168+
// just adding the ones I want to test, for now:
1169+
#[deriving_eq]
1170+
pub enum call {
1171+
CallToEmitEnum(~str),
1172+
CallToEmitEnumVariant(~str, uint, uint),
1173+
CallToEmitEnumVariantArg(uint),
1174+
CallToEmitUint(uint),
1175+
CallToEmitNil,
1176+
// all of the ones I was too lazy to handle:
1177+
CallToOther
1178+
}
1179+
// using a mutable field rather than changing the
1180+
// type of self in every method of every encoder everywhere.
1181+
pub struct TestEncoder {mut call_log : ~[call]}
1182+
1183+
pub impl TestEncoder {
1184+
// these self's should be &mut self's, as well....
1185+
fn add_to_log (&self, c : call) {
1186+
self.call_log.push(copy c);
1187+
}
1188+
fn add_unknown_to_log (&self) {
1189+
self.add_to_log (CallToOther)
1190+
}
1191+
}
1192+
1193+
pub impl Encoder for TestEncoder {
1194+
fn emit_nil(&self) { self.add_to_log(CallToEmitNil) }
1195+
1196+
fn emit_uint(&self, +v: uint) {self.add_to_log(CallToEmitUint(v)); }
1197+
fn emit_u64(&self, +_v: u64) { self.add_unknown_to_log(); }
1198+
fn emit_u32(&self, +_v: u32) { self.add_unknown_to_log(); }
1199+
fn emit_u16(&self, +_v: u16) { self.add_unknown_to_log(); }
1200+
fn emit_u8(&self, +_v: u8) { self.add_unknown_to_log(); }
1201+
1202+
fn emit_int(&self, +_v: int) { self.add_unknown_to_log(); }
1203+
fn emit_i64(&self, +_v: i64) { self.add_unknown_to_log(); }
1204+
fn emit_i32(&self, +_v: i32) { self.add_unknown_to_log(); }
1205+
fn emit_i16(&self, +_v: i16) { self.add_unknown_to_log(); }
1206+
fn emit_i8(&self, +_v: i8) { self.add_unknown_to_log(); }
1207+
1208+
fn emit_bool(&self, +_v: bool) { self.add_unknown_to_log(); }
1209+
1210+
fn emit_f64(&self, +_v: f64) { self.add_unknown_to_log(); }
1211+
fn emit_f32(&self, +_v: f32) { self.add_unknown_to_log(); }
1212+
fn emit_float(&self, +_v: float) { self.add_unknown_to_log(); }
1213+
1214+
fn emit_char(&self, +_v: char) { self.add_unknown_to_log(); }
1215+
1216+
fn emit_borrowed_str(&self, +_v: &str) { self.add_unknown_to_log(); }
1217+
fn emit_owned_str(&self, +_v: &str) { self.add_unknown_to_log(); }
1218+
fn emit_managed_str(&self, +_v: &str) { self.add_unknown_to_log(); }
1219+
1220+
fn emit_borrowed(&self, f: fn()) { self.add_unknown_to_log(); f() }
1221+
fn emit_owned(&self, f: fn()) { self.add_unknown_to_log(); f() }
1222+
fn emit_managed(&self, f: fn()) { self.add_unknown_to_log(); f() }
1223+
1224+
fn emit_enum(&self, name: &str, f: fn()) {
1225+
self.add_to_log(CallToEmitEnum(name.to_str())); f(); }
1226+
1227+
fn emit_enum_variant(&self, name: &str, +id: uint,
1228+
+cnt: uint, f: fn()) {
1229+
self.add_to_log(CallToEmitEnumVariant (name.to_str(),id,cnt));
1230+
f();
1231+
}
1232+
1233+
fn emit_enum_variant_arg(&self, +idx: uint, f: fn()) {
1234+
self.add_to_log(CallToEmitEnumVariantArg (idx)); f();
1235+
}
1236+
1237+
fn emit_borrowed_vec(&self, +_len: uint, f: fn()) {
1238+
self.add_unknown_to_log(); f();
1239+
}
1240+
1241+
fn emit_owned_vec(&self, +_len: uint, f: fn()) {
1242+
self.add_unknown_to_log(); f();
1243+
}
1244+
fn emit_managed_vec(&self, +_len: uint, f: fn()) {
1245+
self.add_unknown_to_log(); f();
1246+
}
1247+
fn emit_vec_elt(&self, +_idx: uint, f: fn()) {
1248+
self.add_unknown_to_log(); f();
1249+
}
1250+
1251+
fn emit_rec(&self, f: fn()) {
1252+
self.add_unknown_to_log(); f();
1253+
}
1254+
fn emit_struct(&self, _name: &str, +_len: uint, f: fn()) {
1255+
self.add_unknown_to_log(); f();
1256+
}
1257+
fn emit_field(&self, _name: &str, +_idx: uint, f: fn()) {
1258+
self.add_unknown_to_log(); f();
1259+
}
1260+
1261+
fn emit_tup(&self, +_len: uint, f: fn()) {
1262+
self.add_unknown_to_log(); f();
1263+
}
1264+
fn emit_tup_elt(&self, +_idx: uint, f: fn()) {
1265+
self.add_unknown_to_log(); f();
1266+
}
1267+
}
1268+
1269+
1270+
#[auto_decode]
1271+
#[auto_encode]
1272+
struct Node {id: uint}
1273+
1274+
fn to_call_log (val: Encodable<TestEncoder>) -> ~[call] {
1275+
let mut te = TestEncoder {call_log: ~[]};
1276+
val.encode(&te);
1277+
te.call_log
1278+
}
1279+
/*
1280+
#[test] fn encode_test () {
1281+
check_equal (to_call_log(Node{id:34}
1282+
as Encodable::<std::json::Encoder>),
1283+
~[CallToEnum (~"Node"),
1284+
CallToEnumVariant]);
1285+
}
1286+
*/
1287+
#[auto_encode]
1288+
enum Written {
1289+
Book(uint,uint),
1290+
Magazine(~str)
1291+
}
1292+
1293+
#[test] fn encode_enum_test () {
1294+
check_equal (to_call_log(Book(34,44)
1295+
as Encodable::<TestEncoder>),
1296+
~[CallToEmitEnum (~"Written"),
1297+
CallToEmitEnumVariant (~"Book",0,2),
1298+
CallToEmitEnumVariantArg (0),
1299+
CallToEmitUint (34),
1300+
CallToEmitEnumVariantArg (1),
1301+
CallToEmitUint (44)]);
1302+
}
1303+
}

0 commit comments

Comments
 (0)