Skip to content

Commit 6638c33

Browse files
committed
Add example app
1 parent e3c1fe3 commit 6638c33

File tree

10 files changed

+335
-1
lines changed

10 files changed

+335
-1
lines changed

.github/workflows/rust.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
matrix:
3434
package: [
3535
rinja, rinja_actix, rinja_axum, rinja_derive, rinja_derive_standalone, rinja_escape,
36-
rinja_parser, rinja_rocket, rinja_warp, testing,
36+
rinja_parser, rinja_rocket, rinja_warp, testing, examples/actix-web-app,
3737
]
3838
runs-on: ubuntu-latest
3939
steps:

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ members = [
1010
"rinja_rocket",
1111
"rinja_warp",
1212
"testing",
13+
"examples/actix-web-app",
1314
]
1415
resolver = "2"
1516

examples/actix-web-app/Cargo.toml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "actix-web-app"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[dependencies]
8+
rinja_actix = { version = "0.15.0", path = "../../rinja_actix" }
9+
10+
actix-web = { version = "4.8.0", default-features = false, features = ["macros"] }
11+
env_logger = "0.11.3"
12+
log = "0.4.22"
13+
pretty-error-debug = "0.3.0"
14+
serde = { version = "1.0.203", features = ["derive"] }
15+
strum = { version = "0.26.3", features = ["derive"] }
16+
thiserror = "1.0.61"
17+
tokio = { version = "1.38.0", features = ["sync", "rt-multi-thread"] }
18+
19+
[workspace]
20+
members = ["."]

examples/actix-web-app/LICENSE-APACHE

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE-APACHE

examples/actix-web-app/LICENSE-MIT

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE-MIT

examples/actix-web-app/src/main.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use actix_web::http::{header, Method};
2+
use actix_web::{
3+
get, middleware, web, App, Either, HttpRequest, HttpResponse, HttpServer, Responder, Result,
4+
};
5+
use rinja_actix::Template;
6+
use serde::Deserialize;
7+
use tokio::runtime;
8+
9+
fn main() -> Result<(), Error> {
10+
let env = env_logger::Env::new().default_filter_or("info");
11+
env_logger::try_init_from_env(env).map_err(Error::Log)?;
12+
13+
runtime::Builder::new_multi_thread()
14+
.enable_all()
15+
.build()
16+
.map_err(Error::Rt)?
17+
.block_on(amain())
18+
}
19+
20+
async fn amain() -> Result<(), Error> {
21+
let server = HttpServer::new(|| {
22+
App::new()
23+
.wrap(middleware::Logger::default())
24+
.wrap(middleware::NormalizePath::new(
25+
middleware::TrailingSlash::MergeOnly,
26+
))
27+
.service(start_handler)
28+
.service(index_handler)
29+
.service(greeting_handler)
30+
.default_service(web::to(not_found_handler))
31+
});
32+
let server = server.bind(("127.0.0.1", 8080)).map_err(Error::Bind)?;
33+
for addr in server.addrs() {
34+
println!("Listening on: http://{addr}/");
35+
}
36+
server.run().await.map_err(Error::Run)
37+
}
38+
39+
#[derive(thiserror::Error, pretty_error_debug::Debug)]
40+
enum Error {
41+
#[error("could not setup logger")]
42+
Log(#[source] log::SetLoggerError),
43+
#[error("could not setup async runtime")]
44+
Rt(#[source] std::io::Error),
45+
#[error("could not bind socket")]
46+
Bind(#[source] std::io::Error),
47+
#[error("could not run server")]
48+
Run(#[source] std::io::Error),
49+
}
50+
51+
#[derive(Default, Debug, Clone, Copy, PartialEq, Deserialize, strum::Display, strum::AsRefStr)]
52+
#[allow(non_camel_case_types)]
53+
enum Lang {
54+
#[default]
55+
en,
56+
de,
57+
}
58+
59+
async fn not_found_handler(req: HttpRequest) -> Result<impl Responder> {
60+
#[derive(Debug, Template)]
61+
#[template(path = "404.html")]
62+
struct Tmpl {
63+
req: HttpRequest,
64+
lang: Lang,
65+
}
66+
67+
match req.method() {
68+
&Method::GET => Ok(Either::Left(Tmpl {
69+
req,
70+
lang: Lang::default(),
71+
})),
72+
_ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
73+
}
74+
}
75+
76+
#[get("/")]
77+
async fn start_handler(req: HttpRequest) -> Result<impl Responder> {
78+
let url = req.url_for("index_handler", [Lang::default()])?;
79+
Ok(HttpResponse::Found()
80+
.insert_header((header::LOCATION, url.as_str()))
81+
.finish())
82+
}
83+
84+
#[derive(Debug, Deserialize)]
85+
struct IndexHandlerQuery {
86+
#[serde(default)]
87+
name: String,
88+
}
89+
90+
#[get("/{lang}/index.html")]
91+
async fn index_handler(
92+
req: HttpRequest,
93+
path: web::Path<(Lang,)>,
94+
web::Query(query): web::Query<IndexHandlerQuery>,
95+
) -> Result<impl Responder> {
96+
#[derive(Debug, Template)]
97+
#[template(path = "index.html")]
98+
struct Tmpl {
99+
req: HttpRequest,
100+
lang: Lang,
101+
name: String,
102+
}
103+
104+
let (lang,) = path.into_inner();
105+
Ok(Tmpl {
106+
req,
107+
lang,
108+
name: query.name,
109+
})
110+
}
111+
112+
#[derive(Debug, Deserialize)]
113+
struct GreetingHandlerQuery {
114+
name: String,
115+
}
116+
117+
#[get("/{lang}/greet-me.html")]
118+
async fn greeting_handler(
119+
req: HttpRequest,
120+
path: web::Path<(Lang,)>,
121+
web::Query(query): web::Query<GreetingHandlerQuery>,
122+
) -> Result<impl Responder> {
123+
#[derive(Debug, Template)]
124+
#[template(path = "greet.html")]
125+
struct Tmpl {
126+
req: HttpRequest,
127+
lang: Lang,
128+
name: String,
129+
}
130+
131+
let (lang,) = path.into_inner();
132+
Ok(Tmpl {
133+
req,
134+
lang,
135+
name: query.name,
136+
})
137+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% extends "_layout.html" %}
2+
3+
{% block title -%}
4+
404: Not Found
5+
{%- endblock %}
6+
7+
{% block content -%}
8+
<h1>404: Not Found</h1>
9+
<h2><a href="{{ req.url_for_static("start_handler")? }}">Back to the first page.</a></h2>
10+
{%- endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!DOCTYPE html>
2+
<html lang="{{lang}}">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>{% block title %}{% endblock %}</title>
6+
<meta http-equiv="expires" content="Sat, 01 Dec 2001 00:00:00 GMT" />
7+
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate" />
8+
<meta http-equiv="pragma" content="no-cache" />
9+
<meta name="viewport" content="width=device-width, initial-scale=1" />
10+
<meta name="robots" content="noindex, nofollow" />
11+
12+
<style>
13+
/*<![CDATA[*/
14+
html {
15+
background-color: #eee;
16+
color: #111;
17+
font-size: 62.5%;
18+
min-height: 100vh;
19+
color-scheme: light;
20+
}
21+
* {
22+
line-height: 1.2em;
23+
}
24+
body {
25+
background-color: #fff;
26+
font-size: 1.8rem;
27+
max-width: 40em;
28+
margin: 1em auto;
29+
padding: 2em;
30+
}
31+
h1 { font-size: 2.4rem; }
32+
h2 { font-size: 2.2rem; }
33+
h3 { font-size: 2.0rem; }
34+
a:link, a:visited {
35+
color: #36c;
36+
text-decoration: none;
37+
}
38+
a:active, a:hover, a:focus {
39+
text-decoration: underline;
40+
text-underline-offset: 0.3em;
41+
}
42+
#lang-select {
43+
font-size: 80%;
44+
width: max-content;
45+
margin: 2em 0 0 auto;
46+
}
47+
/*]]>*/
48+
</style>
49+
</head>
50+
<body>
51+
{%~ block content %}{% endblock ~%}
52+
</body>
53+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{% extends "_layout.html" %}
2+
3+
{% block title -%}
4+
{%- match lang -%}
5+
{%- when Lang::en -%} Hello, {{name}}!
6+
{%- when Lang::de -%} Hallo, {{name}}!
7+
{%- endmatch -%}
8+
{%- endblock %}
9+
10+
{%- block content -%}
11+
<h1>
12+
{%- match lang -%}
13+
{%- when Lang::en -%} Hello
14+
{%- when Lang::de -%} Hallo
15+
{%- endmatch -%}
16+
</h1>
17+
<p>
18+
{%- match lang -%}
19+
{%- when Lang::en -%}
20+
Hello, <strong>{{name}}</strong>, nice to meet you! {#-~#}
21+
I'm a <a href="https://rinja.readthedocs.io/en/latest/">Rinja</a> example application.
22+
{%- when Lang::de -%}
23+
Hallo, <strong>{{name}}</strong>, schön dich kennenzulernen! {#-~#}
24+
Ich bin eine <a href="https://rinja.readthedocs.io/en/latest/">Rinja</a>-Beispielanwendung.
25+
{%- endmatch -%}
26+
</p>
27+
<h2>
28+
<a href="{{ req.url_for("index_handler", [lang])? }}?name={{ name|urlencode }}">
29+
{%- match lang -%}
30+
{%- when Lang::en -%} Back to the first page.
31+
{%- when Lang::de -%} Zurück zur ersten Seite.
32+
{%- endmatch -%}
33+
</a>
34+
</h2>
35+
36+
<ul id="lang-select">
37+
{%- if lang != Lang::en -%}
38+
<li><a href="{{ req.url_for("greeting_handler", [Lang::en])? }}?name={{ name|urlencode }}">This page in English</a></li>
39+
{%- endif -%}
40+
{%- if lang != Lang::de -%}
41+
<li><a href="{{ req.url_for("greeting_handler", [Lang::de])? }}?name={{ name|urlencode }}">Diese Seite auf deutsch.</a></li>
42+
{%- endif -%}
43+
</ul>
44+
{%- endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{% extends "_layout.html" %}
2+
3+
{% block title -%}
4+
{%- match lang -%}
5+
{%- when Lang::en -%} Hello
6+
{%- when Lang::de -%} Hallo
7+
{%- endmatch -%}
8+
{%- endblock %}
9+
10+
{%- block content -%}
11+
<h1>
12+
{%- match lang -%}
13+
{%- when Lang::en -%} Hello
14+
{%- when Lang::de -%} Hallo
15+
{%- endmatch -%}
16+
</h1>
17+
<form
18+
method="GET"
19+
action="{{ req.url_for("greeting_handler", [lang])? }}"
20+
autocomplete="off"
21+
>
22+
<p>
23+
{%- match lang -%}
24+
{%- when Lang::en -%}
25+
I would like to say <em>hello</em>. {#-~#}
26+
Would you please tell me your name?
27+
{%- when Lang::de -%}
28+
Ich möchte dir gerne <em>hallo</em> sagen. {#-~#}
29+
Bitte nenne mir doch deinen Namen!
30+
{%- endmatch -%}
31+
</p>
32+
<p>
33+
<label>
34+
{%- match lang -%}
35+
{%- when Lang::en -%} My name is
36+
{%- when Lang::de -%} Ich heiße
37+
{%- endmatch -%}:
38+
<input
39+
type="text"
40+
value="{{name}}"
41+
name="name"
42+
required
43+
style="width: 10em"
44+
/>
45+
</label>
46+
</p>
47+
<p>
48+
<label>
49+
<button type="submit">
50+
{%- match lang -%}
51+
{%- when Lang::en -%} Greet me, then!
52+
{%- when Lang::de -%} Dann begrüße mich!
53+
{%- endmatch -%}
54+
</button>
55+
</label>
56+
</p>
57+
</form>
58+
59+
<ul id="lang-select">
60+
{%- if lang != Lang::en -%}
61+
<li><a href="{{ req.url_for("index_handler", [Lang::en])? }}">This page in English</a></li>
62+
{%- endif -%}
63+
{%- if lang != Lang::de -%}
64+
<li><a href="{{ req.url_for("index_handler", [Lang::de])? }}">Diese Seite auf deutsch.</a></li>
65+
{%- endif -%}
66+
</ul>
67+
{%- endblock %}

0 commit comments

Comments
 (0)