Skip to content

Commit

Permalink
project: upgrade serde_yaml to 0.9, introduce singleton_yaml module
Browse files Browse the repository at this point in the history
Originally in 0.8, the yaml module serialized enum values the same way as
it's done in JSON: to a dict with a single field.

0.9 changed the backend, and also introduced the tagged serialization of
enums. Unfortunately this is not compatible with the original
serialization, and also breaks the json<->yaml bijection.
There's also a limtiation in serde regarding tagged and untagged enums:
dtolnay/serde-yaml#318

So we are staying on the singleton serialization, and I made a small
wrapper module for convenience.
  • Loading branch information
badicsalex committed Sep 2, 2022
1 parent f36f038 commit dddd67f
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -27,7 +27,7 @@ regex = "1"
roman = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
serde_yaml = "0.9"
tempfile = "3"
ureq = "2.4"

Expand Down
3 changes: 2 additions & 1 deletion src/bin/hun_law/main.rs
Expand Up @@ -31,6 +31,7 @@ use hun_law::{
structure::parse_act_structure,
},
structure::Act,
util::singleton_yaml,
};
use log::info;
use serde::Serialize;
Expand Down Expand Up @@ -197,7 +198,7 @@ trait CliOutput: Sized + Serialize {
OutputType::Plain => self.cli_output_plain(false, target)?,
OutputType::TestPlain => self.cli_output_plain(true, target)?,
OutputType::Json => serde_json::to_writer(target, &self)?,
OutputType::Yaml => serde_yaml::to_writer(target, &self)?,
OutputType::Yaml => singleton_yaml::to_writer(target, &self)?,
};
Ok(())
}
Expand Down
6 changes: 3 additions & 3 deletions src/fixups.rs
Expand Up @@ -27,7 +27,7 @@ use crate::{
util::{
debug::{DebugContextString, WithElemContext},
indentedline::{IndentedLine, IndentedLinePart, EMPTY_LINE},
is_default,
is_default, singleton_yaml,
},
};

Expand Down Expand Up @@ -57,7 +57,7 @@ impl Fixups {
.join(act_id.year.to_string())
.join(format!("{}.yml", act_id));
let fixups = if fixup_path.exists() {
serde_yaml::from_reader(File::open(&fixup_path)?)?
singleton_yaml::from_reader(File::open(&fixup_path)?)?
} else {
Vec::new()
};
Expand All @@ -75,7 +75,7 @@ impl Fixups {
.parent()
.ok_or_else(|| anyhow!("No parent for fixup_path"))?,
)?;
serde_yaml::to_writer(&mut File::create(&self.fixup_path)?, &self.fixups)?;
singleton_yaml::to_writer(&mut File::create(&self.fixup_path)?, &self.fixups)?;
Ok(())
}

Expand Down
6 changes: 4 additions & 2 deletions src/util/mod.rs
@@ -1,5 +1,3 @@
use std::collections::HashMap;

// Copyright (C) 2022, Alex Badics
//
// This file is part of Hun-Law.
Expand All @@ -15,11 +13,15 @@ use std::collections::HashMap;
//
// You should have received a copy of the GNU General Public License
// along with Hun-law. If not, see <http://www.gnu.org/licenses/>.

use std::collections::HashMap;

use anyhow::{ensure, Result};

pub mod debug;
pub mod hun_str;
pub mod indentedline;
pub mod singleton_yaml;
use indentedline::IndentedLine;

pub trait IsDefault {
Expand Down
91 changes: 91 additions & 0 deletions src/util/singleton_yaml.rs
@@ -0,0 +1,91 @@
// Copyright (C) 2022, Alex Badics
//
// This file is part of Hun-Law.
//
// Hun-law is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Hun-law is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Hun-law. If not, see <http://www.gnu.org/licenses/>.

use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_yaml::{with::singleton_map_recursive, Deserializer, Result, Serializer};

#[inline]
pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
where
W: std::io::Write,
T: ?Sized + Serialize,
{
let mut ser = Serializer::new(writer);
singleton_map_recursive::serialize(&value, &mut ser)
}

#[inline]
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
where
T: ?Sized + Serialize,
{
let mut writer = Vec::with_capacity(128);
to_writer(&mut writer, value)?;
Ok(writer)
}

#[inline]
pub fn to_string<T>(value: &T) -> Result<String>
where
T: ?Sized + Serialize,
{
let vec = to_vec(value)?;
let string = unsafe {
// We do not emit invalid UTF-8.
String::from_utf8_unchecked(vec)
};
Ok(string)
}

pub fn from_str<'de, T>(s: &'de str) -> Result<T>
where
T: Deserialize<'de>,
{
singleton_map_recursive::deserialize(Deserializer::from_str(s))
}

/// Deserialize an instance of type `T` from an IO stream of YAML.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
pub fn from_reader<R, T>(rdr: R) -> Result<T>
where
R: std::io::Read,
T: DeserializeOwned,
{
singleton_map_recursive::deserialize(Deserializer::from_reader(rdr))
}

/// Deserialize an instance of type `T` from bytes of YAML text.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
pub fn from_slice<'de, T>(v: &'de [u8]) -> Result<T>
where
T: Deserialize<'de>,
{
singleton_map_recursive::deserialize(Deserializer::from_slice(v))
}
3 changes: 2 additions & 1 deletion tests/datatests/test_add_semantic_info.rs
Expand Up @@ -17,6 +17,7 @@
use std::path::Path;

use hun_law::structure::Act;
use hun_law::util::singleton_yaml;

use crate::declare_test;
use crate::test_utils::{clean_quoted_blocks, ensure_eq, parse_txt_as_act, read_all};
Expand All @@ -26,7 +27,7 @@ declare_test!(dir = "data_add_semantic_info", pattern = r"\.txt");
pub fn run_test(path: &Path) -> datatest_stable::Result<()> {
let mut act = parse_txt_as_act(path)?.add_semantic_info()?;
clean_quoted_blocks(&mut act);
let expected_act: Act = serde_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
let expected_act: Act = singleton_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
ensure_eq(&expected_act, &act, "Wrong act contents")?;
Ok(())
}
3 changes: 2 additions & 1 deletion tests/datatests/test_convert_block_amendments.rs
Expand Up @@ -17,6 +17,7 @@
use std::path::Path;

use hun_law::structure::Act;
use hun_law::util::singleton_yaml;

use crate::declare_test;
use crate::test_utils::{clean_quoted_blocks, ensure_eq, parse_txt_as_act, read_all};
Expand All @@ -28,7 +29,7 @@ pub fn run_test(path: &Path) -> datatest_stable::Result<()> {
act.convert_block_amendments()?;
// Clear remaining quoted blocks to make failing output a bit smaller
clean_quoted_blocks(&mut act);
let expected_act: Act = serde_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
let expected_act: Act = singleton_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
ensure_eq(&expected_act, &act, "Wrong act contents")?;
Ok(())
}
3 changes: 2 additions & 1 deletion tests/datatests/test_pdf_parser.rs
Expand Up @@ -17,6 +17,7 @@
use std::path::Path;

use hun_law::parser::pdf::{parse_pdf, CropBox};
use hun_law::util::singleton_yaml;
use hun_law::util::{indentedline::IndentedLine, is_default};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -67,7 +68,7 @@ pub fn run_test(path: &Path) -> datatest_stable::Result<()> {
ensure_eq(&parsed.len(), &1, "Wrong number of pages parsed")?;
let lines: Vec<SimplifiedLine> = parsed[0].lines.iter().map(SimplifiedLine::from).collect();
let expected_lines: Vec<SimplifiedLine> =
serde_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
singleton_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
ensure_eq(&lines, &expected_lines, "Wrong content")?;
Ok(())
}
4 changes: 2 additions & 2 deletions tests/datatests/test_semantic_parser.rs
Expand Up @@ -22,7 +22,7 @@ use hun_law::{
parser::semantic_info::{abbreviation::AbbreviationCache, extract_semantic_info},
reference::Reference,
semantic_info::{ActIdAbbreviation, OutgoingReference, SpecialPhrase},
util::is_default,
util::{is_default, singleton_yaml},
};
use serde::{Deserialize, Serialize};

Expand All @@ -47,7 +47,7 @@ struct TestCase {
}

pub fn run_test(path: &Path) -> Result<()> {
let test_case: TestCase = serde_yaml::from_slice(&read_all(path)?)?;
let test_case: TestCase = singleton_yaml::from_slice(&read_all(path)?)?;
let mut abbreviation_cache = AbbreviationCache::from(test_case.abbreviations.clone());
let semantic_info = extract_semantic_info("", &test_case.text, "", &mut abbreviation_cache)?;

Expand Down
3 changes: 2 additions & 1 deletion tests/datatests/test_structure_parser.rs
Expand Up @@ -17,6 +17,7 @@
use std::path::Path;

use hun_law::structure::Act;
use hun_law::util::singleton_yaml;

use crate::declare_test;
use crate::test_utils::{clean_quoted_blocks, ensure_eq, parse_txt_as_act, read_all};
Expand All @@ -26,7 +27,7 @@ declare_test!(dir = "data_structure_parser", pattern = r"\.txt");
pub fn run_test(path: &Path) -> datatest_stable::Result<()> {
let mut act = parse_txt_as_act(path)?;
clean_quoted_blocks(&mut act);
let expected_act: Act = serde_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
let expected_act: Act = singleton_yaml::from_slice(&read_all(path.with_extension("yml"))?)?;
ensure_eq(&expected_act, &act, "Wrong act contents")?;
Ok(())
}

0 comments on commit dddd67f

Please sign in to comment.