diff --git a/bacon.toml b/bacon.toml index dd06773..ec3d796 100644 --- a/bacon.toml +++ b/bacon.toml @@ -54,7 +54,9 @@ need_stdout = false [jobs.test] command = [ "cargo", "nextest", "run", - "--hide-progress-bar", "--failure-output", "final" + "--hide-progress-bar", + "--failure-output", "final", + "--no-fail-fast" ] need_stdout = true analyzer = "nextest" @@ -62,7 +64,8 @@ analyzer = "nextest" [jobs.nextest] command = [ "cargo", "nextest", "run", - "--hide-progress-bar", "--failure-output", "final" + "--hide-progress-bar", + "--failure-output", "final", ] need_stdout = true analyzer = "nextest" diff --git a/cracked_md/src/ast.rs b/cracked_md/src/ast.rs index f081930..964cd54 100644 --- a/cracked_md/src/ast.rs +++ b/cracked_md/src/ast.rs @@ -1,19 +1,10 @@ -use crate::to_html::ToHtml; +//! Abstract syntax tree of "Markdown". #[derive(Debug, Clone, PartialEq)] pub struct Document { pub blocks: Vec, } -impl ToHtml for Document { - fn to_html(self) -> String { - format!( - "{}", - self.blocks.to_html() - ) - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Block { Paragraph(Vec), @@ -29,24 +20,6 @@ pub enum Block { Quote(Vec), } -impl ToHtml for Block { - fn to_html(self) -> String { - match self { - Self::Paragraph(content) => format!("

{}

", content.to_html()), - Self::Heading { level, content } => { - format!("{}", level, content.to_html(), level) - } - Self::Code { - language: _, - content, - } => { - format!("
{}
", content) - } - _ => todo!(), - } - } -} - #[derive(Debug, Clone, PartialEq)] pub struct ListItem { pub blocks: Vec, @@ -60,167 +33,3 @@ pub enum Inline { Code(String), Link { text: Vec, href: String }, } - -impl ToHtml for Inline { - fn to_html(self) -> String { - match self { - Self::Text(s) => s, - Self::Bold(content) => format!("{}", content.to_html()), - Self::Italic(content) => format!("{}", content.to_html()), - Self::Code(s) => format!("{}", s), - Self::Link { text, href } => format!("{}", href, text.to_html()), - } - } -} - -impl ToHtml for Vec -where - T: ToHtml, -{ - fn to_html(self) -> String { - let mut rendered = String::new(); - for i in self { - rendered.push_str(&i.to_html()); - } - rendered - } -} - -// -------------------- -// TESTS -// -------------------- - -#[cfg(test)] -mod unit_test { - use super::*; - - #[test] - fn single_header() { - let ast = Document { - blocks: vec![Block::Heading { - level: 1, - content: vec![Inline::Text("Heading 1".to_string())], - }], - }; - - let html = ast.to_html(); - - assert_eq!( - html, - "

Heading 1

" - ); - } - - #[test] - fn inline_bold_header() { - let ast = Document { - blocks: vec![Block::Heading { - level: 1, - content: vec![ - Inline::Bold(vec![Inline::Text("Bold".to_string())]), - Inline::Text(" heading 1".to_string()), - ], - }], - }; - - let html = ast.to_html(); - - assert_eq!( - html, - "

Bold heading 1

" - ); - } - - #[test] - fn headings_and_paragraph_nested_code() { - let ast = Document { - blocks: vec![ - Block::Heading { - level: 1, - content: vec![ - Inline::Bold(vec![Inline::Text("Bold".to_string())]), - Inline::Text(" heading 1".to_string()), - ], - }, - Block::Heading { - level: 2, - content: vec![Inline::Text("Heading 2".to_string())], - }, - Block::Paragraph(vec![ - Inline::Text("run ".to_string()), - Inline::Code("sudo rm -rf /".to_string()), - Inline::Text(" on your computer".to_string()), - ]), - ], - }; - - let html = ast.to_html(); - - assert_eq!( - html, - "

Bold heading 1

Heading 2

run sudo rm -rf / on your computer

" - ); - } -} - -#[cfg(test)] -mod convert_md_to_html_test { - use crate::parser::parse; - use crate::to_html::ToHtml; - - #[test] - fn single_header() { - let md = "# Header 1"; - let ast = match parse(md) { - Ok(a) => a, - Err(e) => panic!("{}", e), - }; - let html = ast.to_html(); - - assert_eq!( - html, - "

Header 1

" - ); - } - - #[test] - fn single_header_wrong_format() { - let md = "#Whoops"; - let ast = parse(md); - - assert!(ast.is_err()); - } - - #[test] - fn nested_bold_headers_and_nested_code_paragraph() { - let md = "# *Bold* header 1\n## Header 2\nrun `sudo rm -rf /` on your computer"; - let ast = match parse(md) { - Ok(a) => a, - Err(e) => panic!("{}", e), - }; - let html = ast.to_html(); - - assert_eq!( - html, - "

Bold header 1

Header 2

run sudo rm -rf / on your computer

" - ); - } -} - -#[cfg(test)] -mod parse_real_md { - use std::fs; - - use crate::parser::parse; - - #[test] - fn go() { - let file = "./test.md"; - let md = fs::read_to_string(file).expect("reading ./test.md failed"); - - let _ast = match parse(&md).map_err(|e| e.set_file(file.into())) { - Ok(a) => a, - Err(e) => panic!("{}", e), - }; - } -} diff --git a/cracked_md/src/lib.rs b/cracked_md/src/lib.rs index 7589724..850ee26 100644 --- a/cracked_md/src/lib.rs +++ b/cracked_md/src/lib.rs @@ -1,4 +1,8 @@ +//! A "Markdown" parser and HTML generator. Part of a static site generator `marksmith-rs`. +//! Not following any standards, only vibes. + #![deny(unused_imports)] +#![allow(clippy::needless_pass_by_value)] use fstools::crawl_fs; use parser::parse; @@ -57,6 +61,7 @@ impl MdParseError { } */ + #[must_use] pub fn set_line(self, line: usize) -> Self { Self { file: self.file, @@ -67,6 +72,7 @@ impl MdParseError { } } + #[must_use] pub fn set_file(self, file: PathBuf) -> Self { Self { file: Some(file), @@ -98,7 +104,9 @@ impl std::error::Error for MdParseError {} #[derive(Debug)] pub enum Error { + InDirIsNotDir, OutDirIsNotEmpty, + OutDirIsNotDir, OutDirFileDeleteNotAllowed, OutDirDirectoryInPlaceOfFile, FileRead, @@ -125,7 +133,18 @@ impl std::error::Error for Error {} type Result = std::result::Result; +/// Takes two directories and a force flag as parameters, generates html files to the outdir in the +/// same directory structure as the md files in indir. +/// +/// # Errors +/// Anything wrong with reading files from the directories or parsing the files. pub fn generate(indir: &PathBuf, outdir: &PathBuf, force: bool) -> Result<()> { + if !indir.is_dir() { + Err(Error::InDirIsNotDir)?; + } + if !outdir.is_dir() { + Err(Error::OutDirIsNotDir)?; + } let files = crawl_fs(indir); for path in files { diff --git a/cracked_md/src/parse_trait.rs b/cracked_md/src/parse_trait.rs index 932387f..7eaa15c 100644 --- a/cracked_md/src/parse_trait.rs +++ b/cracked_md/src/parse_trait.rs @@ -65,7 +65,7 @@ pub trait ParsePattern: Iterator + Clone { } */ -pub trait Parse: Iterator { +pub trait Parse: Iterator + Clone { fn follows(&mut self, token: char) -> bool; fn parse_token(&mut self, token: char) -> bool { @@ -77,23 +77,32 @@ pub trait Parse: Iterator { } } - fn parse_str(&mut self, _tokens: &str) -> bool { - todo!() + fn parse_str(&mut self, tokens: &str) -> bool { + let mut cloned = self.clone(); + + for pat_token in tokens.chars() { + if cloned.follows(pat_token) { + cloned.next(); + } else { + return false; + } + } + + *self = cloned; + + true } } impl Parse for std::iter::Peekable> { fn follows(&mut self, token: char) -> bool { - self.peek().map(|c| c == &token).unwrap_or(false) + self.peek().is_some_and(|c| c == &token) } } impl Parse for std::iter::Peekable>> { fn follows(&mut self, token: char) -> bool { - self.peekable() - .peek() - .map(|&(_i, c)| c == token) - .unwrap_or(false) + self.peek().is_some_and(|&(_i, c)| c == token) } } @@ -108,4 +117,60 @@ mod test { assert!(c.follows('a')); assert!(c.follows('a')); } + + #[test] + fn chars_parse_tokens() { + let mut c = "abcdef".chars().peekable(); + + assert!(c.parse_token('a')); + assert!(c.parse_token('b')); + } + + #[test] + fn chars_parse_str() { + let mut c = "abcdef".chars().peekable(); + + assert!(c.parse_str("abc")); + assert!(c.parse_str("def")); + } + + #[test] + fn enumerate_parse_follows_double() { + let mut c = "abc".chars().enumerate().peekable(); + + assert!(c.follows('a')); + assert!(c.follows('a')); + } + + #[test] + fn enumerate_parse_tokens() { + let mut c = "abcdef".chars().enumerate().peekable(); + + assert!(c.parse_token('a')); + assert!(c.parse_token('b')); + } + + #[test] + fn enumerate_parse_str() { + let mut c = "abcdef".chars().enumerate().peekable(); + + assert!(c.parse_str("abc")); + assert!(c.parse_str("def")); + } + + #[test] + fn enumerate_parse_token_failed_not_consume() { + let mut c = "abc".chars().enumerate().peekable(); + + assert!(!c.parse_token('b')); + assert!(c.parse_token('a')); + } + + #[test] + fn enumerate_parse_str_failed_not_consume() { + let mut c = "abcdef".chars().enumerate().peekable(); + + assert!(!c.parse_str("def")); + assert!(c.parse_str("abc")); + } } diff --git a/cracked_md/src/parser.rs b/cracked_md/src/parser.rs index 1a8e0a2..bfeef1f 100644 --- a/cracked_md/src/parser.rs +++ b/cracked_md/src/parser.rs @@ -1,3 +1,5 @@ +//! Parse "Markdown" to AST. + mod block; mod inline; @@ -5,13 +7,15 @@ use block::parse_blocks; use crate::{MdParseError, ast::Document}; +/// Parses the incoming data to a Markdown abstract syntax tree. +/// # Errors +/// This function will return an `MdParseError` when any part of the input is invalid Markdown. pub fn parse(s: &str) -> Result { Ok(Document { blocks: parse_blocks(s)?, }) } -/* #[cfg(test)] mod test { use crate::ast::*; @@ -21,7 +25,7 @@ mod test { fn only_paragraph() { let md = "testing paragraph"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, Document { @@ -36,7 +40,7 @@ mod test { fn different_headers() { let md = "# Header 1\n## Header 2"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, @@ -59,7 +63,7 @@ mod test { fn inline_bold_and_italics() { let md = "some *bold* and _italic_ text"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, @@ -79,7 +83,7 @@ mod test { fn inline_code() { let md = "run command `sudo rm -rf /`"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, @@ -96,7 +100,7 @@ mod test { fn bold_header() { let md = "# Header is *bold*"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, @@ -116,7 +120,7 @@ mod test { fn anonymous_code_block() { let md = "```\necho hello\n```"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, @@ -133,7 +137,7 @@ mod test { fn rust_code_block() { let md = "```rust\nfn main() {\n\tprintln!(\"Hello world!\");\n}\n```"; - let doc = parse(md); + let doc = parse(md).unwrap(); assert_eq!( doc, diff --git a/cracked_md/src/parser/block.rs b/cracked_md/src/parser/block.rs index 410f6b1..d6e000c 100644 --- a/cracked_md/src/parser/block.rs +++ b/cracked_md/src/parser/block.rs @@ -10,6 +10,11 @@ pub fn parse_blocks(input: &str) -> Result, MdParseError> { while let Some((i, line)) = lines.next() { let mut line_chars = line.chars().peekable(); + // empty line + if line_chars.peek().is_none() { + continue; + } + // header let mut heading_level = 0; while line_chars.parse_token('#') { @@ -53,6 +58,7 @@ pub fn parse_blocks(input: &str) -> Result, MdParseError> { }; let mut code = String::new(); + let mut successful = false; for (j, line) in lines.by_ref() { let mut code_line_chars = line.chars().peekable(); // code block end @@ -63,23 +69,31 @@ pub fn parse_blocks(input: &str) -> Result, MdParseError> { language: lang, content: code, }); + successful = true; break; - } else { - Err(MdParseError::from_line( - j + 1, - "```", - format!("```{}", remaining), - ))?; } + Err(MdParseError::from_line( + j + 1, + "```", + format!("```{remaining}"), + ))?; } else { code.push_str(line); code.push('\n'); } } + if successful { + continue; + } Err(MdParseError::from_line(i + 1, "a terminating '```'", ""))?; } // lists TODO + + // paragraph + blocks.push(Block::Paragraph( + parse_inlines(line).map_err(|e| e.set_line(i + 1))?, + )); } Ok(blocks) diff --git a/cracked_md/src/parser/inline.rs b/cracked_md/src/parser/inline.rs index 42a9fd1..543ba55 100644 --- a/cracked_md/src/parser/inline.rs +++ b/cracked_md/src/parser/inline.rs @@ -36,11 +36,12 @@ pub fn parse_inlines(input: &str) -> Result, MdParseError> { _ => { let mut text = String::new(); text.push(c); - while let Some(nc) = chars.next() { + while let Some(&nc) = chars.peek() { if matches!(nc, '*' | '_' | '`' | '[') { break; } - text.push(nc); + let c = chars.next().ok_or(MdParseError::new("a character", ""))?; + text.push(c); } inlines.push(Inline::Text(text)); } @@ -55,7 +56,7 @@ fn collect_until>( end: char, ) -> Result { let mut s = String::new(); - while let Some(c) = chars.next() { + for c in chars.by_ref() { if c == end { return Ok(s); } @@ -63,3 +64,68 @@ fn collect_until>( } Err(MdParseError::new(end, "")) } + +#[cfg(test)] +mod test { + use crate::ast::Inline; + + use super::parse_inlines; + + #[test] + fn bold_text() { + let md = "*abc*"; + let inl = parse_inlines(md).unwrap(); + + assert_eq!( + inl, + vec![Inline::Bold(vec![Inline::Text("abc".to_string())])] + ); + } + + #[test] + fn italic_text() { + let md = "_abc_"; + let inl = parse_inlines(md).unwrap(); + + assert_eq!( + inl, + vec![Inline::Italic(vec![Inline::Text("abc".to_string())])] + ); + } + + #[test] + fn bold_italic_text() { + let md = "*_abc_*"; + let inl = parse_inlines(md).unwrap(); + + assert_eq!( + inl, + vec![Inline::Bold(vec![Inline::Italic(vec![Inline::Text( + "abc".to_string() + )])])] + ); + } + + #[test] + fn code() { + let md = "`sudo rm -rf /`"; + let inl = parse_inlines(md).unwrap(); + + assert_eq!(inl, vec![Inline::Code("sudo rm -rf /".to_string())]); + } + + #[test] + fn text_and_code() { + let md = "run `sudo rm -rf /` on your computer"; + let inl = parse_inlines(md).unwrap(); + + assert_eq!( + inl, + vec![ + Inline::Text("run ".to_string()), + Inline::Code("sudo rm -rf /".to_string()), + Inline::Text(" on your computer".to_string()) + ] + ); + } +} diff --git a/cracked_md/src/to_html.rs b/cracked_md/src/to_html.rs index 41fff3d..b62348d 100644 --- a/cracked_md/src/to_html.rs +++ b/cracked_md/src/to_html.rs @@ -1,3 +1,198 @@ +//! A trait + implementations for generating HTML. + +use crate::ast::{Block, Document, Inline}; + pub trait ToHtml { fn to_html(self) -> String; } + +impl ToHtml for Document { + fn to_html(self) -> String { + format!( + "{}", + self.blocks.to_html() + ) + } +} + +impl ToHtml for Block { + fn to_html(self) -> String { + match self { + Self::Paragraph(content) => format!("

{}

", content.to_html()), + Self::Heading { level, content } => { + format!("{}", level, content.to_html(), level) + } + Self::Code { + language: _, + content, + } => { + format!("
{content}
") + } + _ => todo!(), + } + } +} + +impl ToHtml for Inline { + fn to_html(self) -> String { + match self { + Self::Text(s) => s, + Self::Bold(content) => format!("{}", content.to_html()), + Self::Italic(content) => format!("{}", content.to_html()), + Self::Code(s) => format!("{s}"), + Self::Link { text, href } => format!("{}", href, text.to_html()), + } + } +} + +impl ToHtml for Vec +where + T: ToHtml, +{ + fn to_html(self) -> String { + let mut rendered = String::new(); + for i in self { + rendered.push_str(&i.to_html()); + } + rendered + } +} + +// -------------------- +// TESTS +// -------------------- + +#[cfg(test)] +mod unit_test { + use super::*; + + #[test] + fn single_header() { + let ast = Document { + blocks: vec![Block::Heading { + level: 1, + content: vec![Inline::Text("Heading 1".to_string())], + }], + }; + + let html = ast.to_html(); + + assert_eq!( + html, + "

Heading 1

" + ); + } + + #[test] + fn inline_bold_header() { + let ast = Document { + blocks: vec![Block::Heading { + level: 1, + content: vec![ + Inline::Bold(vec![Inline::Text("Bold".to_string())]), + Inline::Text(" heading 1".to_string()), + ], + }], + }; + + let html = ast.to_html(); + + assert_eq!( + html, + "

Bold heading 1

" + ); + } + + #[test] + fn headings_and_paragraph_nested_code() { + let ast = Document { + blocks: vec![ + Block::Heading { + level: 1, + content: vec![ + Inline::Bold(vec![Inline::Text("Bold".to_string())]), + Inline::Text(" heading 1".to_string()), + ], + }, + Block::Heading { + level: 2, + content: vec![Inline::Text("Heading 2".to_string())], + }, + Block::Paragraph(vec![ + Inline::Text("run ".to_string()), + Inline::Code("sudo rm -rf /".to_string()), + Inline::Text(" on your computer".to_string()), + ]), + ], + }; + + let html = ast.to_html(); + + assert_eq!( + html, + "

Bold heading 1

Heading 2

run sudo rm -rf / on your computer

" + ); + } +} + +#[cfg(test)] +mod convert_md_to_html_test { + use crate::parser::parse; + use crate::to_html::ToHtml; + + #[test] + fn single_header() { + let md = "# Header 1"; + let ast = match parse(md) { + Ok(a) => a, + Err(e) => panic!("{}", e), + }; + let html = ast.to_html(); + + assert_eq!( + html, + "

Header 1

" + ); + } + + #[test] + fn single_header_wrong_format() { + let md = "#Whoops"; + let ast = parse(md); + + assert!(ast.is_err()); + } + + #[test] + fn nested_bold_headers_and_nested_code_paragraph() { + let md = "# *Bold* header 1\n## Header 2\nrun `sudo rm -rf /` on your computer"; + let ast = match parse(md) { + Ok(a) => a, + Err(e) => panic!("{}", e), + }; + let html = ast.to_html(); + + assert_eq!( + html, + "

Bold header 1

Header 2

run sudo rm -rf / on your computer

" + ); + } +} + +#[cfg(test)] +mod parse_real_md { + use std::fs; + + use crate::parser::parse; + + #[test] + fn go() { + let file = "./test.md"; + let md = fs::read_to_string(file).expect("reading ./test.md failed"); + + let _ast = match parse(&md).map_err(|e| e.set_file(file.into())) { + Ok(a) => a, + Err(e) => panic!("{}", e), + }; + } +} diff --git a/cracked_md/test.md b/cracked_md/test.md index 0361040..0ff6334 100644 --- a/cracked_md/test.md +++ b/cracked_md/test.md @@ -1,7 +1,7 @@ # Header *1kkkkkkkkkkkkkkkkkkkkkk* -this is some code: `abc +this is some code: `abc` ```code - oiajwefoijao089uaoisdjfoijasdfoijasdofij +``` diff --git a/fstools/src/lib.rs b/fstools/src/lib.rs index 8b1023d..8271746 100644 --- a/fstools/src/lib.rs +++ b/fstools/src/lib.rs @@ -2,13 +2,14 @@ use std::collections::HashSet; use std::fs; use std::path::PathBuf; -/// Recursively crawl through the directory given and aggregate all file handles to a HashMap with +/// Recursively crawl through the directory given and aggregate all file handles to a `HashMap` with /// their respective (relative) paths as keys. +#[must_use] pub fn crawl_fs(root: &PathBuf) -> HashSet { crawl_fs_rec(root, root) } -/// Helper function: Recursively crawl through the directory given and aggregate all file handles to a HashMap with +/// Helper function: Recursively crawl through the directory given and aggregate all file handles to a `HashMap` with /// their respective (relative) paths as keys. fn crawl_fs_rec(root: &PathBuf, path: &PathBuf) -> HashSet { let mut subdirs = Vec::with_capacity(100); diff --git a/stdsrv/src/args.rs b/stdsrv/src/args.rs index 7481c6f..1ace301 100644 --- a/stdsrv/src/args.rs +++ b/stdsrv/src/args.rs @@ -1,3 +1,5 @@ +//! Simple and program specific command line argument parsing solution. + use std::path::PathBuf; use crate::error::Error; diff --git a/stdsrv/src/fileserver.rs b/stdsrv/src/fileserver.rs index e965f94..bfa4ce6 100644 --- a/stdsrv/src/fileserver.rs +++ b/stdsrv/src/fileserver.rs @@ -1,10 +1,11 @@ -//! FileServer +//! 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 crate::{ - error::*, + error::{Error, ErrorKind, Result}, request::{HttpMethod, HttpRequest}, responder::Responder, response::{HttpResponse, HttpStatus}, @@ -51,7 +52,6 @@ impl Responder for FileServer { let content_type = match req.path.extension() { Some(s) => match s.as_encoded_bytes() { b"html" | b"htm" => "text/html", - b"txt" => "text/plain", b"css" => "text/css", b"js" => "text/javascript", b"pdf" => "application/pdf", diff --git a/stdsrv/src/http_header.rs b/stdsrv/src/http_header.rs index d1c5d74..41f829c 100644 --- a/stdsrv/src/http_header.rs +++ b/stdsrv/src/http_header.rs @@ -1,36 +1,38 @@ +//! Wrapper type of a `HashMap` to contain HTTP headers and format them correctly. + use std::{collections::HashMap, fmt::Display}; #[derive(Debug)] pub struct HttpHeaders { - _inner: HashMap, + inner: HashMap, } impl HttpHeaders { pub fn new() -> Self { HttpHeaders { - _inner: HashMap::new(), + inner: HashMap::new(), } } pub fn add(&mut self, k: &str, v: &str) { - self._inner.insert(k.to_string(), v.to_string()); + self.inner.insert(k.to_string(), v.to_string()); } #[cfg(test)] pub fn get(&self, k: &str) -> Option<&String> { - self._inner.get(k) + self.inner.get(k) } #[cfg(test)] pub fn len(&self) -> usize { - self._inner.len() + self.inner.len() } } impl Display for HttpHeaders { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (k, v) in self._inner.iter() { - write!(f, "{}: {}\r\n", k, v)?; + for (k, v) in &self.inner { + write!(f, "{k}: {v}\r\n")?; } Ok(()) } diff --git a/stdsrv/src/logger.rs b/stdsrv/src/logger.rs index 4ec0b1d..3ec0b95 100644 --- a/stdsrv/src/logger.rs +++ b/stdsrv/src/logger.rs @@ -24,6 +24,9 @@ impl Display for Level { } } +/// A logging macro. Takes a [`Level`] and a formatted string. +/// +/// [`Level`]: ./logger/enum.Level.html #[macro_export] macro_rules! log { ($level:expr, $($arg:tt)*) => {{ diff --git a/stdsrv/src/main.rs b/stdsrv/src/main.rs index 10dc05a..3595a8a 100644 --- a/stdsrv/src/main.rs +++ b/stdsrv/src/main.rs @@ -1,6 +1,4 @@ -//! This is my very monolithic file server with zero dependencies (other than rust -//! stdlib). -//! +//! 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). use std::{ @@ -32,7 +30,7 @@ fn main() -> Result<(), Box> { if args.generate { match generate(&args.indir, &args.outdir, args.force) { - Ok(_) => log!( + Ok(()) => log!( Level::Info, "HTML generation from `{}` to `{}` successful", args.indir.display(), @@ -50,7 +48,7 @@ fn main() -> Result<(), Box> { log!(Level::Error, "HTML generation failed with error: {}", e,); process::exit(1); } - }; + } } let listener = TcpListener::bind(&args.addr)?; @@ -59,7 +57,7 @@ fn main() -> Result<(), Box> { for stream in listener.incoming() { match stream { Ok(stream) => { - let outdir = args.outdir.to_owned(); + let outdir = args.outdir.clone(); std::thread::spawn(move || { log!(Level::Debug, "TcpStream handler spawned"); let mut reader = BufReader::new(&stream); diff --git a/stdsrv/src/request.rs b/stdsrv/src/request.rs index 3be4814..ccb76a5 100644 --- a/stdsrv/src/request.rs +++ b/stdsrv/src/request.rs @@ -32,7 +32,7 @@ impl TryFrom<&str> for HttpMethod { impl Display for HttpMethod { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -154,7 +154,7 @@ impl TryFrom<&str> for HttpRequest { for line in lines { if let Some(v) = line.split_once(": ") { - headers.add(v.0, v.1) + headers.add(v.0, v.1); } } diff --git a/stdsrv/src/responder.rs b/stdsrv/src/responder.rs index b5850ed..dc698de 100644 --- a/stdsrv/src/responder.rs +++ b/stdsrv/src/responder.rs @@ -1,55 +1,12 @@ -//! Traits helping HTTP connections +//! Helper trait(s). use crate::request::HttpRequest; use crate::response::HttpResponse; -/// Responder trait. Just a respond method that turns a HttpRequest to a HttpResponse. +/// Responder trait. Just a respond method that turns a [`HttpRequest`] to a [`HttpResponse`]. +/// +/// [`HttpRequest`]: ../request/struct.HttpRequest.html +/// [`HttpResponse`]: ../response/struct.HttpResponse.html pub trait Responder { fn respond(&self, req: HttpRequest) -> HttpResponse; } - -/* -/// Size trait. Number of bytes when encoded. -pub trait Size { - fn size(&self) -> usize; -} - -// Standard implementations for Size trait - -impl Size for u8 { - fn size(&self) -> usize { - 1 - } -} - -impl Size for Vec -where - T: Size, -{ - fn size(&self) -> usize { - if let Some(elem) = self.first() { - elem.size() * self.len() - } else { - 0 - } - } -} - -impl Size for Option -where - T: Size, -{ - fn size(&self) -> usize { - match self { - Some(t) => t.size(), - None => 0, - } - } -} - -impl Size for String { - fn size(&self) -> usize { - self.len() - } -} -*/ diff --git a/stdsrv/src/response.rs b/stdsrv/src/response.rs index 9b4ed24..4abfa74 100644 --- a/stdsrv/src/response.rs +++ b/stdsrv/src/response.rs @@ -146,7 +146,7 @@ impl Display for HttpResponse { write!( f, "{}\r\n", - String::from_utf8(s.to_vec()).unwrap_or("".to_string()) + String::from_utf8(s.clone()).unwrap_or("".to_string()) )?; } Ok(())