Lot of thing
This commit is contained in:
parent
3ebbe8172b
commit
e2e54b8f43
12 changed files with 421 additions and 227 deletions
|
|
@ -8,14 +8,24 @@ edition = "2018"
|
|||
actix-web = "2"
|
||||
actix-rt = "1"
|
||||
actix-files = "0.2"
|
||||
askama = "0.9" # Template system
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
listenfd = "0.3" # To watch file modifications and automatically launch a build process (only used in dev/debug).
|
||||
ron = "0.5" # Rust object notation, to load configuration files.
|
||||
itertools = "0.9"
|
||||
|
||||
r2d2_sqlite = "0.15" # Connection pool with rusqlite (SQLite access).
|
||||
r2d2 = "0.8"
|
||||
|
||||
futures = "0.3" # Needed by askam with the feature 'with-actix-web'.
|
||||
|
||||
common = { path = "../common" }
|
||||
|
||||
[dependencies.rusqlite]
|
||||
version = "0.23"
|
||||
version = "0.22"
|
||||
features = ["bundled"]
|
||||
|
||||
# Template system.
|
||||
[dependencies.askama]
|
||||
version = "0.9"
|
||||
features = ["with-actix-web"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,25 @@
|
|||
/*
|
||||
Additionnal build, doc: https://doc.rust-lang.org/cargo/reference/build-scripts.html
|
||||
|
||||
What is build here:
|
||||
- Compile the SASS file to CSS file.
|
||||
*/
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=style.scss");
|
||||
|
||||
Command::new("sass")
|
||||
.arg("./style.scss")
|
||||
.arg("./static/style.css")
|
||||
.output()
|
||||
.expect("Unable to compile SASS file, install SASS, see https://sass-lang.com/");
|
||||
let output =
|
||||
Command::new("sass")
|
||||
.arg("./style.scss")
|
||||
.arg("./static/style.css")
|
||||
.output()
|
||||
.expect("Unable to compile SASS file, install SASS, see https://sass-lang.com/");
|
||||
|
||||
if !output.status.success() {
|
||||
//panic!("Unable to compile SASS file, install SASS, see https://sass-lang.com/")
|
||||
let error = std::fs::read_to_string("./static/style.css").expect("unable to read style.css");
|
||||
panic!(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,46 +4,94 @@ use std::fs;
|
|||
//use rusqlite::types::ToSql;
|
||||
//use rusqlite::{Connection, Result, NO_PARAMS};
|
||||
|
||||
//extern crate r2d2;
|
||||
//extern crate r2d2_sqlite;
|
||||
//extern crate rusqlite;
|
||||
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use r2d2::Pool;
|
||||
|
||||
|
||||
use super::consts;
|
||||
|
||||
const CURRENT_DB_VERSION: u32 = 1;
|
||||
|
||||
pub struct Connection {
|
||||
con: rusqlite::Connection
|
||||
//con: rusqlite::Connection
|
||||
pool: Pool<SqliteConnectionManager>
|
||||
}
|
||||
|
||||
pub struct Recipe {
|
||||
pub title: String,
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn new() -> Connection {
|
||||
|
||||
// TODO: use a constant in consts module.
|
||||
let data_dir = Path::new("data");
|
||||
let data_dir = Path::new(consts::DB_DIRECTORY);
|
||||
|
||||
if !data_dir.exists() {
|
||||
fs::DirBuilder::new().create(data_dir).unwrap();
|
||||
}
|
||||
|
||||
Connection { con: rusqlite::Connection::open(data_dir.join("recipes.sqlite")).unwrap() }
|
||||
let manager = SqliteConnectionManager::file("file.db");
|
||||
let pool = r2d2::Pool::new(manager).unwrap();
|
||||
|
||||
let connection = Connection { pool };
|
||||
connection.create_or_update();
|
||||
connection
|
||||
}
|
||||
|
||||
pub fn create_or_update(self: &Self) -> rusqlite::Result<&str> {
|
||||
//let connection = Connection::new();
|
||||
fn create_or_update(self: &Self) {
|
||||
// let connection = Connection::new();
|
||||
// let mut stmt = connection.sqlite_con.prepare("SELECT * FROM versions ORDER BY date").unwrap();
|
||||
// let mut stmt = connection.sqlite_con..prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='versions'").unwrap();
|
||||
|
||||
// Check the Database version.
|
||||
let con = self.pool.get().unwrap();
|
||||
|
||||
let version = {
|
||||
let stmt_version_table = self.con.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='versions'")?;
|
||||
/*if stmt_version_table.query(rusqlite::NO_PARAMS)?.count() == 0 {
|
||||
0
|
||||
} else {
|
||||
1 // let stmt_versions = self.con.prepare("SELECT number FROM [")
|
||||
}*/
|
||||
0
|
||||
match
|
||||
con.query_row(
|
||||
"SELECT [name] FROM [sqlite_master] WHERE [type] = 'table' AND [name] = 'Version'",
|
||||
rusqlite::NO_PARAMS,
|
||||
|row| row.get::<usize, String>(0)
|
||||
) {
|
||||
|
||||
Ok(_) => con.query_row("SELECT [version] FROM [Version]", rusqlite::NO_PARAMS, |row| row.get(0)).unwrap_or_default(),
|
||||
Err(_) => 0
|
||||
}
|
||||
};
|
||||
|
||||
self.con.query_row(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='versions'",
|
||||
rusqlite::NO_PARAMS,
|
||||
|row| Ok(dbg!("test"))
|
||||
)
|
||||
match version {
|
||||
0 => {
|
||||
println!("Update to version 1...");
|
||||
con.execute(
|
||||
"
|
||||
CREATE TABLE [Version] (
|
||||
[id] INTEGER PRIMARY KEY,
|
||||
[version] INTEGER NOT NULL,
|
||||
[datetime] INTEGER DATETIME
|
||||
)
|
||||
",
|
||||
rusqlite::NO_PARAMS
|
||||
);
|
||||
con.execute(
|
||||
"
|
||||
CREATE TABLE [Recipe] (
|
||||
[id] INTEGER PRIMARY KEY,
|
||||
[title] INTEGER NOT NULL,
|
||||
[description] INTEGER DATETIME
|
||||
)
|
||||
",
|
||||
rusqlite::NO_PARAMS
|
||||
);
|
||||
()
|
||||
}
|
||||
v =>
|
||||
panic!("Unsupported database version: {}", v)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_all_recipes() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::io::prelude::*;
|
|||
use std::{fs::File, env::args};
|
||||
|
||||
use actix_files as fs;
|
||||
use actix_web::{get, web, Responder, middleware, App, HttpServer, HttpResponse, web::Query};
|
||||
use actix_web::{get, web, Responder, middleware, App, HttpServer, HttpResponse, HttpRequest, web::Query};
|
||||
|
||||
use askama::Template;
|
||||
use listenfd::ListenFd;
|
||||
|
|
@ -15,9 +15,16 @@ mod consts;
|
|||
mod db;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "main.html")]
|
||||
struct MainTemplate<'a> {
|
||||
test: &'a str,
|
||||
#[template(path = "home.html")]
|
||||
struct HomeTemplate {
|
||||
recipes: Vec<db::Recipe>
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "view_recipe.html")]
|
||||
struct ViewRecipeTemplate {
|
||||
recipes: Vec<db::Recipe>,
|
||||
current_recipe: db::Recipe
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -25,12 +32,15 @@ pub struct Request {
|
|||
m: Option<String>
|
||||
}
|
||||
|
||||
fn main_page(query: Query<Request>) -> HttpResponse {
|
||||
#[get("/")]
|
||||
async fn home_page(req: HttpRequest) -> impl Responder {
|
||||
HomeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ] }
|
||||
}
|
||||
|
||||
let main_template = MainTemplate { test: &"*** test ***" };
|
||||
|
||||
let s = main_template.render().unwrap();
|
||||
HttpResponse::Ok().content_type("text/html").body(s)
|
||||
#[get("/recipe/view/{id}")]
|
||||
async fn view_page(req: HttpRequest, path: web::Path<(i32,)>) -> impl Responder {
|
||||
panic!("ERROR");
|
||||
ViewRecipeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ], current_recipe: db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 } }
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
@ -70,7 +80,8 @@ async fn main() -> std::io::Result<()> {
|
|||
|| {
|
||||
App::new()
|
||||
.wrap(middleware::Compress::default())
|
||||
.service(web::resource("/").to(main_page))
|
||||
.service(home_page)
|
||||
.service(view_page)
|
||||
.service(fs::Files::new("/static", "static").show_files_listing())
|
||||
}
|
||||
);
|
||||
|
|
@ -98,7 +109,6 @@ fn process_args() -> bool {
|
|||
return true
|
||||
} else if args.iter().any(|arg| arg == "--test") {
|
||||
let db_connection = db::Connection::new();
|
||||
db_connection.create_or_update();
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,22 +3,57 @@
|
|||
@font-face{font-family: Fira Code; font-weight:600; src:url(FiraCode-SemiBold.woff2) format("woff2"); }
|
||||
@font-face{font-family: Fira Code; font-weight:700; src:url(FiraCode-Bold.woff2) format("woff2"); }
|
||||
|
||||
$primary: #182430;
|
||||
|
||||
$background: darken($primary, 5%);
|
||||
$background-container: lighten($primary, 10%);
|
||||
|
||||
* {
|
||||
margin: 50px;
|
||||
padding: 0px;
|
||||
margin: 10px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: lighten($primary, 40%);
|
||||
text-decoration: none;
|
||||
&:hover { color: lighten($primary, 60%); }
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 18px;
|
||||
font-family: Fira Code, Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
text-shadow: 2px 2px 2px rgb(0, 0, 0);
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(24, 24, 27);
|
||||
margin: 0px;
|
||||
font-size: 13px;
|
||||
font-family: Fira Code, Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
text-shadow: 2px 2px 2px rgb(0, 0, 0);
|
||||
text-align: center;
|
||||
// line-height: 18px;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: $background;
|
||||
margin: 0px;
|
||||
|
||||
/*
|
||||
.header-container {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
|
||||
.list {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: $background-container;
|
||||
border: 1px solid white;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0px;
|
||||
border: 0px;
|
||||
}
|
||||
17
backend/templates/base.html
Normal file
17
backend/templates/base.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Recettes de cuisine</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header-container"><h1><a href="/">RECIPES</a></h1></div>
|
||||
<div class="main-container">
|
||||
{% block main_container %}{% endblock %}
|
||||
</div>
|
||||
<div class="footer-container">gburri - 2020</div>
|
||||
</body>
|
||||
</html>
|
||||
14
backend/templates/base_with_list.html
Normal file
14
backend/templates/base_with_list.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block main_container %}
|
||||
<div class="list">
|
||||
<ul>
|
||||
{% for recipe in recipes %}
|
||||
<li><a href="/recipe/view/{{ recipe.id }}">{{ recipe.title|escape }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
5
backend/templates/home.html
Normal file
5
backend/templates/home.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "base_with_list.html" %}
|
||||
|
||||
{% block content %}
|
||||
HOME
|
||||
{% endblock %}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Recettes de cuisine</title>
|
||||
<link rel="stylesheet" type="text/css" href="static/style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{{ test }}</h1>
|
||||
</div>
|
||||
<p>Text to test the new font.</p>
|
||||
<p>=> [|1, 2, 3|] <-</p>
|
||||
</body>
|
||||
</html>
|
||||
7
backend/templates/view_recipe.html
Normal file
7
backend/templates/view_recipe.html
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "base_with_list.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ current_recipe.title }}
|
||||
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue