diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 903fa566..cd84f2ca 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index 4eac6682..0e76e517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "rinja_rocket", "rinja_warp", "testing", + "examples/actix-web-app", ] resolver = "2" diff --git a/examples/actix-web-app/Cargo.toml b/examples/actix-web-app/Cargo.toml new file mode 100644 index 00000000..cfdf5081 --- /dev/null +++ b/examples/actix-web-app/Cargo.toml @@ -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 = ["."] diff --git a/examples/actix-web-app/LICENSE-APACHE b/examples/actix-web-app/LICENSE-APACHE new file mode 120000 index 00000000..1cd601d0 --- /dev/null +++ b/examples/actix-web-app/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/examples/actix-web-app/LICENSE-MIT b/examples/actix-web-app/LICENSE-MIT new file mode 120000 index 00000000..b2cfbdc7 --- /dev/null +++ b/examples/actix-web-app/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/examples/actix-web-app/src/main.rs b/examples/actix-web-app/src/main.rs new file mode 100644 index 00000000..dbf5394e --- /dev/null +++ b/examples/actix-web-app/src/main.rs @@ -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 { + #[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 { + 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, +) -> Result { + #[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, +) -> Result { + #[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, + }) +} diff --git a/examples/actix-web-app/templates/404.html b/examples/actix-web-app/templates/404.html new file mode 100644 index 00000000..8e31cf58 --- /dev/null +++ b/examples/actix-web-app/templates/404.html @@ -0,0 +1,10 @@ +{% extends "_layout.html" %} + +{% block title -%} + 404: Not Found +{%- endblock %} + +{% block content -%} +

404: Not Found

+

Back to the first page.

+{%- endblock %} diff --git a/examples/actix-web-app/templates/_layout.html b/examples/actix-web-app/templates/_layout.html new file mode 100644 index 00000000..5a0d11cd --- /dev/null +++ b/examples/actix-web-app/templates/_layout.html @@ -0,0 +1,53 @@ + + + + + {% block title %}{% endblock %} + + + + + + + + + + {%~ block content %}{% endblock ~%} + + diff --git a/examples/actix-web-app/templates/greet.html b/examples/actix-web-app/templates/greet.html new file mode 100644 index 00000000..1ff47f4c --- /dev/null +++ b/examples/actix-web-app/templates/greet.html @@ -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 -%} +

+ {%- match lang -%} + {%- when Lang::en -%} Hello + {%- when Lang::de -%} Hallo + {%- endmatch -%} +

+

+ {%- match lang -%} + {%- when Lang::en -%} + Hello, {{name}}, nice to meet you! {#-~#} + I'm a Rinja example application. + {%- when Lang::de -%} + Hallo, {{name}}, schön dich kennenzulernen! {#-~#} + Ich bin eine Rinja-Beispielanwendung. + {%- endmatch -%} +

+

+ + {%- match lang -%} + {%- when Lang::en -%} Back to the first page. + {%- when Lang::de -%} Zurück zur ersten Seite. + {%- endmatch -%} + +

+ + +{%- endblock %} diff --git a/examples/actix-web-app/templates/index.html b/examples/actix-web-app/templates/index.html new file mode 100644 index 00000000..65b548a8 --- /dev/null +++ b/examples/actix-web-app/templates/index.html @@ -0,0 +1,67 @@ +{% extends "_layout.html" %} + +{% block title -%} + {%- match lang -%} + {%- when Lang::en -%} Hello + {%- when Lang::de -%} Hallo + {%- endmatch -%} +{%- endblock %} + +{%- block content -%} +

+ {%- match lang -%} + {%- when Lang::en -%} Hello + {%- when Lang::de -%} Hallo + {%- endmatch -%} +

+
+

+ {%- match lang -%} + {%- when Lang::en -%} + I would like to say hello. {#-~#} + Would you please tell me your name? + {%- when Lang::de -%} + Ich möchte dir gerne hallo sagen. {#-~#} + Bitte nenne mir doch deinen Namen! + {%- endmatch -%} +

+

+ +

+

+ +

+
+ + +{%- endblock %}