refactor: split cli and logging to separate crates
All checks were successful
Test the running changes / Test (push) Successful in 42s
All checks were successful
Test the running changes / Test (push) Successful in 42s
This commit is contained in:
@@ -3,11 +3,7 @@ name = "stdsrv"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "gravel"
|
||||
path = "src/main.rs"
|
||||
|
||||
# local dependencies
|
||||
[dependencies]
|
||||
cracked_md = { path = "../cracked_md" }
|
||||
fstools = { path = "../fstools" }
|
||||
slogger = { path = "../slogger" }
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
//! Simple and program specific command line argument parsing solution.
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::error::ErrorKind;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
pub static VERBOSE: OnceLock<bool> = OnceLock::new();
|
||||
|
||||
pub struct ProgramArgs {
|
||||
pub outdir: PathBuf,
|
||||
pub indir: PathBuf,
|
||||
pub generate: bool,
|
||||
pub force: bool,
|
||||
pub addr: String,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl Default for ProgramArgs {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
indir: PathBuf::from("./web"),
|
||||
outdir: PathBuf::from("./html"),
|
||||
generate: false,
|
||||
force: false,
|
||||
addr: "0.0.0.0:8080".to_string(),
|
||||
verbose: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<std::env::Args> for ProgramArgs {
|
||||
type Error = crate::error::Error;
|
||||
fn try_from(mut value: std::env::Args) -> Result<Self, Self::Error> {
|
||||
let mut a = Self::default();
|
||||
let _ = value.next(); // ignore executable path
|
||||
while let Some(v) = value.next() {
|
||||
match v.as_str() {
|
||||
"-i" => {
|
||||
a.indir = value
|
||||
.next()
|
||||
.ok_or(Error::new(
|
||||
ErrorKind::CommandLineArgsParse,
|
||||
"Expected input directory after option `-i`",
|
||||
))?
|
||||
.into();
|
||||
}
|
||||
"-a" => {
|
||||
a.addr = value.next().ok_or(Error::new(
|
||||
ErrorKind::CommandLineArgsParse,
|
||||
"Expected listener address after option `-a`",
|
||||
))?;
|
||||
}
|
||||
"-g" => a.generate = true,
|
||||
"-f" => a.force = true,
|
||||
"-v" => {
|
||||
a.verbose = true;
|
||||
VERBOSE.get_or_init(|| true);
|
||||
}
|
||||
_ => {
|
||||
a.outdir = v.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
VERBOSE.get_or_init(|| false);
|
||||
Ok(a)
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ pub struct Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[must_use]
|
||||
pub fn new(kind: ErrorKind, msg: &str) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! A simple server implementation that just responds with the contents of the file requested in
|
||||
//! the provided directory.
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use crate::{
|
||||
error::{Error, ErrorKind, Result},
|
||||
@@ -16,7 +16,7 @@ pub struct FileServer {
|
||||
}
|
||||
|
||||
impl FileServer {
|
||||
pub fn new(root: &PathBuf) -> Result<FileServer> {
|
||||
pub fn new(root: &Path) -> Result<FileServer> {
|
||||
if !root.is_dir() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::DirNotFound,
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
use crate::error::ErrorKind;
|
||||
use crate::log;
|
||||
use crate::logger::Level;
|
||||
use std::io::Read;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
|
||||
use crate::{error::Error, request::HttpRequest};
|
||||
|
||||
pub struct HttpStream {
|
||||
tcp_listener: TcpListener,
|
||||
}
|
||||
|
||||
impl HttpStream {
|
||||
pub fn new(addr: &str) -> Self {
|
||||
let tcp_listener = TcpListener::bind(addr)
|
||||
.unwrap_or_else(|e| panic!("Failed to bind on address `{}`: {}", addr, e));
|
||||
log!(Level::Info, "Listening on `{}`", addr);
|
||||
|
||||
Self { tcp_listener }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for HttpStream {
|
||||
type Item = (HttpRequest, TcpStream);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// safe to unwrap, because Incoming never returns None
|
||||
let mut stream = self.tcp_listener.incoming().next().unwrap().ok()?;
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
let _read_bytes = stream
|
||||
.read(&mut buf)
|
||||
.or(Err(Error::new(
|
||||
ErrorKind::StreamReadFailed,
|
||||
"Reading from TCP stream failed",
|
||||
)))
|
||||
.ok()?;
|
||||
Some((
|
||||
String::from_utf8_lossy(&buf[..]).trim().try_into().ok()?,
|
||||
stream,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,36 @@
|
||||
//! A simple web server with 0 dependencies (other than Rust's stdlib).
|
||||
//! Documentation is a work in progress, go see my webpage at [jlux.dev](https://jlux.dev).
|
||||
|
||||
#![feature(never_type)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
io::{BufReader, BufWriter},
|
||||
net::TcpListener,
|
||||
process,
|
||||
net::{Ipv4Addr, TcpListener},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use args::ProgramArgs;
|
||||
use cracked_md::generate;
|
||||
use error::Error;
|
||||
use fileserver::FileServer;
|
||||
use logger::Level;
|
||||
use request::HttpRequest;
|
||||
use responder::Responder;
|
||||
use slogger::{Level, log};
|
||||
|
||||
mod args;
|
||||
mod error;
|
||||
pub mod error;
|
||||
mod fileserver;
|
||||
mod http_header;
|
||||
//mod http_stream;
|
||||
mod logger;
|
||||
mod request;
|
||||
mod responder;
|
||||
mod response;
|
||||
|
||||
/// Entrypoint to the program.
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// Opens a file server on a specified address and port which serves all files in dir.
|
||||
///
|
||||
/// # Errors
|
||||
/// Errors that come up while serving files. Look at [`Error`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Never. Added to allow compiler to check for ! type.
|
||||
pub fn serve(addr: Ipv4Addr, port: u16, dir: PathBuf) -> Result<!, Error> {
|
||||
/*
|
||||
let args: ProgramArgs = std::env::args().try_into()?;
|
||||
|
||||
if args.generate {
|
||||
@@ -51,18 +56,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
let listener = TcpListener::bind(&args.addr)?;
|
||||
log!(Level::Info, "Listening on addr `{}`", &args.addr);
|
||||
*/
|
||||
let listener = TcpListener::bind((addr, port))?;
|
||||
log!(Level::Info, "Listening on addr `{}:{}`", addr, port);
|
||||
|
||||
// todo: refactor this
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
let outdir = args.outdir.clone();
|
||||
let outdir = dir.clone();
|
||||
std::thread::spawn(move || {
|
||||
log!(Level::Debug, "TcpStream handler spawned");
|
||||
let mut reader = BufReader::new(&stream);
|
||||
let mut writer = BufWriter::new(&stream);
|
||||
let server = match FileServer::new(&outdir) {
|
||||
let server = match FileServer::new(outdir.as_path()) {
|
||||
Ok(s) => s,
|
||||
Err(_e) => return,
|
||||
};
|
||||
@@ -82,5 +89,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
panic!("Code shouldn't get to here");
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(PartialEq)]
|
||||
pub enum Level {
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
}
|
||||
|
||||
impl Display for Level {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}]\x1b[0m",
|
||||
match self {
|
||||
Self::Error => "\x1b[1;31m[ERROR",
|
||||
Self::Warn => "\x1b[1;33m[WARN",
|
||||
Self::Info => "\x1b[0;32m[INFO",
|
||||
Self::Debug => "\x1b[0;36m[DEBUG",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A logging macro. Takes a [`Level`] and a formatted string.
|
||||
///
|
||||
/// [`Level`]: ./logger/enum.Level.html
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($level:expr, $($arg:tt)*) => {{
|
||||
if $level != Level::Debug || crate::args::VERBOSE.get().unwrap().to_owned() {
|
||||
println!(
|
||||
"{} {}:{}:{}: {}",
|
||||
$level,
|
||||
std::module_path!(),
|
||||
std::file!(),
|
||||
std::line!(),
|
||||
format!($($arg)*)
|
||||
);
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
use crate::error::{Error, ErrorKind, Result};
|
||||
use crate::http_header::HttpHeaders;
|
||||
use crate::log;
|
||||
use crate::logger::Level;
|
||||
use slogger::{Level, log};
|
||||
use std::fmt::Display;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::net::TcpStream;
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::http_header::HttpHeaders;
|
||||
use crate::log;
|
||||
use crate::logger::Level;
|
||||
use slogger::{Level, log};
|
||||
use std::{fmt::Display, io::Write};
|
||||
|
||||
/// Macro for generating Http status codes (AI generated).
|
||||
|
||||
Reference in New Issue
Block a user