Skip to content

Commit

Permalink
Add example app
Browse files Browse the repository at this point in the history
  • Loading branch information
Kijewski committed Jul 7, 2024
1 parent e3c1fe3 commit 6638c33
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
matrix:
package: [
rinja, rinja_actix, rinja_axum, rinja_derive, rinja_derive_standalone, rinja_escape,
rinja_parser, rinja_rocket, rinja_warp, testing,
rinja_parser, rinja_rocket, rinja_warp, testing, examples/actix-web-app,
]
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"rinja_rocket",
"rinja_warp",
"testing",
"examples/actix-web-app",
]
resolver = "2"

Expand Down
20 changes: 20 additions & 0 deletions examples/actix-web-app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "actix-web-app"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
rinja_actix = { version = "0.15.0", path = "../../rinja_actix" }

actix-web = { version = "4.8.0", default-features = false, features = ["macros"] }
env_logger = "0.11.3"
log = "0.4.22"
pretty-error-debug = "0.3.0"
serde = { version = "1.0.203", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] }
thiserror = "1.0.61"
tokio = { version = "1.38.0", features = ["sync", "rt-multi-thread"] }

[workspace]
members = ["."]
1 change: 1 addition & 0 deletions examples/actix-web-app/LICENSE-APACHE
1 change: 1 addition & 0 deletions examples/actix-web-app/LICENSE-MIT
137 changes: 137 additions & 0 deletions examples/actix-web-app/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use actix_web::http::{header, Method};
use actix_web::{
get, middleware, web, App, Either, HttpRequest, HttpResponse, HttpServer, Responder, Result,
};
use rinja_actix::Template;
use serde::Deserialize;
use tokio::runtime;

fn main() -> Result<(), Error> {
let env = env_logger::Env::new().default_filter_or("info");
env_logger::try_init_from_env(env).map_err(Error::Log)?;

runtime::Builder::new_multi_thread()
.enable_all()
.build()
.map_err(Error::Rt)?
.block_on(amain())
}

async fn amain() -> Result<(), Error> {
let server = HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.wrap(middleware::NormalizePath::new(
middleware::TrailingSlash::MergeOnly,
))
.service(start_handler)
.service(index_handler)
.service(greeting_handler)
.default_service(web::to(not_found_handler))
});
let server = server.bind(("127.0.0.1", 8080)).map_err(Error::Bind)?;
for addr in server.addrs() {
println!("Listening on: http://{addr}/");
}
server.run().await.map_err(Error::Run)
}

#[derive(thiserror::Error, pretty_error_debug::Debug)]
enum Error {
#[error("could not setup logger")]
Log(#[source] log::SetLoggerError),
#[error("could not setup async runtime")]
Rt(#[source] std::io::Error),
#[error("could not bind socket")]
Bind(#[source] std::io::Error),
#[error("could not run server")]
Run(#[source] std::io::Error),
}

#[derive(Default, Debug, Clone, Copy, PartialEq, Deserialize, strum::Display, strum::AsRefStr)]
#[allow(non_camel_case_types)]
enum Lang {
#[default]
en,
de,
}

async fn not_found_handler(req: HttpRequest) -> Result<impl Responder> {
#[derive(Debug, Template)]
#[template(path = "404.html")]
struct Tmpl {
req: HttpRequest,
lang: Lang,
}

match req.method() {
&Method::GET => Ok(Either::Left(Tmpl {
req,
lang: Lang::default(),
})),
_ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
}
}

#[get("/")]
async fn start_handler(req: HttpRequest) -> Result<impl Responder> {
let url = req.url_for("index_handler", [Lang::default()])?;
Ok(HttpResponse::Found()
.insert_header((header::LOCATION, url.as_str()))
.finish())
}

#[derive(Debug, Deserialize)]
struct IndexHandlerQuery {
#[serde(default)]
name: String,
}

#[get("/{lang}/index.html")]
async fn index_handler(
req: HttpRequest,
path: web::Path<(Lang,)>,
web::Query(query): web::Query<IndexHandlerQuery>,
) -> Result<impl Responder> {
#[derive(Debug, Template)]
#[template(path = "index.html")]
struct Tmpl {
req: HttpRequest,
lang: Lang,
name: String,
}

let (lang,) = path.into_inner();
Ok(Tmpl {
req,
lang,
name: query.name,
})
}

#[derive(Debug, Deserialize)]
struct GreetingHandlerQuery {
name: String,
}

#[get("/{lang}/greet-me.html")]
async fn greeting_handler(
req: HttpRequest,
path: web::Path<(Lang,)>,
web::Query(query): web::Query<GreetingHandlerQuery>,
) -> Result<impl Responder> {
#[derive(Debug, Template)]
#[template(path = "greet.html")]
struct Tmpl {
req: HttpRequest,
lang: Lang,
name: String,
}

let (lang,) = path.into_inner();
Ok(Tmpl {
req,
lang,
name: query.name,
})
}
10 changes: 10 additions & 0 deletions examples/actix-web-app/templates/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "_layout.html" %}

{% block title -%}
404: Not Found
{%- endblock %}

{% block content -%}
<h1>404: Not Found</h1>
<h2><a href="{{ req.url_for_static("start_handler")? }}">Back to the first page.</a></h2>
{%- endblock %}
53 changes: 53 additions & 0 deletions examples/actix-web-app/templates/_layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="{{lang}}">
<head>
<meta charset="UTF-8" />
<title>{% block title %}{% endblock %}</title>
<meta http-equiv="expires" content="Sat, 01 Dec 2001 00:00:00 GMT" />
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex, nofollow" />

<style>
/*<![CDATA[*/
html {
background-color: #eee;
color: #111;
font-size: 62.5%;
min-height: 100vh;
color-scheme: light;
}
* {
line-height: 1.2em;
}
body {
background-color: #fff;
font-size: 1.8rem;
max-width: 40em;
margin: 1em auto;
padding: 2em;
}
h1 { font-size: 2.4rem; }
h2 { font-size: 2.2rem; }
h3 { font-size: 2.0rem; }
a:link, a:visited {
color: #36c;
text-decoration: none;
}
a:active, a:hover, a:focus {
text-decoration: underline;
text-underline-offset: 0.3em;
}
#lang-select {
font-size: 80%;
width: max-content;
margin: 2em 0 0 auto;
}
/*]]>*/
</style>
</head>
<body>
{%~ block content %}{% endblock ~%}
</body>
</html>
44 changes: 44 additions & 0 deletions examples/actix-web-app/templates/greet.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% extends "_layout.html" %}

{% block title -%}
{%- match lang -%}
{%- when Lang::en -%} Hello, {{name}}!
{%- when Lang::de -%} Hallo, {{name}}!
{%- endmatch -%}
{%- endblock %}

{%- block content -%}
<h1>
{%- match lang -%}
{%- when Lang::en -%} Hello
{%- when Lang::de -%} Hallo
{%- endmatch -%}
</h1>
<p>
{%- match lang -%}
{%- when Lang::en -%}
Hello, <strong>{{name}}</strong>, nice to meet you! {#-~#}
I'm a <a href="https://rinja.readthedocs.io/en/latest/">Rinja</a> example application.
{%- when Lang::de -%}
Hallo, <strong>{{name}}</strong>, schön dich kennenzulernen! {#-~#}
Ich bin eine <a href="https://rinja.readthedocs.io/en/latest/">Rinja</a>-Beispielanwendung.
{%- endmatch -%}
</p>
<h2>
<a href="{{ req.url_for("index_handler", [lang])? }}?name={{ name|urlencode }}">
{%- match lang -%}
{%- when Lang::en -%} Back to the first page.
{%- when Lang::de -%} Zurück zur ersten Seite.
{%- endmatch -%}
</a>
</h2>

<ul id="lang-select">
{%- if lang != Lang::en -%}
<li><a href="{{ req.url_for("greeting_handler", [Lang::en])? }}?name={{ name|urlencode }}">This page in English</a></li>
{%- endif -%}
{%- if lang != Lang::de -%}
<li><a href="{{ req.url_for("greeting_handler", [Lang::de])? }}?name={{ name|urlencode }}">Diese Seite auf deutsch.</a></li>
{%- endif -%}
</ul>
{%- endblock %}
67 changes: 67 additions & 0 deletions examples/actix-web-app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{% extends "_layout.html" %}

{% block title -%}
{%- match lang -%}
{%- when Lang::en -%} Hello
{%- when Lang::de -%} Hallo
{%- endmatch -%}
{%- endblock %}

{%- block content -%}
<h1>
{%- match lang -%}
{%- when Lang::en -%} Hello
{%- when Lang::de -%} Hallo
{%- endmatch -%}
</h1>
<form
method="GET"
action="{{ req.url_for("greeting_handler", [lang])? }}"
autocomplete="off"
>
<p>
{%- match lang -%}
{%- when Lang::en -%}
I would like to say <em>hello</em>. {#-~#}
Would you please tell me your name?
{%- when Lang::de -%}
Ich möchte dir gerne <em>hallo</em> sagen. {#-~#}
Bitte nenne mir doch deinen Namen!
{%- endmatch -%}
</p>
<p>
<label>
{%- match lang -%}
{%- when Lang::en -%} My name is
{%- when Lang::de -%} Ich heiße
{%- endmatch -%}:
<input
type="text"
value="{{name}}"
name="name"
required
style="width: 10em"
/>
</label>
</p>
<p>
<label>
<button type="submit">
{%- match lang -%}
{%- when Lang::en -%} Greet me, then!
{%- when Lang::de -%} Dann begrüße mich!
{%- endmatch -%}
</button>
</label>
</p>
</form>

<ul id="lang-select">
{%- if lang != Lang::en -%}
<li><a href="{{ req.url_for("index_handler", [Lang::en])? }}">This page in English</a></li>
{%- endif -%}
{%- if lang != Lang::de -%}
<li><a href="{{ req.url_for("index_handler", [Lang::de])? }}">Diese Seite auf deutsch.</a></li>
{%- endif -%}
</ul>
{%- endblock %}

0 comments on commit 6638c33

Please sign in to comment.