stanhope/
write_script.rs

1//! *Write various kinds of script files* into a Process folder from the Commands
2//! 
3//! General workflow, most of which occurs outside of this module:
4//! * User composes a process in an EBML file, with one or more "Command" lines
5//! * A call to stanhope to "scriptify" a process invokes the **read_ebml** module to collect the Command lines
6//! * Functions in this module know how to write scripts to the filesystem in various languages
7//! 
8//! NOTE: this module doesn't help the user compose their "Command" lines in proper syntax, nor do any of these functions convert "Command" lines from one language to another. As far as these functions are concerned, it is assumed that "Command" line text is already in proper syntax for the script file type selected when running stanhope to *scriptify*.
9use std::{
10	fs::{OpenOptions},
11	io::{Write},
12};
13use crate::read_ebml::Process;
14//use crate::read_ebml::Section;
15//use crate::read_ebml::Step;
16use crate::read_ebml::SubStep;
17
18
19//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
20// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
21// ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
22// ▐░▌               ▐░▌     ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌               ▐░▌     
23// ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌▐░▌               ▐░▌     
24// ▐░░░░░░░░░░░▌     ▐░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌               ▐░▌     
25//  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌       ▐░▌▐░▌               ▐░▌     
26//           ▐░▌     ▐░▌     ▐░▌     ▐░▌  ▐░▌       ▐░▌▐░▌               ▐░▌     
27//  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
28// ▐░░░░░░░░░░░▌     ▐░▌     ▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
29//  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀   
30
31/// Scriptification requires a set of information describing the basic components of a scripting language. Each field of the Language struct includes sytax information.
32pub struct Language {
33
34	/// Human-readable name of the scripting language
35	name: 	String,
36
37	/// First line of a script file in this language (e.g. shebang statement)
38	first: 	String,
39
40	/// Single-line comments start with this string. "c_open" means "single-line Comment OPENing"
41	c_open:	String,
42
43	/// Single-line comments end with this string. Most comments only need an "open" but for completeness a "single-line Comment CLOSE" is defined
44	c_close:String,
45
46	/// Last line of a script file in this language (e.g. exit or return statement)
47	last: 	String,
48
49	/// File extension for scripts in this language, including the period (e.g. ".sh")
50	ext: 	String,
51
52	/// Syntactically correct way to wait for the user to press the ENTER key (functionality might vary by language limitations)
53	wait: 	String,
54
55	/// Printing information out to stdout starts with this text (e.g. "echo ")
56	p_open: String,
57
58	/// Printing information out to stdout ends with this text
59	p_close:String,
60}
61
62//  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄        ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
63// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░▌      ▐░▌▐░░░░░░░░░░░▌
64// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌░▌     ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
65// ▐░▌          ▐░▌       ▐░▌▐░▌▐░▌    ▐░▌▐░▌          
66// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌ ▐░▌   ▐░▌▐░▌          
67// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌▐░▌          
68// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌   ▐░▌ ▐░▌▐░▌          
69// ▐░▌          ▐░▌       ▐░▌▐░▌    ▐░▌▐░▌▐░▌          
70// ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄▄▄ 
71// ▐░▌          ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌
72//  ▀            ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀ 
73
74/// "Learn" a script language from user input (which language), simply by populating a Language struct
75/// 
76/// | Key | Status |
77/// | --- | ------ |
78/// | ⬜ | In backlog, not implemented |
79/// | 🟨 | Implemented, according to syntax found on the internet (send feedback!) |
80/// | 🟩 | Implemented and ad hoc tested (send feedback!) |
81/// 
82/// 
83/// 🟨 ActionScript\
84/// 🟩 AppleScript\
85/// 🟩 Bash\
86/// 🟨 CoffeeScript\
87/// 🟨 Dart\
88/// 🟨 Elixir\
89/// 🟨 Erg\
90/// ⬜ GDScript\
91/// 🟨 JavaScript\
92/// 🟨 Julia\
93/// 🟨 Lua\
94/// 🟩 MATLAB\
95/// 🟨 Nushell\
96/// 🟨 Perl\
97/// 🟨 PHP\
98/// 🟩 PowerShell\
99/// 🟩 Python 3\
100/// 🟨 R\
101/// 🟨 Ruby\
102/// ⬜ SQL -> T-SQL\
103/// ⬜ SQL -> PL/SQL\
104/// ⬜ SQL -> PL/pgSQL\
105/// ⬜ SQL -> MySQL\
106/// ⬜ SQL -> MariaDB SQL\
107/// ⬜ SQL -> SQLite\
108/// ⬜ SQL -> PRQL\
109/// ⬜ SQL -> Spark SQL\
110/// ⬜ SQL -> HQL\
111/// 🟨 TypeScript\
112/// 🟨 VB.NET\
113/// ⬜ VBScript\
114/// ⬜ Verse
115/// 
116/// To request a language for the backlog, or to report a bug in the implementation of any scripting language, please email <mailto:feature.request@stanhope.strativusgroup.com>
117/// 
118/// When requesting a scripting language to add, please include syntax guides (or links to such) sufficient to fill a [Language] struct, and contact information. It would be great if you could help test your language's implementation!
119pub fn learn(lang:&str) -> Language { 
120	
121	// Whatever is passed into this function, we're going to strip it of all whitespace, collapse it, force it uppercase, and then use that string to compare
122	match lang.split_whitespace().collect::<Vec<_>>().join("").to_uppercase().as_str() {
123		"ACTIONSCRIPT" | "AS3" => Language{
124			name:	String::from("ActionScript"),
125			first:	String::from("#!/usr/bin/as3shebang //\nimport shell.Program;\nimport flash.system.System;"),
126			c_open:	String::from("// "),
127			c_close:String::from(""),
128			last:	String::from("function onKeyPress(event:KeyboardEvent):void\n{\n  if (event.keyCode == Keyboard.ENTER)\n  {    stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyPress);\n    trace(\"Enter key pressed! Proceeding...\");\n    play();\n  }\n}\n\nreturn;"),
129			ext:	String::from(".asc"),
130			wait:	String::from("stop();\nstage.addEventListener(KeyboardEvent.KEY_UP, onKeyPress);"),
131			p_open: String::from("System.output(\""),
132			p_close:String::from("\n\");"),
133		},
134		"APPLESCRIPT" | "APPLE" => Language{
135			name:	String::from("AppleScript"),
136			first:	String::from("#!/usr/bin/osascript"),
137			c_open:	String::from("-- "),
138			c_close:String::from(""),
139			last:	String::from(""),
140			ext:	String::from(".command"),
141			wait:	String::from("read -p \"Press Enter to continue...\""),
142			p_open: String::from("echo '"),
143			p_close:String::from("'"),
144		},
145		"BASH" | "BOURNEAGAINSHELL" | "BORNAGAINSHELL" => Language{
146			name:	String::from("bash"),
147			first:	String::from("#!/bin/bash"),
148			c_open:	String::from("# "),
149			c_close:String::from(""),
150			last:	String::from("exit 0"),
151			ext:	String::from(".sh"),
152			wait:	String::from("read -p \"Press Enter to continue...\""),
153			p_open: String::from("echo '"),
154			p_close:String::from("'"),
155		},
156		"COFFEESCRIPT" | "COFFEE" => Language{
157			name:	String::from("CoffeeScript"),
158			first:	String::from("#!/usr/bin/env coffee\n\nreadline = require 'readline'\n\nwaitForEnter = ->\n  new Promise (resolve) ->\n    rl = readline.createInterface\n      input: process.stdin\n      output: process.stdout\n    rl.question 'Press Enter to continue...', ->\n      rl.close()\n      resolve()\n\n"),
159			c_open:	String::from("# "),
160			c_close:String::from(""),
161			last:	String::from("process.exit(1)"),
162			ext:	String::from(".coffee"),
163			wait:	String::from("await waitForEnter()"),
164			p_open: String::from("console.log \""),
165			p_close:String::from("\""),
166		},
167		"DART" => Language{
168			name:	String::from("Dart"),
169			first:	String::from("#!/usr/bin/env dart\nimport 'dart:io';\nvoid main() {"),
170			c_open:	String::from("// "),
171			c_close:String::from(""),
172			last:	String::from("exit(0);\n}"),
173			ext:	String::from(".dart"),
174			wait:	String::from("print('Press Enter to continue...');\nstdin.readLineSync();"),
175			p_open: String::from("print('"),
176			p_close:String::from("');"),
177		},
178		"ELIXIR" | "EXS" => Language{
179			name:	String::from("Elixir"),
180			first:	String::from("#!/usr/bin/env erg"),
181			c_open:	String::from("# "),
182			c_close:String::from(""),
183			last:	String::from("Process.exit(self, :normal)"),
184			ext:	String::from(".exs"),
185			wait:	String::from("IO.gets(\"Press Enter to continue...\")"),
186			p_open: String::from("IO.puts(\""),
187			p_close:String::from("\")"),
188		},
189		"ERG" => Language{
190			name:	String::from("Erg"),
191			first:	String::from("#!/usr/bin/env elixir"),
192			c_open:	String::from("# "),
193			c_close:String::from(""),
194			last:	String::from(""),
195			ext:	String::from(".er"),
196			wait:	String::from("input(\"Press Enter to continue...\")"),
197			p_open: String::from("print!(\""),
198			p_close:String::from("\")"),
199		},
200		"JAVASCRIPT" | "JS" | "NODE.JS" => Language{
201			name:	String::from("JavaScript"),
202			first:	String::from("#!/usr/bin/env node"),
203			c_open:	String::from("// "),
204			c_close:String::from(""),
205			last:	String::from("process.exit(0)"),
206			ext:	String::from(".js"),
207			wait:	String::from(""),
208			p_open: String::from("console.log(\""),
209			p_close:String::from("\");"),
210		},
211		"JULIA" | "JL" => Language{
212			name:	String::from("Julia"),
213			first:	String::from("#!/usr/bin/env -S julia --color=yes --startup-file=no"),
214			c_open:	String::from("# "),
215			c_close:String::from(""),
216			last:	String::from("exit(0)"),
217			ext:	String::from(".jl"),
218			wait:	String::from("Base.prompt(\"Press Enter to continue...\")"),
219			p_open: String::from("println(\""),
220			p_close:String::from("\")"),
221		},
222		"LUA" => Language{
223			name:	String::from("Lua"),
224			first:	String::from("#!/usr/bin/env lua"),
225			c_open:	String::from("-- "),
226			c_close:String::from(""),
227			last:	String::from("return"),
228			ext:	String::from(".lua"),
229			wait:	String::from("print(\"Press Enter to continue...\")\nio.read()"),
230			p_open: String::from("println(\""),
231			p_close:String::from("\")"),
232		},
233		"MATLAB" | "MATHWORKS" | "MAT" | "M" => Language{
234			name:	String::from("MATLAB"),
235			first:	String::from("% no shebang statement for MATLAB"),
236			c_open:	String::from("% "),
237			c_close:String::from(""),
238			last:	String::from("exit(0)"),
239			ext:	String::from(".m"),
240			wait:	String::from("disp('Press Enter to continue...'); input('');"),
241			p_open: String::from("disp('"),
242			p_close:String::from("');"),
243		},
244		"NUSHELL" | "NU" => Language{
245			name:	String::from("NuShell"),
246			first:	String::from("#!/usr/bin/env -S nu --stdin"),
247			c_open:	String::from("# "),
248			c_close:String::from(""),
249			last:	String::from(""),
250			ext:	String::from(".nu"),
251			wait:	String::from("print -n \"Press Enter to continue...\"\nlet _ = (input)"),
252			p_open: String::from("print -n \""),
253			p_close:String::from("\""),
254		},
255		"PERL" | "PL" => Language{
256			name:	String::from("Perl"),
257			first:	String::from("#!/usr/bin/env perl"),
258			c_open:	String::from("# "),
259			c_close:String::from(""),
260			last:	String::from("exit 1;"),
261			ext:	String::from(".pl"),
262			wait:	String::from("print \"Press Enter to continue...\";\n<STDIN>;"),
263			p_open: String::from("print \""),
264			p_close:String::from("\";"),
265		},
266		"PHP" | "HYPERTEXTPREPROCESSOR" => Language{
267			name:	String::from("PHP"),
268			first:	String::from("#!/usr/bin/env php"),
269			c_open:	String::from("// "),
270			c_close:String::from(""),
271			last:	String::from("exit(0);"),
272			ext:	String::from(".php"),
273			wait:	String::from("readline(\"Press Enter to proceed...\");"),
274			p_open: String::from("echo \""),
275			p_close:String::from("\";"),
276		},
277		"POWERSHELL" | "PS" | "PS1" => Language{
278			name:	String::from("PowerShell"),
279			first:	String::from("#!/usr/bin/env pwsh"),
280			c_open:	String::from("# "),
281			c_close:String::from(""),
282			last:	String::from("exit 0"),
283			ext:	String::from(".ps1"),
284			wait:	String::from("Read-Host -Prompt \"Press Enter to continue...\""),
285			p_open: String::from("Write-Host \""),
286			p_close:String::from("\""),
287		},
288		"PYTHON" | "PYTHON3" | "PY" => Language{
289			name:	String::from("Python 3"),
290			first:	String::from("#!/usr/bin/python3"),
291			c_open:	String::from("-- "),
292			c_close:String::from(""),
293			last:	String::from(""),
294			ext:	String::from(".py"),
295			wait:	String::from("input(\"Press Enter to continue...\")"),
296			p_open: String::from("print(\""),
297			p_close:String::from("\")"),
298		},
299		"R" => Language{
300			name:	String::from("R"),
301			first:	String::from("#!/usr/bin/env Rscript"),
302			c_open:	String::from("# "),
303			c_close:String::from(""),
304			last:	String::from("quit()"),
305			ext:	String::from(".R"),
306			wait:	String::from("readline(prompt = \"Press Enter to continue...\")"),
307			p_open: String::from("print(\""),
308			p_close:String::from("\")"),
309		},		
310		"RUBY" | "RB" => Language{
311			name:	String::from("Ruby"),
312			first:	String::from("#!/usr/bin/env ruby"),
313			c_open:	String::from("// "),
314			c_close:String::from(""),
315			last:	String::from("exit(0)"),
316			ext:	String::from(".rb"),
317			wait:	String::from("puts \"Press Enter to continue...\"\ngets"),
318			p_open: String::from("puts \""),
319			p_close:String::from("\""),
320		},
321		"TYPESCRIPT" | "TS" => Language{
322			name:	String::from("TypeScript"),
323			first:	String::from("#!/usr/bin/env tsx"),
324			c_open:	String::from("# "),
325			c_close:String::from(""),
326			last:	String::from("process.exit(0)"),
327			ext:	String::from(".ts"),
328			wait:	String::from(""),
329			p_open: String::from("console.log(\""),
330			p_close:String::from("\")"),
331		},
332		"VB.NET" | "VBNET" | "VB" | "VISUALBASIC" => Language{
333			name:	String::from("VB.NET"),
334			first:	String::from("#!/usr/bin/env dotnet run"),
335			c_open:	String::from("' "),
336			c_close:String::from(""),
337			last:	String::from("Return"),
338			ext:	String::from(".vb"),
339			wait:	String::from("Console.WriteLine(\"Press Enter to continue...\")\nConsole.ReadLine()"),
340			p_open: String::from("Console.WriteLine(\""),
341			p_close:String::from("\""),
342		},
343		_ => Language{
344			name:	String::from("Unknown"),
345			first:	String::from("#!/bin/sh"),
346			c_open:	String::from("# "),
347			c_close:String::from(""),
348			last:	String::from("exit 0"),
349			ext:	String::from(".sh"),
350			wait:	String::from("read -p \"Press Enter to continue...\""),
351			p_open: String::from("echo '"),
352			p_close:String::from("'"),
353		},
354	}
355}
356
357/// Create a script file using the Process struct, interpreted in a specified Language.
358/// * Some pre-canned comment blocks and stdout messages appear first
359/// * "Context" EBML lines appear as comments in the script
360/// * "Command" EBML lines appear as their own lines, verbatim
361/// * "Objective" EBML lines are announced to the command line output
362/// * All other EBML is ignored
363/// 
364/// The script file appears alongside the process itself in its Process Folder.
365pub fn script_file_from_process(p:Process,lang:Language,wait:bool) {
366	
367	// An empty vector to fill with lines of text
368	let mut script_lines:Vec<String> = vec![];
369
370	// The [[Language]] struct includes a field for the language-specific first line of a script, e.g. shebang
371	script_lines.push(lang.first.to_owned());
372
373	// 
374	script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
375	script_lines.push(comment_to_file(&(p.get_number().to_owned() + " Rev " + &p.get_revision() + "   " + &p.get_title()),&lang));
376	script_lines.push(comment_to_file(&("Author:   ".to_owned() + &p.get_author()),&lang));
377	script_lines.push(comment_to_file(&("Reviewer: ".to_owned() + &p.get_reviewer()),&lang));
378	script_lines.push(comment_to_file(&("".to_string()),&lang));
379	script_lines.push(comment_to_file(&(lang.name.to_owned()+ " generated by stanhope " + env!("CARGO_PKG_VERSION")),&lang));
380	script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
381	
382	script_lines.push("".to_string());
383	
384	script_lines.push(print_to_stdout(&(p.get_number().to_owned() + " Rev " + &p.get_revision() + " " + &p.get_title()),&lang));
385	script_lines.push(print_to_stdout(&("".to_string()),&lang));
386	script_lines.push(print_to_stdout(&("Author:   ".to_owned() + &p.get_author()),&lang));
387	script_lines.push(print_to_stdout(&("Reviewer: ".to_owned() + &p.get_reviewer()),&lang));
388	script_lines.push(print_to_stdout(&("".to_string()),&lang));
389
390	script_lines.push("".to_string());
391
392	// Hold before start if we're in slow mode
393	if wait { script_lines.push(lang.wait.to_owned()) };
394
395	script_lines.push("".to_string());
396
397	// Loop through all of the sections
398	let mut secnum:u16 = 0;
399	for section in p.get_all_sections() {
400		secnum += 1;
401		let mut stpnum:u16 = 0;
402
403		// Add section title as a comment
404		script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
405		script_lines.push(comment_to_file(&("Section ".to_string() + &secnum.to_string() + ": " + &section.get_title()),&lang));
406		script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
407		script_lines.push("".to_string());
408
409		// Loop through all of the steps in this section
410		for step in section.get_all_steps() {
411			stpnum += 1;
412			// Add step title as a comment
413			script_lines.push(print_to_stdout(&("Step ".to_string() + &secnum.to_string() + "." + &stpnum.to_string()),&lang));
414			script_lines.push(print_to_stdout(&(step.get_text().trim().to_string()),&lang));
415			script_lines.push(print_to_stdout(&("".to_string()),&lang));
416
417			// Loop though all substeps of this step
418			for substep in step.get_all_sub_steps() {
419				match substep {
420
421					// Context lines are added as comments
422					// TODO: verbose stdout
423					SubStep::Context(txt) => { script_lines.push(comment_to_file(&(txt.trim().to_string()),&lang)); },
424
425					SubStep::Command(txt) => {
426						// Command lines are passed through unmolested
427						script_lines.push(txt.trim().to_string());
428						// TODO: verbose stdout
429
430						// If "wait" is true, add language-specific wait
431						if wait { script_lines.push(lang.wait.to_owned()) };
432					},
433
434					/*
435					* * * * * * * * * *
436					Objective Achieved!
437					*/
438					SubStep::Objective(txt) => {
439						script_lines.push(print_to_stdout(&("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *".to_string()),&lang));
440						script_lines.push(print_to_stdout(&("* Objective Achieved!".to_string()),&lang));
441						script_lines.push(print_to_stdout(&("* ".to_owned()+&txt.trim()),&lang));
442						script_lines.push(print_to_stdout(&("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *".to_string()),&lang));
443						script_lines.push(print_to_stdout(&("".to_string()),&lang));
444					},
445					
446					// Ignore all other substep types
447					// TODO: consider including verifications to stdout
448					_ => {},
449				}
450			}
451			// add a spacer line to stdout at the end of the step, and in the script itself for readability
452			script_lines.push(print_to_stdout(&("".to_string()),&lang));
453			script_lines.push("".to_string());
454		}
455		// Add another spacer line at the end of the section
456		script_lines.push("".to_string());
457	}
458
459	// Last line
460	script_lines.push(lang.last.to_owned());
461
462	// Now all the lines of the script are stored in script_lines, and they need to be written out to a new file (or overwritten...)
463
464	// Assumption: every process has its own folder, and that folder is named the PROCESS NUMBER
465	let path_to_file = "./".to_string() + p.get_number() + " - " + p.get_title() + "/" + p.get_number() + " - " + p.get_title() + &lang.ext;
466
467	// Open the file to write
468	let mut file = OpenOptions::new()
469	            .read(true)
470	            .write(true)
471	            .create(true)
472	            .truncate(true)
473	            .open(&path_to_file)
474	            .expect("Could not open the file!");
475
476	for line in script_lines {
477		file.write((line + "\n").as_bytes()).expect("Could not write template line into new file!");
478	}
479
480	println!("You will need to make the script file executable if it isn't already. For example, you might need to run:\n\x1b[36mchmod +x \"{}\"\x1b[0m",&path_to_file);
481
482}
483
484/// Standardize how to write a comment line, given the text to comment, and the language in which to comment
485fn comment_to_file(txt:&String,lang:&Language) -> String { lang.c_open.to_owned() + txt + &lang.c_close }
486
487/// Standardize how to print to stdout, given the text to print out, and the language in which to print to stdout
488fn print_to_stdout(txt:&String,lang:&Language) -> String { 
489	let txt_san = txt.replace('"',"").replace("'","");
490	lang.p_open.to_owned() + &txt_san + &lang.p_close 
491}
492
493
494//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
495// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
496//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
497//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
498//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
499//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
500//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
501//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
502//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
503//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
504//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀     
505
506#[cfg(test)]
507mod tests {
508
509	use super::*;
510
511	#[test]
512	fn test_new_language_struct() { 
513		let mut test_language:Language = Language{
514			name: 	String::from("NAME"),
515			first: 	String::from("FIRST LINE(S)"),
516			c_open:	String::from("SINGLE-LINE COMMENT OPENER"),
517			c_close:String::from("SINGLE-LINE COMMENT CLOSER"),
518			last: 	String::from("LAST LINE(S)"),
519			ext: 	String::from("FILE EXTENSION"),
520			wait: 	String::from("WAIT FOR THE USER TO PRESS ENTER"),
521			p_open: String::from("PRINT TO STDOUT OPENER"),
522			p_close:String::from("PRINT TO STDOUT CLOSER"),
523		};
524		println!("We successfully created a new Language struct.");
525
526		test_language.name = String::from("A NEW AND IMPROVED NAME");
527		println!("Although it's not really best practices, we've proved we can poke Language fields (within this module anyway).")
528	}
529
530	#[test]
531	fn test_learn_language_names() {
532
533		println!("\n=== ActionScript ===");
534		name_tester("a c t ion SC    RIP   t","ActionScript");
535		name_tester("as3","ActionScript");
536		name_tester(" action script ","ActionScript");
537		name_tester("   A    S    3     ","ActionScript");
538
539		println!("\n=== AppleScript ===");
540		name_tester("applescript","AppleScript");
541		name_tester("apple","AppleScript");
542		name_tester("   a p p l e s      CRIPT","AppleScript");
543		name_tester("APP   LESC  RIPT    ","AppleScript");
544
545		println!("\n=== bash ===");
546		name_tester("bash","bash");
547		name_tester(" b a s h ","bash");
548		name_tester(" bourne again shell ","bash");
549		name_tester(" born again shell ","bash");
550
551		println!("\n=== CoffeeScript ===");
552		name_tester("coffee","CoffeeScript");
553		name_tester(" c o f fee   ","CoffeeScript");
554		name_tester("coffee   script   ","CoffeeScript");
555		name_tester(" C off EE   S  crip   T","CoffeeScript");
556
557		println!("\n=== Dart ===");
558		name_tester("dart","Dart");
559		name_tester(" d   ar    T ","Dart");
560		name_tester("DA   rt","Dart");
561		name_tester(" dar T ","Dart");
562
563		println!("\n=== Elixir ===");
564		name_tester("elixir","Elixir");
565		name_tester(" e LIX  ir ","Elixir");
566		name_tester(" e x s ","Elixir");
567		name_tester(" E     XS  ","Elixir");
568
569		println!("\n=== JavaScript ===");
570		name_tester("JAVA   S   crip   T","JavaScript");
571		name_tester("  j     s     ","JavaScript");
572		name_tester("node.js","JavaScript");
573		name_tester(" no  de  .    j s","JavaScript");
574
575		println!("\n=== Julia ===");
576		name_tester(" julia ","Julia");
577		name_tester(" j u l i a ","Julia");
578		name_tester(" jl ","Julia");
579		name_tester(" j      L  ","Julia");
580
581		println!("\n=== Lua ===");
582		name_tester(" lua ","Lua");
583		name_tester(" l u a ","Lua");
584		name_tester(" L  ua ","Lua");
585		name_tester(" LU    a","Lua");
586
587		println!("\n=== MATLAB ===");
588		name_tester(" m a t l a b ","MATLAB");
589		name_tester(" m a t h w o r k s ","MATLAB");
590		name_tester(" m a t ","MATLAB");
591		name_tester(" m ","MATLAB");
592
593		println!("\n=== Perl ===");
594		name_tester(" perl ","Perl");
595		name_tester(" p e r l ","Perl");
596		name_tester(" pl ","Perl");
597		name_tester(" p l ","Perl");
598
599		println!("\n=== PHP ===");
600		name_tester(" php ","PHP");
601		name_tester(" p h p ","PHP");
602		name_tester(" hypertext preprocessor ","PHP");
603		name_tester(" h y p e r t e x t p r e p r o c e s s o r ","PHP");
604
605		println!("\n=== Python 3 ===");
606		name_tester(" python ","Python 3");
607		name_tester(" p y t h o n 3 ","Python 3");
608		name_tester(" P Y ","Python 3");
609		name_tester(" p y ","Python 3");
610
611		println!("\n=== R ===");
612		name_tester(" r ","R");
613		name_tester(" R ","R");
614
615		println!("\n=== Ruby ===");
616		name_tester(" ruby ","Ruby");
617		name_tester(" r u b y ","Ruby");
618		name_tester(" rb ","Ruby");
619		name_tester(" r b ","Ruby");
620
621		println!("\n=== TypeScript ===");
622		name_tester(" typescript ","TypeScript");
623		name_tester(" t y p e s c r i p t ","TypeScript");
624		name_tester(" ts ","TypeScript");
625		name_tester(" t s ","TypeScript");
626
627		println!("\n=== VB.NET ===");
628		name_tester(" vb . net ","VB.NET");
629		name_tester(" v b n e t ","VB.NET");
630		name_tester(" v b ","VB.NET");
631		name_tester(" v i s u a l b a s i c ","VB.NET");
632
633		fn name_tester(in_str:&str,out_str:&str) {
634			print!("Checking if user wants to script in \"{}\" that they get \"{}\" ... ",in_str,out_str);
635			assert_eq!(learn(in_str).name,String::from(out_str));
636			print!("Success!\n")
637		}
638	}
639
640	#[test]
641	fn test_learn_language_comments() {
642
643		comment_tester(vec!("AppleScript","Lua","Python 3"),"-- ","");
644		comment_tester(vec!("ActionScript","Dart","JavaScript","PHP","Ruby"),"// ","");
645		comment_tester(vec!("bash","CoffeeScript","Elixir","Julia","Perl","PowerShell","R","TypeScript"),"# ","");
646		comment_tester(vec!("MATLAB"),"% ","");
647		comment_tester(vec!("VB.NET"),"' ","");
648
649		fn comment_tester(langs:Vec<&str>,c_start:&str,c_end:&str) {
650
651			println!("############################################");
652			println!("# Languages with \"{}\" type comments",c_start);
653			println!("############################################");
654
655			for lang in langs {
656				print!("Checking if {} comments start with \"{}\" and end with \"{}\" ... ",lang,c_start,c_end);
657				let eval_lang = learn(lang);
658				assert_eq!(&eval_lang.c_open,c_start);
659				assert_eq!(&eval_lang.c_close,c_end);
660				print!("Success!\n");
661			}
662
663			println!("");
664		}
665	}
666
667	/* 
668	#[test]
669	fn test_learn_language_shebang() { }
670
671	#[test]
672	fn test_learn_language_file_extension() { }
673
674	#[test]
675	fn test_learn_language_exit_statement() { }
676
677	#[test]
678	fn test_learn_language_wait() { }
679
680	#[test]
681	fn test_learn_language_stdout() { }
682	*/
683
684}