Skip to content

Commit

Permalink
feat(oapi):Add decimal_float feature. (#412)
Browse files Browse the repository at this point in the history
* feat(oapi):Add decimal_float feature.

* feat(oapi): Allow additionalProperties to be an array

* wip

* wip
  • Loading branch information
chrislearn authored Sep 12, 2023
1 parent 7b9d91e commit 885ff42
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 53 deletions.
1 change: 1 addition & 0 deletions crates/oapi-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ uuid = { workspace = true, optional = true }
default = []
chrono = []
decimal = []
decimal-float = []
ulid = ["dep:ulid"]
url = ["dep:url"]
uuid = ["dep:uuid"]
Expand Down
21 changes: 15 additions & 6 deletions crates/oapi-macros/src/schema_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl SchemaType<'_> {
#[cfg(not(any(
feature = "chrono",
feature = "decimal",
feature = "decimal-float",
feature = "ulid",
feature = "url",
feature = "uuid",
Expand All @@ -40,6 +41,7 @@ impl SchemaType<'_> {
#[cfg(any(
feature = "chrono",
feature = "decimal",
feature = "decimal-float",
feature = "ulid",
feature = "url",
feature = "uuid",
Expand All @@ -53,7 +55,7 @@ impl SchemaType<'_> {
primitive = is_primitive_chrono(name);
}

#[cfg(feature = "decimal")]
#[cfg(any(feature = "decimal", feature = "decimal-float"))]
if !primitive {
primitive = is_primitive_rust_decimal(name);
}
Expand Down Expand Up @@ -143,7 +145,7 @@ fn is_primitive_chrono(name: &str) -> bool {
}

#[inline]
#[cfg(feature = "decimal")]
#[cfg(any(feature = "decimal", feature = "decimal-float"))]
fn is_primitive_rust_decimal(name: &str) -> bool {
matches!(name, "Decimal")
}
Expand Down Expand Up @@ -173,8 +175,10 @@ impl ToTokens for SchemaType<'_> {
"NaiveDate" => tokens.extend(quote!(#oapi::oapi::SchemaType::String)),
#[cfg(any(feature = "chrono", feature = "time"))]
"Date" | "Duration" => tokens.extend(quote! { #oapi::oapi::SchemaType::String }),
#[cfg(feature = "decimal")]
#[cfg(all(feature = "decimal", not(feature = "decimal-float")))]
"Decimal" => tokens.extend(quote! { #oapi::oapi::SchemaType::String }),
#[cfg(all(not(feature = "decimal"), feature = "decimal-float"))]
"Decimal" => tokens.extend(quote! { utoipa::openapi::SchemaType::Number }),
#[cfg(feature = "ulid")]
"Ulid" => tokens.extend(quote! { #oapi::oapi::SchemaType::String }),
#[cfg(feature = "uuid")]
Expand Down Expand Up @@ -242,9 +246,10 @@ impl Type<'_> {

#[cfg(not(any(
feature = "chrono",
feature = "decimal-float",
feature = "ulid",
feature = "url",
feature = "uuid",
feature = "url",
feature = "time"
)))]
{
Expand All @@ -253,9 +258,10 @@ impl Type<'_> {

#[cfg(any(
feature = "chrono",
feature = "decimal-float",
feature = "ulid",
feature = "url",
feature = "uuid",
feature = "url",
feature = "time"
))]
{
Expand All @@ -265,7 +271,10 @@ impl Type<'_> {
if !known_format {
known_format = matches!(name, "DateTime" | "Date" | "NaiveDate" | "NaiveDateTime");
}

#[cfg(feature = "decimal-float")]
if !known_format {
known_format = matches!(name, "Decimal");
}
#[cfg(feature = "ulid")]
if !known_format {
known_format = matches!(name, "Ulid");
Expand Down
1 change: 1 addition & 0 deletions crates/oapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rapidoc = []
redoc = []
chrono = ["salvo-oapi-macros/chrono"]
decimal = ["salvo-oapi-macros/decimal"]
decimal_float = ["salvo-oapi-macros/decimal-float"]
yaml = ["dep:serde_yaml"]
ulid = ["salvo-oapi-macros/ulid"]
uuid = ["salvo-oapi-macros/uuid"]
Expand Down
28 changes: 15 additions & 13 deletions crates/oapi/docs/lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@ you can use to annotate your code to have items documented.
# Crate Features

- **yaml** Enables **serde_yaml** serialization of OpenAPI objects.

- **chrono** Add support for [chrono](https://crates.io/crates/chrono) `DateTime`, `Date`, `NaiveDate` and `Duration`
types. By default these types are parsed to `string` types with additional `format` information.
`format: date-time` for `DateTime` and `format: date` for `Date` and `NaiveDate` according
[RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) as `ISO-8601`. To
override default `string` representation users have to use `value_type` attribute to override the type.
See [docs](https://docs.rs/salvo_oapi/latest/salvo_oapi/derive.ToSchema.html) for more details.
- **time** Add support for [time](https://crates.io/crates/time) `OffsetDateTime`, `PrimitiveDateTime`, `Date`, and `Duration` types.
By default these types are parsed as `string`. `OffsetDateTime` and `PrimitiveDateTime` will use `date-time` format. `Date` will use
`date` format and `Duration` will not have any format. To override default `string` representation users have to use `value_type` attribute
to override the type. See [docs](https://docs.rs/salvo_oapi/latest/salvo_oapi/derive.ToSchema.html) for more details.
- **decimal** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
it is interpreted as `String`. If you wish to change the format you need to override the type.
See the `value_type` in [`ToSchema` derive docs][to_schema_derive].
- **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with
format `uuid` in OpenAPI spec.
- **ulid** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with
format `ulid` in OpenAPI spec.
- **url** Add support for [url](https://github.com/servo/rust-url). `Url` type will be presented as `String` with
format `uri` in OpenAPI spec.

- **time** Add support for [time](https://crates.io/crates/time) `OffsetDateTime`, `PrimitiveDateTime`, `Date`, and `Duration` types. By default these types are parsed as `string`. `OffsetDateTime` and `PrimitiveDateTime` will use `date-time` format. `Date` will use `date` format and `Duration` will not have any format. To override default `string` representation users have to use `value_type` attribute to override the type. See [docs](https://docs.rs/salvo_oapi/latest/salvo_oapi/derive.ToSchema.html) for more details.

- **decimal** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** it is interpreted as `String`. If you wish to change the format you need to override the type. See the `value_type` in [`ToSchema` derive docs][to_schema_derive].

- **decimal-float** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** it is interpreted as `Number`. This feature is mutually exclusive with **decimal** and allow to change the default type used in your documentation for `Decimal` much like `serde_with_float` feature exposed by rust_decimal.

- **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with format `uuid` in OpenAPI spec.

- **ulid** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with format `ulid` in OpenAPI spec.

- **url** Add support for [url](https://github.com/servo/rust-url). `Url` type will be presented as `String` with format `uri` in OpenAPI spec.

- **smallvec** Add support for [smallvec](https://crates.io/crates/smallvec). `SmallVec` will be treated as `Vec`.

- **indexmap** Add support for [indexmap](https://crates.io/crates/indexmap). When enabled `IndexMap` will be rendered as a map similar to
`BTreeMap` and `HashMap`.

Expand Down
20 changes: 20 additions & 0 deletions crates/oapi/src/openapi/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ impl From<Object> for AdditionalProperties<Schema> {
}
}

impl From<Array> for AdditionalProperties<Schema> {
fn from(value: Array) -> Self {
Self::RefOr(RefOr::T(Schema::Array(value)))
}
}

impl From<Ref> for AdditionalProperties<Schema> {
fn from(value: Ref) -> Self {
Self::RefOr(RefOr::Ref(value))
Expand Down Expand Up @@ -423,6 +429,20 @@ mod tests {
})
);

let json_value = Object::new().additional_properties(Array::new(Object::new().schema_type(SchemaType::Number)));
assert_json_eq!(
json_value,
json!({
"type": "object",
"additionalProperties": {
"items": {
"type": "number",
},
"type": "array",
}
})
);

let json_value = Object::new().additional_properties(Ref::from_schema_name("ComplexModel"));
assert_json_eq!(
json_value,
Expand Down
3 changes: 2 additions & 1 deletion examples/acme-http01-quinn/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ async fn main() {
.acme()
.cache_path("temp/letsencrypt")
.add_domain("test.salvo.rs")
.http01_challege(&mut router).quinn("0.0.0.0:443");
.http01_challege(&mut router)
.quinn("0.0.0.0:443");
let acceptor = listener.join(TcpListener::new("0.0.0.0:80")).bind().await;
Server::new(acceptor).serve(router).await;
}
9 changes: 5 additions & 4 deletions examples/cors/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use salvo::cors::Cors;
use salvo::catcher::Catcher;
use salvo::cors::Cors;
use salvo::http::Method;
use salvo::prelude::*;


#[tokio::main]
async fn main() {
tracing_subscriber::fmt().init();
Expand All @@ -22,7 +21,9 @@ async fn backend_server() {
.allow_headers("authorization")
.into_handler();

let router = Router::with_hoop(cors.clone()).push(Router::with_path("hello").post(hello)).options(handler::empty());
let router = Router::with_hoop(cors.clone())
.push(Router::with_path("hello").post(hello))
.options(handler::empty());

let acceptor = TcpListener::new("0.0.0.0:5600").bind().await;
let service = Service::new(router).catcher(Catcher::default().hoop(cors));
Expand Down Expand Up @@ -62,4 +63,4 @@ document.getElementById("btn").addEventListener("click", function() {
</script>
</body>
</html>
"#;
"#;
13 changes: 7 additions & 6 deletions examples/db-sea-orm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ struct AppState {
#[handler]
async fn create(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Result<()> {
let state = depot
.obtain::<AppState>().map_err(|_|StatusError::internal_server_error())?;
.obtain::<AppState>()
.map_err(|_| StatusError::internal_server_error())?;
let form = req
.parse_form::<post::Model>()
.await
Expand All @@ -46,7 +47,7 @@ async fn create(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res
async fn list(req: &mut Request, depot: &mut Depot) -> Result<Text<String>> {
let state = depot
.obtain::<AppState>()
.map_err(|_|StatusError::internal_server_error())?;
.map_err(|_| StatusError::internal_server_error())?;
let page = req.query("page").unwrap_or(1);
let posts_per_page = req.query("posts_per_page").unwrap_or(DEFAULT_POSTS_PER_PAGE);
let paginator = post::Entity::find()
Expand Down Expand Up @@ -75,7 +76,7 @@ async fn list(req: &mut Request, depot: &mut Depot) -> Result<Text<String>> {
async fn new(depot: &mut Depot) -> Result<Text<String>> {
let state = depot
.obtain::<AppState>()
.map_err(|_|StatusError::internal_server_error())?;
.map_err(|_| StatusError::internal_server_error())?;
let ctx = tera::Context::new();
let body = state
.templates
Expand All @@ -88,7 +89,7 @@ async fn new(depot: &mut Depot) -> Result<Text<String>> {
async fn edit(req: &mut Request, depot: &mut Depot) -> Result<Text<String>> {
let state = depot
.obtain::<AppState>()
.map_err(|_|StatusError::internal_server_error())?;
.map_err(|_| StatusError::internal_server_error())?;
let id = req.param::<i32>("id").unwrap_or_default();
let post: post::Model = post::Entity::find_by_id(id)
.one(&state.conn)
Expand All @@ -110,7 +111,7 @@ async fn edit(req: &mut Request, depot: &mut Depot) -> Result<Text<String>> {
async fn update(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Result<()> {
let state = depot
.obtain::<AppState>()
.map_err(|_|StatusError::internal_server_error())?;
.map_err(|_| StatusError::internal_server_error())?;
let id = req.param::<i32>("id").unwrap_or_default();
let form = req
.parse_form::<post::Model>()
Expand All @@ -132,7 +133,7 @@ async fn update(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res
async fn delete(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Result<()> {
let state = depot
.obtain::<AppState>()
.map_err(|_|StatusError::internal_server_error())?;
.map_err(|_| StatusError::internal_server_error())?;
let id = req.param::<i32>("id").unwrap_or_default();
let post: post::ActiveModel = post::Entity::find_by_id(id)
.one(&state.conn)
Expand Down
26 changes: 11 additions & 15 deletions examples/extract-data/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@ async fn edit(req: &mut Request) -> String {
}

#[derive(Serialize, Deserialize, Extractible, Debug)]
#[salvo(
extract(
default_source(from = "query"),
default_source(from = "param"),
default_source(from = "body")
)
)]
#[salvo(extract(
default_source(from = "query"),
default_source(from = "param"),
default_source(from = "body")
))]
struct BadMan<'a> {
id: i64,
username: &'a str,
Expand All @@ -53,14 +51,12 @@ struct BadMan<'a> {
lovers: Vec<String>,
}
#[derive(Serialize, Deserialize, Extractible, Debug)]
#[salvo(
extract(
default_source(from = "query"),
default_source(from = "param"),
default_source(from = "body"),
rename_all = "camelCase"
)
)]
#[salvo(extract(
default_source(from = "query"),
default_source(from = "param"),
default_source(from = "body"),
rename_all = "camelCase"
))]
struct GoodMan<'a> {
id: i64,
username: &'a str,
Expand Down
5 changes: 3 additions & 2 deletions examples/hello/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ async fn main() {
}

fn route() -> Router {
Router::new().get(hello)
.push(Router::with_path("a").get(hello2))
Router::new()
.get(hello)
.push(Router::with_path("a").get(hello2))
.push(Router::with_path("你好").get(hello_zh))
.push(Router::with_path("hello3").get(hello3))
}
2 changes: 1 addition & 1 deletion examples/rate-limiter-dynamic/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::hash::Hash;

use once_cell::sync::Lazy;
use salvo::prelude::*;
use salvo::Error;
use salvo::rate_limiter::{CelledQuota, MokaStore, QuotaGetter, RateIssuer, RateLimiter, SlidingGuard};
use salvo::Error;

static USER_QUOTAS: Lazy<HashMap<String, CelledQuota>> = Lazy::new(|| {
let mut map = HashMap::new();
Expand Down
2 changes: 1 addition & 1 deletion examples/request-id/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ async fn main() {
let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
let router = Router::new().hoop(RequestId::new()).get(hello);
Server::new(acceptor).serve(router).await;
}
}
2 changes: 1 addition & 1 deletion examples/todos-utoipa/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ mod tests {
.send(super::route())
.await;

assert_eq!(res .status_code.unwrap(), StatusCode::BAD_REQUEST);
assert_eq!(res.status_code.unwrap(), StatusCode::BAD_REQUEST);
}

fn test_todo() -> Todo {
Expand Down
7 changes: 4 additions & 3 deletions examples/with-listenfd/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::net::SocketAddr;

use listenfd::ListenFd;
use salvo::conn::tcp::TcpAcceptor;
use salvo::prelude::*;
use salvo::conn::{tcp::TcpAcceptor, };

#[handler]
async fn hello() -> &'static str {
"Hello World"
}

#[tokio::main]
async fn main() -> Result<(), salvo::Error> {
async fn main() -> Result<(), salvo::Error> {
tracing_subscriber::fmt().init();

let router = Router::new().get(hello);
Expand All @@ -27,7 +27,8 @@ async fn main() -> Result<(), salvo::Error> {
std::env::var("HOST").unwrap_or("0.0.0.0".into()),
std::env::var("PORT").unwrap_or("8080".into())
)
.parse().unwrap();
.parse()
.unwrap();
(addr, tokio::net::TcpListener::bind(addr).await.unwrap())
};

Expand Down

0 comments on commit 885ff42

Please sign in to comment.