Skip to content

Commit

Permalink
Add initial Pattern Serde impl
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc committed Mar 7, 2024
1 parent 1734713 commit b2b0a57
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions utils/pattern/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ all-features = true
displaydoc = { version = "0.2.3", default-features = false }
writeable = { workspace = true }
databake = { workspace = true, features = ["derive"], optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
yoke = { workspace = true, features = ["derive"], optional = true }
zerofrom = { workspace = true, features = ["derive"], optional = true }

Expand All @@ -35,5 +36,6 @@ zerovec = { workspace = true, features = ["databake", "serde"] }
alloc = []
std = ["alloc"]
databake = ["dep:databake"]
serde = ["dep:serde"]
yoke = ["dep:yoke"]
zerofrom = ["dep:zerofrom"]
11 changes: 10 additions & 1 deletion utils/pattern/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub enum PatternItem<'a, T> {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_enums)] // Part of core data model
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum PatternItemCow<'a, T> {
/// A placeholder of the type specified on this [`PatternItemCow`].
Placeholder(T),
Expand All @@ -45,6 +45,15 @@ pub enum PatternItemCow<'a, T> {
Literal(Cow<'a, str>),
}

impl<'a, T> From<PatternItem<'a, T>> for PatternItemCow<'a, T> {
fn from(value: PatternItem<'a, T>) -> Self {
match value {
PatternItem::Placeholder(t) => Self::Placeholder(t),
PatternItem::Literal(s) => Self::Literal(Cow::Borrowed(s)),
}
}
}

/// Types that implement backing data models for [`Pattern`] implement this trait.
///
/// The trait has no public methods and is not implementable outside of this crate.
Expand Down
2 changes: 2 additions & 0 deletions utils/pattern/src/frontend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#[cfg(feature = "databake")]
mod databake;
#[cfg(feature = "serde")]
mod serde;

use core::{
fmt::{self, Write},
Expand Down
64 changes: 64 additions & 0 deletions utils/pattern/src/frontend/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use super::*;
use alloc::borrow::Cow;

use ::serde::{Deserialize, Deserializer, Serialize, Serializer};

type HumanReadablePatternForSerde<'a, T> = Vec<PatternItemCow<'a, T>>;

impl<'de, 'data, B> Deserialize<'de> for Pattern<B, Cow<'data, B::Store>>
where
'de: 'data,
B: PatternBackend,
B::Store: ToOwned,
<B::Store as ToOwned>::Owned: Deserialize<'de>,
B::PlaceholderKey: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let pattern_items =
<HumanReadablePatternForSerde<B::PlaceholderKey>>::deserialize(deserializer)?;
let pattern_owned: Pattern<B, <B::Store as ToOwned>::Owned> =
Pattern::try_from_items(pattern_items.into_iter())
.map_err(|e| <D::Error as ::serde::de::Error>::custom(e))?;
let pattern_cow: Pattern<B, Cow<B::Store>> =
Pattern::from_store_unchecked(Cow::Owned(pattern_owned.take_store()));
Ok(pattern_cow)
} else {
let store = Cow::<B::Store>::deserialize(deserializer)?;
let pattern = Self::try_from_store(store)
.map_err(|e| <D::Error as ::serde::de::Error>::custom(e))?;
Ok(pattern)
}
}
}

impl<B, Store> Serialize for Pattern<B, Store>
where
B: PatternBackend,
B::Store: Serialize,
B::PlaceholderKey: Serialize,
Store: AsRef<B::Store>,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let pattern_items: HumanReadablePatternForSerde<B::PlaceholderKey> =
B::iter_items(self.store.as_ref())
.map(|x| x.into())
.collect();
pattern_items.serialize(serializer)
} else {
let bytes = self.store.as_ref();
bytes.serialize(serializer)
}
}
}

0 comments on commit b2b0a57

Please sign in to comment.