master
Ondřej Hruška 3 years ago
parent 1c015c3b83
commit e17681d6f5
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2280
      Cargo.lock
  2. 24
      Cargo.toml
  3. 19
      yopa-test/Cargo.toml
  4. 0
      yopa-test/src/main.rs
  5. 21
      yopa-web/Cargo.toml
  6. 14
      yopa-web/build.rs
  7. 200
      yopa-web/resources/static/style.css
  8. 18
      yopa-web/resources/templates/_layout.html.tera
  9. 17
      yopa-web/resources/templates/index.html.tera
  10. 69
      yopa-web/src/main.rs
  11. 25
      yopa-web/src/scratch.rs
  12. 30
      yopa-web/src/tera_ext.rs

2280
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,19 +1,7 @@
[package]
name = "yopa-test"
version = "0.1.0"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
[workspace]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.13"
simple-logging = "2.0.2"
yopa = { path = "./yopa", features = [ "uuid-ids" ] }
serde_json = "1.0.61"
serde = { version = "1.0.120", features = ["derive"] }
anyhow = "1.0.38"
thiserror = "1.0.23"
members = [
"yopa",
"yopa-web",
#"yopa-test",
]

@ -0,0 +1,19 @@
[package]
name = "yopa-test"
version = "0.1.0"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.13"
simple-logging = "2.0.2"
yopa = { path = "../yopa", features = [ "uuid-ids" ] }
serde_json = "1.0.61"
serde = { version = "1.0.120", features = ["derive"] }
anyhow = "1.0.38"
thiserror = "1.0.23"

@ -0,0 +1,21 @@
[package]
name = "yopa-web"
version = "0.1.0"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.14"
simple-logging = "2.0.2"
actix-web = "3"
parking_lot = "0.11.1"
include_dir = "0.6.0"
tera = "1.6.1"
actix-web-static-files = "3.0"
lazy_static = "1.4.0"
[build-dependencies]
actix-web-static-files = "3.0"

@ -0,0 +1,14 @@
use actix_web_static_files::resource_dir;
use std::path::Path;
fn main() {
// Magic to bundle static files for actix to serve
let out_dir = std::env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir);
resource_dir("./resources/static")
.with_generated_filename(out_path.join("static_files.rs"))
.with_generated_fn("included_static_files")
.build().unwrap();
}

@ -0,0 +1,200 @@
*, *::before, *::after {
box-sizing: border-box;
}
html, textarea, select {
font-family: "IBM Plex", "DejaVu Sans", "Helvetica", sans-serif;
}
.Form {
display: block;
width: 900px;
margin: 0 auto;
}
nav.top-nav {
margin-bottom: .5rem;
border-bottom: 1px solid silver;
}
nav.top-nav, .content {
margin: 0 auto;
width: 900px;
}
a {
color: gray;
text-decoration: none;
}
a:hover {
color: black;
text-decoration: underline;
}
nav.top-nav a {
display: inline-block;
padding: .75rem;
color: gray;
text-decoration: none;
}
nav.top-nav a:hover {
color: black;
text-decoration: underline;
}
.Form .Row {
display: flex;
padding: .25rem;
}
.Form .Row.indented {
padding-left: 10.25rem;
}
input[type="text"],
input[type="number"],
textarea,
.tag-input {
border: 1px solid silver;
padding: 0.5rem;
border-radius: 5px;
font-size: 1rem;
}
input[type="text"]:focus,
input[type="number"]:focus,
textarea:focus,
.tag-input.active {
box-shadow: inset 0 0 0 1px #3c97ff;
border-color: #3c97ff;
outline: 0 none !important;
}
.Form label {
flex-shrink: 0;
width: 10rem;
height: 2.1rem;
line-height: 2.1rem;
vertical-align: middle;
text-align: right;
display: inline-block;
padding-right: .5rem;
align-self: flex-start;
}
.Form input[type="text"],
.Form input[type="number"],
.Form select {
height: 2.1rem;
width: 15rem;
}
.Form textarea {
flex-shrink: 1;
width: 30rem;
height: 6rem;
}
.Form label.checkbox-wrap {
width: 15rem;
padding-right: 1rem;
text-align: left !important;
}
.tag-input {
position: relative;
width: 30rem;
padding-bottom: 0rem !important;
}
.tag-input input,
.tag-input input:focus {
border: 0 transparent;
padding: 0;
margin: 0;
box-shadow: none;
outline: 0 none !important;
}
.cards-table {
border-collapse: collapse;
margin: 0 auto;
margin-top: 1rem;
}
.cards-table .actions {
font-size: 90%;
}
.cards-table td,
.cards-table th {
padding: .5rem;
}
.cards-table thead th {
border-bottom: 2px solid silver;
}
.cards-table tbody td {
border-bottom: 1px solid silver;
}
.cards-table tbody tr:last-child td {
border-bottom: 2px solid silver;
}
.cards-table td.tags {
padding: .25rem;
}
.cards-table .tag {
background: #E2E1DF;
font-size: 90%;
padding: 0.25rem .45rem;
border-radius: 3px;
display: inline-block;
}
.paginate {
margin: 1rem auto;
width: 300px;
text-align: center;
white-space: nowrap;
}
.paginate span,
.paginate a {
padding: .5rem 1rem;
border-radius: .5rem;
border: 1px solid silver;
text-decoration: none;
}
.paginate span.num {
border: 1px solid #ccc;
color: gray;
}
.paginate a {
cursor: pointer;
user-select: none;
}
.paginate a:hover {
background: #ccc;
text-decoration: none;
}
.paginate .disabled {
opacity: .5;
cursor: default;
}
.paginate .disabled:hover {
color: gray;
background: transparent;
}

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock title %} &bull; YOPA</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="stylesheet" href="/static/taggle.css">
<script src="/static/taggle.min.js"></script>
</head>
<body>
<nav class="top-nav">
{% block nav %}{% endblock %}
</nav>
<div class="content">
{% block content %}{% endblock %}
</div>
</body>
</html>

@ -0,0 +1,17 @@
{% extends "_layout" %}
{% block title -%}
Index
{%- endblock %}
{% block nav -%}
<a href="/">Home</a>
{%- endblock %}
{% block content -%}
<h1>Welcome to tera on actix</h1>
Number of visits: {{visits}}
{%- endblock %}

@ -0,0 +1,69 @@
#[macro_use] extern crate log;
#[macro_use] extern crate lazy_static;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, HttpRequest};
use parking_lot::Mutex;
use actix_web::web::{service, scope};
use actix_web::http::StatusCode;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::path::{PathBuf, Path};
use actix_web_static_files;
use tera::Tera;
use include_dir::Dir;
use std::sync::Arc;
use log::LevelFilter;
mod tera_ext;
// Embed static files
include!(concat!(env!("OUT_DIR"), "/static_files.rs"));
// Embed templates
static TEMPLATES: include_dir::Dir = include_dir::include_dir!("./resources/templates");
lazy_static! {
static ref TERA : Tera = {
let mut tera = Tera::default();
tera_ext::tera_includedir_walk_folder(&mut tera, &TEMPLATES);
tera
};
}
struct VisitCounter {
pub counter : AtomicUsize,
}
impl VisitCounter {
pub fn count_visit(&self) -> usize {
self.counter.fetch_add(1, Ordering::Relaxed)
}
}
#[get("/")]
async fn service_index(req: HttpRequest, visits : web::Data<VisitCounter>) -> actix_web::Result<impl Responder> {
let mut context = tera::Context::new();
context.insert("visits", &visits.count_visit());
let html = TERA.render("index", &context).map_err(|e| {
actix_web::error::ErrorInternalServerError(e)
})?;
Ok(HttpResponse::Ok().body(html))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
simple_logging::log_to_stderr(LevelFilter::Debug);
let counter = web::Data::new(VisitCounter { counter : Default::default() });
HttpServer::new(move || {
let static_files = actix_web_static_files::ResourceFiles::new("/static", included_static_files())
.do_not_resolve_defaults();
App::new()
.app_data(counter.clone())
.service(service_index)
.service(static_files)
})
.bind("127.0.0.1:8080")?.run().await
}

@ -0,0 +1,25 @@
// #[get("/")]
// async fn hello(state : web::Data<VisitCounter>) -> impl Responder {
// HttpResponse::Ok().body(format!("Hello world! {}", state.count_visit()))
// }
//
// #[post("/echo")]
// async fn echo(req_body: String) -> impl Responder {
// HttpResponse::Ok().body(req_body)
// }
// async fn static_files(req: HttpRequest) -> actix_web::Result<NamedFile> {
// let path: PathBuf = req.match_info().query("filename").parse().unwrap();
//
//
//
// Ok(NamedFile::open(path)?)
// }
//
// #[get("/users/{user_id}/{friend}")] // <- define path parameters
// async fn user_friend(web::Path((user_id, friend)): web::Path<(u32, String)>, state : web::Data<VisitCounter>) -> impl Responder {
// HttpResponse::Ok()
// .header("Content-Type", "text/html")
// .body(format!("<h1>Welcome {}, user_id {}!</h1>Visitor nr {}\n", friend, user_id, state.count_visit()))
// }

@ -0,0 +1,30 @@
use tera::Tera;
use include_dir::Dir;
/// Add templates from include_dir!() to Tera
pub(crate) fn tera_includedir_walk_folder(tera : &mut Tera, dir: &Dir) {
let mut templates = vec![];
tera_includedir_walk_folder_inner(&mut templates, tera, dir);
tera.add_raw_templates(templates);
}
fn tera_includedir_walk_folder_inner(collected : &mut Vec<(String, String)>, tera : &mut Tera, dir: &Dir) {
for f in dir.files() {
if f.path().extension().unwrap_or_default() != "tera" {
continue;
}
let name = f.path().file_stem().unwrap().to_str().unwrap().split('.').nth(0).unwrap();
let content = f.contents_utf8().unwrap();
let p = f.path().parent().unwrap().join(name);
let template_path = p.to_str().unwrap();
debug!("Add template: {}", template_path);
collected.push((template_path.to_string(), content.to_string()));
}
for subdir in dir.dirs() {
tera_includedir_walk_folder_inner(collected, tera, subdir)
}
}
Loading…
Cancel
Save