Skip to content

Instantly share code, notes, and snippets.

@colbyhall
Last active March 22, 2020 04:56
Show Gist options
  • Save colbyhall/dac667f66bb6e61baa4b88c94ce7be29 to your computer and use it in GitHub Desktop.
Save colbyhall/dac667f66bb6e61baa4b88c94ce7be29 to your computer and use it in GitHub Desktop.
use std::fs;
use std::io::prelude::*;
const EXTENSION: &str = "md";
const HEADER: &str =
"
<!doctype htmls>
<html>
<head>
<meta chaset=\"utf8\">
<title>Test 123</title>
<link rel=\"stylesheet\" href=\"blog.css\">
</head>
<body>
";
const FOOTER: &str =
"
</body>
</html>
";
fn consume_and_count_char_repeated(line: &str, c: char) -> (usize, &str) {
let mut size = 0;
for it in line.chars() {
if it != c {
break;
}
size += it.len_utf8();
}
(size, &line[size..])
}
fn parse_and_output_header(out_file: &mut fs::File, line: &str) {
let orig = line;
let (num_header_tag, line) = consume_and_count_char_repeated(line, '#');
if line.starts_with(' ') {
writeln!(out_file, "<h{}>{}</h{}>", num_header_tag, line.trim_start().trim_end(), num_header_tag).unwrap();
} else {
output_line_as_p(out_file, orig);
}
}
fn parse_and_output_star<'a>(out_file: &mut fs::File, line: &'a str) -> &'a str {
let orig_line = line;
// @NOTE(CHall): count stars at beginning. if there are too many just print out the first and return a slice 1 char ahead
let (start_stars_count, line) = consume_and_count_char_repeated(line, '*');
if start_stars_count > 3 {
write!(out_file, "*").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): try to find end stars. if we can't print out first star and return a slice 1 char ahead
let end_stars_index = line.find('*');
if end_stars_index.is_none() {
// @NOTE(CHall): this isnt the best solution. but it works
write!(out_file, "*").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): count the end chars. if theyre is less of them than stars at the begging
// just print out the first star and return a slace 1 char ahead
let end_stars_index = end_stars_index.unwrap();
let at_end_stars = &line[end_stars_index..];
let (end_stars_count, _) = consume_and_count_char_repeated(at_end_stars, '*');
if end_stars_count < start_stars_count {
write!(out_file, "*").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): Switch on start stars count. not end stars because there could be more than start stars
let between_stars = &line[..end_stars_index];
match start_stars_count {
1 => {
write!(out_file, "<i>").unwrap();
parse_and_output_raw_text(out_file, &between_stars);
write!(out_file, "</i>").unwrap();
},
2 => {
write!(out_file, "<b>").unwrap();
parse_and_output_raw_text(out_file, &between_stars);
write!(out_file, "</b>").unwrap();
},
3 => {
write!(out_file, "<i><b>").unwrap();
parse_and_output_raw_text(out_file, &between_stars);
write!(out_file, "</b></i>").unwrap();
}
_ => panic!("Invalid codepath")
}
&at_end_stars[start_stars_count..]
}
fn parse_and_output_inline_code<'a>(out_file: &mut fs::File, line: &'a str) -> &'a str {
let orig_line = line;
// @TODO(CHall): count tilde at beginning. if over 1 just return a slice 1 char forward
let (start_tilde_count, line) = consume_and_count_char_repeated(line, '`');
if start_tilde_count > 1 {
write!(out_file, "`").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): find end tilde index. if invalid return a slice 1 char forward
let end_tilde_index = line.find('`');
if end_tilde_index.is_none() {
write!(out_file, "`").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): count tilde at end. if over 1 return a slice 1 char forward
let end_tilde_index = end_tilde_index.unwrap();
let at_end_tilde = &line[end_tilde_index..];
write!(out_file, "<span class=\"inline_code\">{}</span>", &line[..end_tilde_index]).unwrap();
&at_end_tilde[1..]
}
fn parse_and_output_inline_link<'a>(out_file: &mut fs::File, line: &'a str) -> &'a str {
assert!(line.starts_with('['));
let orig_line = line;
// @NOTE(CHall): find last bracket. if we couldnt return a slice 1 char forward
let line = &line[1..];
let end_bracket_index = line.find(']');
if end_bracket_index.is_none() {
write!(out_file, "[").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): make sure there is text to put in the hyperlink. if we couldnt return a slice 1 char forward
let end_bracket_index = end_bracket_index.unwrap();
let text = &line[..end_bracket_index];
if text.len() <= 0 {
write!(out_file, "[").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): if we couldnt find a first paren return a slice 1 char forward
let line = &line[end_bracket_index + 1..].trim_start();
if !line.starts_with('(') {
write!(out_file, "[").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): if we couldnt find an end paren return a slice 1 char forward
let line = &line[1..];
let end_paren_index = line.find(')');
if end_paren_index.is_none() {
write!(out_file, "[").unwrap();
return &orig_line[1..];
}
// @NOTE(CHall): if the link is empty return a slice 1 char forward
let end_paren_index = end_paren_index.unwrap();
let link = &line[..end_paren_index];
if link.len() <= 0 {
write!(out_file, "[").unwrap();
return &orig_line[1..];
}
write!(out_file, "<a href=\"{}\">", link).unwrap();
parse_and_output_raw_text(out_file, text);
write!(out_file, "</a>").unwrap();
&line[end_paren_index + 1..]
}
fn parse_and_output_raw_text(out_file: &mut fs::File, line: &str) {
let mut mut_line = line; // @TODO(CHall): Should i just pass in a mut line?
// @NOTE(CHall): run through line char by char. switch on char for special inline voodoo.
// voodoo returns a new str to run through
loop {
let c = mut_line.chars().next();
if c.is_none() { break; }
let c = c.unwrap();
match c {
'*' => mut_line = parse_and_output_star(out_file, mut_line),
'`' => mut_line = parse_and_output_inline_code(out_file, mut_line),
'[' => mut_line = parse_and_output_inline_link(out_file, mut_line),
_ => {
write!(out_file, "{}", c).unwrap();
mut_line = &mut_line[c.len_utf8()..];
}
}
}
}
fn output_line_as_p(out_file: &mut fs::File, line: &str) {
write!(out_file, "<p>").unwrap();
parse_and_output_raw_text(out_file, line);
writeln!(out_file, "</p>").unwrap();
}
fn output_newline(out_file: &mut fs::File) {
writeln!(out_file, "<br>").unwrap();
}
fn parse_and_output_code_block<'a>(out_file: &mut fs::File, rest_of_file: &'a str) -> &'a str{
let line = rest_of_file.lines().next().unwrap();
let (start_tilde_count, rest_of_file) = consume_and_count_char_repeated(rest_of_file, '`');
if start_tilde_count != 3 {
output_line_as_p(out_file, line);
return &rest_of_file[line.len()..];
}
let end_tilde_index = rest_of_file.find('`');
if end_tilde_index.is_none() {
output_line_as_p(out_file, line);
return &rest_of_file[line.len()..];
}
let end_tilde_index = end_tilde_index.unwrap();
let at_end_tilde = &rest_of_file[end_tilde_index..];
let (end_tilde_count, _) = consume_and_count_char_repeated(at_end_tilde, '`');
if end_tilde_count < 3 {
output_line_as_p(out_file, line);
return &rest_of_file[line.len()..];
}
// @TODO(CHall): parse on given code type which is in line
let line = rest_of_file.lines().next().unwrap();
let out_code = &rest_of_file[line.len()..end_tilde_index].trim_start();
writeln!(out_file, "<span class=\"code_block\">{}</span>", out_code).unwrap();
&at_end_tilde[3..]
}
fn main() -> std::io::Result<()> {
let home_dir = fs::read_dir(".")?;
for entry in home_dir {
let entry = entry?;
let file_type = entry.file_type()?;
if file_type.is_file() {
let entry_path = entry.path();
let extension = entry_path.extension();
if extension.is_none() { continue; }
if extension.unwrap() != EXTENSION { continue; }
let loaded_file = fs::read_to_string( entry_path.as_path())?;
let mut out_file_path = entry_path.clone();
out_file_path.set_extension("html");
let mut out_file = fs::File::create(out_file_path.as_path())?;
write!(out_file, "{}", HEADER)?;
let mut mut_loaded_file: &str = &loaded_file;
loop {
let line = mut_loaded_file.lines().next();
if line.is_none() { break; }
let line = line.unwrap();
write!(out_file, " ")?;
if line.len() <= 0 || line.trim().is_empty() {
output_newline(&mut out_file);
continue;
}
match line.chars().next().unwrap() {
'#' => {
parse_and_output_header(&mut out_file, line);
mut_loaded_file = &mut_loaded_file[line.len()..];
mut_loaded_file = &mut_loaded_file.trim_start();
},
'`' => mut_loaded_file = parse_and_output_code_block(&mut out_file, mut_loaded_file),
_ => {
output_line_as_p(&mut out_file, line);
mut_loaded_file = &mut_loaded_file[line.len()..];
mut_loaded_file = &mut_loaded_file.trim_start();
},
}
}
write!(out_file, "{}", FOOTER)?;
}
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment