diff --git a/src/handlers.rs b/src/handlers.rs index ca1c94b..7d2d5da 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,13 +1,13 @@ use crate::http::{ requests::{Request, RequestStatus, URL}, - responses::{Response, UnitOrBoxedError}, + responses::{Body, Response, UnitOrBoxedError}, }; use std::{ collections::HashMap, net::{Shutdown, TcpListener}, }; -type DynHandlerFn = dyn Fn(Request) -> Response<'static>; +type DynHandlerFn = dyn Fn(Request) -> Response; type BoxedHandlerFn = Box; // Collection of handlers for requests @@ -31,7 +31,7 @@ impl Handlers { /// /// path: Path to match (no trailing /) /// handler: Function to handle the request. Must return a Response - pub fn add_handler(&mut self, path: &str, handler: impl Fn(Request) -> Response<'static> + 'static) { + pub fn add_handler(&mut self, path: &str, handler: impl Fn(Request) -> Response + 'static) { self.matchers.insert(path.to_string(), Box::from(handler)); } @@ -87,10 +87,10 @@ impl Handlers { for (url_segment, path_segment) in url_segements.iter().zip(path_segments.iter()) { if path_segment.starts_with('[') { // e.g. /path/[id] - if path_segment.ends_with(']') { + if path_segment.ends_with(']') { continue; } - + // e.g. /path/prefix[id].suffix let prefix = path_segment.split('[').collect::>()[0]; let suffix = path_segment.split(']').collect::>()[1]; @@ -120,7 +120,7 @@ impl Handlers { match self.match_handler(&req.url) { Some(handler) => handler(req).send()?, None => { - return Response::new(req, 404, "Not Found").send(); + return Response::new(req, 404, Body::Static("Not Found")).send(); } }; diff --git a/src/http/responses.rs b/src/http/responses.rs index 553c5bc..d6ce308 100644 --- a/src/http/responses.rs +++ b/src/http/responses.rs @@ -1,18 +1,25 @@ -use std::error::Error; -use std::io::prelude::*; -use std::net::TcpStream; +use std::{error::Error, io::prelude::*, net::TcpStream}; use super::requests::Request; -pub struct Response<'a> { +pub enum Body { + String(String), + Static(&'static str), +} + +pub struct Response { request: Request, - pub body: &'a str, + pub body: Body, pub status: u16, } -impl<'a> Response<'a> { - pub fn new(request: Request, status: u16, body: &'a str) -> Self { - Response { request, body, status } +impl Response { + pub fn new(request: Request, status: u16, body: Body) -> Self { + Response { + request, + body, + status, + } } pub fn send(self) -> UnitOrBoxedError { @@ -22,7 +29,7 @@ impl<'a> Response<'a> { pub type UnitOrBoxedError = Result<(), Box>; -fn send_response(mut stream: TcpStream, status: u16, body: &str) -> UnitOrBoxedError { +fn send_response(mut stream: TcpStream, status: u16, body: Body) -> UnitOrBoxedError { let status_line = match status { 200 => "200 OK", 400 => "400 BAD REQUEST", @@ -31,12 +38,16 @@ fn send_response(mut stream: TcpStream, status: u16, body: &str) -> UnitOrBoxedE _ => "500 INTERNAL SERVER ERROR", }; - let response = format!( - "{}\r\nContent-Length: {}\r\nServer: luciders/0.1.0\r\n\r\nHTTP/1.1 {}", - status_line, - body.len(), - body - ); + let mut response = format!("HTTP/1.1 {}\r\nServer: luciders/0.1.0\r\n", status_line); + + response.push_str(&format!( + "Content-Length: {}\r\n", + match &body { + Body::String(s) => s.len(), + Body::Static(s) => s.len(), + + } + )); match stream.write(response.as_bytes()) { Ok(_) => {} diff --git a/src/main.rs b/src/main.rs index 3cb7048..44abac4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,29 @@ -use std::{net::TcpListener, process}; +use std::{env, ffi::OsString, fs, net::TcpListener, process}; use ctrlc; -use http::responses::Response; +use http::responses::{Response, Body}; mod handlers; mod http; fn main() { + println!("luciders starting..."); + + let icons_dir_arg = env::args_os() + .nth(1) + .unwrap_or(OsString::from("vendor/lucide/icons")); + + println!("Using icons from: {:?}", icons_dir_arg); + + let icon_filenames: Vec<_> = fs::read_dir(icons_dir_arg) + .unwrap_or_else(|_| fatal("Failed to read icons directory! Does the path exist?")) + .map(|entry| entry.unwrap_or_else(|_| fatal("Failed to read icon entry"))) + .map(|entry| entry.file_name()) + .filter(|entry| entry.to_str().unwrap_or("").ends_with(".svg")) + .collect(); + + println!("Found {} icons", icon_filenames.len()); + let listener = TcpListener::bind("[::]:7878").unwrap_or_else(|_| fatal("Failed to bind to address")); @@ -19,11 +36,28 @@ fn main() { let mut handlers = handlers::Handlers::new(); handlers.add_handler("/", |req| { - if req.method == "GET" { - Response::new(req, 200, "OK - luciders is running") - } else { - Response::new(req, 405, "Method Not Allowed") + if req.method != "GET" { + return Response::new(req, 405, Body::Static("Method Not Allowed")); } + + Response::new(req, 200, Body::Static("OK - luciders is running")) + }); + + handlers.add_handler("/icons/[icon].png", move |req| { + if req.method != "GET" { + return Response::new(req, 405, Body::Static("Method Not Allowed")); + } + + let icon_name = match req.url.get_path_segment(1) { + Some(icon) => icon, + None => return Response::new(req, 404, Body::Static("Icon Not Found")), + }; + + if !icon_filenames.contains(&OsString::from(&icon_name[..icon_name.len() - 4])) { + return Response::new(req, 404, Body::Static("Icon Not Found")); + } + + return Response::new(req, 200, Body::Static("Icon Not Found")); }); handlers.bind(listener);