Skip to content
This repository has been archived by the owner on Mar 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #328 from dtolnay/taggedliteral
Browse files Browse the repository at this point in the history
Support deserializing tagged literal scalar into primitive
  • Loading branch information
dtolnay committed Sep 14, 2022
2 parents f08c55e + c68127f commit 129a24e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 6 deletions.
42 changes: 36 additions & 6 deletions src/de.rs
Expand Up @@ -1149,6 +1149,18 @@ where
}
}

fn is_plain_or_tagged_literal_scalar(
expected: &str,
scalar: &Scalar,
tagged_already: bool,
) -> bool {
match (scalar.style, &scalar.tag, tagged_already) {
(ScalarStyle::Plain, _, _) => true,
(ScalarStyle::Literal, Some(tag), false) => tag == expected,
_ => false,
}
}

fn invalid_type(event: &Event, exp: &dyn Expected) -> Error {
enum Void {}

Expand Down Expand Up @@ -1250,11 +1262,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_bool(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::BOOL, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(boolean) = parse_bool(value) {
break visitor.visit_bool(boolean);
Expand Down Expand Up @@ -1293,11 +1308,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_i64(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::INT, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(int) = parse_signed_int(value, i64::from_str_radix) {
break visitor.visit_i64(int);
Expand All @@ -1315,11 +1333,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_i128(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::INT, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(int) = parse_signed_int(value, i128::from_str_radix) {
break visitor.visit_i128(int);
Expand Down Expand Up @@ -1358,11 +1379,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_u64(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::INT, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(int) = parse_unsigned_int(value, u64::from_str_radix) {
break visitor.visit_u64(int);
Expand All @@ -1380,11 +1404,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_u128(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::INT, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(int) = parse_unsigned_int(value, u128::from_str_radix) {
break visitor.visit_u128(int);
Expand All @@ -1409,11 +1436,14 @@ impl<'de, 'document> de::Deserializer<'de> for &mut DeserializerFromEvents<'de,
where
V: Visitor<'de>,
{
let tagged_already = self.current_enum.is_some();
let (next, mark) = self.next_event_mark()?;
loop {
match next {
Event::Alias(mut pos) => break self.jump(&mut pos)?.deserialize_f64(visitor),
Event::Scalar(scalar) if scalar.style == ScalarStyle::Plain => {
Event::Scalar(scalar)
if is_plain_or_tagged_literal_scalar(Tag::FLOAT, scalar, tagged_already) =>
{
if let Ok(value) = str::from_utf8(&scalar.value) {
if let Some(float) = parse_f64(value) {
break visitor.visit_f64(float);
Expand Down
25 changes: 25 additions & 0 deletions tests/test_de.rs
Expand Up @@ -569,3 +569,28 @@ fn test_empty_scalar() {
};
test_de(yaml, &expected);
}

#[test]
fn test_python_safe_dump() {
#[derive(Deserialize, PartialEq, Debug)]
struct Frob {
foo: u32,
}

// This matches output produced by PyYAML's `yaml.safe_dump` when using the
// default_style parameter.
//
// >>> import yaml
// >>> d = {"foo": 7200}
// >>> print(yaml.safe_dump(d, default_style="|"))
// "foo": !!int |-
// 7200
//
let yaml = indoc! {r#"
"foo": !!int |-
7200
"#};

let expected = Frob { foo: 7200 };
test_de(yaml, &expected);
}

0 comments on commit 129a24e

Please sign in to comment.