stanhope/
write_webmenu.rs

1//! *Write a single HTML file that serves as a Web Menu for all processes in a Process Library*
2//! 
3//! The purpose of this module is to write a nice HTML file that lists useful information about all processes in a library. Carl Sagan: "If you want to make an apple pie from scratch, you must first invent the universe."
4//! * What processes are in the library?
5//! * What is the useful information?
6//! 
7//! All information from all processes in a Process Library (user's flat list of folders) is stored in the *Library* struct, which essentially houses a vector of [crate::read_ebml::Process] structs. This module defines the *Library* struct, creates one, and then reads it to create "useful" HTML.
8//! 
9//! The Library struct is public, but its associated fields are private. "Get" and "Set"/"Add" functions exist to safely read/write Library fields.
10use crate::read_ebml::read_ebml;
11use crate::read_ebml::Process;
12use crate::write_ebml::get_process_folder_name;
13
14use std::fs::File;
15use std::path::{PathBuf,Path};
16use std::fs::{self,OpenOptions,metadata};
17use std::io::{self,Write};
18use std::env;
19
20//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
21// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
22// ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
23// ▐░▌               ▐░▌     ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌               ▐░▌     
24// ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌▐░▌               ▐░▌     
25// ▐░░░░░░░░░░░▌     ▐░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌               ▐░▌     
26//  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌       ▐░▌▐░▌               ▐░▌     
27//           ▐░▌     ▐░▌     ▐░▌     ▐░▌  ▐░▌       ▐░▌▐░▌               ▐░▌     
28//  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
29// ▐░░░░░░░░░░░▌     ▐░▌     ▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
30//  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
31                                                                              
32/// **Top-level struct containing all information read from an entire Process Library**
33/// 
34/// All fields are private, but are accessible with "get" functions, e.g. Library.get_name() returns a reference to the Library's name (&String).
35pub struct Library {
36
37	/// The name of the process library, e.g. "Process Library"
38	name: String,
39
40	/// A vector of [crate::read_ebml::Process] structs, each containing all information about its associated EBML file.
41	all_processes: Vec<Process>,
42}
43
44//  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄        ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
45// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░▌      ▐░▌▐░░░░░░░░░░░▌
46// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌░▌     ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
47// ▐░▌          ▐░▌       ▐░▌▐░▌▐░▌    ▐░▌▐░▌          
48// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌ ▐░▌   ▐░▌▐░▌          
49// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌▐░▌          
50// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌   ▐░▌ ▐░▌▐░▌          
51// ▐░▌          ▐░▌       ▐░▌▐░▌    ▐░▌▐░▌▐░▌          
52// ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄▄▄ 
53// ▐░▌          ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌
54//  ▀            ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀ 
55
56// <!-- Modified from: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0e9857bd7f29eab065dfe52313f98af7
57
58/// Helper function that looks at the name of every directory that shares a parent with the stanhope application binary.
59/// 
60/// This function calls itself recursively, adding elements to the input "vec" if they are folders (directories), and the name of the folder isn't "assets"
61fn _list_files(vec: &mut Vec<PathBuf>, path: &Path) -> io::Result<()> {
62    if metadata(&path)?.is_dir() {
63        let paths = fs::read_dir(&path)?;
64        for path_result in paths {
65            let full_path = path_result?.path();
66            if metadata(&full_path)?.is_dir() {
67            	let path_str = &full_path.as_os_str();
68            	match path_str.to_str() {
69        			Some("./assets") => (),
70        			Some(".\\assets") => (),
71        			Some("./assets-old") => (),
72        			Some(".\\assets-old") => (),
73        			Some("./assets-organizations") => (),
74        			Some(".\\assets-organizations") => (),
75        			Some("./assets_old") => (),
76        			Some("./assets_organizations") => (),
77        			Some(".\\assets_old") => (),
78        			Some(".\\assets_organizations") => (),
79        			_ => vec.push(full_path),
80        		}
81            } 
82        }
83    }
84    Ok(())
85}
86
87/// Wrapper function that calls the recursive helper to get the list of non-"assets" folders that exist next to the stanhope binary.
88fn list_files(path: &Path) -> io::Result<Vec<PathBuf>> {
89    let mut vec = Vec::new();
90    _list_files(&mut vec,&path)?;
91    Ok(vec)
92}
93
94// -->
95
96/// Returns the name of the library, which... we define as the folder name of the directory that contains the stanhope binary and all process folders.
97fn find_name_of_library() -> String {
98	let exe = env::current_exe().expect("Naw1");
99	exe.parent().expect("NawX").file_name().expect("Naw1").to_str().expect("Naw2").to_string()
100}
101
102/// Wrapper function that calls the wrapper function.
103pub fn find_all_process_folders(dir:&str) -> Vec<PathBuf> {
104    list_files(Path::new(dir)).expect("that's not a good path?")
105}
106
107/// The only public function, because the only purpose of this module is to write an HTML file!
108/// 
109/// Many "compose" functions are called to somewhat modularize and break up the really ugly autocoding here. This one might be even worse than [crate::write_html::generate_complete_html]!
110/// 
111/// No input arguments are supplied, because the location and filename of the Web Menu HTML file are assumed a priori:
112/// * location is the Process Library root (next to stanhope binary)
113/// * filename is "WebMenu.html"
114pub fn generate_complete_webmenu(verbose:&bool) {
115
116	// Open the filename input to write HTML out to that file
117	if *verbose { print!("\n[A] Attempting to edit WebMenu.html..."); }
118    let mut html_file = OpenOptions::new()
119        .read(true)
120        .write(true)
121        .create(true)
122        .truncate(true)
123        .open("./WebMenu.html")
124        .expect("cannot open file");
125    if *verbose { print!("success! WebMenu.html is open for editing.\n"); }
126
127    if *verbose { print!("[B] Attempting to create new Library struct..."); }
128    let mut new_library = Library::new();
129    if *verbose { print!("success! New Library struct created.\n"); }
130
131    if *verbose { print!("[C] Attempting to change the name of the Library to this folder's name...")}
132    new_library.set_name(&find_name_of_library().as_str());
133    if *verbose { print!("success! New name: {}\n",new_library.get_name()); }
134
135    if *verbose { print!("[D] Attempting list of all folders (excluding 'assets')..."); }
136    let list_of_process_folders = find_all_process_folders(".");
137    if *verbose { for folder in &list_of_process_folders { println!("{}",folder.file_name().expect("No bueno?").to_str().expect("Really bueno?").to_string());} }
138    if *verbose { print!("success! Folders found, now look for files to process:\n"); }
139    for folder in list_of_process_folders {
140    	let folder_string = folder.file_name().expect("No bueno?").to_str().expect("Really bueno?").to_string();
141    	if *verbose { print!("\t{}",&folder_string); }
142    	let new_ebml_str = folder_string.clone() + "/" + &folder_string + ".ebml";
143    	if *verbose { print!("\n\t\t-> {}",&new_ebml_str); }
144    	let new_process = read_ebml(&("./".to_owned()+&new_ebml_str));
145    	if *verbose { print!("\n\t\t-> processed "); }
146    	new_library.add_process(new_process);
147    	if *verbose { print!("\n\t\t-> added to Library\n"); }
148    }
149
150    if *verbose { print!("[E] Attempting to write the top of the HTML file..."); }
151    compose_top_of_webmenu(&mut html_file);
152    if *verbose { print!("success!\n"); }
153
154    if *verbose { print!("[F] Attempting to write the head of the HTML file, including all CSS and Javascript..."); }
155    compose_head_of_webmenu(&mut html_file,&new_library);
156    if *verbose { print!("success!\n"); }
157
158    if *verbose { print!("[G] Attempting to write the body of the HTML file, including everything that appears on screen..."); }
159	compose_body_of_webmenu(&mut html_file,&new_library);
160    if *verbose { print!("success!\n"); }
161
162
163	if *verbose { print!("[H] Attempting to write the bottom of the HTML file..."); }
164	compose_bottom_of_webmenu(&mut html_file);
165    if *verbose { print!("success!\n"); }
166
167    if *verbose { print!("\n\n"); new_library.display(); print!("\n\n"); }
168}
169
170/// Simple: doctype declaration and opening of the HTML tag.
171fn compose_top_of_webmenu(f:&mut File) {
172	f.write(b"<!DOCTYPE html>\n").expect("write failed");
173	f.write(b"<html lang=\"en-US\">\n").expect("write failed");
174}
175
176/// All "head" metadata, including:
177/// * embedded CSS, because this file is going to (almost) stand alone
178/// * embedded JavaScript to sort tables, because we don't want this to depend on the internet, or break if external files move
179/// 
180/// Note: some needed images referenced here and in the body are in the "assets" folder...
181fn compose_head_of_webmenu(f:&mut File, lib:&Library) {
182	f.write(b"\t<head>\n").expect("write failed");
183	f.write(b"\t\t<meta charset=\"utf-8\" />\n").expect("write failed");
184	f.write(b"\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=1\" />\n").expect("write failed");
185	f.write(b"\t\t<style>\n").expect("write failed");
186	f.write(b"\t\t\t
187			@media print { .no-print, .no-print * { display: none !important; }  br { display:none !important; } table { font-size: 0.5em; } }
188			body { font-family: sans-serif; padding: 0px; margin: 0px;}
189			#pre-header { padding: 15px; border: 15px solid #BA6F47; background-color: #BEA465; color: #2F495A; text-align: center; font-size: 0.7em; }
190			header { z-index: 100; border-bottom: 3px solid black; background-color: #2F495A; color: white; position: sticky; top:0px;}
191			header, main { padding: 20px; margin:0px;}
192			table { margin-top: 20px; }
193
194			input + span { margin: 0px 5px 0px 5px; }
195
196			.doc-box { display:inline-block; padding: 1px; border: 1px solid black; margin: 2px; width: 25px; }
197			.doc-icon { width: 25px; height: 25px; overflow: hidden; position: relative; }
198			.doc-format img { position: absolute; height: 25px; left: 0px; }
199
200			input { z-index: 0; margin: 0px 5px 0px 5px; transform: scale(1.3);}
201
202			.doc-box { display:inline-block; padding: 1px; border: 1px solid black; margin: 2px; width: 25px; }
203			.doc-icon { width: 25px; height: 25px; overflow: hidden; position: relative; }
204			.doc-format img { position: absolute; height: 25px; left: 0px; }
205
206			.toggle-column-01:not(:checked) ~ table th:nth-child(1) { display: none; }
207			.toggle-column-01:not(:checked) ~ table td:nth-child(1) { display: none; }
208
209			.toggle-column-02:not(:checked) ~ table th:nth-child(2) { display: none; }
210			.toggle-column-02:not(:checked) ~ table td:nth-child(2) { display: none; }
211
212			.toggle-column-03:not(:checked) ~ table th:nth-child(3) { display: none; }
213			.toggle-column-03:not(:checked) ~ table td:nth-child(3) { display: none; }
214
215			.toggle-column-04:not(:checked) ~ table th:nth-child(4) { display: none; }
216			.toggle-column-04:not(:checked) ~ table td:nth-child(4) { display: none; }
217
218			.toggle-column-05:not(:checked) ~ table th:nth-child(5) { display: none; }
219			.toggle-column-05:not(:checked) ~ table td:nth-child(5) { display: none; }
220
221			.toggle-column-06:not(:checked) ~ table th:nth-child(6) { display: none; }
222			.toggle-column-06:not(:checked) ~ table td:nth-child(6) { display: none; }
223
224			.toggle-column-07:not(:checked) ~ table th:nth-child(7) { display: none; }
225			.toggle-column-07:not(:checked) ~ table td:nth-child(7) { display: none; }
226
227			.toggle-column-08:not(:checked) ~ table th:nth-child(8) { display: none; }
228			.toggle-column-08:not(:checked) ~ table td:nth-child(8) { display: none; }
229
230			.toggle-column-09:not(:checked) ~ table th:nth-child(9) { display: none; }
231			.toggle-column-09:not(:checked) ~ table td:nth-child(9) { display: none; }
232
233			.toggle-column-10:not(:checked) ~ table th:nth-child(10) { display: none; }
234			.toggle-column-10:not(:checked) ~ table td:nth-child(10) { display: none; }
235
236			.toggle-column-11:not(:checked) ~ table th:nth-child(11) { display: none; }
237			.toggle-column-11:not(:checked) ~ table td:nth-child(11) { display: none; }
238
239			.toggle-column-12:not(:checked) ~ table th:nth-child(12) { display: none; }
240			.toggle-column-12:not(:checked) ~ table td:nth-child(12) { display: none; }
241
242			.toggle-column-13:not(:checked) ~ table th:nth-child(13) { display: none; }
243			.toggle-column-13:not(:checked) ~ table td:nth-child(13) { display: none; }
244
245			.toggle-column-14:not(:checked) ~ table th:nth-child(14) { display: none; }
246			.toggle-column-14:not(:checked) ~ table td:nth-child(14) { display: none; }
247
248			.toggle-column-15:not(:checked) ~ table th:nth-child(15) { display: none; }
249			.toggle-column-15:not(:checked) ~ table td:nth-child(15) { display: none; }
250
251			.toggle-column-16:not(:checked) ~ table th:nth-child(16) { display: none; }
252			.toggle-column-16:not(:checked) ~ table td:nth-child(16) { display: none; }
253
254			.toggle-column-17:not(:checked) ~ table th:nth-child(17) { display: none; }
255			.toggle-column-17:not(:checked) ~ table td:nth-child(17) { display: none; }
256
257			.toggle-column-18:not(:checked) ~ table th:nth-child(18) { display: none; }
258			.toggle-column-18:not(:checked) ~ table td:nth-child(18) { display: none; }
259
260			.toggle-column-19:not(:checked) ~ table th:nth-child(19) { display: none; }
261			.toggle-column-19:not(:checked) ~ table td:nth-child(19) { display: none; }
262
263			.toggle-column-20:not(:checked) ~ table th:nth-child(20) { display: none; }
264			.toggle-column-20:not(:checked) ~ table td:nth-child(20) { display: none; }
265
266			.toggle-column-21:not(:checked) ~ table th:nth-child(21) { display: none; }
267			.toggle-column-21:not(:checked) ~ table td:nth-child(21) { display: none; }
268
269			.toggle-column-22:not(:checked) ~ table th:nth-child(22) { display: none; }
270			.toggle-column-22:not(:checked) ~ table td:nth-child(22) { display: none; }
271
272			.toggle-column-23:not(:checked) ~ table th:nth-child(23) { display: none; }
273			.toggle-column-23:not(:checked) ~ table td:nth-child(23) { display: none; }
274
275			.toggle-column-24:not(:checked) ~ table th:nth-child(24) { display: none; }
276			.toggle-column-24:not(:checked) ~ table td:nth-child(24) { display: none; }
277
278			.toggle-column-25:not(:checked) ~ table th:nth-child(25) { display: none; }
279			.toggle-column-25:not(:checked) ~ table td:nth-child(25) { display: none; }
280
281			.toggle-column-26:not(:checked) ~ table th:nth-child(26) { display: none; }
282			.toggle-column-26:not(:checked) ~ table td:nth-child(26) { display: none; }
283
284			.toggle-column-27:not(:checked) ~ table th:nth-child(27) { display: none; }
285			.toggle-column-27:not(:checked) ~ table td:nth-child(27) { display: none; }
286
287			.toggle-column-28:not(:checked) ~ table th:nth-child(28) { display: none; }
288			.toggle-column-28:not(:checked) ~ table td:nth-child(28) { display: none; }
289
290			.toggle-column-29:not(:checked) ~ table th:nth-child(29) { display: none; }
291			.toggle-column-29:not(:checked) ~ table td:nth-child(29) { display: none; }
292	\n").expect("write failed");
293	f.write(b"\t\t\ttable { border-collapse: collapse; }
294	\t\tth, td { padding: 0.4em; border: 1px solid black;}
295	\t\tth { background-color: black; color: white; }
296	\t\tth[role=columnheader]:not(.no-sort) { cursor: pointer; }
297	\t\tth[role=columnheader]:not(.no-sort):after {
298	\t\t	content: '';
299	\t\t	float: right;
300	\t\t	margin-top: 7px;
301	\t\t  	margin-left: 5px;
302	\t\t	border-width: 0 4px 4px;
303	\t\t	border-style: solid;
304	\t\t	border-color: white transparent;
305	\t\t	visibility: hidden;
306	\t\t	opacity: 0;
307	\t\t	-ms-user-select: none;
308	\t\t	-webkit-user-select: none;
309	\t\t	-moz-user-select: none;
310	\t\t	user-select: none;
311	\t\t}
312	\t\tth[aria-sort=ascending]:not(.no-sort):after { border-bottom: none; border-width: 4px 4px 0; }
313	\t\tth[aria-sort]:not(.no-sort):after {	visibility: visible; opacity: 0.4; }
314	\t\tth[role=columnheader]:not(.no-sort):hover:after { visibility: visible; opacity: 1; }\n").expect("write failed");
315	f.write(b"\t\t</style>\n").expect("write failed");
316	
317	// This is the actual sorting javascript
318	f.write(b"\t\t<script>\n").expect("write failed");
319	f.write(b"\t\t\t!function(){function a(b,c){if(!(this instanceof a))return new a(b,c);if(!b||\"TABLE\"!==b.tagName)throw new Error(\"Element must be a table\");this.init(b,c||{})}var b=[],c=function(a){var b;return window.CustomEvent&&\"function\"==typeof window.CustomEvent?b=new CustomEvent(a):(b=document.createEvent(\"CustomEvent\"),b.initCustomEvent(a,!1,!1,void 0)),b},d=function(a){return a.getAttribute(\"data-sort\")||a.textContent||a.innerText||\"\"},e=function(a,b){return a=a.trim().toLowerCase(),b=b.trim().toLowerCase(),a===b?0:a<b?1:-1},f=function(a,b){return function(c,d){var e=a(c.td,d.td);return 0===e?b?d.index-c.index:c.index-d.index:e}};a.extend=function(a,c,d){if(\"function\"!=typeof c||\"function\"!=typeof d)throw new Error(\"Pattern and sort must be a function\");b.push({name:a,pattern:c,sort:d})},a.prototype={init:function(a,b){var c,d,e,f,g=this;if(g.table=a,g.thead=!1,g.options=b,a.rows&&a.rows.length>0)if(a.tHead&&a.tHead.rows.length>0){for(e=0;e<a.tHead.rows.length;e++)if(\"thead\"===a.tHead.rows[e].getAttribute(\"data-sort-method\")){c=a.tHead.rows[e];break}c||(c=a.tHead.rows[a.tHead.rows.length-1]),g.thead=!0}else c=a.rows[0];if(c){var h=function(){g.current&&g.current!==this&&g.current.removeAttribute(\"aria-sort\"),g.current=this,g.sortTable(this)};for(e=0;e<c.cells.length;e++)f=c.cells[e],f.setAttribute(\"role\",\"columnheader\"),\"none\"!==f.getAttribute(\"data-sort-method\")&&(f.tabindex=0,f.addEventListener(\"click\",h,!1),null!==f.getAttribute(\"data-sort-default\")&&(d=f));d&&(g.current=d,g.sortTable(d))}},sortTable:function(a,g){var h=this,i=a.cellIndex,j=e,k=\"\",l=[],m=h.thead?0:1,n=a.getAttribute(\"data-sort-method\"),o=a.getAttribute(\"aria-sort\");if(h.table.dispatchEvent(c(\"beforeSort\")),g||(o=\"ascending\"===o?\"descending\":\"descending\"===o?\"ascending\":h.options.descending?\"descending\":\"ascending\",a.setAttribute(\"aria-sort\",o)),!(h.table.rows.length<2)){if(!n){for(;l.length<3&&m<h.table.tBodies[0].rows.length;)k=d(h.table.tBodies[0].rows[m].cells[i]),k=k.trim(),k.length>0&&l.push(k),m++;if(!l)return}for(m=0;m<b.length;m++)if(k=b[m],n){if(k.name===n){j=k.sort;break}}else if(l.every(k.pattern)){j=k.sort;break}for(h.col=i,m=0;m<h.table.tBodies.length;m++){var p,q=[],r={},s=0,t=0;if(!(h.table.tBodies[m].rows.length<2)){for(p=0;p<h.table.tBodies[m].rows.length;p++)k=h.table.tBodies[m].rows[p],\"none\"===k.getAttribute(\"data-sort-method\")?r[s]=k:q.push({tr:k,td:d(k.cells[h.col]),index:s}),s++;for(\"descending\"===o?q.sort(f(j,!0)):(q.sort(f(j,!1)),q.reverse()),p=0;p<s;p++)r[p]?(k=r[p],t++):k=q[p-t].tr,h.table.tBodies[m].appendChild(k)}}h.table.dispatchEvent(c(\"afterSort\"))}},refresh:function(){void 0!==this.current&&this.sortTable(this.current,!0)}},\"undefined\"!=typeof module&&module.exports?module.exports=a:window.Tablesort=a}();\n").expect("write failed");
320	f.write(b"\t\t</script>\n").expect("write failed");
321
322	// This puts the sort function where it needs to go
323	f.write(b"\t\t<script>\n").expect("write failed");
324	f.write(b"\t\t\tfunction onPageReady() {\n").expect("write failed");
325	f.write(b"\t\t\t\tnew Tablesort(document.getElementById('library'));\n").expect("write failed");
326	f.write(b"\t\t\t}\n").expect("write failed");
327	f.write(b"\t\t\tdocument.addEventListener('DOMContentLoaded', onPageReady, false);\n").expect("write failed");
328	f.write(b"\t\t</script>\n").expect("write failed");
329
330	f.write(b"\t\t<link rel=\"shortcut icon\" sizes=\"16x16 32x32 48x48\" type=\"image/png\" href=\"./assets/favicon.png\" />\n").expect("write failed");
331	f.write(b"\t\t<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"./assets/favicon.png\" />\n").expect("write failed");
332	f.write(b"\t\t<link rel=\"mask-icon\" href=\"./assets/favicon.svg\" color=\"#000000\" />\n").expect("write failed");
333	let temp_str = String::from("\t\t<title>&#x1F4DA; ") + &lib.get_name() + " &#x1F4DA;</title>\n";
334    f.write(temp_str.as_bytes()).expect("write failed");
335	f.write(b"\t</head>\n").expect("write failed");
336}
337
338/// The entire composition of the body contents you see on screen, broken up into
339/// * header
340/// * table, which actually includes non-table stuff... like the checkboxes and text above the table
341/// * footer, which doesn't actually exist
342fn compose_body_of_webmenu(f:&mut File, lib:&Library) {
343	f.write(b"\t<body>\n").expect("write failed");
344	compose_body_header(f,lib);
345	compose_body_table(f,lib);
346	compose_body_footer(f,lib);
347	f.write(b"\t</body>\n").expect("write failed");
348}
349
350/// The header tag on this page is preceded by a scroll-by advertisement; the header itself is sticky.
351fn compose_body_header(f:&mut File, lib:&Library) {
352	f.write(b"\t\t<div id=\"pre-header\" class=\"no-print\"><h1>Contact Strativus Group for help of any kind! For example:</h1><h2>&#128218; Help with your Process Library &#128218;</h2><h2>&#128202; Help with Project Management &#128202;</h2><h2>&#128101; Help with People-Focused Leadership &#128101;</h2><h2><span style=\"background-color: #2F495A; color: #BEA465; padding: 10px;\">contact@strativusgroup.com</span></h2></div>\n").expect("write failed");
353	f.write(b"\t\t<header>\n").expect("write failed");
354	let temp_str = String::from("\t\t\t<h1>") + &lib.get_name() + "</h1>\n";
355    f.write(temp_str.as_bytes()).expect("write failed");
356	let temp_str = String::from("\t\t\t<p>Process Library with ") + &lib.get_all_processes().len().to_string() + " processes.</p>\n";
357    f.write(temp_str.as_bytes()).expect("write failed");
358	f.write(b"\t\t</header>\n").expect("write failed");
359}
360
361/// Compose the actual functional purpose of this module: make a useful table that has information about every process in a library.
362/// 
363/// 16 columns in the table use CSS to display or not, but all are written in at stanhope execution time, not browser render time.
364fn compose_body_table(f:&mut File, lib:&Library) {
365	f.write(b"\t\t<main>\n").expect("write failed");
366	f.write(b"\t\t\t<h2 class=\"no-print\">Process metadata columns to show in the table below:</h2>\n").expect("write failed");
367	f.write(b"\t\t\t<input class=\"toggle-column-01 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Links</span><br/>\n").expect("write failed");
368	f.write(b"\t\t\t<input class=\"toggle-column-02 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Document Number</span><br/>\n").expect("write failed");
369	f.write(b"\t\t\t<input class=\"toggle-column-03 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Templates</span><br/>\n").expect("write failed");
370	f.write(b"\t\t\t<input class=\"toggle-column-04 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Templates</span><br/>\n").expect("write failed");
371	f.write(b"\t\t\t<input class=\"toggle-column-05 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Current Revision</span><br/>\n").expect("write failed");
372	f.write(b"\t\t\t<input class=\"toggle-column-06 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Title</span><br/>\n").expect("write failed");
373	f.write(b"\t\t\t<input class=\"toggle-column-07 no-print\" type=\"checkbox\" ><span class=\"no-print\">Subject</span><br/>\n").expect("write failed");
374	f.write(b"\t\t\t<input class=\"toggle-column-08 no-print\" type=\"checkbox\" ><span class=\"no-print\">Product</span><br/>\n").expect("write failed");
375	f.write(b"\t\t\t<input class=\"toggle-column-09 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Author</span><br/>\n").expect("write failed");
376	f.write(b"\t\t\t<input class=\"toggle-column-10 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Reviewer</span><br/>\n").expect("write failed");
377	f.write(b"\t\t\t<input class=\"toggle-column-11 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Number of Objectives</span><br/>\n").expect("write failed");
378	f.write(b"\t\t\t<input class=\"toggle-column-12 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Objectives</span><br/>\n").expect("write failed");
379	f.write(b"\t\t\t<input class=\"toggle-column-13 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Out of Scope Declarations</span><br/>\n").expect("write failed");
380	f.write(b"\t\t\t<input class=\"toggle-column-14 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Out of Scope Declarations</span><br/>\n").expect("write failed");
381	f.write(b"\t\t\t<input class=\"toggle-column-15 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Sections</span><br/>\n").expect("write failed");
382	f.write(b"\t\t\t<input class=\"toggle-column-16 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Section References</span><br/>\n").expect("write failed");
383	f.write(b"\t\t\t<input class=\"toggle-column-17 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Steps</span><br/>\n").expect("write failed");
384	f.write(b"\t\t\t<input class=\"toggle-column-18 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Requirement Verifications</span><br/>\n").expect("write failed");
385	f.write(b"\t\t\t<input class=\"toggle-column-19 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Requirement Verifications</span><br/>\n").expect("write failed");
386	f.write(b"\t\t\t<input class=\"toggle-column-20 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Resources</span><br/>\n").expect("write failed");
387	f.write(b"\t\t\t<input class=\"toggle-column-21 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Resources</span><br/>\n").expect("write failed");
388	f.write(b"\t\t\t<input class=\"toggle-column-22 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Calibrated Resources</span><br/>\n").expect("write failed");
389	f.write(b"\t\t\t<input class=\"toggle-column-23 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Calibrated Resources</span><br/>\n").expect("write failed");
390	f.write(b"\t\t\t<input class=\"toggle-column-24 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Context/Comment Lines</span><br/>\n").expect("write failed");
391	f.write(b"\t\t\t<input class=\"toggle-column-25 no-print\" type=\"checkbox\" ><span class=\"no-print\">Number of Command Lines</span><br/>\n").expect("write failed");
392	f.write(b"\t\t\t<input class=\"toggle-column-26 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Number of Unique Images</span><br/>\n").expect("write failed");
393	f.write(b"\t\t\t<input class=\"toggle-column-27 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Images</span><br/>\n").expect("write failed");
394	f.write(b"\t\t\t<input class=\"toggle-column-28 no-print\" type=\"checkbox\" checked><span class=\"no-print\">Number of Missing Images</span><br/>\n").expect("write failed");
395	f.write(b"\t\t\t<input class=\"toggle-column-29 no-print\" type=\"checkbox\" ><span class=\"no-print\">List of Missing Images</span><br/>\n").expect("write failed");
396
397	f.write(b"\t\t\t<h2 class=\"no-print\">Click on any column header to sort ascending/descending.</h2>\n").expect("write failed");
398	
399	f.write(b"\t\t\t<table id=\"library\">\n").expect("write failed");
400	f.write(b"\t\t\t\t<thead>\n").expect("write failed");
401	f.write(b"\t\t\t\t\t<tr data-sort-method=\"none\">\n").expect("write failed");
402	f.write(b"\t\t\t\t\t\t<th class=\"col01 no-print\">Links</th>\n").expect("write failed");
403	f.write(b"\t\t\t\t\t\t<th class=\"col02\">Number</th>\n").expect("write failed");
404	f.write(b"\t\t\t\t\t\t<th class=\"col03\">CSS</th>\n").expect("write failed");
405	f.write(b"\t\t\t\t\t\t<th class=\"col04\">Templates</th>\n").expect("write failed");
406	f.write(b"\t\t\t\t\t\t<th class=\"col05\">Rev</th>\n").expect("write failed");
407	f.write(b"\t\t\t\t\t\t<th class=\"col06\">Title</th>\n").expect("write failed");
408	f.write(b"\t\t\t\t\t\t<th class=\"col07\">Subject</th>\n").expect("write failed");
409	f.write(b"\t\t\t\t\t\t<th class=\"col08\">Product</th>\n").expect("write failed");
410	f.write(b"\t\t\t\t\t\t<th class=\"col09\">Author</th>\n").expect("write failed");
411	f.write(b"\t\t\t\t\t\t<th class=\"col10\">Reviewer</th>\n").expect("write failed");
412	f.write(b"\t\t\t\t\t\t<th class=\"col11\">OBJ</th>\n").expect("write failed");
413	f.write(b"\t\t\t\t\t\t<th class=\"col12\">Objectives</th>\n").expect("write failed");
414	f.write(b"\t\t\t\t\t\t<th class=\"col13\">OOS</th>\n").expect("write failed");
415	f.write(b"\t\t\t\t\t\t<th class=\"col14\">Out of Scope Declarations</th>\n").expect("write failed");
416	f.write(b"\t\t\t\t\t\t<th class=\"col15\">SEC</th>\n").expect("write failed");
417	f.write(b"\t\t\t\t\t\t<th class=\"col16\">SECREF</th>\n").expect("write failed");
418	f.write(b"\t\t\t\t\t\t<th class=\"col17\">STP</th>\n").expect("write failed");
419	f.write(b"\t\t\t\t\t\t<th class=\"col18\">VER</th>\n").expect("write failed");
420	f.write(b"\t\t\t\t\t\t<th class=\"col19\">Requirement Verifications</th>\n").expect("write failed");
421	f.write(b"\t\t\t\t\t\t<th class=\"col20\">RES</th>\n").expect("write failed");
422	f.write(b"\t\t\t\t\t\t<th class=\"col21\">Resources</th>\n").expect("write failed");
423	f.write(b"\t\t\t\t\t\t<th class=\"col22\">CAL</th>\n").expect("write failed");
424	f.write(b"\t\t\t\t\t\t<th class=\"col23\">Calibrated Resources</th>\n").expect("write failed");
425	f.write(b"\t\t\t\t\t\t<th class=\"col24\">TXT</th>\n").expect("write failed");
426	f.write(b"\t\t\t\t\t\t<th class=\"col25\">CMD</th>\n").expect("write failed");
427	f.write(b"\t\t\t\t\t\t<th class=\"col26\">IMG</th>\n").expect("write failed");
428	f.write(b"\t\t\t\t\t\t<th class=\"col27\">All Images</th>\n").expect("write failed");
429	f.write(b"\t\t\t\t\t\t<th class=\"col28\">IMG?</th>\n").expect("write failed");
430	f.write(b"\t\t\t\t\t\t<th class=\"col29\">Missing Images</th>\n").expect("write failed");
431	f.write(b"\t\t\t\t\t</tr>\n").expect("write failed");
432	f.write(b"\t\t\t\t</thead>\n").expect("write failed");
433	f.write(b"\t\t\t\t<tbody>\n").expect("write failed");
434	for proc in lib.get_all_processes() {
435		f.write(b"\t\t\t\t\t<tr>\n").expect("write failed");
436
437		// <div class="doc-box" style="background-color: #DDD;"><div class="doc-icon"><div class="wi-format"><img src="./assets/html.png"/></div></div></div>
438		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col01 no-print\">
439							<a href=\"") + &get_process_folder_name(&proc) + "/" + &get_process_folder_name(&proc) + ".ebml\"><div class=\"doc-box\" style=\"background-color: #DDD;\"><div class=\"doc-icon\"><div class=\"doc-format\"><img src=\"./assets/EBML.png\" alt=\"EBML\"/></div></div></div></a>
440							<a href=\"" + &get_process_folder_name(&proc) + "/" + &get_process_folder_name(&proc) + ".html\"><div class=\"doc-box\" style=\"background-color: #DDD;\"><div class=\"doc-icon\"><div class=\"doc-format\"><img src=\"./assets/html.png\" alt=\"HTML\"/></div></div></div></a>
441							<a href=\"" + &get_process_folder_name(&proc) + "/" + &get_process_folder_name(&proc) + ".pdf\"><div class=\"doc-box\" style=\"background-color: #DDD;\"><div class=\"doc-icon\"><div class=\"doc-format\"><img src=\"./assets/pdf.png\" alt=\"PDF\"/></div></div></div></a>
442						</td>\n";
443    	f.write(temp_str.as_bytes()).expect("write failed");
444
445		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col02\">") + proc.get_number() + "</td>\n";
446    	f.write(temp_str.as_bytes()).expect("write failed");
447
448		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col03\">") + &format!("{:03}",&proc.get_all_templates().len()) + "</td>\n";
449    	f.write(temp_str.as_bytes()).expect("write failed");
450		
451		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col04\"><ul>\n");
452    	f.write(temp_str.as_bytes()).expect("write failed");
453    	let all_templates = proc.get_all_templates();
454    	if all_templates.len() > 0 {
455    		for template in all_templates {
456    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &template + "</li>\n";
457    			f.write(temp_str.as_bytes()).expect("write failed");		
458    		}
459    	}
460    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
461    	f.write(temp_str.as_bytes()).expect("write failed");
462		
463		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col05\">") + proc.get_revision() + "</td>\n";
464    	f.write(temp_str.as_bytes()).expect("write failed");
465		
466		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col06\">") + proc.get_title() + "</td>\n";
467    	f.write(temp_str.as_bytes()).expect("write failed");
468		
469		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col07\">") + proc.get_subject() + "</td>\n";
470    	f.write(temp_str.as_bytes()).expect("write failed");
471		
472		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col08\">") + proc.get_product() + "</td>\n";
473    	f.write(temp_str.as_bytes()).expect("write failed");
474		
475		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col09\">") + proc.get_author() + "</td>\n";
476    	f.write(temp_str.as_bytes()).expect("write failed");
477		
478		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col10\">") + proc.get_reviewer() + "</td>\n";
479    	f.write(temp_str.as_bytes()).expect("write failed");
480		
481		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col11\">") + &format!("{:03}",&proc.get_all_objectives().len()) + "</td>\n";
482    	f.write(temp_str.as_bytes()).expect("write failed");
483		
484		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col12\"><ul>\n");
485    	f.write(temp_str.as_bytes()).expect("write failed");
486    	let all_objectives = proc.get_all_objectives();
487    	if all_objectives.len() > 0 {
488    		for objective in all_objectives {
489    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &objective.0 + "</li>\n";
490    			f.write(temp_str.as_bytes()).expect("write failed");		
491    		}
492    	}
493    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
494    	f.write(temp_str.as_bytes()).expect("write failed");
495		
496		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col13\">") + &format!("{:03}",&proc.get_all_out_of_scopes().len()) + "</td>\n";
497    	f.write(temp_str.as_bytes()).expect("write failed");
498		
499		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col14\"><ul>\n");
500    	f.write(temp_str.as_bytes()).expect("write failed");
501    	let all_out_of_scopes = proc.get_all_out_of_scopes();
502    	if all_out_of_scopes.len() > 0 {
503    		for out_of_scope in all_out_of_scopes {
504    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &out_of_scope.0 + "</li>\n";
505    			f.write(temp_str.as_bytes()).expect("write failed");		
506    		}
507    	}
508    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
509    	f.write(temp_str.as_bytes()).expect("write failed");
510
511    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col15\">") + &format!("{:03}",&proc.get_all_sections().len()) + "</td>\n";
512    	f.write(temp_str.as_bytes()).expect("write failed");
513
514    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col16\">") + &format!("{:03}",&proc.get_section_reference_count()) + "</td>\n";
515    	f.write(temp_str.as_bytes()).expect("write failed");
516
517    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col17\">") + &format!("{:03}",&proc.get_step_count()) + "</td>\n";
518    	f.write(temp_str.as_bytes()).expect("write failed");
519
520    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col18\">") + &format!("{:03}",&proc.get_all_verifications().len()) + "</td>\n";
521    	f.write(temp_str.as_bytes()).expect("write failed");
522		
523		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col19\"><ul>\n");
524    	f.write(temp_str.as_bytes()).expect("write failed");
525    	let all_verifications = proc.get_all_verifications();
526    	if all_verifications.len() > 0 {
527    		for verification in all_verifications {
528    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &verification.0.get_id() + " (" + &((verification.0.get_method()).chars().nth(0).expect("How does this work?").to_string()) + ")</li>\n";
529    			f.write(temp_str.as_bytes()).expect("write failed");
530    		}
531    	}
532    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
533    	f.write(temp_str.as_bytes()).expect("write failed");
534		
535		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col20\">") + &format!("{:03}",&proc.get_all_resources().len()) + "</td>\n";
536    	f.write(temp_str.as_bytes()).expect("write failed");
537		
538		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col21\"><ul>\n");
539    	f.write(temp_str.as_bytes()).expect("write failed");
540    	let all_resources = proc.get_all_resources();
541    	if all_resources.len() > 0 {
542    		for resource in all_resources {
543    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &resource.0.get_name() + "</li>\n";
544    			f.write(temp_str.as_bytes()).expect("write failed");		
545    		}
546    	}
547    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
548    	f.write(temp_str.as_bytes()).expect("write failed");
549
550    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col22\">") + &format!("{:03}",&proc.get_all_calibrated_resources().len()) + "</td>\n";
551    	f.write(temp_str.as_bytes()).expect("write failed");
552		
553		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col23\"><ul>\n");
554    	f.write(temp_str.as_bytes()).expect("write failed");
555    	let all_calibrated_resources = proc.get_all_calibrated_resources();
556    	if all_calibrated_resources.len() > 0 {
557    		for calibrated_resource in all_calibrated_resources {
558    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &calibrated_resource.get_name() + "</li>\n";
559    			f.write(temp_str.as_bytes()).expect("write failed");		
560    		}
561    	}
562    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
563    	f.write(temp_str.as_bytes()).expect("write failed");
564
565    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col24\">") + &format!("{:03}",&proc.get_all_context_lines().len()) + "</td>\n";
566    	f.write(temp_str.as_bytes()).expect("write failed");
567
568    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col25\">") + &format!("{:03}",&proc.get_all_command_lines().len()) + "</td>\n";
569    	f.write(temp_str.as_bytes()).expect("write failed");
570
571    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col26\">") + &format!("{:03}",&proc.get_unique_image_count()) + "</td>\n";
572    	f.write(temp_str.as_bytes()).expect("write failed");
573		
574		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col27\"><ul>\n");
575    	f.write(temp_str.as_bytes()).expect("write failed");
576    	let all_images = proc.get_all_images();
577    	if all_images.len() > 0 {
578    		for image in all_images {
579    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &image.0 + "</li>\n";
580    			f.write(temp_str.as_bytes()).expect("write failed");		
581    		}
582    	}
583    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
584    	f.write(temp_str.as_bytes()).expect("write failed");
585
586    	let temp_str = String::from("\t\t\t\t\t\t<td class=\"col28\">") + &format!("{:03}",&proc.get_missing_image_count()) + "</td>\n";
587    	f.write(temp_str.as_bytes()).expect("write failed");
588		
589		let temp_str = String::from("\t\t\t\t\t\t<td class=\"col29\"><ul>\n");
590    	f.write(temp_str.as_bytes()).expect("write failed");
591    	let all_missing_images = proc.get_missing_images();
592    	if all_missing_images.len() > 0 {
593    		for missing_image in all_missing_images {
594    			let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &missing_image + "</li>\n";
595    			f.write(temp_str.as_bytes()).expect("write failed");		
596    		}
597    	}
598    	let temp_str = String::from("\t\t\t\t\t\t</ul></td>\n");
599    	f.write(temp_str.as_bytes()).expect("write failed");
600
601    	/*
602		
603		f.write(b"\t\t\t\t\t\t<th class=\"col27\">All Images</th>\n").expect("write failed");
604		f.write(b"\t\t\t\t\t\t<th class=\"col28\">IMG?</th>\n").expect("write failed");
605		f.write(b"\t\t\t\t\t\t<th class=\"col29\">Missing Images</th>\n").expect("write failed");
606    	*/
607
608    	f.write(b"\t\t\t\t\t</tr>\n").expect("write failed");
609
610	}
611	f.write(b"\t\t\t\t</tbody>\n").expect("write failed");
612	f.write(b"\t\t\t</table>\n").expect("write failed");
613	f.write(b"\t\t</main>\n").expect("write failed");
614}
615
616/// TODO: currently empty
617fn compose_body_footer(_f:&mut File, _lib:&Library) {}
618
619/// Simply close out the HTML tag.
620fn compose_bottom_of_webmenu(f:&mut File) {
621	f.write(b"</html>\n").expect("write failed");
622}
623
624//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄       ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄           
625// ▐░░░░░░░░░░░▌▐░░▌     ▐░░▌▐░░░░░░░░░░░▌▐░▌          
626//  ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌   ▐░▐░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌          
627//      ▐░▌     ▐░▌▐░▌ ▐░▌▐░▌▐░▌       ▐░▌▐░▌          
628//      ▐░▌     ▐░▌ ▐░▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌          
629//      ▐░▌     ▐░▌  ▐░▌  ▐░▌▐░░░░░░░░░░░▌▐░▌          
630//      ▐░▌     ▐░▌   ▀   ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌          
631//      ▐░▌     ▐░▌       ▐░▌▐░▌          ▐░▌          
632//  ▄▄▄▄█░█▄▄▄▄ ▐░▌       ▐░▌▐░▌          ▐░█▄▄▄▄▄▄▄▄▄ 
633// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          ▐░░░░░░░░░░░▌
634//  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀            ▀▀▀▀▀▀▀▀▀▀▀ 
635
636/// Since all fields are private, "get" and "set"/"add" functions safely read and write a Library struct
637impl Library {
638
639	/// Instantiate a new Library struct, with empty name and Process vec
640	pub fn new() -> Library {
641		Library { 
642			name: "".to_string(),
643			all_processes: vec![],
644		}
645	}
646
647	/// Return the contents of the "name" field
648	pub fn get_name(&self) -> &String { &self.name }
649	/// Return the contents of the "all_processes" field
650	pub fn get_all_processes(&self) -> &Vec<Process> { &self.all_processes }
651
652	/// Change the name of the "name" field
653	pub fn set_name(&mut self, lib_name: &str) {
654		self.name = String::from(lib_name.trim());
655	}
656
657	/// push a new [crate::read_ebml::Process] to the all_processes vector
658	pub fn add_process(&mut self, process: Process) {
659		self.all_processes.push(process);
660	}
661
662	/// Write to stdout a multi-line visual of the contents of a Library. This is mostly for debugging, because the HTML itself is supposed to be the nice output of even more information.
663	pub fn display(&self) {
664		let name = self.get_name();
665		let all_processes = self.get_all_processes();
666		println!("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
667		println!("XX LIBRARY VISUALIZER          XX");
668		println!("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
669		println!(" -> Name:       {}",name);
670		println!(" -> Processes:");
671		for proc in all_processes {
672			println!("    -> {}",proc.get_number());
673		}
674	}
675}
676
677//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
678// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
679//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
680//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
681//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
682//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
683//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
684//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
685//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
686//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
687//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
688
689#[cfg(test)]
690mod tests {
691    // Note this useful idiom: importing names from outer (for mod tests) scope.
692	use super::*;
693	//use std::fs;
694
695	/*
696	#[test]
697	fn test_find_all_process_folders() {
698		// I don't know how the test compiler works, where it works, how many folders are in the working directory... so if this runs it runs...
699		let test_dir = "test_dir";
700		let _ = fs::create_dir(&test_dir);
701		let all_process_folders = find_all_process_folders(".");
702		//assert_eq!(all_process_folders.len(),1);
703		//assert_eq!(all_process_folders[0].clone().into_os_string().into_string().unwrap(),test_dir.to_string());
704		let mut cnt = -1;
705		for process_folder in all_process_folders.iter() {
706			cnt += 1;
707			println!("all_process_folders[{}] -> {}",cnt,process_folder.clone().into_os_string().into_string().unwrap());
708		}
709		println!("test_dir               -> {}",&test_dir.to_string());
710		let _ = fs::remove_dir(&test_dir);
711	}
712	*/
713
714	#[test]
715	fn test_find_name_of_library() {
716		println!("result of find_name_of_library() -> {}",find_name_of_library());	
717	}
718
719	/*
720	#[test]
721	fn test_list_files() {
722		// I don't know how the test compiler works, where it works, how many folders are in the working directory... so if this runs it runs...
723		let path = Path::new(".");
724		let less_one = list_files(&path).expect("Naw").len();
725
726		let test_dir = "test_dir";
727		let _ = fs::create_dir(&test_dir);
728
729		assert!(less_one < list_files(&path).expect("Naw").len());
730
731		let _ = fs::remove_dir(&test_dir);
732
733		let again = list_files(&path).expect("Naw").len();
734		assert_eq!(less_one,again);
735	}
736	*/
737
738	#[test]
739	fn test_library_new() {
740		Library::new();
741	}
742
743	#[test]
744    fn test_library_get_functions() {
745		
746		let lib:Library = Library::new();
747
748		assert_eq!(&lib.name,lib.get_name());
749		println!("struct Library / Result of get_name() -> {:?}",lib.get_name());
750
751		assert!(lib.all_processes.len()==0);
752		assert_eq!(lib.all_processes.len(),lib.get_all_processes().len());
753		let _is_right_type:&Vec<Process> = lib.get_all_processes();
754		println!("struct Library / Result of get_all_processes() -> [it's empty]");
755	}
756
757	#[test]
758	fn test_library_display() {
759		Library::new().display();
760	}
761
762	#[test]
763	fn test_library_set_functions() {
764		let mut lib:Library = Library::new();
765
766		lib.set_name("SET_LIBRARY_NAME");
767		assert_eq!(&lib.name,lib.get_name());
768
769		let proc = Process::new();
770		let number = &proc.get_number().clone();
771		lib.add_process(proc);
772		assert_eq!(lib.get_all_processes()[0].get_number(),number);
773
774		lib.display();
775	}
776
777
778}