1use std::{
10 fs::{OpenOptions},
11 io::{Write},
12};
13use crate::read_ebml::Process;
14use crate::read_ebml::SubStep;
17
18
19pub struct Language {
33
34 name: String,
36
37 first: String,
39
40 c_open: String,
42
43 c_close:String,
45
46 last: String,
48
49 ext: String,
51
52 wait: String,
54
55 p_open: String,
57
58 p_close:String,
60}
61
62pub fn learn(lang:&str) -> Language {
120
121 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
357pub fn script_file_from_process(p:Process,lang:Language,wait:bool) {
366
367 let mut script_lines:Vec<String> = vec![];
369
370 script_lines.push(lang.first.to_owned());
372
373 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 if wait { script_lines.push(lang.wait.to_owned()) };
394
395 script_lines.push("".to_string());
396
397 let mut secnum:u16 = 0;
399 for section in p.get_all_sections() {
400 secnum += 1;
401 let mut stpnum:u16 = 0;
402
403 script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
405 script_lines.push(comment_to_file(&("Section ".to_string() + &secnum.to_string() + ": " + §ion.get_title()),&lang));
406 script_lines.push(comment_to_file(&("=============================================================".to_string()),&lang));
407 script_lines.push("".to_string());
408
409 for step in section.get_all_steps() {
411 stpnum += 1;
412 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 for substep in step.get_all_sub_steps() {
419 match substep {
420
421 SubStep::Context(txt) => { script_lines.push(comment_to_file(&(txt.trim().to_string()),&lang)); },
424
425 SubStep::Command(txt) => {
426 script_lines.push(txt.trim().to_string());
428 if wait { script_lines.push(lang.wait.to_owned()) };
432 },
433
434 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 _ => {},
449 }
450 }
451 script_lines.push(print_to_stdout(&("".to_string()),&lang));
453 script_lines.push("".to_string());
454 }
455 script_lines.push("".to_string());
457 }
458
459 script_lines.push(lang.last.to_owned());
461
462 let path_to_file = "./".to_string() + p.get_number() + " - " + p.get_title() + "/" + p.get_number() + " - " + p.get_title() + &lang.ext;
466
467 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
484fn comment_to_file(txt:&String,lang:&Language) -> String { lang.c_open.to_owned() + txt + &lang.c_close }
486
487fn 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#[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 }