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/// ⬜ Based C++\
86/// 🟩 Bash\
87/// 🟨 CoffeeScript\
88/// 🟨 Dart\
89/// 🟨 Elixir\
90/// ⬜ Erg\
91/// ⬜ GDScript\
92/// ⬜ Gleam\
93/// ⬜ Groovy\
94/// 🟨 JavaScript\
95/// ⬜ JoraLang\
96/// 🟨 Julia\
97/// ⬜ KeyVa\
98/// 🟨 Lua\
99/// 🟩 MATLAB\
100/// ⬜ Mojo\
101/// ⬜ Nushell\
102/// 🟨 Perl\
103/// 🟨 PHP\
104/// 🟩 PowerShell\
105/// 🟩 Python 3\
106/// 🟨 R\
107/// ⬜ Roto\
108/// 🟨 Ruby\
109/// ⬜ SQL -> T-SQL\
110/// ⬜ SQL -> PL/SQL\
111/// ⬜ SQL -> PL/pgSQL\
112/// ⬜ SQL -> MySQL\
113/// ⬜ SQL -> MariaDB SQL\
114/// ⬜ SQL -> SQLite\
115/// ⬜ SQL -> PRQL\
116/// ⬜ SQL -> Spark SQL\
117/// ⬜ SQL -> HQL\
118/// ⬜ Tilde\
119/// 🟨 TypeScript\
120/// 🟨 VB.NET\
121/// ⬜ VBScript\
122/// ⬜ Verse
123/// 
124/// 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>
125/// 
126/// 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!
127pub fn learn(lang:&str) -> Language { 
128	
129	// 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
130	match lang.split_whitespace().collect::<Vec<_>>().join("").to_uppercase().as_str() {
131		"ACTIONSCRIPT" | "AS3" => Language{
132			name:	String::from("ActionScript"),
133			first:	String::from("#!/usr/bin/as3shebang //\nimport shell.Program;\nimport flash.system.System;"),
134			c_open:	String::from("// "),
135			c_close:String::from(""),
136			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;"),
137			ext:	String::from(".asc"),
138			wait:	String::from("stop();\nstage.addEventListener(KeyboardEvent.KEY_UP, onKeyPress);"),
139			p_open: String::from("System.output(\""),
140			p_close:String::from("\n\");"),
141		},
142		"APPLESCRIPT" | "APPLE" => Language{
143			name:	String::from("AppleScript"),
144			first:	String::from("#!/usr/bin/osascript"),
145			c_open:	String::from("-- "),
146			c_close:String::from(""),
147			last:	String::from(""),
148			ext:	String::from(".command"),
149			wait:	String::from("read -p \"Press Enter to continue...\""),
150			p_open: String::from("echo '"),
151			p_close:String::from("'"),
152		},
153		"BASH" | "BOURNEAGAINSHELL" | "BORNAGAINSHELL" => Language{
154			name:	String::from("bash"),
155			first:	String::from("#!/bin/bash"),
156			c_open:	String::from("# "),
157			c_close:String::from(""),
158			last:	String::from("exit 0"),
159			ext:	String::from(".sh"),
160			wait:	String::from("read -p \"Press Enter to continue...\""),
161			p_open: String::from("echo '"),
162			p_close:String::from("'"),
163		},
164		"COFFEESCRIPT" | "COFFEE" => Language{
165			name:	String::from("CoffeeScript"),
166			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"),
167			c_open:	String::from("# "),
168			c_close:String::from(""),
169			last:	String::from("process.exit(1)"),
170			ext:	String::from(".coffee"),
171			wait:	String::from("await waitForEnter()"),
172			p_open: String::from("console.log \""),
173			p_close:String::from("\""),
174		},
175		"DART" => Language{
176			name:	String::from("Dart"),
177			first:	String::from("#!/usr/bin/env dart\nimport 'dart:io';\nvoid main() {"),
178			c_open:	String::from("// "),
179			c_close:String::from(""),
180			last:	String::from("exit(0);\n}"),
181			ext:	String::from(".dart"),
182			wait:	String::from("print('Press Enter to continue...');\nstdin.readLineSync();"),
183			p_open: String::from("print('"),
184			p_close:String::from("');"),
185		},
186		"ELIXIR" | "EXS" => Language{
187			name:	String::from("Elixir"),
188			first:	String::from("#!/usr/bin/env elixir"),
189			c_open:	String::from("# "),
190			c_close:String::from(""),
191			last:	String::from("Process.exit(self, :normal)"),
192			ext:	String::from(".exs"),
193			wait:	String::from("IO.gets(\"Press Enter to continue...\")"),
194			p_open: String::from("IO.puts(\""),
195			p_close:String::from("\")"),
196		},
197		"JAVASCRIPT" | "JS" | "NODE.JS" => Language{
198			name:	String::from("JavaScript"),
199			first:	String::from("#!/usr/bin/env node"),
200			c_open:	String::from("// "),
201			c_close:String::from(""),
202			last:	String::from("process.exit(0)"),
203			ext:	String::from(".js"),
204			wait:	String::from(""),
205			p_open: String::from("console.log(\""),
206			p_close:String::from("\");"),
207		},
208		"JULIA" | "JL" => Language{
209			name:	String::from("Julia"),
210			first:	String::from("#!/usr/bin/env -S julia --color=yes --startup-file=no"),
211			c_open:	String::from("# "),
212			c_close:String::from(""),
213			last:	String::from("exit(0)"),
214			ext:	String::from(".jl"),
215			wait:	String::from("Base.prompt(\"Press Enter to continue...\")"),
216			p_open: String::from("println(\""),
217			p_close:String::from("\")"),
218		},
219		"LUA" => Language{
220			name:	String::from("Lua"),
221			first:	String::from("#!/usr/bin/env lua"),
222			c_open:	String::from("-- "),
223			c_close:String::from(""),
224			last:	String::from("return"),
225			ext:	String::from(".lua"),
226			wait:	String::from("print(\"Press Enter to continue...\")\nio.read()"),
227			p_open: String::from("println(\""),
228			p_close:String::from("\")"),
229		},
230		"MATLAB" | "MATHWORKS" | "MAT" | "M" => Language{
231			name:	String::from("MATLAB"),
232			first:	String::from("% no shebang statement for MATLAB"),
233			c_open:	String::from("% "),
234			c_close:String::from(""),
235			last:	String::from("exit(0)"),
236			ext:	String::from(".m"),
237			wait:	String::from("disp('Press Enter to continue...'); input('');"),
238			p_open: String::from("disp('"),
239			p_close:String::from("');"),
240		},
241		"PERL" | "PL" => Language{
242			name:	String::from("Perl"),
243			first:	String::from("#!/usr/bin/env perl"),
244			c_open:	String::from("# "),
245			c_close:String::from(""),
246			last:	String::from("exit 1;"),
247			ext:	String::from(".pl"),
248			wait:	String::from("print \"Press Enter to continue...\";\n<STDIN>;"),
249			p_open: String::from("print \""),
250			p_close:String::from("\";"),
251		},
252		"PHP" | "HYPERTEXTPREPROCESSOR" => Language{
253			name:	String::from("PHP"),
254			first:	String::from("#!/usr/bin/env php"),
255			c_open:	String::from("// "),
256			c_close:String::from(""),
257			last:	String::from("exit(0);"),
258			ext:	String::from(".php"),
259			wait:	String::from("readline(\"Press Enter to proceed...\");"),
260			p_open: String::from("echo \""),
261			p_close:String::from("\";"),
262		},
263		"POWERSHELL" | "PS" | "PS1" => Language{
264			name:	String::from("PowerShell"),
265			first:	String::from("#!/usr/bin/env pwsh"),
266			c_open:	String::from("# "),
267			c_close:String::from(""),
268			last:	String::from("exit 0"),
269			ext:	String::from(".ps1"),
270			wait:	String::from("Read-Host -Prompt \"Press Enter to continue...\""),
271			p_open: String::from("Write-Host \""),
272			p_close:String::from("\""),
273		},
274		"PYTHON" | "PYTHON3" | "PY" => Language{
275			name:	String::from("Python 3"),
276			first:	String::from("#!/usr/bin/python3"),
277			c_open:	String::from("-- "),
278			c_close:String::from(""),
279			last:	String::from(""),
280			ext:	String::from(".py"),
281			wait:	String::from("input(\"Press Enter to continue...\")"),
282			p_open: String::from("print(\""),
283			p_close:String::from("\")"),
284		},
285		"R" => Language{
286			name:	String::from("R"),
287			first:	String::from("#!/usr/bin/env Rscript"),
288			c_open:	String::from("# "),
289			c_close:String::from(""),
290			last:	String::from("quit()"),
291			ext:	String::from(".R"),
292			wait:	String::from("readline(prompt = \"Press Enter to continue...\")"),
293			p_open: String::from("print(\""),
294			p_close:String::from("\")"),
295		},
296		"RUBY" | "RB" => Language{
297			name:	String::from("Ruby"),
298			first:	String::from("#!/usr/bin/env ruby"),
299			c_open:	String::from("// "),
300			c_close:String::from(""),
301			last:	String::from("exit(0)"),
302			ext:	String::from(".rb"),
303			wait:	String::from("puts \"Press Enter to continue...\"\ngets"),
304			p_open: String::from("puts \""),
305			p_close:String::from("\""),
306		},
307		"TYPESCRIPT" | "TS" => Language{
308			name:	String::from("TypeScript"),
309			first:	String::from("#!/usr/bin/env tsx"),
310			c_open:	String::from("# "),
311			c_close:String::from(""),
312			last:	String::from("process.exit(0)"),
313			ext:	String::from(".ts"),
314			wait:	String::from(""),
315			p_open: String::from("console.log(\""),
316			p_close:String::from("\")"),
317		},
318		"VB.NET" | "VBNET" | "VB" | "VISUALBASIC" => Language{
319			name:	String::from("VB.NET"),
320			first:	String::from("#!/usr/bin/env dotnet run"),
321			c_open:	String::from("' "),
322			c_close:String::from(""),
323			last:	String::from("Return"),
324			ext:	String::from(".vb"),
325			wait:	String::from("Console.WriteLine(\"Press Enter to continue...\")\nConsole.ReadLine()"),
326			p_open: String::from("Console.WriteLine(\""),
327			p_close:String::from("\""),
328		},
329		_ => Language{
330			name:	String::from("Unknown"),
331			first:	String::from("#!/bin/sh"),
332			c_open:	String::from("# "),
333			c_close:String::from(""),
334			last:	String::from("exit 0"),
335			ext:	String::from(".sh"),
336			wait:	String::from("read -p \"Press Enter to continue...\""),
337			p_open: String::from("echo '"),
338			p_close:String::from("'"),
339		},
340	}
341}
342
343/// Create a script file using the Process struct, interpreted in a specified Language.
344/// * Some pre-canned comment blocks and stdout messages appear first
345/// * "Context" EBML lines appear as comments in the script
346/// * "Command" EBML lines appear as their own lines, verbatim
347/// * "Objective" EBML lines are announced to the command line output
348/// * All other EBML is ignored
349/// 
350/// The script file appears alongside the process itself in its Process Folder.
351pub fn script_file_from_process(p:Process,lang:Language,wait:bool) {
352	
353	// An empty vector to fill with lines of text
354	let mut script_lines:Vec<String> = vec![];
355
356	// The [[Language]] struct includes a field for the language-specific first line of a script, e.g. shebang
357	script_lines.push(lang.first.to_owned());
358
359	// 
360	script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
361	script_lines.push(comment_to_file(&(p.get_number().to_owned() + " Rev " + &p.get_revision() + "   " + &p.get_title()),&lang));
362	script_lines.push(comment_to_file(&("Author:   ".to_owned() + &p.get_author()),&lang));
363	script_lines.push(comment_to_file(&("Reviewer: ".to_owned() + &p.get_reviewer()),&lang));
364	script_lines.push(comment_to_file(&("".to_string()),&lang));
365	script_lines.push(comment_to_file(&(lang.name.to_owned()+ " generated by stanhope " + env!("CARGO_PKG_VERSION")),&lang));
366	script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
367	
368	script_lines.push("".to_string());
369	
370	script_lines.push(print_to_stdout(&(p.get_number().to_owned() + " Rev " + &p.get_revision() + " " + &p.get_title()),&lang));
371	script_lines.push(print_to_stdout(&("".to_string()),&lang));
372	script_lines.push(print_to_stdout(&("Author:   ".to_owned() + &p.get_author()),&lang));
373	script_lines.push(print_to_stdout(&("Reviewer: ".to_owned() + &p.get_reviewer()),&lang));
374	script_lines.push(print_to_stdout(&("".to_string()),&lang));
375
376	script_lines.push("".to_string());
377
378	// Hold before start if we're in slow mode
379	if wait { script_lines.push(lang.wait.to_owned()) };
380
381	script_lines.push("".to_string());
382
383	// Loop through all of the sections
384	let mut secnum:u16 = 0;
385	for section in p.get_all_sections() {
386		secnum += 1;
387		let mut stpnum:u16 = 0;
388
389		// Add section title as a comment
390		script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
391		script_lines.push(comment_to_file(&("Section ".to_string() + &secnum.to_string() + ": " + &section.get_title()),&lang));
392		script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
393		script_lines.push("".to_string());
394
395		// Loop through all of the steps in this section
396		for step in section.get_all_steps() {
397			stpnum += 1;
398			// Add step title as a comment
399			script_lines.push(print_to_stdout(&("Step ".to_string() + &secnum.to_string() + "." + &stpnum.to_string()),&lang));
400			script_lines.push(print_to_stdout(&(step.get_text().trim().to_string()),&lang));
401			script_lines.push(print_to_stdout(&("".to_string()),&lang));
402
403			// Loop though all substeps of this step
404			for substep in step.get_all_sub_steps() {
405				match substep {
406
407					// Context lines are added as comments
408					// TODO: verbose stdout
409					SubStep::Context(txt) => { script_lines.push(comment_to_file(&(txt.trim().to_string()),&lang)); },
410
411					SubStep::Command(txt) => {
412						// Command lines are passed through unmolested
413						script_lines.push(txt.trim().to_string());
414						// TODO: verbose stdout
415
416						// If "wait" is true, add language-specific wait
417						if wait { script_lines.push(lang.wait.to_owned()) };
418					},
419
420					/*
421					* * * * * * * * * *
422					Objective Achieved!
423					*/
424					SubStep::Objective(txt) => {
425						script_lines.push(print_to_stdout(&("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *".to_string()),&lang));
426						script_lines.push(print_to_stdout(&("* Objective Achieved!".to_string()),&lang));
427						script_lines.push(print_to_stdout(&("* ".to_owned()+&txt.trim()),&lang));
428						script_lines.push(print_to_stdout(&("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *".to_string()),&lang));
429						script_lines.push(print_to_stdout(&("".to_string()),&lang));
430					},
431					
432					// Ignore all other substep types
433					// TODO: consider including verifications to stdout
434					_ => {},
435				}
436			}
437			// add a spacer line to stdout at the end of the step, and in the script itself for readability
438			script_lines.push(print_to_stdout(&("".to_string()),&lang));
439			script_lines.push("".to_string());
440		}
441		// Add another spacer line at the end of the section
442		script_lines.push("".to_string());
443	}
444
445	// Last line
446	script_lines.push(lang.last.to_owned());
447
448	// 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...)
449
450	// Assumption: every process has its own folder, and that folder is named the PROCESS NUMBER
451	let path_to_file = "./".to_string() + p.get_number() + " - " + p.get_title() + "/" + p.get_number() + " - " + p.get_title() + &lang.ext;
452
453	// Open the file to write
454	let mut file = OpenOptions::new()
455	            .read(true)
456	            .write(true)
457	            .create(true)
458	            .truncate(true)
459	            .open(&path_to_file)
460	            .expect("Could not open the file!");
461
462	for line in script_lines {
463		file.write((line + "\n").as_bytes()).expect("Could not write template line into new file!");
464	}
465
466	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);
467
468}
469
470/// Standardize how to write a comment line, given the text to comment, and the language in which to comment
471fn comment_to_file(txt:&String,lang:&Language) -> String { lang.c_open.to_owned() + txt + &lang.c_close }
472
473/// Standardize how to print to stdout, given the text to print out, and the language in which to print to stdout
474fn print_to_stdout(txt:&String,lang:&Language) -> String { 
475	let txt_san = txt.replace('"',"").replace("'","");
476	lang.p_open.to_owned() + &txt_san + &lang.p_close 
477}
478
479
480//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
481// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
482//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
483//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
484//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
485//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
486//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
487//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
488//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
489//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
490//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀     
491
492#[cfg(test)]
493mod tests {
494
495	use super::*;
496
497	#[test]
498	fn test_new_language_struct() { 
499		let mut test_language:Language = Language{
500			name: 	String::from("NAME"),
501			first: 	String::from("FIRST LINE(S)"),
502			c_open:	String::from("SINGLE-LINE COMMENT OPENER"),
503			c_close:String::from("SINGLE-LINE COMMENT CLOSER"),
504			last: 	String::from("LAST LINE(S)"),
505			ext: 	String::from("FILE EXTENSION"),
506			wait: 	String::from("WAIT FOR THE USER TO PRESS ENTER"),
507			p_open: String::from("PRINT TO STDOUT OPENER"),
508			p_close:String::from("PRINT TO STDOUT CLOSER"),
509		};
510		println!("We successfully created a new Language struct.");
511
512		test_language.name = String::from("A NEW AND IMPROVED NAME");
513		println!("Although it's not really best practices, we've proved we can poke Language fields (within this module anyway).")
514	}
515
516	#[test]
517	fn test_learn_language_names() {
518
519		println!("\n=== ActionScript ===");
520		name_tester("a c t ion SC    RIP   t","ActionScript");
521		name_tester("as3","ActionScript");
522		name_tester(" action script ","ActionScript");
523		name_tester("   A    S    3     ","ActionScript");
524
525		println!("\n=== AppleScript ===");
526		name_tester("applescript","AppleScript");
527		name_tester("apple","AppleScript");
528		name_tester("   a p p l e s      CRIPT","AppleScript");
529		name_tester("APP   LESC  RIPT    ","AppleScript");
530
531		println!("\n=== bash ===");
532		name_tester("bash","bash");
533		name_tester(" b a s h ","bash");
534		name_tester(" bourne again shell ","bash");
535		name_tester(" born again shell ","bash");
536
537		println!("\n=== CoffeeScript ===");
538		name_tester("coffee","CoffeeScript");
539		name_tester(" c o f fee   ","CoffeeScript");
540		name_tester("coffee   script   ","CoffeeScript");
541		name_tester(" C off EE   S  crip   T","CoffeeScript");
542
543		println!("\n=== Dart ===");
544		name_tester("dart","Dart");
545		name_tester(" d   ar    T ","Dart");
546		name_tester("DA   rt","Dart");
547		name_tester(" dar T ","Dart");
548
549		println!("\n=== Elixir ===");
550		name_tester("elixir","Elixir");
551		name_tester(" e LIX  ir ","Elixir");
552		name_tester(" e x s ","Elixir");
553		name_tester(" E     XS  ","Elixir");
554
555		println!("\n=== JavaScript ===");
556		name_tester("JAVA   S   crip   T","JavaScript");
557		name_tester("  j     s     ","JavaScript");
558		name_tester("node.js","JavaScript");
559		name_tester(" no  de  .    j s","JavaScript");
560
561		println!("\n=== Julia ===");
562		name_tester(" julia ","Julia");
563		name_tester(" j u l i a ","Julia");
564		name_tester(" jl ","Julia");
565		name_tester(" j      L  ","Julia");
566
567		println!("\n=== Lua ===");
568		name_tester(" lua ","Lua");
569		name_tester(" l u a ","Lua");
570		name_tester(" L  ua ","Lua");
571		name_tester(" LU    a","Lua");
572
573		println!("\n=== MATLAB ===");
574		name_tester(" m a t l a b ","MATLAB");
575		name_tester(" m a t h w o r k s ","MATLAB");
576		name_tester(" m a t ","MATLAB");
577		name_tester(" m ","MATLAB");
578
579		println!("\n=== Perl ===");
580		name_tester(" perl ","Perl");
581		name_tester(" p e r l ","Perl");
582		name_tester(" pl ","Perl");
583		name_tester(" p l ","Perl");
584
585		println!("\n=== PHP ===");
586		name_tester(" php ","PHP");
587		name_tester(" p h p ","PHP");
588		name_tester(" hypertext preprocessor ","PHP");
589		name_tester(" h y p e r t e x t p r e p r o c e s s o r ","PHP");
590
591		println!("\n=== Python 3 ===");
592		name_tester(" python ","Python 3");
593		name_tester(" p y t h o n 3 ","Python 3");
594		name_tester(" P Y ","Python 3");
595		name_tester(" p y ","Python 3");
596
597		println!("\n=== R ===");
598		name_tester(" r ","R");
599		name_tester(" R ","R");
600
601		println!("\n=== Ruby ===");
602		name_tester(" ruby ","Ruby");
603		name_tester(" r u b y ","Ruby");
604		name_tester(" rb ","Ruby");
605		name_tester(" r b ","Ruby");
606
607		println!("\n=== TypeScript ===");
608		name_tester(" typescript ","TypeScript");
609		name_tester(" t y p e s c r i p t ","TypeScript");
610		name_tester(" ts ","TypeScript");
611		name_tester(" t s ","TypeScript");
612
613		println!("\n=== VB.NET ===");
614		name_tester(" vb . net ","VB.NET");
615		name_tester(" v b n e t ","VB.NET");
616		name_tester(" v b ","VB.NET");
617		name_tester(" v i s u a l b a s i c ","VB.NET");
618
619		fn name_tester(in_str:&str,out_str:&str) {
620			print!("Checking if user wants to script in \"{}\" that they get \"{}\" ... ",in_str,out_str);
621			assert_eq!(learn(in_str).name,String::from(out_str));
622			print!("Success!\n")
623		}
624	}
625
626	#[test]
627	fn test_learn_language_comments() {
628
629		comment_tester(vec!("AppleScript","Lua","Python 3"),"-- ","");
630		comment_tester(vec!("ActionScript","Dart","JavaScript","PHP","Ruby"),"// ","");
631		comment_tester(vec!("bash","CoffeeScript","Elixir","Julia","Perl","PowerShell","R","TypeScript"),"# ","");
632		comment_tester(vec!("MATLAB"),"% ","");
633		comment_tester(vec!("VB.NET"),"' ","");
634
635		fn comment_tester(langs:Vec<&str>,c_start:&str,c_end:&str) {
636
637			println!("############################################");
638			println!("# Languages with \"{}\" type comments",c_start);
639			println!("############################################");
640
641			for lang in langs {
642				print!("Checking if {} comments start with \"{}\" and end with \"{}\" ... ",lang,c_start,c_end);
643				let eval_lang = learn(lang);
644				assert_eq!(&eval_lang.c_open,c_start);
645				assert_eq!(&eval_lang.c_close,c_end);
646				print!("Success!\n");
647			}
648
649			println!("");
650		}
651	}
652
653	/* 
654	#[test]
655	fn test_learn_language_shebang() { }
656
657	#[test]
658	fn test_learn_language_file_extension() { }
659
660	#[test]
661	fn test_learn_language_exit_statement() { }
662
663	#[test]
664	fn test_learn_language_wait() { }
665
666	#[test]
667	fn test_learn_language_stdout() { }
668	*/
669
670}