Mercurial > md2html
comparison src/main.rs @ 0:9875208e49a0 tip
First entry
| author | ferencd |
|---|---|
| date | Thu, 16 Feb 2023 15:22:52 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:9875208e49a0 |
|---|---|
| 1 mod list; | |
| 2 mod header; | |
| 3 mod blockquote; | |
| 4 | |
| 5 use base64::{engine::general_purpose, Engine as _}; | |
| 6 | |
| 7 use crate::list::close_opened_lists; | |
| 8 use crate::list::deal_with_list; | |
| 9 use crate::list::insert_list_entry; | |
| 10 | |
| 11 use crate::header::header; | |
| 12 use crate::header::format_header; | |
| 13 use crate::header::handle_as_header; | |
| 14 | |
| 15 use crate::blockquote::deal_with_blockquote; | |
| 16 | |
| 17 use std::{env, process}; | |
| 18 use std::fs::{File}; | |
| 19 use std::io::{self, BufRead, BufReader, Read, Write}; | |
| 20 use std::path::{Path, PathBuf}; | |
| 21 | |
| 22 // The output is wrapped in a Result to allow matching on errors | |
| 23 // Returns an Iterator to the Reader of the lines of the file. | |
| 24 fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> | |
| 25 where P: AsRef<Path>, { | |
| 26 let file = File::open(filename)?; | |
| 27 Ok(io::BufReader::new(file).lines()) | |
| 28 } | |
| 29 | |
| 30 // returns the current working directory | |
| 31 fn get_current_working_dir() -> std::io::Result<PathBuf> { | |
| 32 env::current_dir() | |
| 33 } | |
| 34 | |
| 35 // | |
| 36 // bold_it - whether a bold_italic was started (true) or not (false), goes out, comes in | |
| 37 // bold - whether the bold tag was activated or not | |
| 38 // italic - whether the italic tag was activated or not | |
| 39 fn interpret_line(p :&str, bold_it:&mut bool, bold:&mut bool, italic:&mut bool) -> String | |
| 40 { | |
| 41 let s = p.trim(); | |
| 42 | |
| 43 // first check: is this a header? | |
| 44 let hsize = [6,5,4,3,2,1]; | |
| 45 for hs in hsize { | |
| 46 if header(s, hs) { | |
| 47 return format_header(s.to_string(), hs, 0, bold_it, bold, italic); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 let mut t = s.to_string(); | |
| 52 | |
| 53 handle_tag(&mut t, bold_it, "***", "<strong><em>", "</strong></em>"); | |
| 54 handle_tag(&mut t, bold, "**", "<strong>", "</strong>"); | |
| 55 handle_tag(&mut t, italic, "*", "<em>", "</em>"); | |
| 56 | |
| 57 // image? | |
| 58 let mut imgsp = t.find("!["); | |
| 59 if imgsp != None { | |
| 60 while imgsp != None { | |
| 61 let mut res:String = String::new(); | |
| 62 res = res + &t.as_str()[..imgsp.unwrap()]; | |
| 63 let imgep = t.find("]("); | |
| 64 if imgep != None { | |
| 65 let tag = &t[imgsp.unwrap() + 2..imgep.unwrap()]; | |
| 66 let closep = t.find(")"); | |
| 67 if closep != None { | |
| 68 println!("{} {}", imgep.unwrap() + 2, closep.unwrap()); | |
| 69 let imgpath = &t[imgep.unwrap() + 2..closep.unwrap()]; | |
| 70 // grab the image | |
| 71 let f = File::open(imgpath).expect("Not found image"); | |
| 72 let mut reader = BufReader::new(f); | |
| 73 let mut buffer = Vec::new(); | |
| 74 | |
| 75 // Read file into vector. | |
| 76 reader.read_to_end(&mut buffer).expect("Cannot read file"); | |
| 77 let encoded =general_purpose::STANDARD.encode(&buffer); | |
| 78 res = res + "<img src=\"data:image/png;base64, " + &encoded + "\"" + " alt=\"" + tag + "\">" + &t[closep.unwrap()+1..]; | |
| 79 } else { | |
| 80 println!("Invalid image tag, missing close parenthesis"); | |
| 81 process::exit(0x0100); | |
| 82 } | |
| 83 } else { | |
| 84 println!("Invalid image tag, missing close tag: {}", t); | |
| 85 process::exit(0x0100); | |
| 86 } | |
| 87 imgsp = t.find("!["); | |
| 88 t = res; | |
| 89 imgsp = t.find("!["); | |
| 90 } | |
| 91 } | |
| 92 return t.to_string(); | |
| 93 } | |
| 94 | |
| 95 // handles the tag in the given string | |
| 96 fn handle_tag(t: &mut String, mdtag_flag: &mut bool, mdtag: &str, open_tag: &str, close_tag: &str) { | |
| 97 loop { | |
| 98 if let Some(_s3) = t.find(mdtag) { | |
| 99 if !*mdtag_flag { | |
| 100 *t = t.replacen(mdtag, open_tag, 1); | |
| 101 *mdtag_flag = true; | |
| 102 } else { | |
| 103 *t = t.replacen(mdtag, close_tag, 1); | |
| 104 *mdtag_flag = false; | |
| 105 } | |
| 106 } else { | |
| 107 break; | |
| 108 } | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 // will replace the last occurence of `w` (what) with `ww` (with what) in `s` | |
| 113 fn replace_last(s:String, w:String, ww:String) -> String { | |
| 114 let i = s.rfind(w.as_str()); | |
| 115 if i.is_some() { | |
| 116 let p1 = &s[..i.unwrap()].to_string(); | |
| 117 let p2 = &s[i.unwrap() + w.len()..].to_string(); | |
| 118 let result = p1.to_owned() + &ww + p2; | |
| 119 return result; | |
| 120 } | |
| 121 return s; | |
| 122 } | |
| 123 | |
| 124 // close all the open tags | |
| 125 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) | |
| 126 { | |
| 127 // usually lists end with an empty line | |
| 128 close_opened_lists(&mut html_lines, &mut olist_count, &mut olist_space_count, "</ol>"); | |
| 129 close_opened_lists(&mut html_lines, &mut ulist_count, &mut ulist_space_count, "</ul>"); | |
| 130 html_lines.push("<p>".to_string()); | |
| 131 } | |
| 132 | |
| 133 // | |
| 134 // Main | |
| 135 // | |
| 136 fn main() { | |
| 137 let cwd = get_current_working_dir().unwrap(); | |
| 138 let tmp = cwd.to_str(); | |
| 139 let filename = tmp.unwrap().to_owned() + &"/amalgamated.md".to_string(); | |
| 140 // File hosts must exist in current path before this produces output | |
| 141 if let Ok(lines) = read_lines(filename.clone()) { | |
| 142 // will be written to the html file | |
| 143 let mut html_lines:Vec<String> = Vec::new(); | |
| 144 let mut bold_it:bool = false; // whether the bold_italic tag was activated or not | |
| 145 let mut bold : bool = false; // whether the bold tag was activated or not | |
| 146 let mut italic : bool = false; // whether the italic tag was activated or not | |
| 147 let mut blockquote : i8 = 0; // whether we have started a blockqoute tag or not. Tells us the nesting level of blockquotes | |
| 148 let mut olist_count : i8 = 0; // whether we have an ordered list started, also counts the nested ones | |
| 149 let mut olist_space_count:i8 = 0;// the previous stage of the ordered lists | |
| 150 let mut ulist_count : i8 = 0; // whether we have an ordered list started, also counts the nested ones | |
| 151 let mut ulist_space_count:i8 = 0;// the previous stage of the ordered lists | |
| 152 | |
| 153 // create the html lines | |
| 154 for line in lines { | |
| 155 if let Ok(lcheck) = line { | |
| 156 // taking a copy for further operations | |
| 157 let mut cline:String = lcheck.to_string(); | |
| 158 let tip = cline.clone(); | |
| 159 let orig_ip = cline.clone(); | |
| 160 let trimmed_line = tip.trim(); | |
| 161 | |
| 162 if cline.is_empty() { | |
| 163 close_opened_tags(&mut html_lines, &mut olist_count, &mut olist_space_count, &mut ulist_count, &mut ulist_space_count); | |
| 164 continue; | |
| 165 } | |
| 166 | |
| 167 // is this a blockquote? | |
| 168 deal_with_blockquote(&mut html_lines, &mut blockquote, &mut cline); | |
| 169 | |
| 170 // ordered list checker | |
| 171 let ol_start_checker = |tip: &str, idx: isize | -> bool { tip.chars().nth(idx.try_into().unwrap()).unwrap().is_numeric() }; | |
| 172 deal_with_list(&mut html_lines, &mut olist_count, &mut olist_space_count, &orig_ip, trimmed_line, ol_start_checker, "<ol>", "</ol>", '.'); | |
| 173 | |
| 174 // unordered list checker | |
| 175 let ul_start_checker = |tip: &str, idx: isize | -> bool { | |
| 176 if idx == 2 { | |
| 177 return false; | |
| 178 } | |
| 179 let c = tip.chars().nth(idx.try_into().unwrap()).unwrap(); | |
| 180 match c { | |
| 181 '*'|'-'|'+'=>true, | |
| 182 _=>false | |
| 183 } | |
| 184 }; | |
| 185 deal_with_list(&mut html_lines, &mut ulist_count, &mut ulist_space_count, &orig_ip, trimmed_line, ul_start_checker, "<ul>", "</ul>", ' '); | |
| 186 | |
| 187 // paragraph first check: is this a ===== line, denoting that the previous line was a h1 header | |
| 188 if handle_as_header(&cline, &mut html_lines, 1, &mut bold_it, &mut bold, &mut italic) { | |
| 189 continue; | |
| 190 } | |
| 191 | |
| 192 // paragraph second check: is this a ---- line, denoting that the previous line was a h2 header | |
| 193 if !handle_as_header(&cline, &mut html_lines, 2, &mut bold_it, &mut bold, &mut italic) { | |
| 194 // it was not a header, must be a normal line | |
| 195 let mut inted = interpret_line(cline.as_str(), &mut bold_it, &mut bold, &mut italic) + "\n"; | |
| 196 insert_list_entry(&mut olist_count, trimmed_line, &mut inted, ol_start_checker, '.'); | |
| 197 insert_list_entry(&mut ulist_count, trimmed_line, &mut inted, ul_start_checker,' '); | |
| 198 html_lines.push(inted); | |
| 199 } | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 // closing all the opened tags | |
| 204 close_opened_tags(&mut html_lines, &mut olist_count, &mut olist_space_count, &mut ulist_count, &mut ulist_space_count); | |
| 205 | |
| 206 // joining the lines | |
| 207 let mut full_html_line:String = Default::default(); | |
| 208 for html_line in html_lines { | |
| 209 full_html_line += &html_line; | |
| 210 } | |
| 211 | |
| 212 // did we encountered an unclosed bold_it | |
| 213 if bold_it { | |
| 214 full_html_line = replace_last(full_html_line, "<strong><em>".to_string(), "***".to_string()); | |
| 215 } | |
| 216 if bold { | |
| 217 full_html_line = replace_last(full_html_line, "<strong>".to_string(), "**".to_string()); | |
| 218 } | |
| 219 if italic { | |
| 220 full_html_line = replace_last(full_html_line, "<em>".to_string(), "*".to_string()); | |
| 221 } | |
| 222 | |
| 223 // create a html file | |
| 224 let mut file = File::create("blaa.html").expect("Cannot create file"); | |
| 225 file.write_all(full_html_line.as_bytes()).expect("Cannot write to file"); | |
| 226 } | |
| 227 else { | |
| 228 println!("{} not found", filename.to_string()); | |
| 229 return; | |
| 230 } | |
| 231 } |
