view src/main.rs @ 0:9875208e49a0 tip

First entry
author ferencd
date Thu, 16 Feb 2023 15:22:52 +0100
parents
children
line wrap: on
line source
mod list;
mod header;
mod blockquote;

use base64::{engine::general_purpose, Engine as _};

use crate::list::close_opened_lists;
use crate::list::deal_with_list;
use crate::list::insert_list_entry;

use crate::header::header;
use crate::header::format_header;
use crate::header::handle_as_header;

use crate::blockquote::deal_with_blockquote;

use std::{env, process};
use std::fs::{File};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Path, PathBuf};

// The output is wrapped in a Result to allow matching on errors
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

// returns the current working directory
fn get_current_working_dir() -> std::io::Result<PathBuf> {
    env::current_dir()
}

//
// bold_it - whether a bold_italic was started (true) or not (false), goes out, comes in
// bold    - whether the bold tag was activated or not
// italic  - whether the italic tag was activated or not
fn interpret_line(p :&str, bold_it:&mut bool, bold:&mut bool, italic:&mut bool) -> String
{
    let s = p.trim();

    // first check: is this a header?
    let hsize = [6,5,4,3,2,1];
    for hs in hsize {
        if header(s, hs) {
            return format_header(s.to_string(), hs, 0, bold_it, bold, italic);
        }
    }

    let mut t = s.to_string();

    handle_tag(&mut t, bold_it, "***", "<strong><em>", "</strong></em>");
    handle_tag(&mut t, bold, "**", "<strong>", "</strong>");
    handle_tag(&mut t, italic, "*", "<em>", "</em>");

    // image?
    let mut imgsp = t.find("![");
    if imgsp != None {
        while imgsp != None {
            let mut res:String = String::new();
            res = res + &t.as_str()[..imgsp.unwrap()];
            let imgep = t.find("](");
            if imgep != None {
                let tag = &t[imgsp.unwrap() + 2..imgep.unwrap()];
                let closep = t.find(")");
                if closep != None {
                    println!("{} {}", imgep.unwrap() + 2, closep.unwrap());
                    let imgpath = &t[imgep.unwrap() + 2..closep.unwrap()];
                    // grab the image
                    let f = File::open(imgpath).expect("Not found image");
                    let mut reader = BufReader::new(f);
                    let mut buffer = Vec::new();

                    // Read file into vector.
                    reader.read_to_end(&mut buffer).expect("Cannot read file");
                    let encoded =general_purpose::STANDARD.encode(&buffer);
                    res = res + "<img src=\"data:image/png;base64, " + &encoded + "\"" + " alt=\"" + tag + "\">" + &t[closep.unwrap()+1..];
                } else {
                    println!("Invalid image tag, missing close parenthesis");
                    process::exit(0x0100);
                }
            } else {
                println!("Invalid image tag, missing close tag: {}", t);
                process::exit(0x0100);
            }
            imgsp = t.find("![");
            t = res;
            imgsp = t.find("![");
        }
    }
    return t.to_string();
}

// handles the tag in the given string
fn handle_tag(t: &mut String, mdtag_flag: &mut bool, mdtag: &str, open_tag: &str, close_tag: &str) {
    loop {
        if let Some(_s3) = t.find(mdtag) {
            if !*mdtag_flag {
                *t = t.replacen(mdtag, open_tag, 1);
                *mdtag_flag = true;
            } else {
                *t = t.replacen(mdtag, close_tag, 1);
                *mdtag_flag = false;
            }
        } else {
            break;
        }
    }
}

// will replace the last occurence of `w` (what) with `ww` (with what) in `s`
fn replace_last(s:String, w:String, ww:String) -> String {
    let i = s.rfind(w.as_str());
    if i.is_some() {
        let p1 = &s[..i.unwrap()].to_string();
        let p2 = &s[i.unwrap() + w.len()..].to_string();
        let result = p1.to_owned() + &ww + p2;
        return result;
    }
    return s;
}

// close all the open tags
fn close_opened_tags(mut html_lines: &mut Vec<String>, mut olist_count: &mut i8, mut olist_space_count: &mut i8, mut ulist_count: &mut i8, mut ulist_space_count: &mut i8)
{
    // usually lists end with an empty line
    close_opened_lists(&mut html_lines, &mut olist_count, &mut olist_space_count, "</ol>");
    close_opened_lists(&mut html_lines, &mut ulist_count, &mut ulist_space_count, "</ul>");
    html_lines.push("<p>".to_string());
}

//
// Main
//
fn main() {
    let cwd = get_current_working_dir().unwrap();
    let tmp = cwd.to_str();
    let filename = tmp.unwrap().to_owned() + &"/amalgamated.md".to_string();
    // File hosts must exist in current path before this produces output
    if let Ok(lines) = read_lines(filename.clone()) {
        // will be written to the html file
        let mut html_lines:Vec<String> = Vec::new();
        let mut bold_it:bool = false;    // whether the bold_italic tag was activated or not
        let mut bold : bool = false;     // whether the bold tag was activated or not
        let mut italic : bool = false;   // whether the italic tag was activated or not
        let mut blockquote : i8 = 0;     // whether we have started a blockqoute tag or not. Tells us the nesting level of blockquotes
        let mut olist_count : i8 = 0;    // whether we have an ordered list started, also counts the nested ones
        let mut olist_space_count:i8 = 0;// the previous stage of the ordered lists
        let mut ulist_count : i8 = 0;    // whether we have an ordered list started, also counts the nested ones
        let mut ulist_space_count:i8 = 0;// the previous stage of the ordered lists

        // create the html lines
        for line in lines {
            if let Ok(lcheck) = line {
                // taking a copy for further operations
                let mut cline:String = lcheck.to_string();
                let tip = cline.clone();
                let orig_ip = cline.clone();
                let trimmed_line = tip.trim();

                if cline.is_empty() {
                    close_opened_tags(&mut html_lines, &mut olist_count, &mut olist_space_count, &mut ulist_count, &mut ulist_space_count);
                    continue;
                }

                // is this a blockquote?
                deal_with_blockquote(&mut html_lines, &mut blockquote, &mut cline);

                // ordered list checker
                let ol_start_checker = |tip: &str, idx: isize | -> bool { tip.chars().nth(idx.try_into().unwrap()).unwrap().is_numeric() };
                deal_with_list(&mut html_lines, &mut olist_count, &mut olist_space_count, &orig_ip, trimmed_line, ol_start_checker, "<ol>", "</ol>", '.');

                // unordered list checker
                let ul_start_checker = |tip: &str, idx: isize | -> bool {
                    if idx == 2 {
                        return false;
                    }
                    let c = tip.chars().nth(idx.try_into().unwrap()).unwrap();
                    match c {
                        '*'|'-'|'+'=>true,
                        _=>false
                    }
                };
                deal_with_list(&mut html_lines, &mut ulist_count, &mut ulist_space_count, &orig_ip, trimmed_line, ul_start_checker, "<ul>", "</ul>", ' ');

                // paragraph first check: is this a ===== line, denoting that the previous line was a h1 header
                if handle_as_header(&cline, &mut html_lines, 1, &mut bold_it, &mut bold, &mut italic) {
                    continue;
                }

                // paragraph second check: is this a ---- line, denoting that the previous line was a h2 header
                if !handle_as_header(&cline, &mut html_lines, 2, &mut bold_it, &mut bold, &mut italic) {
                    // it was not a header, must be a normal line
                    let mut inted = interpret_line(cline.as_str(), &mut bold_it, &mut bold, &mut italic) + "\n";
                    insert_list_entry(&mut olist_count, trimmed_line, &mut inted, ol_start_checker, '.');
                    insert_list_entry(&mut ulist_count, trimmed_line, &mut inted, ul_start_checker,' ');
                    html_lines.push(inted);
                }
            }
        }

        // closing all the opened tags
        close_opened_tags(&mut html_lines, &mut olist_count, &mut olist_space_count, &mut ulist_count, &mut ulist_space_count);

        // joining the lines
        let mut full_html_line:String = Default::default();
        for html_line in html_lines {
            full_html_line += &html_line;
        }

        // did we encountered an unclosed bold_it
        if bold_it {
            full_html_line = replace_last(full_html_line, "<strong><em>".to_string(), "***".to_string());
        }
        if bold {
            full_html_line = replace_last(full_html_line, "<strong>".to_string(), "**".to_string());
        }
        if italic {
            full_html_line = replace_last(full_html_line, "<em>".to_string(), "*".to_string());
        }

        // create a html file
        let mut file =  File::create("blaa.html").expect("Cannot create file");
        file.write_all(full_html_line.as_bytes()).expect("Cannot write to file");
    }
    else {
        println!("{} not found", filename.to_string());
        return;
    }
}