Crates.io | schema2code |
lib.rs | schema2code |
version | 0.1.4 |
source | src |
created_at | 2023-09-03 09:02:34.776738 |
updated_at | 2023-09-25 07:25:08.989493 |
description | Generate Code in any language from a JSON Schema compliant document |
homepage | |
repository | https://github.com/lur1an/schema2code |
max_upload_size | |
id | 962154 |
size | 42,679 |
A language agnostic model generator for a subset of JSON schemas. (Rust only for now, next planned: python + pydantic)
Check out the codegen-test
crate to see how it can be used to generate your code at compile time.
While writing the parser
and deserializer
I put some constraints of possible schemas, this only allows a subset of possible asyncapi
schema definitions.
components -> schemas
part of the document needs to be an actual schema, this would not be allowed:components:
schemas:
MyEntity:
$ref: '#/components/schemas/AnotherEntity'
[AllOf, OneOf, AnyOf, type: object]
, top-level array
types don't currently work, in your asyncapi schema I'd recommend creating an anonymous schema in the messages
part
of the specification and creating the specific item
type for the array
items in the components/schemas
section such that your code will have the type for the items and you can easily deserialize payloads by wrapping it in language specific collections.const
value is specified there must be a type
with it.format
directive is simply ignoredAllOf
works duplicate properties will cause errors in Rust, the current codegenerator
just takes the combined schemas, creates an AnonymousEntity
for each (or a named one if title
is set) and then combines them with #[serde(flatten)]
in a struct, this will cause the deserialization to fail if the combined schemas define overlapping properties. (Fixing this is on my roadmap but not a priority, in OOP languages my codegenerator will simply extend all AllOf
schema classes and duplicate properties will be handled by the inheritance of the programming language)OneOf
schemas with a specific discriminator
set it currently only works if the discriminator matches the name of the entity (For anonymous entity set the title
property for a deterministic name), otherwise if you use special values for the discriminator inside of const
fields you need to omit the discriminator
for now and just use the #[serde(untagged)]
enum that is generated, const
fields will be respected through the use of the monostate
crate.AllOf
schemas currently don't merge properties, out of lazyness they create struct for inner schemas and then put them in a single struct through #[serde(flatten)]
. (Out of simplicity I may use a solution like this in other languages, having a named empty class inherit from anonymous/named structs for its fields)pydantic
model generatorThe deserializer
defines untagged
enums with monostate::MustBe
for the deserialization of a schema, this leads to quite unhelpful error messages when you schema does not match, most of the errors are Did not match any variant in SchemaDef
Asyncapi schema definitions:
RequestBase:
type: object
additionalProperties:
type: array
properties:
id:
type: string
description: "correlation id to match request and response"
kind:
type: string
const: request
tupleProp:
type: array
items: false
prefixItems:
- type: string
- type: object
required:
- id
GetUser:
description: TODO
allOf:
- $ref: '#/components/schemas/RequestBase'
- type: object
title: GetUserInner
properties:
data:
title: GetUserData
type: object
properties:
userId:
type: string
name:
type: string
required:
- userId
required:
- data
- event
DeleteUser:
description: TODO
allOf:
- $ref: '#/components/schemas/RequestBase'
- type: object
title: DeleteUserInner
properties:
data:
title: DeleteUserData
type: object
properties:
userId:
type: string
required:
- userId
required:
- data
- event
SampleRequestPayload:
description: "SampleRequestPayload"
discriminator: event
oneOf:
- $ref: '#/components/schemas/GetUser'
- $ref: '#/components/schemas/DeleteUser'
Generated rust code:
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct RequestBase {
#[serde(rename = "id")]
id: String,
#[serde(rename = "kind")]
kind: Option<monostate::MustBe!("request")>,
#[serde(rename = "tupleProp")]
tuple_prop: Option<(String, serde_json::Value)>,
#[serde(flatten)]
additional_properties: std::collections::HashMap<String, Vec<serde_json::Value>>,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct DeleteUserData {
#[serde(rename = "userId")]
user_id: String,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct DeleteUserInner {
#[serde(rename = "data")]
data: DeleteUserData,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct DeleteUser {
#[serde(flatten)]
request_base: RequestBase,
#[serde(flatten)]
delete_user_inner: DeleteUserInner,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct GetUserData {
#[serde(rename = "userId")]
user_id: String,
#[serde(rename = "name")]
name: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct GetUserInner {
#[serde(rename = "data")]
data: GetUserData,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
pub struct GetUser {
#[serde(flatten)]
request_base: RequestBase,
#[serde(flatten)]
get_user_inner: GetUserInner,
}
#[derive(Debug, Clone, Eq, PartialEq, serde :: Deserialize, serde :: Serialize)]
#[serde(tag = "event")]
pub enum SampleRequestPayload {
GetUser(GetUser),
DeleteUser(DeleteUser),
}