Skip to content

Commit 6162872

Browse files
committed
Init
0 parents  commit 6162872

File tree

16 files changed

+363
-0
lines changed

16 files changed

+363
-0
lines changed

.github/workflows/ci-cargo.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
on: [push]
2+
3+
name: CI
4+
5+
jobs:
6+
build_and_test:
7+
name: Rust project
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- uses: actions-rs/toolchain@v1
12+
with:
13+
toolchain: stable
14+
- uses: actions-rs/cargo@v1
15+
with:
16+
command: build
17+
args: --release --all-features
18+
- uses: actions-rs/cargo@v1
19+
with:
20+
command: test

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
/Cargo.lock
3+
.idea

Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "cdd-rust"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Samuel Marks"]
6+
description = "OpenAPI ↔ Rust"
7+
readme = true
8+
license = "Apache-2.0 OR MIT"
9+
keywords = ["openapi", "actix", "actix-web", "diesel", "rest", "orm"]
10+
categories = [
11+
"development-tools",
12+
"development-tools::testing",
13+
"parser-implementations",
14+
"template-engine",
15+
"web-programming::http-server"
16+
]
17+
18+
[toolchain]
19+
channel = "nightly" # Needed by diesel
20+
21+
[dependencies]
22+
rowan = { git = "https://github.com/rust-analyzer/rowan", branch = "master" }
23+
24+
[dev-dependencies]
25+
actix_web_mocks = { path = "src/actix_web_mocks" }
26+
diesel_mocks = { path = "src/diesel_mocks" }

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
cdd-rust
2+
--------
3+
[![Rust: nightly](https://img.shields.io/badge/Rust-nightly-blue.svg)](https://www.rust-lang.org)
4+
[![License: (Apache-2.0 OR MIT)](https://img.shields.io/badge/LICENSE-Apache--2.0%20OR%20MIT-orange)](LICENSE-APACHE)
5+
[![CI](https://github.com/offscale/cdd-python/workflows/CI/badge.svg)](https://github.com/offscale/cdd-rust/actions)
6+
7+
OpenAPI ↔ Rust. Compiler Driven Development (CDD) is a new development methodology, with implementations in many languages.
8+
9+
The central idea is to statically code-generate from target language to OpenAPI, and from OpenAPI back to target language.
10+
All without having an untouchable 'generated' directory and without requiring `#[openapi]` annotations on `struct`s and routes.
11+
12+
Key other advantages are:
13+
14+
- automated updating of tests and docs, making it feasible to maintain 100% coverage without trading off development agility;
15+
- synchronisation across language boundaries (e.g., between the frontends, and from them to the backend).
16+
17+
Longer-term there are many other advantages, including:
18+
19+
- inversion of control, enabling the business analyst to design schemas (Google Forms or even MS Access style);
20+
- simplifying separating projects out into multiple smaller projects, and smaller projects into a big project;
21+
- providing an alternative to NoSQL for many user-defined schema scenarios (such as a survey-builder site).
22+
23+
---
24+
25+
## Developer guide
26+
27+
Install the latest version of [Rust](https://www.rust-lang.org). We tend to use nightly versions. [CLI tool for installing Rust](https://rustup.rs).
28+
29+
We use [rust-clippy](https://github.com/rust-lang-nursery/rust-clippy) linters to improve code quality.
30+
31+
There are plenty of [IDEs](https://areweideyet.com) and other [Rust development tools to consider](https://github.com/rust-unofficial/awesome-rust#development-tools).
32+
33+
### Step-by-step guide
34+
35+
```bash
36+
# Install Rust (nightly)
37+
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain nightly
38+
# Install cargo-make (cross-platform feature-rich reimplementation of Make)
39+
$ cargo install --force cargo-make
40+
# Install rustfmt (Rust formatter)
41+
$ rustup component add rustfmt
42+
# Clone this repo
43+
$ git clone https://github.com/offscale/cdd-rust && cd cdd-rust
44+
# Run tests
45+
$ cargo test
46+
# Format, build and test
47+
$ cargo make
48+
```
49+
50+
## License
51+
52+
Licensed under either of
53+
54+
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
55+
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
56+
57+
at your option.
58+
59+
### Contribution
60+
61+
Unless you explicitly state otherwise, any contribution intentionally submitted
62+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
63+
dual licensed as above, without any additional terms or conditions.

src/actix_web_mocks/COPYING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The Cargo.toml and *.rs files in this directory are Apache-2.0 and taken from:
2+
https://github.com/actix/examples/tree/4dc919/json/json

src/actix_web_mocks/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "actix_web_mocks"
3+
version = "1.0.0"
4+
edition = "2021"
5+
6+
[toolchain]
7+
channel = "nightly"
8+
9+
[dependencies]
10+
actix-web = "4"
11+
futures = "0.3"
12+
env_logger = "0.9.0"
13+
serde = { version = "1.0", features = ["derive"] }
14+
serde_json = "1.0"
15+
json = "0.12"

src/actix_web_mocks/src/lib.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use actix_web::{error, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
2+
use futures::StreamExt;
3+
use json::JsonValue;
4+
use serde::{Deserialize, Serialize};
5+
6+
#[derive(Debug, Serialize, Deserialize)]
7+
struct MyObj {
8+
name: String,
9+
number: i32,
10+
}
11+
12+
/// This handler uses json extractor
13+
async fn index(item: web::Json<MyObj>) -> HttpResponse {
14+
println!("model: {:?}", &item);
15+
HttpResponse::Ok().json(item.0) // <- send response
16+
}
17+
18+
/// This handler uses json extractor with limit
19+
async fn extract_item(item: web::Json<MyObj>, req: HttpRequest) -> HttpResponse {
20+
println!("request: {:?}", req);
21+
println!("model: {:?}", item);
22+
23+
HttpResponse::Ok().json(item.0) // <- send json response
24+
}
25+
26+
const MAX_SIZE: usize = 262_144; // max payload size is 256k
27+
28+
/// This handler manually load request payload and parse json object
29+
async fn index_manual(mut payload: web::Payload) -> Result<HttpResponse, Error> {
30+
// payload is a stream of Bytes objects
31+
let mut body = web::BytesMut::new();
32+
while let Some(chunk) = payload.next().await {
33+
let chunk = chunk?;
34+
// limit max size of in-memory payload
35+
if (body.len() + chunk.len()) > MAX_SIZE {
36+
return Err(error::ErrorBadRequest("overflow"));
37+
}
38+
body.extend_from_slice(&chunk);
39+
}
40+
41+
// body is loaded, now we can deserialize serde-json
42+
let obj = serde_json::from_slice::<MyObj>(&body)?;
43+
Ok(HttpResponse::Ok().json(obj)) // <- send response
44+
}
45+
46+
/// This handler manually load request payload and parse json-rust
47+
async fn index_mjsonrust(body: web::Bytes) -> Result<HttpResponse, Error> {
48+
// body is loaded, now we can deserialize json-rust
49+
let result = json::parse(std::str::from_utf8(&body).unwrap()); // return Result
50+
let injson: JsonValue = match result {
51+
Ok(v) => v,
52+
Err(e) => json::object! {"err" => e.to_string() },
53+
};
54+
Ok(HttpResponse::Ok()
55+
.content_type("application/json")
56+
.body(injson.dump()))
57+
}
58+
59+
#[actix_web::main]
60+
async fn main() -> std::io::Result<()> {
61+
std::env::set_var("RUST_LOG", "actix_web=info");
62+
env_logger::init();
63+
64+
HttpServer::new(|| {
65+
App::new()
66+
// enable logger
67+
.wrap(middleware::Logger::default())
68+
.app_data(web::JsonConfig::default().limit(4096)) // <- limit size of the payload (global configuration)
69+
.service(web::resource("/extractor").route(web::post().to(index)))
70+
.service(
71+
web::resource("/extractor2")
72+
.app_data(web::JsonConfig::default().limit(1024)) // <- limit size of the payload (resource level)
73+
.route(web::post().to(extract_item)),
74+
)
75+
.service(web::resource("/manual").route(web::post().to(index_manual)))
76+
.service(web::resource("/mjsonrust").route(web::post().to(index_mjsonrust)))
77+
.service(web::resource("/").route(web::post().to(index)))
78+
})
79+
.bind(("127.0.0.1", 8080))?
80+
.run()
81+
.await
82+
}
83+
84+
#[cfg(test)]
85+
mod tests {
86+
use super::*;
87+
use actix_web::body::to_bytes;
88+
use actix_web::dev::Service;
89+
use actix_web::{http, test, web, App};
90+
91+
#[actix_web::test]
92+
async fn test_index() {
93+
let app =
94+
test::init_service(App::new().service(web::resource("/").route(web::post().to(index))))
95+
.await;
96+
97+
let req = test::TestRequest::post()
98+
.uri("/")
99+
.set_json(&MyObj {
100+
name: "my-name".to_owned(),
101+
number: 43,
102+
})
103+
.to_request();
104+
let resp = app.call(req).await.unwrap();
105+
106+
assert_eq!(resp.status(), http::StatusCode::OK);
107+
108+
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
109+
assert_eq!(body_bytes, r##"{"name":"my-name","number":43}"##);
110+
}
111+
}

src/diesel_mocks/COPYING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The *.toml and *.rs files in this directory are (Apache-2.0 OR MIT) and taken from:
2+
https://github.com/diesel-rs/diesel/tree/9edd8a/examples/sqlite/getting_started_step_1

src/diesel_mocks/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "diesel_mocks"
3+
version = "0.1.0"
4+
license = "MIT OR Apache-2.0"
5+
authors = ["Taryn Hill <[email protected]>"]
6+
edition = "2018"
7+
8+
[dependencies]
9+
diesel = { git = "https://github.com/diesel-rs/diesel", rev = "9edd8a", features = ["sqlite"] }
10+
dotenv = "0.15"
11+
12+
[lib]
13+
doc = false

src/diesel_mocks/diesel.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[print_schema]
2+
file = "src/schema.rs"

0 commit comments

Comments
 (0)