Skip to content

Commit 595d746

Browse files
authored
chore(Rust): Add Viz (TechEmpower#7488)
1 parent 662fe35 commit 595d746

20 files changed

+936
-0
lines changed

frameworks/Rust/viz/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.cargo
2+
target

frameworks/Rust/viz/Cargo.toml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[package]
2+
name = "viz"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Fangdun Tsai <[email protected]>"]
6+
7+
[[bin]]
8+
name = "viz"
9+
path = "src/main.rs"
10+
11+
[[bin]]
12+
name = "viz-pg"
13+
path = "src/main_pg.rs"
14+
required-features = ["tokio-postgres", "yarte"]
15+
16+
[[bin]]
17+
name = "viz-sqlx"
18+
path = "src/main_sqlx.rs"
19+
required-features = ["sqlx", "markup", "v_htmlescape"]
20+
21+
[dependencies]
22+
viz = "0.4.3"
23+
hyper = "0.14"
24+
atoi = "2.0.0"
25+
serde = { version = "1.0", features = ["derive"] }
26+
tokio = { version = "1.21", features = ["macros", "rt-multi-thread"] }
27+
nanorand = "0.7"
28+
thiserror = "1.0"
29+
futures-util = "0.3.24"
30+
stretto = { version = "0.7", features = ["async"] }
31+
32+
tokio-postgres = { version = "0.7.7", optional = true }
33+
sqlx = { version = "0.6.2", features = [
34+
"postgres",
35+
"macros",
36+
"runtime-tokio-native-tls",
37+
], optional = true }
38+
# diesel = { version = "1.4.8", features = ["postgres"], optional = true }
39+
40+
yarte = { version = "0.15", features = ["bytes-buf", "json"], optional = true }
41+
markup = { version = "0.13.1", optional = true }
42+
v_htmlescape = { version = "0.15.7", optional = true }
43+
44+
[profile.release]
45+
lto = true
46+
opt-level = 3
47+
codegen-units = 1

frameworks/Rust/viz/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# [viz](https://github.com/viz-rs) web framework
2+
3+
## Description
4+
5+
Fast, robust, flexible, lightweight web framework for Rust.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"framework": "viz",
3+
"tests": [
4+
{
5+
"default": {
6+
"json_url": "/json",
7+
"plaintext_url": "/plaintext",
8+
"port": 8080,
9+
"approach": "Realistic",
10+
"classification": "Platform",
11+
"database": "none",
12+
"framework": "Viz",
13+
"language": "Rust",
14+
"flavor": "None",
15+
"orm": "Raw",
16+
"platform": "Rust",
17+
"webserver": "Hyper",
18+
"os": "Linux",
19+
"database_os": "Linux",
20+
"display_name": "Viz",
21+
"notes": "",
22+
"versus": "None"
23+
},
24+
"pg": {
25+
"db_url": "/db",
26+
"fortune_url": "/fortunes",
27+
"query_url": "/queries?q=",
28+
"update_url": "/updates?q=",
29+
"cached_query_url": "/cached_queries?q=",
30+
"port": 8080,
31+
"approach": "Realistic",
32+
"classification": "Fullstack",
33+
"database": "postgres",
34+
"framework": "Viz",
35+
"language": "Rust",
36+
"flavor": "None",
37+
"orm": "Raw",
38+
"platform": "Rust",
39+
"webserver": "Hyper",
40+
"os": "Linux",
41+
"database_os": "Linux",
42+
"display_name": "Viz [Postgresql]",
43+
"notes": "",
44+
"versus": "None"
45+
},
46+
"sqlx": {
47+
"db_url": "/db",
48+
"fortune_url": "/fortunes",
49+
"query_url": "/queries?q=",
50+
"update_url": "/updates?q=",
51+
"cached_query_url": "/cached_queries?q=",
52+
"port": 8080,
53+
"approach": "Realistic",
54+
"classification": "Fullstack",
55+
"database": "postgres",
56+
"framework": "Viz",
57+
"language": "Rust",
58+
"flavor": "None",
59+
"orm": "Raw",
60+
"platform": "Rust",
61+
"webserver": "Hyper",
62+
"os": "Linux",
63+
"database_os": "Linux",
64+
"display_name": "Viz [Postgresql - SQLx]",
65+
"notes": "",
66+
"versus": "None"
67+
}
68+
}
69+
]
70+
}

frameworks/Rust/viz/config.toml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[framework]
2+
name = "viz"
3+
4+
[main]
5+
urls.plaintext = "/plaintext"
6+
urls.json = "/json"
7+
approach = "Realistic"
8+
classification = "Micro"
9+
database = "Postgres"
10+
database_os = "Linux"
11+
os = "Linux"
12+
orm = "Raw"
13+
platform = "None"
14+
webserver = "viz"
15+
versus = ""
16+
17+
[pg]
18+
urls.db = "/db"
19+
urls.query = "/queries?q="
20+
urls.update = "/updates?q="
21+
urls.fortune = "/fortunes"
22+
approach = "Realistic"
23+
classification = "Fullstack"
24+
database = "Postgres"
25+
database_os = "Linux"
26+
os = "Linux"
27+
orm = "Raw"
28+
platform = "None"
29+
webserver = "viz"
30+
versus = ""
31+
32+
[sqlx]
33+
urls.db = "/db"
34+
urls.query = "/queries?q="
35+
urls.update = "/updates?q="
36+
urls.fortune = "/fortunes"
37+
approach = "Realistic"
38+
classification = "Fullstack"
39+
database = "Postgres"
40+
database_os = "Linux"
41+
os = "Linux"
42+
orm = "Raw"
43+
platform = "None"
44+
webserver = "viz"
45+
versus = ""

frameworks/Rust/viz/rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_width = 89
2+
reorder_imports = true

frameworks/Rust/viz/src/db_pg.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use std::fmt::Write;
2+
use std::io;
3+
4+
use futures_util::{stream::FuturesUnordered, TryFutureExt, TryStreamExt};
5+
use nanorand::{Rng, WyRand};
6+
use tokio_postgres::{connect, types::ToSql, Client, NoTls, Statement};
7+
use viz::{Error, IntoResponse, Response, StatusCode};
8+
9+
use crate::models::{Fortune, World};
10+
11+
/// Postgres Error
12+
#[derive(Debug, thiserror::Error)]
13+
pub enum PgError {
14+
#[error("connect to database was failed")]
15+
Connect,
16+
#[error(transparent)]
17+
Io(#[from] io::Error),
18+
#[error(transparent)]
19+
Pg(#[from] tokio_postgres::Error),
20+
}
21+
22+
impl From<PgError> for Error {
23+
fn from(e: PgError) -> Self {
24+
Error::Responder(e.into_response())
25+
}
26+
}
27+
28+
impl IntoResponse for PgError {
29+
fn into_response(self) -> Response {
30+
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
31+
}
32+
}
33+
34+
/// Postgres interface
35+
pub struct PgConnection {
36+
rng: WyRand,
37+
client: Client,
38+
world: Statement,
39+
fortune: Statement,
40+
updates: Vec<Statement>,
41+
}
42+
43+
impl PgConnection {
44+
pub async fn connect(db_url: &str) -> PgConnection {
45+
let (client, conn) = connect(db_url, NoTls)
46+
.await
47+
.expect("can not connect to postgresql");
48+
49+
// Spawn connection
50+
tokio::spawn(async move {
51+
if let Err(error) = conn.await {
52+
eprintln!("Connection error: {}", error);
53+
}
54+
});
55+
56+
let fortune = client.prepare("SELECT * FROM fortune").await.unwrap();
57+
let mut updates = Vec::new();
58+
59+
for num in 1..=500u16 {
60+
let mut pl = 1;
61+
let mut q = String::new();
62+
63+
q.push_str("UPDATE world SET randomnumber = CASE id ");
64+
65+
for _ in 1..=num {
66+
let _ = write!(q, "when ${} then ${} ", pl, pl + 1);
67+
pl += 2;
68+
}
69+
70+
q.push_str("ELSE randomnumber END WHERE id IN (");
71+
72+
for _ in 1..=num {
73+
let _ = write!(q, "${},", pl);
74+
pl += 1;
75+
}
76+
77+
q.pop();
78+
q.push(')');
79+
80+
updates.push(client.prepare(&q).await.unwrap());
81+
}
82+
83+
let world = client
84+
.prepare("SELECT * FROM world WHERE id = $1")
85+
.await
86+
.unwrap();
87+
88+
PgConnection {
89+
rng: WyRand::new(),
90+
world,
91+
client,
92+
fortune,
93+
updates,
94+
}
95+
}
96+
}
97+
98+
impl PgConnection {
99+
async fn query_one_world(&self, id: i32) -> Result<World, PgError> {
100+
self.client
101+
.query_one(&self.world, &[&id])
102+
.await
103+
.map(|row| World {
104+
id: row.get(0),
105+
randomnumber: row.get(1),
106+
})
107+
.map_err(PgError::Pg)
108+
}
109+
110+
pub async fn get_world(&self) -> Result<World, PgError> {
111+
let random_id = (self.rng.clone().generate::<u32>() % 10_000 + 1) as i32;
112+
self.query_one_world(random_id).await
113+
}
114+
115+
pub async fn get_worlds(&self, num: u16) -> Result<Vec<World>, PgError> {
116+
let mut rng = self.rng.clone();
117+
(0..num)
118+
.map(|_| {
119+
let id = (rng.generate::<u32>() % 10_000 + 1) as i32;
120+
self.query_one_world(id)
121+
})
122+
.collect::<FuturesUnordered<_>>()
123+
.try_collect()
124+
.await
125+
}
126+
127+
pub async fn get_worlds_by_limit(&self, limit: i64) -> Result<Vec<World>, PgError> {
128+
self.client
129+
.query("SELECT * FROM world LIMIT $1", &[&limit])
130+
.await
131+
.map(|rows| {
132+
rows.iter()
133+
.map(|row| World {
134+
id: row.get(0),
135+
randomnumber: row.get(1),
136+
})
137+
.collect()
138+
})
139+
.map_err(PgError::Pg)
140+
}
141+
142+
pub async fn update(&self, num: u16) -> Result<Vec<World>, PgError> {
143+
let mut rng = self.rng.clone();
144+
145+
let worlds: Vec<World> = (0..num)
146+
.map(|_| {
147+
let id = (rng.generate::<u32>() % 10_000 + 1) as i32;
148+
let rid = (rng.generate::<u32>() % 10_000 + 1) as i32;
149+
self.query_one_world(id).map_ok(move |mut world| {
150+
world.randomnumber = rid;
151+
world
152+
})
153+
})
154+
.collect::<FuturesUnordered<_>>()
155+
.try_collect()
156+
.await?;
157+
158+
let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(num as usize * 3);
159+
160+
for w in &worlds {
161+
params.push(&w.id);
162+
params.push(&w.randomnumber);
163+
}
164+
165+
for w in &worlds {
166+
params.push(&w.id);
167+
}
168+
169+
let st = self.updates[(num as usize) - 1].clone();
170+
171+
self.client.query(&st, &params[..]).await?;
172+
173+
Ok(worlds)
174+
}
175+
176+
pub async fn tell_fortune(&self) -> Result<Vec<Fortune>, PgError> {
177+
let mut items = Vec::with_capacity(32);
178+
179+
items.push(Fortune {
180+
id: 0,
181+
message: "Additional fortune added at request time.".to_string(),
182+
});
183+
184+
self.client
185+
.query(&self.fortune, &[])
186+
.await?
187+
.iter()
188+
.for_each(|row| {
189+
items.push(Fortune {
190+
id: row.get(0),
191+
message: row.get(1),
192+
})
193+
});
194+
195+
items.sort_by(|it, next| it.message.cmp(&next.message));
196+
197+
Ok(items)
198+
}
199+
}

0 commit comments

Comments
 (0)