1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! JSON:API Request Handling
//!
//! This module provides data structures and utilities for handling JSON:API requests.
//! It includes structures for parsing JSON:API request documents and query parameters,
//! as well as helper functions for validation and extraction.
use ;
use HashMap;
/// Generic JSON:API request data structure
///
/// This struct represents the `data` member of a JSON:API request document.
/// It contains the resource type and attributes for the resource being created or updated.
///
/// # Type Parameters
///
/// * `T` - The type representing the resource attributes
/// JSON:API request wrapper
///
/// This struct represents a complete JSON:API request document.
/// It contains the `data` member which holds the resource information.
///
/// # Type Parameters
///
/// * `T` - The type representing the resource attributes
/// JSON:API Query Parameters for 1.1 compliance
///
/// This struct represents the query parameters supported by JSON:API 1.1.
/// It includes fields for inclusion, sparse fieldsets, sorting, pagination, and filtering.
/// A trait for defining a JSON:API resource type.
///
/// This trait is implemented by application-specific models to specify their
/// JSON:API resource type. This is used by the `JsonApi` extractor to validate
/// the incoming request's `type` member.
///
/// # Example
///
/// ```rust
/// use rjapi::JsonApiResource;
/// use serde::Deserialize;
///
/// #[derive(Deserialize, serde::Serialize)]
/// pub struct Post {
/// pub title: String,
/// pub content: String,
/// }
///
/// impl JsonApiResource for Post {
/// const RESOURCE_TYPE: &'static str = "posts";
/// fn id(&self) -> String { "1".to_string() }
/// fn attributes(&self) -> serde_json::Value { serde_json::to_value(self).unwrap() }
/// }
/// ```
/// A `FromRequest` extractor for JSON:API requests.
///
/// This extractor deserializes the request body into a `JsonApiRequest<T>`
/// and then validates the resource `type`. If the `type` does not match
/// `T::RESOURCE_TYPE`, a `400 Bad Request` response is returned.
///
/// This simplifies request handling in frameworks like Axum, where you can
/// use `JsonApi<T>` directly as a handler parameter.
;
// In a real application, you would use a macro or a library to implement
// `FromRequest` for different web frameworks. For simplicity, we'll
// keep this as a conceptual example.
// Note: To make this code compile, you'd need to add `async-trait` and
// a web framework like `axum` as a dependency. The following is a
// conceptual implementation for `axum`.
/*
use async_trait::async_trait;
use axum::{
extract::{FromRequest, Request},
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
#[async_trait]
impl<S, T> FromRequest<S> for JsonApi<T>
where
S: Send + Sync,
T: JsonApiResource + for<'de> Deserialize<'de>,
{
type Rejection = (StatusCode, String);
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let Json(request): Json<JsonApiRequest<T>> = Json::from_request(req, state)
.await
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
if request.data.resource_type != T::RESOURCE_TYPE {
return Err((
StatusCode::BAD_REQUEST,
format!("Expected resource type '{}'", T::RESOURCE_TYPE),
));
}
Ok(JsonApi(request.data.attributes))
}
}
*/