Skip to content

Commit ac1e077

Browse files
committed
feat(serde): Support chars as keys
Fixes #626
1 parent f3dec32 commit ac1e077

File tree

6 files changed

+195
-6
lines changed

6 files changed

+195
-6
lines changed

crates/toml/src/de/deserializer/key.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ impl<'de> serde_core::de::Deserializer<'de> for KeyDeserializer<'de> {
120120
key.into_deserializer().deserialize_u128(visitor)
121121
}
122122

123+
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
124+
where
125+
V: serde_core::de::Visitor<'de>,
126+
{
127+
let key: char = self.key.parse().map_err(serde_core::de::Error::custom)?;
128+
key.into_deserializer().deserialize_char(visitor)
129+
}
130+
123131
fn deserialize_enum<V>(
124132
self,
125133
name: &str,
@@ -165,7 +173,7 @@ impl<'de> serde_core::de::Deserializer<'de> for KeyDeserializer<'de> {
165173
}
166174

167175
serde_core::forward_to_deserialize_any! {
168-
f32 f64 char str string seq
176+
f32 f64 str string seq
169177
bytes byte_buf map option unit
170178
ignored_any unit_struct tuple_struct tuple identifier
171179
}

crates/toml/src/ser/value/key.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ impl serde_core::ser::Serializer for KeySerializer<'_> {
8181
Err(Error::key_not_string())
8282
}
8383

84-
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
85-
Err(Error::key_not_string())
84+
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
85+
let mut b = [0; 4];
86+
let result = v.encode_utf8(&mut b);
87+
self.dst.key(&*result)?;
88+
Ok(())
8689
}
8790

8891
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {

crates/toml/tests/serde/de_key.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,67 @@ Document {
273273
assert_data_eq!(result.unwrap().to_debug(), expected);
274274
}
275275
}
276+
277+
mod char_key {
278+
use super::*;
279+
280+
type Map = super::Map<char>;
281+
type Document = super::Document<char>;
282+
283+
#[test]
284+
fn from_str() {
285+
let input = "k = 'value'";
286+
let expected = str![[r#"
287+
{
288+
'k': "value",
289+
}
290+
291+
"#]];
292+
let result = crate::from_str::<Map>(input);
293+
assert_data_eq!(result.unwrap().to_debug(), expected);
294+
}
295+
296+
#[test]
297+
fn value_from_inline_table() {
298+
let input = "{ k = 'value' }";
299+
let expected = str![[r#"
300+
{
301+
'k': "value",
302+
}
303+
304+
"#]];
305+
let result = crate::value_from_str::<Map>(input);
306+
assert_data_eq!(result.unwrap().to_debug(), expected);
307+
}
308+
309+
#[test]
310+
fn from_inline_table() {
311+
let input = "map = { k = 'value' }";
312+
let expected = str![[r#"
313+
Document {
314+
map: {
315+
'k': "value",
316+
},
317+
}
318+
319+
"#]];
320+
let result = crate::from_str::<Document>(input);
321+
assert_data_eq!(result.unwrap().to_debug(), expected);
322+
}
323+
324+
#[test]
325+
fn from_std_table() {
326+
let input = "[map]
327+
k = 'value'";
328+
let expected = str![[r#"
329+
Document {
330+
map: {
331+
'k': "value",
332+
},
333+
}
334+
335+
"#]];
336+
let result = crate::from_str::<Document>(input);
337+
assert_data_eq!(result.unwrap().to_debug(), expected);
338+
}
339+
}

crates/toml/tests/serde/ser_key.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,106 @@ mod i16_key {
453453
assert_eq!(json, input);
454454
}
455455
}
456+
457+
mod char_key {
458+
use super::*;
459+
460+
type Map = super::Map<char>;
461+
type Document = super::Document<char>;
462+
463+
fn key() -> char {
464+
'k'
465+
}
466+
467+
#[test]
468+
fn to_string_value() {
469+
let expected = str![[r#"{ k = "value" }"#]];
470+
let input = [(key(), "value".to_owned())].into_iter().collect::<Map>();
471+
let toml = t!(crate::to_string_value(&input));
472+
assert_data_eq!(&toml, expected);
473+
let roundtrip = t!(crate::value_from_str::<Map>(&toml));
474+
assert_eq!(roundtrip, input);
475+
let json = json_from_toml_value_str::<Map>(&toml);
476+
assert_eq!(json, input);
477+
}
478+
479+
#[test]
480+
fn to_string() {
481+
let expected = str![[r#"
482+
k = "value"
483+
484+
"#]];
485+
let input = [(key(), "value".to_owned())].into_iter().collect::<Map>();
486+
let toml = t!(crate::to_string(&input));
487+
assert_data_eq!(&toml, expected);
488+
let roundtrip = t!(crate::from_str::<Map>(&toml));
489+
assert_eq!(roundtrip, input);
490+
let json = json_from_toml_str::<Map>(&toml);
491+
assert_eq!(json, input);
492+
}
493+
494+
#[test]
495+
fn to_string_pretty() {
496+
let expected = str![[r#"
497+
k = "value"
498+
499+
"#]];
500+
let input = [(key(), "value".to_owned())].into_iter().collect::<Map>();
501+
let toml = t!(crate::to_string_pretty(&input));
502+
assert_data_eq!(&toml, expected);
503+
let roundtrip = t!(crate::from_str::<Map>(&toml));
504+
assert_eq!(roundtrip, input);
505+
let json = json_from_toml_str::<Map>(&toml);
506+
assert_eq!(json, input);
507+
}
508+
509+
#[test]
510+
fn nested_to_string_value() {
511+
let expected = str![[r#"{ map = { k = "value" } }"#]];
512+
let input = Document {
513+
map: [(key(), "value".to_owned())].into_iter().collect::<Map>(),
514+
};
515+
let toml = t!(crate::to_string_value(&input));
516+
assert_data_eq!(&toml, expected);
517+
let roundtrip = t!(crate::value_from_str::<Document>(&toml));
518+
assert_eq!(roundtrip, input);
519+
let json = json_from_toml_value_str::<Document>(&toml);
520+
assert_eq!(json, input);
521+
}
522+
523+
#[test]
524+
fn nested_to_string() {
525+
let expected = str![[r#"
526+
[map]
527+
k = "value"
528+
529+
"#]];
530+
let input = Document {
531+
map: [(key(), "value".to_owned())].into_iter().collect::<Map>(),
532+
};
533+
let toml = t!(crate::to_string(&input));
534+
assert_data_eq!(&toml, expected);
535+
let roundtrip = t!(crate::from_str::<Document>(&toml));
536+
assert_eq!(roundtrip, input);
537+
let json = json_from_toml_str::<Document>(&toml);
538+
assert_eq!(json, input);
539+
}
540+
541+
#[test]
542+
fn nested_to_string_pretty() {
543+
let expected = str![[r#"
544+
[map]
545+
k = "value"
546+
547+
"#]];
548+
let input = Document {
549+
map: [(key(), "value".to_owned())].into_iter().collect::<Map>(),
550+
};
551+
let toml = t!(crate::to_string_pretty(&input));
552+
assert_data_eq!(&toml, expected);
553+
let roundtrip = t!(crate::from_str::<Document>(&toml));
554+
assert_eq!(roundtrip, input);
555+
let json = json_from_toml_str::<Document>(&toml);
556+
assert_eq!(json, input);
557+
}
558+
}

crates/toml_edit/src/de/key.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ impl<'de> serde_core::de::Deserializer<'de> for KeyDeserializer {
152152
key.into_deserializer().deserialize_u128(visitor)
153153
}
154154

155+
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
156+
where
157+
V: serde_core::de::Visitor<'de>,
158+
{
159+
let key: char = self
160+
.key
161+
.parse()
162+
.map_err(<Error as serde_core::de::Error>::custom)?;
163+
key.into_deserializer().deserialize_char(visitor)
164+
}
165+
155166
fn deserialize_enum<V>(
156167
self,
157168
name: &str,
@@ -202,7 +213,7 @@ impl<'de> serde_core::de::Deserializer<'de> for KeyDeserializer {
202213
}
203214

204215
serde_core::forward_to_deserialize_any! {
205-
f32 f64 char str string seq
216+
f32 f64 str string seq
206217
bytes byte_buf map option unit
207218
ignored_any unit_struct tuple_struct tuple identifier
208219
}

crates/toml_edit/src/ser/key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ impl serde_core::ser::Serializer for KeySerializer {
6767
Err(Error::key_not_string())
6868
}
6969

70-
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
71-
Err(Error::key_not_string())
70+
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
71+
Ok(Key::new(v.to_string()))
7272
}
7373

7474
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {

0 commit comments

Comments
 (0)