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