Added basic server and logger in Rust.

This commit is contained in:
Yohan Boujon 2025-05-02 10:54:35 +02:00
parent 2f896195d5
commit 6873a71363
11 changed files with 113 additions and 155 deletions

3
backend/.gitignore vendored
View file

@ -1 +1,2 @@
.env
**/target/**
Cargo.lock

10
backend/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "backend"
version = "0.1.0"
edition = "2024"
[dependencies]
actix-web = "4.10.2"
chrono = "0.4.41"
serde = { version = "1.0.219", features = ["derive"]}
serde_json = "1.0.140"

View file

View file

@ -1,3 +0,0 @@
module backend
go 1.24.2

View file

@ -1,14 +0,0 @@
package main
import (
"backend/server"
"backend/util"
)
func main() {
// Checking arguments
util.SetLevel(util.NOLOG)
// Rest API start on port
server.Start(8085)
}

View file

@ -1,16 +0,0 @@
package server
import (
"backend/util"
"net/http"
)
func getphoto(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
util.Logformat(util.ERROR, "%s: Missing 'id' parameter.", util.GetFunctionName())
http.Error(w, "Missing or invalid parameters : id", http.StatusBadRequest)
} else {
sendResponse(w, 0)
}
}

View file

@ -1,44 +0,0 @@
package server
import (
"backend/util"
"encoding/json"
"net/http"
"strconv"
)
func Start(port int64) {
// Creating endpoints
http.HandleFunc("/getphoto", wrapHeader(getphoto))
util.Logformat(util.INFO, "Starting server on port %d...\n", port)
if err := http.ListenAndServe(":"+strconv.FormatInt(port, 10), nil); err != nil {
util.Logformat(util.ERROR, "Could not start server (%s)\n", err.Error())
panic(err)
}
}
func sendResponse(w http.ResponseWriter, r any) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(r); err != nil {
util.Logformat(util.ERROR, "%s\n", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// Not sure about that, might change it later... (added by Lemonochrme on 'yoboujon/automatic-management')
func wrapHeader(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
next(w, r)
}
}

80
backend/src/log.rs Normal file
View file

@ -0,0 +1,80 @@
use std::collections::HashMap;
use std::fmt::Arguments;
use chrono::{Datelike, Local, Timelike};
use std::io::{self, Write};
#[derive(Eq, PartialEq, Hash)]
pub enum LogLevel {
NOLOG,
INFO,
CORRECT,
WARNING,
ERROR,
}
#[allow(dead_code)]
pub enum ColorFormat {
BLUE,
GREEN,
YELLOW,
RED,
LIGHTRED,
WHITE,
RESET,
}
impl ColorFormat {
fn as_str(&self) -> &'static str {
match self {
ColorFormat::BLUE => "\x1b[34m",
ColorFormat::GREEN => "\x1b[32m",
ColorFormat::YELLOW => "\x1b[33m",
ColorFormat::RED => "\x1b[31m",
ColorFormat::LIGHTRED => "\x1b[91m",
ColorFormat::WHITE => "\x1b[97m",
ColorFormat::RESET => "\x1b[0m",
}
}
}
fn get_color_print() -> HashMap<LogLevel, &'static str> {
let mut map = HashMap::new();
map.insert(LogLevel::NOLOG, "");
map.insert(LogLevel::INFO, concat!("\x1b[34m","[INFO]","\x1b[0m"));
map.insert(LogLevel::CORRECT, concat!("\x1b[32m","[OK ]", "\x1b[92m"));
map.insert(LogLevel::WARNING, concat!("\x1b[33m", "[WARN]", "\x1b[93m"));
map.insert(LogLevel::ERROR, concat!("\x1b[31m", "[ERR ]", "\x1b[91m"));
map
}
pub fn log_format(level: LogLevel, args: Arguments) {
let color_map = get_color_print();
let header = color_map.get(&level).copied().unwrap_or("");
let message = format!("{}", args);
let now = Local::now();
let time = format!(
"[{:02}/{:02}/{} {:02}:{:02}:{:02}]",
now.day(),
now.month(),
now.year(),
now.hour(),
now.minute(),
now.second()
);
if level == LogLevel::NOLOG {
println!("{}\t\t{}", time, message);
} else if level != LogLevel::INFO {
println!("{}{}\t{}{}", header, time, message, ColorFormat::RESET.as_str());
} else {
println!("{}{}\t{}", header, time, message);
}
io::stdout().flush().unwrap();
}
#[macro_export]
macro_rules! logf {
($level:expr, $fmt:expr $(, $arg:expr)* $(,)?) => {
log::log_format($level, format_args!($fmt $(, $arg)*))
};
}

21
backend/src/main.rs Normal file
View file

@ -0,0 +1,21 @@
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
mod log;
use log::LogLevel;
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
logf!(LogLevel::INFO, "This is an info log.");
logf!(LogLevel::CORRECT, "This is a correct log.");
logf!(LogLevel::ERROR, "This is an error log.");
logf!(LogLevel::WARNING, "This is a warning log.");
logf!(LogLevel::NOLOG, "This is no log.");
HttpServer::new(|| App::new().route("/hello", web::get().to(hello)))
.bind("127.0.0.1:8080")?
.run()
.await
}

View file

@ -1,59 +0,0 @@
package util
import (
"fmt"
"time"
)
type colorFormat string
type LogLevel int
const (
BLUE colorFormat = "\033[34m"
GREEN colorFormat = "\033[32m"
YELLOW colorFormat = "\033[33m"
RED colorFormat = "\033[31m"
LIGHT_RED colorFormat = "\033[91m"
WHITE colorFormat = "\033[97m"
RESET colorFormat = "\033[0m"
)
const (
NOLOG LogLevel = iota
INFO
CORRECT
WARNING
ERROR
)
var colorPrint = map[LogLevel]colorFormat{
NOLOG: "",
INFO: BLUE + "[INFO]" + RESET,
CORRECT: GREEN,
WARNING: YELLOW + "[WARN]",
ERROR: RED + "[ERR.]" + LIGHT_RED,
}
var minimumlog = NOLOG
func Logformat(level LogLevel, format string, args ...interface{}) {
if level < minimumlog {
return
}
header := colorPrint[level]
message := fmt.Sprintf(format, args...)
currentTime := time.Now()
time := fmt.Sprintf("[%02d/%02d/%d %02d:%02d:%02d]", currentTime.Day(), currentTime.Month(), currentTime.Year(), currentTime.Hour(), currentTime.Minute(), currentTime.Second())
if level == NOLOG {
fmt.Printf("%s\t\t%s", time, message)
} else if level != INFO {
fmt.Printf("%s%s\t%s%s", header, time, message, RESET)
} else {
fmt.Printf("%s%s\t%s", header, time, message)
}
}
func SetLevel(level LogLevel) {
minimumlog = level
}

View file

@ -1,18 +0,0 @@
package util
import (
"net/http"
"runtime"
"strings"
)
func GetFunctionName() string {
pc, _, _, _ := runtime.Caller(1)
f := runtime.FuncForPC(pc)
return strings.Split(strings.Split(f.Name(), "/")[1], ".")[1]
}
func HasSubURI(r *http.Request) (bool, string) {
url := strings.Split(r.URL.Path, "/")
return (len(url[2]) > 0), url[2]
}