Skip to content

Commit

Permalink
Merge pull request #46 from yassun7010/move_export
Browse files Browse the repository at this point in the history
Move export
  • Loading branch information
yassun7010 authored Jan 14, 2024
2 parents 6079c59 + 97be762 commit 83ba82c
Show file tree
Hide file tree
Showing 17 changed files with 164 additions and 133 deletions.
2 changes: 2 additions & 0 deletions axum_serde_valid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ serde_valid = { version = "0.17.0", path = "../serde_valid", features = [
] }
tracing = "^0.1"
serde_urlencoded = "0.7.1"
unic-langid-impl = { version = "0.9", optional = true }

[dev-dependencies]
hyper = "^1.0"
Expand All @@ -35,3 +36,4 @@ tower = { version = "0.4", features = ["util"] }
default = []
jsonschema = ["dep:jsonschema", "dep:schemars"]
aide = ["dep:aide", "dep:jsonschema", "dep:schemars"]
fluent = ["serde_valid/fluent", "unic-langid-impl"]
3 changes: 3 additions & 0 deletions axum_serde_valid/src/features.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
#[cfg(feature = "jsonschema")]
pub mod jsonschema;

#[cfg(feature = "fluent")]
pub mod fluent;
20 changes: 20 additions & 0 deletions axum_serde_valid/src/features/fluent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use unic_langid_impl::LanguageIdentifier;

type FluentBundle =
serde_valid::export::fluent::FluentBundle<serde_valid::export::fluent::FluentResource>;

pub trait FluentState {
fn get_fluent_bundle(&self) -> Option<&FluentBundle>;

fn get_fluent_bundle_on_lang(&self, lang: LanguageIdentifier) -> Option<&FluentBundle>;
}

impl<T> FluentState for T {
fn get_fluent_bundle(&self) -> Option<&FluentBundle> {
None
}

fn get_fluent_bundle_on_lang(&self, _lang: LanguageIdentifier) -> Option<&FluentBundle> {
None
}
}
62 changes: 61 additions & 1 deletion axum_serde_valid/src/features/jsonschema.rs
Original file line number Diff line number Diff line change
@@ -1 +1,61 @@
pub mod context;
use std::{
any::{type_name, TypeId},
cell::RefCell,
collections::{HashMap, VecDeque},
};

use jsonschema::{
output::{BasicOutput, ErrorDescription, OutputUnit},
JSONSchema,
};
use schemars::gen::{SchemaGenerator, SchemaSettings};
use serde_json::{Map, Value};

thread_local! {
static CONTEXT: RefCell<SchemaContext> = RefCell::new(SchemaContext::new());
}

pub(crate) struct SchemaContext {
pub generator: SchemaGenerator,
pub schemas: HashMap<TypeId, JSONSchema>,
}

impl SchemaContext {
pub fn new() -> Self {
Self {
generator: SchemaSettings::draft07()
.with(|settings| settings.inline_subschemas = true)
.into_generator(),
schemas: HashMap::default(),
}
}

pub fn validate<T>(value: &Value) -> Result<(), VecDeque<OutputUnit<ErrorDescription>>>
where
T: crate::validated::Deserialize + schemars::JsonSchema + 'static,
{
CONTEXT.with(|ctx| {
let ctx = &mut *ctx.borrow_mut();
let schema = ctx.schemas.entry(TypeId::of::<T>()).or_insert_with(|| {
match jsonschema::JSONSchema::compile(
&serde_json::to_value(ctx.generator.root_schema_for::<T>()).unwrap(),
) {
Ok(s) => s,
Err(error) => {
tracing::error!(
%error,
type_name = type_name::<T>(),
"invalid JSON schema for type"
);
JSONSchema::compile(&Value::Object(Map::default())).unwrap()
}
}
});

match schema.apply(value).basic() {
BasicOutput::Valid(_) => Ok(()),
BasicOutput::Invalid(v) => Err(v),
}
})
}
}
61 changes: 0 additions & 61 deletions axum_serde_valid/src/features/jsonschema/context.rs

This file was deleted.

2 changes: 1 addition & 1 deletion axum_serde_valid/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<T> From<T> for Json<T> {
impl<T, S> FromRequest<S> for Json<T>
where
T: crate::validated::Deserialize + 'static,
S: Send + Sync,
S: crate::state::State,
{
type Rejection = crate::rejection::Rejection;

Expand Down
1 change: 1 addition & 0 deletions axum_serde_valid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod json_pointer;
mod query;
pub mod rejection;
mod request;
pub mod state;
mod validated;

#[allow(unused_imports)]
Expand Down
30 changes: 15 additions & 15 deletions axum_serde_valid/src/rejection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,25 @@ impl From<Rejection> for JsonErrorResponse {
Rejection::Json(error) => Self::FormatError(error.to_string()),
Rejection::SerdeJson(error) => Self::FormatError(error.to_string()),
Rejection::SerdeUrlEncoded(error) => Self::FormatError(error.to_string()),
Rejection::SerdeValid(errors) => Self::ValidationError(JsonSchemaErrorResponse {
errors: errors
.into_flat()
.into_iter()
.map(|error| Error {
error: error.error,
instance_location: JsonPointer(error.instance_location.to_string()),
keyword_location: None,
})
.collect::<Vec<_>>(),
}),
Rejection::SerdeValid(errors) => {
let iter = errors.into_flat().into_iter().map(|err| Error {
error: err.error,
instance_location: JsonPointer(err.instance_location.to_string()),
keyword_location: None,
});

Self::ValidationError(JsonSchemaErrorResponse {
errors: iter.collect::<Vec<_>>(),
})
}
#[cfg(feature = "jsonschema")]
Rejection::Jsonschema(errors) => Self::ValidationError(JsonSchemaErrorResponse {
errors: errors
.into_iter()
.map(|error| Error {
error: error.error_description().to_string(),
instance_location: JsonPointer(error.instance_location().to_string()),
keyword_location: Some(JsonPointer(error.keyword_location().to_string())),
.map(|err| Error {
error: err.error_description().to_string(),
instance_location: JsonPointer(err.instance_location().to_string()),
keyword_location: Some(JsonPointer(err.keyword_location().to_string())),
})
.collect::<Vec<_>>(),
}),
Expand Down
4 changes: 2 additions & 2 deletions axum_serde_valid/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub async fn from_request<S, T>(
state: &S,
) -> Result<T, crate::rejection::Rejection>
where
S: Send + Sync,
S: crate::state::State,
T: crate::validated::Deserialize + 'static,
{
let value: Value = match axum::Json::from_request(req, state).await {
Expand All @@ -18,7 +18,7 @@ where

#[cfg(feature = "jsonschema")]
{
crate::jsonschema::context::SchemaContext::validate::<T>(&value)
crate::jsonschema::SchemaContext::validate::<T>(&value)
.map_err(crate::rejection::Rejection::Jsonschema)?;
}

Expand Down
17 changes: 17 additions & 0 deletions axum_serde_valid/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[cfg(feature = "fluent")]
mod inner {
use crate::fluent::FluentState;

pub trait State: Send + Sync + FluentState {}

impl<T> State for T where T: Send + Sync + FluentState {}
}

#[cfg(not(feature = "fluent"))]
mod inner {
pub trait State: Send + Sync {}

impl<T> State for T where T: Send + Sync {}
}

pub use inner::State;
2 changes: 1 addition & 1 deletion serde_valid.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
],
"settings": {
"rust-analyzer.cargo.features": "all",
"rust-analyzer.checkOnSave.command": "clippy"
"rust-analyzer.checkOnSave.command": "clippy",
},
"extensions": {
"recommendations": [
Expand Down
32 changes: 16 additions & 16 deletions serde_valid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ assert!(s.validate().is_ok());

Serde Valid support standard validation based JSON Schema.

| Type | Serde Valid(validate derive) | Serde Valid(validate trait) | Json Schema |
| :-----: | :----------------------------------- | :--------------------------- | :----------------------------------------------------------------------------------------------------- |
| String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string.html#length) |
| String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string.html#length) |
| String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string.html#regular-expressions) |
| Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric.html#range) |
| Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric.html#range) |
| Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric.html#range) |
| Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric.html#range) |
| Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric.html#multiples) |
| Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object.html#size) |
| Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object.html#size) |
| Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array.html#length) |
| Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array.html#length) |
| Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array.html#unique_items) |
| Generic | `#[validate(enumerate(5, 10, 15))]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values) |
| Type | Serde Valid(validate derive) | Serde Valid(validate trait) | Json Schema |
| :-----: | :----------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- |
| String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) |
| String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) |
| String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) |
| Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
| Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
| Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
| Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
| Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) |
| Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) |
| Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) |
| Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) |
| Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) |
| Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) |
| Generic | `#[validate(enumerate(5, 10, 15))]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) |

## Complete Constructor (Deserialization)

Expand Down
1 change: 0 additions & 1 deletion serde_valid/src/features/fluent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ mod message;
mod try_localize;

pub use error::LocalizedError;
pub use fluent::FluentValue;
pub use localize::Localize;
pub use message::Message;
pub use try_localize::TryLocalize;
4 changes: 2 additions & 2 deletions serde_valid/src/features/fluent/localize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ mod test {
use crate::fluent::Message;

use super::*;
use fluent::{bundle::FluentBundle, FluentResource, FluentValue};
use fluent::{FluentBundle, FluentResource, FluentValue};
use serde_json::json;
use unic_langid::LanguageIdentifier;

fn get_bundle() -> FluentBundle<FluentResource, intl_memoizer::IntlLangMemoizer> {
fn get_bundle() -> FluentBundle<FluentResource> {
let ftl_string = ["hello-world = Hello, world!", "intro = Welcome, { $name }."]
.join("\n")
.to_string();
Expand Down
Loading

0 comments on commit 83ba82c

Please sign in to comment.