Skip to content

Commit

Permalink
Merge pull request #42 from yassun7010/support_fluent_format
Browse files Browse the repository at this point in the history
Support fluent format
  • Loading branch information
yassun7010 authored Jan 13, 2024
2 parents ecd4607 + e4f9ccd commit 0e22d50
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 65 deletions.
3 changes: 2 additions & 1 deletion serde_valid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ thiserror = "^1.0"
unicode-segmentation = "^1.7"

[dev-dependencies]
unic-langid = "0.9.1"
intl-memoizer = "0.5"
unic-langid = "0.9"

[features]
default = ["i128", "json"]
Expand Down
1 change: 1 addition & 0 deletions serde_valid/src/features/fluent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod message;
mod try_localize;

pub use error::LocalizedError;
pub use fluent_0::FluentValue;
pub use localize::Localize;
pub use message::Message;
pub use try_localize::TryLocalize;
10 changes: 10 additions & 0 deletions serde_valid/src/features/fluent/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ pub enum LocalizedError {
Items(ArrayErrors<LocalizedError>),
Properties(ObjectErrors<LocalizedError>),
}

impl std::fmt::Display for LocalizedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LocalizedError::String(string) => write!(f, "{}", string),
LocalizedError::Items(items) => write!(f, "{}", items),
LocalizedError::Properties(properties) => write!(f, "{}", properties),
}
}
}
65 changes: 50 additions & 15 deletions serde_valid/src/features/fluent/localize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fluent_0::{FluentBundle, FluentResource};
use fluent_0::{bundle::FluentBundle, FluentResource};

use crate::validation::error::{
ArrayErrors, Errors, FormatDefault, ItemErrorsMap, ObjectErrors, PropertyErrorsMap, VecErrors,
Expand All @@ -9,13 +9,18 @@ use super::{LocalizedError, TryLocalize};
pub trait Localize {
type Target;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target;
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind;
}

impl Localize for Errors<crate::validation::Error> {
type Target = Errors<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
match self {
Errors::Array(array) => Errors::Array(array.localize(bundle)),
Errors::Object(object) => Errors::Object(object.localize(bundle)),
Expand All @@ -27,7 +32,10 @@ impl Localize for Errors<crate::validation::Error> {
impl Localize for ArrayErrors<crate::validation::Error> {
type Target = ArrayErrors<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
ArrayErrors {
errors: self.errors.localize(bundle),
items: self.items.localize(bundle),
Expand All @@ -38,7 +46,10 @@ impl Localize for ArrayErrors<crate::validation::Error> {
impl Localize for ObjectErrors<crate::validation::Error> {
type Target = ObjectErrors<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
ObjectErrors {
errors: self.errors.localize(bundle),
properties: self.properties.localize(bundle),
Expand All @@ -49,15 +60,21 @@ impl Localize for ObjectErrors<crate::validation::Error> {
impl Localize for VecErrors<crate::validation::Error> {
type Target = VecErrors<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
self.iter().map(|error| error.localize(bundle)).collect()
}
}

impl Localize for ItemErrorsMap<crate::validation::Error> {
type Target = ItemErrorsMap<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
self.iter()
.map(|(index, error)| (*index, error.localize(bundle)))
.collect()
Expand All @@ -67,7 +84,10 @@ impl Localize for ItemErrorsMap<crate::validation::Error> {
impl Localize for PropertyErrorsMap<crate::validation::Error> {
type Target = PropertyErrorsMap<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
self.iter()
.map(|(property, error)| (property.to_string(), error.localize(bundle)))
.collect()
Expand All @@ -77,7 +97,10 @@ impl Localize for PropertyErrorsMap<crate::validation::Error> {
impl Localize for crate::validation::Error {
type Target = LocalizedError;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
match self {
Self::Minimum(message) => message.localize(bundle),
Self::Maximum(message) => message.localize(bundle),
Expand Down Expand Up @@ -109,7 +132,10 @@ where
{
type Target = LocalizedError;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
self.try_localize(bundle)
.unwrap_or_else(|_| LocalizedError::String(self.format_default()))
}
Expand All @@ -118,7 +144,10 @@ where
impl Localize for crate::features::fluent::Message {
type Target = Option<LocalizedError>;

fn localize(&self, bundle: &FluentBundle<FluentResource>) -> Self::Target {
fn localize<M>(&self, bundle: &FluentBundle<FluentResource, M>) -> Self::Target
where
M: fluent_0::memoizer::MemoizerKind,
{
self.try_localize(bundle)
.unwrap_or_else(|e: Vec<fluent_0::FluentError>| {
Some(LocalizedError::String(format!("FluentErrors: {:?}", e)))
Expand All @@ -135,22 +164,28 @@ mod test {
use serde_json::json;
use unic_langid::LanguageIdentifier;

#[test]
fn localize_without_args() -> crate::tests::Result<()> {
let ftl_string = "hello-world = Hello, world!".to_string();
fn get_bundle() -> FluentBundle<FluentResource, intl_memoizer::IntlLangMemoizer> {
let ftl_string = ["hello-world = Hello, world!", "intro = Welcome, { $name }."]
.join("\n")
.to_string();
let res = FluentResource::try_new(ftl_string).expect("Failed to parse an FTL string.");

let langid_en: LanguageIdentifier = "en-US".parse().expect("Parsing failed");
let mut bundle = FluentBundle::new(vec![langid_en]);
bundle.add_resource(res).unwrap();

bundle
}

#[test]
fn localize_without_args() -> crate::tests::Result<()> {
let error = crate::validation::Error::Fluent(Message {
id: "hello-world",
args: vec![],
});

assert_eq!(
serde_json::to_value(error.localize(&bundle))?,
serde_json::to_value(error.localize(&get_bundle()))?,
json!("Hello, world!")
);

Expand Down
91 changes: 60 additions & 31 deletions serde_valid/src/features/fluent/try_localize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fluent_0::{FluentArgs, FluentBundle, FluentError, FluentResource};
use fluent_0::{bundle::FluentBundle, FluentArgs, FluentError, FluentResource};

use crate::validation::error::{
ArrayErrors, Errors, FormatDefault, ItemErrorsMap, ObjectErrors, PropertyErrorsMap, VecErrors,
Expand All @@ -9,19 +9,24 @@ use super::LocalizedError;
pub trait TryLocalize {
type Target;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>>;
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind;
}

impl TryLocalize for Errors<crate::validation::Error> {
type Target = Errors<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
match self {
Errors::Array(array) => Ok(Errors::Array(array.try_localize(bundle)?)),
Errors::Object(object) => Ok(Errors::Object(object.try_localize(bundle)?)),
Expand All @@ -33,10 +38,13 @@ impl TryLocalize for Errors<crate::validation::Error> {
impl TryLocalize for ArrayErrors<crate::validation::Error> {
type Target = ArrayErrors<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
match (
self.errors.try_localize(bundle),
self.items.try_localize(bundle),
Expand All @@ -52,10 +60,13 @@ impl TryLocalize for ArrayErrors<crate::validation::Error> {
impl TryLocalize for ObjectErrors<crate::validation::Error> {
type Target = ObjectErrors<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
match (
self.errors.try_localize(bundle),
self.properties.try_localize(bundle),
Expand All @@ -71,10 +82,13 @@ impl TryLocalize for ObjectErrors<crate::validation::Error> {
impl TryLocalize for VecErrors<crate::validation::Error> {
type Target = VecErrors<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
self.iter()
.map(|error| error.try_localize(bundle))
.collect()
Expand All @@ -84,10 +98,13 @@ impl TryLocalize for VecErrors<crate::validation::Error> {
impl TryLocalize for ItemErrorsMap<crate::validation::Error> {
type Target = ItemErrorsMap<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
let mut errors = vec![];
let target = self
.iter()
Expand All @@ -113,10 +130,13 @@ impl TryLocalize for ItemErrorsMap<crate::validation::Error> {
impl TryLocalize for PropertyErrorsMap<crate::validation::Error> {
type Target = PropertyErrorsMap<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
let mut errors = vec![];
let target = self
.iter()
Expand All @@ -142,10 +162,13 @@ impl TryLocalize for PropertyErrorsMap<crate::validation::Error> {
impl TryLocalize for crate::validation::Error {
type Target = LocalizedError;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
match self {
Self::Minimum(message) => message.try_localize(bundle),
Self::Maximum(message) => message.try_localize(bundle),
Expand Down Expand Up @@ -179,10 +202,13 @@ where
{
type Target = LocalizedError;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
if let Some(message) = self.fluent_message() {
if let Some(localized) = message.try_localize(bundle)? {
return Ok(localized);
Expand All @@ -195,10 +221,13 @@ where
impl TryLocalize for crate::features::fluent::Message {
type Target = Option<LocalizedError>;

fn try_localize(
fn try_localize<M>(
&self,
bundle: &FluentBundle<FluentResource>,
) -> Result<Self::Target, Vec<FluentError>> {
bundle: &FluentBundle<FluentResource, M>,
) -> Result<Self::Target, Vec<FluentError>>
where
M: fluent_0::memoizer::MemoizerKind,
{
if let Some(msg) = bundle.get_message(self.id) {
if let Some(pattern) = msg.value() {
let mut errors = vec![];
Expand Down
5 changes: 4 additions & 1 deletion serde_valid/src/validation/error/array_erros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ where
}
}

impl std::fmt::Display for ArrayErrors {
impl<E> std::fmt::Display for ArrayErrors<E>
where
E: serde::Serialize,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match serde_json::to_string(&self) {
Ok(json_string) => {
Expand Down
5 changes: 4 additions & 1 deletion serde_valid/src/validation/error/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ where
}
}

impl std::fmt::Display for Errors {
impl<E> std::fmt::Display for Errors<E>
where
E: serde::Serialize + std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Array(errors) => std::fmt::Display::fmt(errors, f),
Expand Down
Loading

0 comments on commit 0e22d50

Please sign in to comment.