Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement RustGenerator proof-of-concept #818

Merged
merged 20 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6a86fc1
feat: Rust Generator proof-of-concept
leigh-johnson Jul 26, 2022
8afbc83
chore: appease sonar linter 🐐
leigh-johnson Jul 26, 2022
90c9038
chore: regenerate rust-generate-crate snapshot from example doc
leigh-johnson Jul 26, 2022
925307a
nit: fix copied comment
leigh-johnson Jul 26, 2022
ba57117
chore: update README, usage and language docs
leigh-johnson Jul 29, 2022
81be6ba
chore: cleanup
leigh-johnson Jul 29, 2022
4cbe2ae
chore: add Rust to blackbox test spec
leigh-johnson Jul 29, 2022
1958a07
chore: add @leigh-johnson as Rust language CODEOWNER
leigh-johnson Jul 29, 2022
93986f2
chore: fix 2 sonarjs code smells
leigh-johnson Aug 2, 2022
31e6ba1
chore: re-lint + fix flubbed merge conflict and re-generate rust-gene…
leigh-johnson Aug 2, 2022
2e1420e
split cargo crate supporting files into RustFileGenerator.generateToP…
leigh-johnson Aug 2, 2022
ea25b20
Update src/generators/rust/renderers/EnumRenderer.ts
leigh-johnson Aug 6, 2022
aa58b4f
Update src/generators/rust/presets/CommonPreset.ts
leigh-johnson Aug 6, 2022
ee61e52
Update src/generators/rust/RustGenerator.ts
leigh-johnson Aug 6, 2022
e34ce6c
Update src/generators/rust/RustGenerator.ts
leigh-johnson Aug 6, 2022
74fd6f8
fix: concat does not mutate input array
leigh-johnson Aug 6, 2022
7f073b3
Update src/generators/rust/RustGenerator.ts
leigh-johnson Aug 6, 2022
1800efc
re-generate snapshots, not sure why 6 calls to spy? (should be 5)
leigh-johnson Aug 6, 2022
07ab817
Update src/generators/rust/renderers/EnumRenderer.ts
leigh-johnson Aug 6, 2022
410ac7c
fix: linter
leigh-johnson Aug 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@
*/generators/csharp @ron-debajyoti

# Language Champions for Dart and it's presets
*/generators/dart
*/generators/dart

# Language Champions for Rust and its presets
*/generators/rust @leigh-johnson
jonaslagoni marked this conversation as resolved.
Show resolved Hide resolved

3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ RUN apt install apt-transport-https dirmngr gnupg ca-certificates -yq \
&& apt update -yq \
&& apt install mono-devel -yq

# Install rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y

# Setup library
COPY package-lock.json .
RUN npm install
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ To see the complete feature list for each language, please click the individual
<td><a href="./docs/usage.md#generate-dart-models">Dart</a></td>
<td>Class and enum generation: json_annotation</td>
</tr>
<tr>
<td><a href="./docs/usage.md#generate-dart-models">Dart</a></td>
<td>Class and enum generation: json_annotation</td>
</tr>
leigh-johnson marked this conversation as resolved.
Show resolved Hide resolved
<tr>
<td><a href="./docs/usage.md#generate-rust-models">Rust</a></td>
<td>Struct/tuple and enum generation: <em>generation of `implement Default`, generate serde macros, custom indentation type and size, etc</em></td>
</tr>
</table>

## Roadmap
Expand Down
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ Each language has its own limitations, corner cases, and features; thus, each la
- [Go](./languages/Go.md)
- [Java](./languages/Java.md)
- [JavaScript](./languages/JavaScript.md)
- [TypeScript](./languages/TypeScript.md)
- [TypeScript](./languages/TypeScript.md)
- [Rust](./languages/Rust.md)
57 changes: 57 additions & 0 deletions docs/languages/Rust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Rust

<!-- toc is generated with GitHub Actions do not remove toc markers -->

<!-- toc -->

- [Language Features](#language-features)
- [Generator Features](#generator-features)
- [Implement `new`](#implement-new)
- [Implement `default`](#implement-default)
- [Implement `From<String> (serde_json)`](#implement-from_json_string)
- [Implement `Into<String> (serde_json)`](#implement-to_json_stringn)
- [Implement `From<FramedByteStream> (tokio_serde)`](#implement-from-framed-byte-stream)
- [Implement `Into<FramedByteStream> (tokio_serde)`](#implement-into-framed-byte-stream)
<!-- tocstop -->

## Language Features

Generated code depends on the following Cargo features:

- derive
- alloc

## Generator Features


| **Feature** | **Status** | **Info** |
|------------------------------|---------------|---------------------------------------------------------------------------------------|
| Union (polymorphic type) | ✅ done | |
| Enum (group of constants) | ✅ done | |
| Array (unordered collection) | ✅ done | |
| Tuple (ordered collection) | ✅ done | |


## Implement `new`

To generate a `new` method, use the preset `RUST_COMMON_PRESET` and provide the option `implementNew: true`.

## Implement `Default` for enums

To generate `Default` implementation for enums that provide a default value, use the preset `RUST_COMMON_PRESET` and provide the option `implementDefault: true`.

## Implement `From<String>` (serde_json)

TODO

## Implement `Into<String>` (serde_json)

TODO

## Implement `From<FramedByteStream>` (tokio_serde)

TODO

## Implement `From<FramedByteStream>` (tokio_serde)

TOOD
5 changes: 5 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ For more specific integration options, please check out the [integration documen
- [Generate TypeScript models](#generate-typescript-models)
- [Generate JavaScript models](#generate-javascript-models)
- [Generate Dart models](#generate-dart-models)
- [Generate Rust models](#generate-rust-models)

<!-- tocstop -->

Expand Down Expand Up @@ -120,3 +121,7 @@ JavaScript is one of the many output languages we support. Check out this [basic
## Generate Dart models

Dart is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-dart-models) and the following [Dart documentation for more advanced use-cases](./languages/Dart.md).

## Generate Rust models

Rust is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-rust-crate) and the following [Rust documentation for more advanced use-cases](./languages/Rust.md).
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ This directory contains a series of self-contained examples that you can use as
- [java-from-typescript-type-with-options](./java-from-typescript-type-with-options/) - A basic example that shows how to generate a Java model from a TypeScript type input file along with user provided options.
- [overwrite-naming-formatting](./overwrite-naming-formatting) - A basic example how to overwrite default naming format constraint in this case, overwriting returning a constant case format.
- [overwrite-default-constraint](./overwrite-default-constraint/) - A basic example how to overwrite the entire constraint logic and not just a single single part of the default behavior, in this case overwriting the model naming constraint.
- [rust-generate-crate](./rust-generate-crate/) - A basic example showing how to generate a Rust package.
1 change: 1 addition & 0 deletions examples/rust-generate-crate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output
23 changes: 23 additions & 0 deletions examples/rust-generate-crate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Rust Data Models

A basic example of how to use Modelina and output a Rust crate.

## Requirements

- [Rust](https://rustup.rs/)

Rust is required to compile this example.

## How to run this example

Run this example using:

```sh
npm i && npm run start
```

If you are on Windows, use the `start:windows` script instead:

```sh
npm i && npm run start:windows
```
127 changes: 127 additions & 0 deletions examples/rust-generate-crate/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Should be able to render Rust Models and should log expected output to console 1`] = `
Array [
"// Address represents a Address model.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Address {
#[serde(rename=\\"street_name\\")]
street_name: String,
#[serde(rename=\\"city\\")]
city: String,
#[serde(rename=\\"state\\")]
state: String,
#[serde(rename=\\"house_number\\")]
house_number: f64,
#[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")]
marriage: Option<bool>,
#[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")]
members: Option<Box<crate::Members>>,
#[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")]
tuple_type: Option<Box<crate::TupleType>>,
#[serde(rename=\\"array_type\\")]
array_type: Vec<String>,
#[serde(rename=\\"enum_type\\", skip_serializing_if = \\"Option::is_none\\")]
enum_type: Option<Box<crate::EnumType>>,
#[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")]
additional_properties: Option<std::collections::HashMap<String, String>>,
}

impl Address {
pub fn new(street_name: String, city: String, state: String, house_number: f64, marriage: Option<bool>, members: Option<crate::Members>, tuple_type: Option<crate::TupleType>, array_type: Vec<String>, enum_type: Option<crate::EnumType>, additional_properties: Option<std::collections::HashMap<String, String>>) -> Address {
Address {
street_name,
city,
state,
house_number,
marriage,
members: members.map(Box::new),
tuple_type: tuple_type.map(Box::new),
array_type,
enum_type: enum_type.map(Box::new),
additional_properties,
}
}
}
",
]
`;

exports[`Should be able to render Rust Models and should log expected output to console 2`] = `
Array [
"// Members represents a union of types: String, f64, bool
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum Members {
#[serde(rename=\\"Members0\\")]
Members0(String),
#[serde(rename=\\"Members1\\")]
Members1(f64),
#[serde(rename=\\"Members2\\")]
Members2(bool),
}

",
]
`;

exports[`Should be able to render Rust Models and should log expected output to console 3`] = `
Array [
"// TupleType represents a TupleType model.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct TupleType(String, f64);

impl TupleType {
pub fn new(value_0: String, value_1: f64) -> TupleType {
TupleType(value_0, value_1)
}
}

",
]
`;

exports[`Should be able to render Rust Models and should log expected output to console 4`] = `
Array [
"[package]
name = \\"asyncapi-rs-example\\"
version = \\"1.0.0\\"
authors = [\\"AsyncAPI Rust Champions\\"]
homepage = \\"https://www.asyncapi.com/tools/modelina\\"
repository = \\"https://github.com/asyncapi/modelina\\"
license = \\"Apache-2.0\\"
description = \\"Rust models generated by AsyncAPI Modelina\\"
edition = \\"2018\\"

[dependencies]
serde = { version = \\"1\\", features = [\\"derive\\"] }
serde_json = { version=\\"1\\", optional = true }

[dev-dependencies]

[features]
default = [\\"json\\"]
json = [\\"dep:serde_json\\"]",
]
`;

exports[`Should be able to render Rust Models and should log expected output to console 5`] = `
Array [
"#[macro_use]
extern crate serde;
extern crate serde_json;

pub mod address;
pub use self::address::*;

pub mod members;
pub use self::members::*;

pub mod tuple_type;
pub use self::tuple_type::*;

pub mod enum_type;
pub use self::enum_type::*;",
]
`;

exports[`Should be able to render Rust Models and should log expected output to console 6`] = `undefined`;
17 changes: 17 additions & 0 deletions examples/rust-generate-crate/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; });
import { generate } from './index';
describe('Should be able to render Rust Models', () => {
afterAll(() => {
jest.restoreAllMocks();
});
test('and should log expected output to console', async () => {
await generate();
expect(spy.mock.calls.length).toEqual(6);
expect(spy.mock.calls[0]).toMatchSnapshot();
expect(spy.mock.calls[1]).toMatchSnapshot();
expect(spy.mock.calls[2]).toMatchSnapshot();
expect(spy.mock.calls[4]).toMatchSnapshot();
expect(spy.mock.calls[5]).toMatchSnapshot();
expect(spy.mock.calls[6]).toMatchSnapshot();
});
});
67 changes: 67 additions & 0 deletions examples/rust-generate-crate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { RustFileGenerator, RustRenderCompleteModelOptions, RUST_COMMON_PRESET, defaultRustRenderCompleteModelOptions, RustPackageFeatures } from '../../src/generators';
import * as path from 'path';

const doc = {
$id: '_address',
type: 'object',
properties: {
street_name: { type: 'string' },
city: { type: 'string', description: 'City description' },
state: { type: 'string' },
house_number: { type: 'number' },
marriage: { type: 'boolean', description: 'Status if marriage live in given house' },
members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], },
tuple_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: false },
array_type: { type: 'array', items: { type: 'string' }, additionalItems: false },
enum_type: {
enum: ['Texas', 'Alabama', 'California'],
default: 'California'
}
},
required: ['street_name', 'city', 'state', 'house_number', 'array_type'],
additionalProperties: {
type: 'string'
},
};

export async function generate(): Promise<void> {
// initialize the generator from a preset
const generator = new RustFileGenerator({
presets: [
{
preset: RUST_COMMON_PRESET, options: {
implementNew: true,
implementDefault: true
}
}
]
});
// Generated files will be written to output/ directory
const outDir = path.join(__dirname, 'output');

// Run the file generator with options
const models = await generator.generateToPackage(doc, outDir, {
...defaultRustRenderCompleteModelOptions,
supportFiles: true, // generate Cargo.toml and lib.rs
package: {
packageName: 'asyncapi-rs-example',
packageVersion: '1.0.0',
// set authors, homepage, repository, and license
authors: ['AsyncAPI Rust Champions'],
homepage: 'https://www.asyncapi.com/tools/modelina',
repository: 'https://github.com/asyncapi/modelina',
license: 'Apache-2.0',
description: 'Rust models generated by AsyncAPI Modelina',
// support 2018 editions and up
edition: '2018',
// enable serde_json
packageFeatures: [RustPackageFeatures.json] as RustPackageFeatures[]
}
leigh-johnson marked this conversation as resolved.
Show resolved Hide resolved
} as RustRenderCompleteModelOptions);
for (const model of models) {
console.log(model.result);
}
}
if (require.main === module) {
generate();
}
10 changes: 10 additions & 0 deletions examples/rust-generate-crate/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions examples/rust-generate-crate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"config": {
"example_name": "rust-generate-crate"
},
"scripts": {
"install": "cd ../.. && npm i",
"clean": "rm -rf output",
"start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts generate && cargo build --manifest-path=output/Cargo.toml ",
"start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts",
"test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts",
"test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts"
}
}
Loading