Skip to content

Commit 83ba82c

Browse files
authored
Merge pull request #46 from yassun7010/move_export
Move export
2 parents 6079c59 + 97be762 commit 83ba82c

File tree

17 files changed

+164
-133
lines changed

17 files changed

+164
-133
lines changed

axum_serde_valid/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ serde_valid = { version = "0.17.0", path = "../serde_valid", features = [
2424
] }
2525
tracing = "^0.1"
2626
serde_urlencoded = "0.7.1"
27+
unic-langid-impl = { version = "0.9", optional = true }
2728

2829
[dev-dependencies]
2930
hyper = "^1.0"
@@ -35,3 +36,4 @@ tower = { version = "0.4", features = ["util"] }
3536
default = []
3637
jsonschema = ["dep:jsonschema", "dep:schemars"]
3738
aide = ["dep:aide", "dep:jsonschema", "dep:schemars"]
39+
fluent = ["serde_valid/fluent", "unic-langid-impl"]

axum_serde_valid/src/features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
#[cfg(feature = "jsonschema")]
22
pub mod jsonschema;
3+
4+
#[cfg(feature = "fluent")]
5+
pub mod fluent;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use unic_langid_impl::LanguageIdentifier;
2+
3+
type FluentBundle =
4+
serde_valid::export::fluent::FluentBundle<serde_valid::export::fluent::FluentResource>;
5+
6+
pub trait FluentState {
7+
fn get_fluent_bundle(&self) -> Option<&FluentBundle>;
8+
9+
fn get_fluent_bundle_on_lang(&self, lang: LanguageIdentifier) -> Option<&FluentBundle>;
10+
}
11+
12+
impl<T> FluentState for T {
13+
fn get_fluent_bundle(&self) -> Option<&FluentBundle> {
14+
None
15+
}
16+
17+
fn get_fluent_bundle_on_lang(&self, _lang: LanguageIdentifier) -> Option<&FluentBundle> {
18+
None
19+
}
20+
}
Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,61 @@
1-
pub mod context;
1+
use std::{
2+
any::{type_name, TypeId},
3+
cell::RefCell,
4+
collections::{HashMap, VecDeque},
5+
};
6+
7+
use jsonschema::{
8+
output::{BasicOutput, ErrorDescription, OutputUnit},
9+
JSONSchema,
10+
};
11+
use schemars::gen::{SchemaGenerator, SchemaSettings};
12+
use serde_json::{Map, Value};
13+
14+
thread_local! {
15+
static CONTEXT: RefCell<SchemaContext> = RefCell::new(SchemaContext::new());
16+
}
17+
18+
pub(crate) struct SchemaContext {
19+
pub generator: SchemaGenerator,
20+
pub schemas: HashMap<TypeId, JSONSchema>,
21+
}
22+
23+
impl SchemaContext {
24+
pub fn new() -> Self {
25+
Self {
26+
generator: SchemaSettings::draft07()
27+
.with(|settings| settings.inline_subschemas = true)
28+
.into_generator(),
29+
schemas: HashMap::default(),
30+
}
31+
}
32+
33+
pub fn validate<T>(value: &Value) -> Result<(), VecDeque<OutputUnit<ErrorDescription>>>
34+
where
35+
T: crate::validated::Deserialize + schemars::JsonSchema + 'static,
36+
{
37+
CONTEXT.with(|ctx| {
38+
let ctx = &mut *ctx.borrow_mut();
39+
let schema = ctx.schemas.entry(TypeId::of::<T>()).or_insert_with(|| {
40+
match jsonschema::JSONSchema::compile(
41+
&serde_json::to_value(ctx.generator.root_schema_for::<T>()).unwrap(),
42+
) {
43+
Ok(s) => s,
44+
Err(error) => {
45+
tracing::error!(
46+
%error,
47+
type_name = type_name::<T>(),
48+
"invalid JSON schema for type"
49+
);
50+
JSONSchema::compile(&Value::Object(Map::default())).unwrap()
51+
}
52+
}
53+
});
54+
55+
match schema.apply(value).basic() {
56+
BasicOutput::Valid(_) => Ok(()),
57+
BasicOutput::Invalid(v) => Err(v),
58+
}
59+
})
60+
}
61+
}

axum_serde_valid/src/features/jsonschema/context.rs

Lines changed: 0 additions & 61 deletions
This file was deleted.

axum_serde_valid/src/json.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<T> From<T> for Json<T> {
4242
impl<T, S> FromRequest<S> for Json<T>
4343
where
4444
T: crate::validated::Deserialize + 'static,
45-
S: Send + Sync,
45+
S: crate::state::State,
4646
{
4747
type Rejection = crate::rejection::Rejection;
4848

axum_serde_valid/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod json_pointer;
44
mod query;
55
pub mod rejection;
66
mod request;
7+
pub mod state;
78
mod validated;
89

910
#[allow(unused_imports)]

axum_serde_valid/src/rejection.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,25 +72,25 @@ impl From<Rejection> for JsonErrorResponse {
7272
Rejection::Json(error) => Self::FormatError(error.to_string()),
7373
Rejection::SerdeJson(error) => Self::FormatError(error.to_string()),
7474
Rejection::SerdeUrlEncoded(error) => Self::FormatError(error.to_string()),
75-
Rejection::SerdeValid(errors) => Self::ValidationError(JsonSchemaErrorResponse {
76-
errors: errors
77-
.into_flat()
78-
.into_iter()
79-
.map(|error| Error {
80-
error: error.error,
81-
instance_location: JsonPointer(error.instance_location.to_string()),
82-
keyword_location: None,
83-
})
84-
.collect::<Vec<_>>(),
85-
}),
75+
Rejection::SerdeValid(errors) => {
76+
let iter = errors.into_flat().into_iter().map(|err| Error {
77+
error: err.error,
78+
instance_location: JsonPointer(err.instance_location.to_string()),
79+
keyword_location: None,
80+
});
81+
82+
Self::ValidationError(JsonSchemaErrorResponse {
83+
errors: iter.collect::<Vec<_>>(),
84+
})
85+
}
8686
#[cfg(feature = "jsonschema")]
8787
Rejection::Jsonschema(errors) => Self::ValidationError(JsonSchemaErrorResponse {
8888
errors: errors
8989
.into_iter()
90-
.map(|error| Error {
91-
error: error.error_description().to_string(),
92-
instance_location: JsonPointer(error.instance_location().to_string()),
93-
keyword_location: Some(JsonPointer(error.keyword_location().to_string())),
90+
.map(|err| Error {
91+
error: err.error_description().to_string(),
92+
instance_location: JsonPointer(err.instance_location().to_string()),
93+
keyword_location: Some(JsonPointer(err.keyword_location().to_string())),
9494
})
9595
.collect::<Vec<_>>(),
9696
}),

axum_serde_valid/src/request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub async fn from_request<S, T>(
88
state: &S,
99
) -> Result<T, crate::rejection::Rejection>
1010
where
11-
S: Send + Sync,
11+
S: crate::state::State,
1212
T: crate::validated::Deserialize + 'static,
1313
{
1414
let value: Value = match axum::Json::from_request(req, state).await {
@@ -18,7 +18,7 @@ where
1818

1919
#[cfg(feature = "jsonschema")]
2020
{
21-
crate::jsonschema::context::SchemaContext::validate::<T>(&value)
21+
crate::jsonschema::SchemaContext::validate::<T>(&value)
2222
.map_err(crate::rejection::Rejection::Jsonschema)?;
2323
}
2424

axum_serde_valid/src/state.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[cfg(feature = "fluent")]
2+
mod inner {
3+
use crate::fluent::FluentState;
4+
5+
pub trait State: Send + Sync + FluentState {}
6+
7+
impl<T> State for T where T: Send + Sync + FluentState {}
8+
}
9+
10+
#[cfg(not(feature = "fluent"))]
11+
mod inner {
12+
pub trait State: Send + Sync {}
13+
14+
impl<T> State for T where T: Send + Sync {}
15+
}
16+
17+
pub use inner::State;

0 commit comments

Comments
 (0)