@@ -200,12 +200,7 @@ impl<'a, 'w, W: io::Write> Serializer for &'a mut SeRecord<'w, W> {
200200 self ,
201201 _len : Option < usize > ,
202202 ) -> Result < Self :: SerializeMap , Self :: Error > {
203- // The right behavior for serializing maps isn't clear.
204- Err ( Error :: custom (
205- "serializing maps is not supported, \
206- if you have a use case, please file an issue at \
207- https://github.com/BurntSushi/rust-csv",
208- ) )
203+ Ok ( self )
209204 }
210205
211206 fn serialize_struct (
@@ -299,18 +294,18 @@ impl<'a, 'w, W: io::Write> SerializeMap for &'a mut SeRecord<'w, W> {
299294 & mut self ,
300295 _key : & T ,
301296 ) -> Result < ( ) , Self :: Error > {
302- unreachable ! ( )
297+ Ok ( ( ) )
303298 }
304299
305300 fn serialize_value < T : ?Sized + Serialize > (
306301 & mut self ,
307- _value : & T ,
302+ value : & T ,
308303 ) -> Result < ( ) , Self :: Error > {
309- unreachable ! ( )
304+ value . serialize ( & mut * * self )
310305 }
311306
312307 fn end ( self ) -> Result < Self :: Ok , Self :: Error > {
313- unreachable ! ( )
308+ Ok ( ( ) )
314309 }
315310}
316311
@@ -646,12 +641,7 @@ impl<'a, 'w, W: io::Write> Serializer for &'a mut SeHeader<'w, W> {
646641 self ,
647642 _len : Option < usize > ,
648643 ) -> Result < Self :: SerializeMap , Self :: Error > {
649- // The right behavior for serializing maps isn't clear.
650- Err ( Error :: custom (
651- "serializing maps is not supported, \
652- if you have a use case, please file an issue at \
653- https://github.com/BurntSushi/rust-csv",
654- ) )
644+ self . handle_container ( "map" )
655645 }
656646
657647 fn serialize_struct (
@@ -743,20 +733,37 @@ impl<'a, 'w, W: io::Write> SerializeMap for &'a mut SeHeader<'w, W> {
743733
744734 fn serialize_key < T : ?Sized + Serialize > (
745735 & mut self ,
746- _key : & T ,
736+ key : & T ,
747737 ) -> Result < ( ) , Self :: Error > {
748- unreachable ! ( )
738+ // Grab old state and update state to `EncounteredStructField`.
739+ let old_state =
740+ mem:: replace ( & mut self . state , HeaderState :: EncounteredStructField ) ;
741+ if let HeaderState :: ErrorIfWrite ( err) = old_state {
742+ return Err ( err) ;
743+ }
744+
745+ let mut key_serializer = SeRecord { wtr : self . wtr } ;
746+ key. serialize ( & mut key_serializer) ?;
747+ self . state = HeaderState :: InStructField ;
748+ Ok ( ( ) )
749749 }
750750
751751 fn serialize_value < T : ?Sized + Serialize > (
752752 & mut self ,
753- _value : & T ,
753+ value : & T ,
754754 ) -> Result < ( ) , Self :: Error > {
755- unreachable ! ( )
755+ if !matches ! ( self . state, HeaderState :: InStructField ) {
756+ return Err ( Error :: new ( ErrorKind :: Serialize (
757+ "Attempted to serialize value without key" . to_string ( ) ,
758+ ) ) ) ;
759+ }
760+ value. serialize ( & mut * * self ) ?;
761+ self . state = HeaderState :: EncounteredStructField ;
762+ Ok ( ( ) )
756763 }
757764
758765 fn end ( self ) -> Result < Self :: Ok , Self :: Error > {
759- unreachable ! ( )
766+ Ok ( ( ) )
760767 }
761768}
762769
@@ -809,6 +816,8 @@ impl<'a, 'w, W: io::Write> SerializeStructVariant for &'a mut SeHeader<'w, W> {
809816
810817#[ cfg( test) ]
811818mod tests {
819+ use std:: collections:: BTreeMap ;
820+
812821 use { bstr:: ByteSlice , serde:: Serialize } ;
813822
814823 use crate :: {
@@ -1325,4 +1334,95 @@ mod tests {
13251334 assert ! ( wrote) ;
13261335 assert_eq ! ( got, "label,num,label2,value,empty,label,num" ) ;
13271336 }
1337+
1338+ #[ test]
1339+ fn flatten ( ) {
1340+ #[ derive( Clone , Serialize , Debug , PartialEq ) ]
1341+ struct Input {
1342+ x : f64 ,
1343+ y : f64 ,
1344+ }
1345+
1346+ #[ derive( Clone , Serialize , Debug , PartialEq ) ]
1347+ struct Properties {
1348+ prop1 : f64 ,
1349+ prop2 : f64 ,
1350+ }
1351+
1352+ #[ derive( Clone , Serialize , Debug , PartialEq ) ]
1353+ struct Row {
1354+ #[ serde( flatten) ]
1355+ input : Input ,
1356+ #[ serde( flatten) ]
1357+ properties : Properties ,
1358+ }
1359+ let row = Row {
1360+ input : Input { x : 1.0 , y : 2.0 } ,
1361+ properties : Properties { prop1 : 3.0 , prop2 : 4.0 } ,
1362+ } ;
1363+
1364+ let got = serialize ( row. clone ( ) ) ;
1365+ assert_eq ! ( got, "1.0,2.0,3.0,4.0\n " ) ;
1366+
1367+ let ( wrote, got) = serialize_header ( row. clone ( ) ) ;
1368+ assert ! ( wrote) ;
1369+ assert_eq ! ( got, "x,y,prop1,prop2" ) ;
1370+ }
1371+
1372+ #[ test]
1373+ fn flatten_map ( ) {
1374+ #[ derive( Clone , Serialize , Debug , PartialEq ) ]
1375+ struct Row {
1376+ x : f64 ,
1377+ y : f64 ,
1378+ #[ serde( flatten) ]
1379+ extra : BTreeMap < & ' static str , f64 > ,
1380+ }
1381+ let mut extra = BTreeMap :: new ( ) ;
1382+ extra. insert ( "extra1" , 3.0 ) ;
1383+ extra. insert ( "extra2" , 4.0 ) ;
1384+ let row = Row { x : 1.0 , y : 2.0 , extra } ;
1385+
1386+ let got = serialize ( row. clone ( ) ) ;
1387+ assert_eq ! ( got, format!( "1.0,2.0,3.0,4.0\n " ) ) ;
1388+
1389+ let ( wrote, got) = serialize_header ( row. clone ( ) ) ;
1390+ assert ! ( wrote) ;
1391+ assert_eq ! ( got, format!( "x,y,extra1,extra2" ) ) ;
1392+ }
1393+
1394+ #[ test]
1395+ fn flatten_map_different_num_entries ( ) {
1396+ #[ derive( Clone , Serialize , Debug , PartialEq ) ]
1397+ struct Row {
1398+ x : f64 ,
1399+ y : f64 ,
1400+ #[ serde( flatten) ]
1401+ extra : BTreeMap < & ' static str , f64 > ,
1402+ }
1403+ let mut wtr = Writer :: from_writer ( vec ! [ ] ) ;
1404+
1405+ let mut extra = BTreeMap :: new ( ) ;
1406+ extra. insert ( "extra1" , 3.0 ) ;
1407+ extra. insert ( "extra2" , 4.0 ) ;
1408+ let row = Row { x : 1.0 , y : 2.0 , extra } ;
1409+ wtr. serialize ( row) . unwrap ( ) ;
1410+
1411+ let mut extra = BTreeMap :: new ( ) ;
1412+ extra. insert ( "extra3" , 3.0 ) ;
1413+ extra. insert ( "extra4" , 4.0 ) ;
1414+ extra. insert ( "extra5" , 5.0 ) ;
1415+ let row = Row { x : 1.0 , y : 2.0 , extra } ;
1416+ let error = wtr. serialize ( row) . unwrap_err ( ) ;
1417+ match * error. kind ( ) {
1418+ ErrorKind :: UnequalLengths {
1419+ pos : None ,
1420+ expected_len : 4 ,
1421+ len : 5 ,
1422+ } => { }
1423+ ref x => {
1424+ panic ! ( "expected ErrorKind::UnequalLengths but got '{x:?}'" )
1425+ }
1426+ }
1427+ }
13281428}
0 commit comments