18 Commits

Author SHA1 Message Date
354f05b0f5 Merge pull request 'meilleurs messages d'erreur' (#6) from gaelg/error-messages into french
Reviewed-on: #6
2024-11-23 16:31:42 +00:00
d97fcaeaa4 meilleurs messages d'erreur 2024-11-23 17:31:23 +01:00
0bd489c42e Merge pull request 'feat: add forbidden adjacent pairs' (#5) from inso/forbidden-pairs into french
Reviewed-on: #5
2024-11-23 15:31:17 +00:00
eacbb9e426 feat: add forbidden adjacent pairs 2024-11-23 16:25:29 +01:00
ec152683e0 upgrade dependencies 2024-11-23 16:24:59 +01:00
9979a0f4fe new algorithm 2024-11-23 16:24:59 +01:00
07c9495736 wip : shuffle at the init phase 2024-11-23 16:24:59 +01:00
a1e23c29f3 add logs 2024-11-23 16:24:59 +01:00
qdegrandmaison
04b62ca4dd feat(verif): no need for a DOM node 2024-11-23 16:24:59 +01:00
qdegrandmaison
d7564300e8 feat(verif): add a confirm dialog for tocards 2024-11-23 16:24:59 +01:00
10c1ba6f04 french lang 2024-11-23 16:24:55 +01:00
qdegrandmaison
a17ce6ad8a fix(js): remove test data 2022-11-30 14:08:11 +01:00
qdegrandmaison
4358baa1b7 fix(css): dirty support of multiple viewports 2022-11-30 14:01:11 +01:00
qdegrandmaison
34314cbdfb feat(css): add customization 2022-11-30 13:20:25 +01:00
Jonas Platte
c2eb6137ae Fix nginx sample config 2022-11-25 14:38:36 +01:00
Jonas Platte
a0876f81c1 Reduce feature set enabled for tokio 2022-11-25 14:36:59 +01:00
Jonas Platte
3bec177cd9 Upgrade axum 2022-11-25 14:36:00 +01:00
Jonas Platte
f54bf11d0b Embed index.html and dedup people 2022-11-25 14:34:02 +01:00
6 changed files with 1174 additions and 408 deletions

647
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.5.17"
tokio = { version = "1.0", features = ["full"] }
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
axum = { version = "0.7.8", features = ["http1", "json", "tokio"] }
tokio = { version = "1.41", features = ["macros", "rt-multi-thread"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
serde = { version = "1.0", features = ["derive"] }
once_cell = "1.8.0"
rand = "0.8.4"
rust-embed = "6.4.2"
once_cell = "1.20"
rand = "0.8.5"
log = "0.4"
env_logger = "0.11"

View File

@@ -1,34 +0,0 @@
<!doctype html>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Secret Santa</title>
<h1>Secret Santa</h1>
<select id="person">
<option selected disabled>Who is drawing?</option>
___REPLACE_BY_PERSONS___
</select>
<div id="result"></div>
<script>
document.getElementById("person").addEventListener('change', event => {
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ "person": event.target.value })
};
console.log(options);
fetch("/secret-santa-api/", options)
.then(response => response.text())
.then(text => {
document.getElementById("result").textContent = `Your draw: ${text}`;
});
});
</script>

View File

@@ -4,10 +4,8 @@ server {
#
# server_name whatever.tld;
# index.html stored in /srv/http/whatever.tld/secret-santa/index.html
location /secret-santa { root /srv/http/whatever.tld; }
# app running on localhost with the port hardcoded in main.rs
location /secret-santa-api/ { proxy_pass http://localhost:3067/secret-santa-api; }
location /secret-santa/ { proxy_pass http://localhost:3067/; }
# SSL config...
}

740
src/index.html Normal file
View File

@@ -0,0 +1,740 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Secret Santa</title>
<!-- Christmas tree from https://codepen.io/lenasta92579651/pen/xxERxBx -->
<!-- CSS snowflakes from https://pajasevi.github.io/CSSnowflakes/ -->
</head>
<body>
<!-- Content -->
<h1>Secret Santa</h1>
<select id="person">
<option selected disabled>Qui es-tu?</option>
__OPTIONS__
</select>
<h2 id="result"></h2>
<!-- Christmas tree -->
<div id="christmas-container">
<div class="christmas">
<div class="tree">
<div class="chain"></div>
<div class="chain2"></div>
</div>
<div class="lights">
<div class="light1"></div>
<div class="light2"></div>
<div class="light3"></div>
<div class="light4"></div>
<div class="light5"></div>
<div class="light6"></div>
<div class="light7"></div>
<div class="light8"></div>
<div class="light9"></div>
<div class="light10"></div>
</div>
<div class="balls">
<div class="ball1"></div>
</div>
<div class="star"></div>
<div class="gift"></div>
<div class="ribbon"></div>
<div class="gift2"></div>
<div class="ribbon2"></div>
<div class="gift3"></div>
<div class="ribbon3"></div>
<div class="shadow"></div>
</div>
</div>
<!-- Snowflakes -->
<div class="snowflakes" aria-hidden="true">
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
<div class="snowflake"></div>
</div>
<script>
document.getElementById("person").addEventListener("change", (event) => {
if (
window.confirm(
`Vérification! Tu es ${event.target.value}, c'est bien ça?`
)
) {
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ person: event.target.value }),
};
console.log(options);
fetch("api", options)
.then((response) => response.text())
.then((text) => {
document.getElementById(
"result"
).textContent = `Hoho! Tu as pioché ${text} ;`;
document.getElementById("person").remove();
});
}
});
</script>
</body>
<style>
/** Page settings **/
body {
background-color: #3f69c7;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
margin: 0;
padding: 0;
overflow: hidden;
}
h1 {
color: white;
font-family: Arial, Helvetica, sans-serif;
font-size: 5em;
text-align: center;
z-index: 10;
}
select {
z-index: 10;
}
h2 {
color: white;
font-family: Arial, Helvetica, sans-serif;
font-size: 3em;
text-align: center;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
}
#christmas-container {
position: fixed;
right: 20%;
bottom: 30%;
}
/** Christmas tree **/
.christmas {
position: relative;
}
.tree {
position: relative;
background-color: #685044;
width: 30px;
height: 80px;
top: 100px;
transform-style: preserve-3d;
}
.tree:before {
content: "";
position: relative;
width: 0;
height: 0;
border-left: 90px solid transparent;
border-right: 90px solid transparent;
border-bottom: 270px solid #0e9594;
border-radius: 30px;
top: -250px;
left: -75px;
}
.chain {
width: 85px;
height: 85px;
border: solid 3px #333;
border-radius: 50%;
top: -185px;
left: -35px;
position: absolute;
transform: rotate3d(8, 0.1, -5, 75deg);
box-sizing: border-box;
backface-visibility: visible !important;
z-index: 5;
}
.chain2 {
width: 145px;
height: 135px;
border: solid 3px #333;
border-radius: 50%;
top: -115px;
left: -65px;
position: absolute;
transform: rotate3d(8, 0.1, -5, 75deg);
box-sizing: border-box;
backface-visibility: visible !important;
z-index: 5;
}
.shadow {
background-color: rgba(0, 0, 0, 0.07);
position: absolute;
width: 250px;
height: 30px;
border-radius: 50%;
top: 170px;
left: -115px;
z-index: -1;
}
.star {
margin: 50px 0;
position: absolute;
display: block;
width: 0px;
height: 0px;
border-right: 25px solid transparent;
border-bottom: 17.5px solid #f9dc5c;
border-left: 25px solid transparent;
transform: rotate(35deg);
top: -190px;
left: -9px;
}
.star:before {
border-bottom: 20px solid #f9dc5c;
border-left: 7.5px solid transparent;
border-right: 7.5px solid transparent;
position: absolute;
height: 0;
width: 0;
top: -12.5px;
left: -17.5px;
display: block;
content: "";
transform: rotate(-35deg);
}
.star:after {
position: absolute;
display: block;
top: 0.75px;
left: -26.25px;
width: 0px;
height: 0px;
border-right: 25px solid transparent;
border-bottom: 17.5px solid #f9dc5c;
border-left: 25px solid transparent;
transform: rotate(-70deg);
content: "";
}
.lights {
position: absolute;
}
.light1 {
position: absolute;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
}
.light1 {
background-color: #ff595e;
top: -100px;
left: -35px;
transform: rotate(40deg);
box-shadow: 1px 1px 15px #faf3dd;
}
.light2 {
position: absolute;
background-color: #ffca3a;
top: -95px;
left: -10px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light3 {
position: absolute;
background-color: #6a4c93;
top: -105px;
left: 15px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light4 {
position: absolute;
background-color: #1982c4;
top: -118px;
left: 35px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light5 {
position: absolute;
background-color: #1982c4;
top: 12px;
left: -55px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light6 {
position: absolute;
background-color: #8ac926;
top: 15px;
left: -25px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light7 {
position: absolute;
background-color: #ff595e;
top: 10px;
left: 2px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light8 {
position: absolute;
background-color: #ffca3a;
top: -2px;
left: 27px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light9 {
position: absolute;
background-color: #9e0059;
top: -17px;
left: 50px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.light10 {
position: absolute;
background-color: #4361ee;
top: -40px;
left: 68px;
box-shadow: 1px 1px 15px #faf3dd;
width: 15px;
height: 15px;
border-radius: 10px 150px 30px 150px;
transform: rotate(40deg);
}
.gift {
position: absolute;
width: 60px;
height: 50px;
background-color: #ffc857;
top: 130px;
left: 30px;
box-shadow: inset -8px 0 0 rgba(0, 0, 0, 0.07);
}
.gift:before {
content: "";
position: absolute;
width: 70px;
height: 15px;
background-color: #ffc857;
left: -5px;
box-shadow: inset -8px -4px 0 rgba(0, 0, 0, 0.07);
}
.gift:after {
content: "";
background-color: #db3a34;
width: 10px;
height: 50px;
position: absolute;
left: 25px;
}
.ribbon {
position: absolute;
width: 20px;
height: 10px;
border: 3px solid #db3a34;
border-radius: 50%;
transform: skew(15deg, 15deg);
top: 116px;
left: 35px;
}
.ribbon:before {
content: "";
position: absolute;
width: 20px;
height: 10px;
border: 3px solid #db3a34;
border-radius: 50%;
transform: skew(-15deg, -20deg);
left: 22px;
top: -8px;
}
.gift2 {
position: absolute;
width: 50px;
height: 40px;
background-color: #08bdbd;
top: 140px;
left: -65px;
box-shadow: inset -8px 0 0 rgba(0, 0, 0, 0.07);
}
.gift2:before {
content: "";
position: absolute;
width: 60px;
height: 15px;
background-color: #08bdbd;
left: -5px;
box-shadow: inset -8px -4px 0 rgba(0, 0, 0, 0.07);
}
.gift2:after {
content: "";
background-color: #abff4f;
width: 10px;
height: 40px;
position: absolute;
left: 15px;
}
.gift3 {
position: absolute;
width: 40px;
height: 30px;
background-color: #7678ed;
top: 150px;
left: -85px;
box-shadow: inset -8px 0 0 rgba(0, 0, 0, 0.07);
}
.gift3:before {
content: "";
position: absolute;
width: 50px;
height: 10px;
background-color: #7678ed;
left: -5px;
box-shadow: inset -8px -4px 0 rgba(0, 0, 0, 0.07);
}
.gift3:after {
content: "";
background-color: #f7b801;
width: 7px;
height: 30px;
position: absolute;
left: 15px;
}
.ribbon2 {
position: absolute;
width: 15px;
height: 7px;
border: 3px solid #abff4f;
border-radius: 50%;
transform: skew(15deg, 15deg);
top: 129px;
left: -65px;
}
.ribbon2:before {
content: "";
position: absolute;
width: 15px;
height: 7px;
border: 3px solid #abff4f;
border-radius: 50%;
transform: skew(-15deg, -20deg);
left: 15px;
top: -8px;
}
.ribbon3 {
position: absolute;
width: 12px;
height: 5px;
border: 3px solid #f7b801;
border-radius: 50%;
transform: skew(15deg, 15deg);
top: 142px;
left: -85px;
}
.ribbon3:before {
content: "";
position: absolute;
width: 12px;
height: 5px;
border: 3px solid #f7b801;
border-radius: 50%;
transform: skew(-15deg, -20deg);
left: 15px;
top: -8px;
}
.balls {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #db3a34;
top: 15px;
left: -15px;
}
.balls:before {
content: "";
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #ffc857;
top: 35px;
left: -15px;
}
.balls:after {
content: "";
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #f07167;
top: 20px;
left: 45px;
}
.ball1 {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #fae588;
top: -90px;
left: 20px;
}
.ball1:before {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #fae588;
content: "";
top: 170px;
left: 50px;
}
.light1,
.light2,
.light3,
.light4,
.light5,
.light6,
.light7,
.light8,
.light9,
.light10 {
-webkit-animation: flash 6s infinite;
}
@-webkit-keyframes flash {
20%,
24%,
55% {
box-shadow: none;
}
0%,
19%,
21%,
23%,
25%,
54%,
56%,
100% {
box-shadow: 0 0 5px #f5de93, 0 0 15px #f5de93, 0 0 20px #f5de93,
0 0 40px #f5de93, 0 0 60px #decea4, 0 0 10px #d6c0a5, 0 0 98px #ff0000;
}
}
/** Snowflakes */
.snowflake {
color: #fff;
font-size: 1em;
font-family: Arial, sans-serif;
text-shadow: 0 0 5px #000;
}
@-webkit-keyframes snowflakes-fall {
0% {
top: -10%;
}
100% {
top: 100%;
}
}
@-webkit-keyframes snowflakes-shake {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
50% {
-webkit-transform: translateX(80px);
transform: translateX(80px);
}
}
@keyframes snowflakes-fall {
0% {
top: -10%;
}
100% {
top: 100%;
}
}
@keyframes snowflakes-shake {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(80px);
}
}
.snowflake {
position: fixed;
top: -10%;
z-index: 9999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: default;
-webkit-animation-name: snowflakes-fall, snowflakes-shake;
-webkit-animation-duration: 10s, 3s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
-webkit-animation-play-state: running, running;
animation-name: snowflakes-fall, snowflakes-shake;
animation-duration: 10s, 3s;
animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite;
animation-play-state: running, running;
}
.snowflake:nth-of-type(0) {
left: 1%;
-webkit-animation-delay: 0s, 0s;
animation-delay: 0s, 0s;
}
.snowflake:nth-of-type(1) {
left: 10%;
-webkit-animation-delay: 1s, 1s;
animation-delay: 1s, 1s;
}
.snowflake:nth-of-type(2) {
left: 20%;
-webkit-animation-delay: 6s, 0.5s;
animation-delay: 6s, 0.5s;
}
.snowflake:nth-of-type(3) {
left: 30%;
-webkit-animation-delay: 4s, 2s;
animation-delay: 4s, 2s;
}
.snowflake:nth-of-type(4) {
left: 40%;
-webkit-animation-delay: 2s, 2s;
animation-delay: 2s, 2s;
}
.snowflake:nth-of-type(5) {
left: 50%;
-webkit-animation-delay: 8s, 3s;
animation-delay: 8s, 3s;
}
.snowflake:nth-of-type(6) {
left: 60%;
-webkit-animation-delay: 6s, 2s;
animation-delay: 6s, 2s;
}
.snowflake:nth-of-type(7) {
left: 70%;
-webkit-animation-delay: 2.5s, 1s;
animation-delay: 2.5s, 1s;
}
.snowflake:nth-of-type(8) {
left: 80%;
-webkit-animation-delay: 1s, 0s;
animation-delay: 1s, 0s;
}
.snowflake:nth-of-type(9) {
left: 90%;
-webkit-animation-delay: 3s, 1.5s;
animation-delay: 3s, 1.5s;
}
.snowflake:nth-of-type(10) {
left: 25%;
-webkit-animation-delay: 2s, 0s;
animation-delay: 2s, 0s;
}
.snowflake:nth-of-type(11) {
left: 65%;
-webkit-animation-delay: 4s, 2.5s;
animation-delay: 4s, 2.5s;
}
</style>
</html>

View File

@@ -1,55 +1,108 @@
use axum::{
response::Html,
routing::{get, post},
Json,
response::{Html, IntoResponse},
Router,
http::{header, HeaderValue, Request, Response, StatusCode},
body::{Body, BoxBody}
Json, Router,
};
use once_cell::sync::Lazy;
use rand::{thread_rng, Rng};
use rand::seq::SliceRandom;
use serde::Deserialize;
use std::net::SocketAddr;
use tokio::sync::Mutex;
use rust_embed::RustEmbed;
use log::debug;
use log::error;
use log::info;
use log::warn;
#[derive(RustEmbed)]
#[folder = "./"]
struct Asset;
#[macro_use]
extern crate log;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
env_logger::init();
info!("let's go!");
// build our application with some routes
let app = Router::new()
.route("/secret-santa-api", post(input))
.route("/", get(root));
.route("/", get(|| async { Html(INDEX_HTML.as_str()) }))
.route("/api", post(input));
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3067));
axum::Server::bind(&addr)
.serve(app.into_make_service())
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
static INDEX_HTML: Lazy<String> = Lazy::new(|| {
let content = include_str!("index.html");
let options: String = everyone()
.into_iter()
.map(|person| format!(r#"<option value="{person}">{person}</option>"#))
.collect();
content.replace("__OPTIONS__", &options)
});
static STATE: Lazy<Mutex<State>> = Lazy::new(Mutex::default);
struct State {
rem_a: Vec<String>,
rem_b: Vec<String>,
participants: Vec<String>,
remaining: Vec<String>,
}
fn has_forbidden_adjacent(participants: &Vec<String>) -> bool {
let forbidden_pairs = vec![("Alice".into(), "Bob".into())];
// Check pairs of adjacent elements, including the circular pair (last, first)
((1..participants.len()).any(|i| {
match (participants[i - 1].as_str(), participants[i].as_str()) {
// Check if the pair is forbidden
(a, b) if forbidden_pairs.contains(&(a.to_string(), b.to_string())) ||
forbidden_pairs.contains(&(b.to_string(), a.to_string())) => {
info!("forbidden pair detected : {:?}/{:?} in {:?}", a, b, participants.join(" => "));
return true;
},
_ => false,
}
}
) || // Check the pair (last, first) for circular adjacency
match (participants.last().unwrap().as_str(), participants[0].as_str()) {
(a, b) if forbidden_pairs.contains(&(a.to_string(), b.to_string())) ||
forbidden_pairs.contains(&(b.to_string(), a.to_string())) => {
info!("forbidden pair detected : {:?}/{:?} in {:?}", a, b, participants.join(" => "));
return true;
},
_ => false,
}
)
}
fn everyone() -> Vec<String> {
vec!["Alice".into(), "Bob".into(), "Carol".into(), "Dave".into()]
vec!["Alice".into(), "Bob".into(), "Carol".into(), "Dave".into(), "Edgar".into(), "France".into()]
}
impl Default for State {
fn default() -> Self {
Self {
rem_a: everyone(),
rem_b: everyone(),
let mut p = everyone();
let mut tries = 0;
while tries < 100 {
p.shuffle(&mut thread_rng());
if !has_forbidden_adjacent(&p) {
info!("distribution : {:?}", p.join(" => "));
return Self {
participants: p,
remaining: everyone()
}
} else {
}
tries += 1;
}
eprintln!("ERROR (could not generate a correct list)");
std::process::exit(1)
}
}
@@ -61,33 +114,32 @@ struct Input {
async fn input(Json(input): Json<Input>) -> String {
let mut state = STATE.lock().await;
if state.rem_a.is_empty() {
return "ERROR (everybody drew already)".into();
if state.remaining.is_empty() {
return "... He mais tout le monde a déja pioché !!".into();
}
match state.rem_a.iter().position(|p| input.person == *p) {
info!("joueurs qui restent : {:?}", state.remaining.join(","));
match state.remaining.iter().position(|p| input.person == *p).map(|e| state.remaining.remove(e)) {
Some(rem) => {
match state.participants.iter().position(|p| input.person == *p) {
Some(pos) => {
state.rem_a.remove(pos);
info!("joueur qui pioche : {:?}", state.participants[pos]);
return match state.participants.iter().nth(pos+1) {
Some(x) => {
info!("joueur qui suit : {:?}", x);
return x.to_string();
}
None => return "ERROR (you drew already)".into(),
None => return match state.participants.first() {
Some(x) => {
info!("joueur qui suit : {:?}", x);
return x.to_string();
}
loop {
let num = thread_rng().gen_range(0..state.rem_b.len());
if state.rem_b[num] != input.person {
return state.rem_b.remove(num);
None => "ERROR".to_string()
}
}
}
pub async fn root() -> Response<BoxBody> {
let data_index_html = Asset::get("index.html").unwrap();
let index_html = String::from_utf8_lossy(&data_index_html.data);
let mut persons_dropdown = String::new();
for person in everyone() {
let dropdown = format!("<option value=\"{}\">{}</option>\n", person, person);
persons_dropdown.push_str(&dropdown);
None => "ERROR".to_string()
}
}
None => "... He mais tu as déja pioché !".to_string()
}
return Html(index_html.replace("___REPLACE_BY_PERSONS___", &persons_dropdown)).into_response();
}