1extern crate reqwest;
2use std::{
3 env,
4 io::{Error, ErrorKind},
5 path::Path,
6 process::{Command, Stdio},
7};
8
9use flate2::read::GzDecoder;
10use regex::Regex;
11use tar::Archive;
12
13#[cfg(target_os = "macos")]
14const FOLDER: &str = "txl10.8b.macosx64";
15#[cfg(target_os = "macos")]
16const URL: &str = "http://txl.ca/download/11206-txl10.8b.macosx64.tar.gz";
17#[cfg(target_os = "macos")]
18const EXE: &str = "";
19#[cfg(target_os = "linux")]
20const FOLDER: &str = "txl10.8b.linux64";
21#[cfg(target_os = "linux")]
22const URL: &str = "http://www.txl.ca/download/13483-txl10.8b.linux64.tar.gz";
23#[cfg(target_os = "linux")]
24const EXE: &str = "";
25#[cfg(target_os = "windows")]
26const FOLDER: &str = "Txl108bwin64";
27#[cfg(target_os = "windows")]
28const URL: &str = "http://txl.ca/download/11888-Txl108bwin64.zip";
29#[cfg(target_os = "windows")]
30const EXE: &str = ".exe";
31
32#[cfg(all())]
33fn get_file_arg_ext(args: Vec<String>) -> (String, String) {
34 let file_args: Vec<&String> = args
35 .iter()
36 .filter(|x| std::path::PathBuf::from(x).exists())
37 .collect();
38 let mut ext = "";
39 if !file_args.is_empty() {
40 ext = file_args[0];
41 }
42 if let Some(ex) = Path::new(ext).extension() {
43 let ex_str = ex.to_string_lossy();
44 let grammar = lang_to_grammar(&ex_str);
45 (ex_str.to_string(), grammar.to_string())
46 } else {
47 ("".to_string(), "".to_string())
48 }
49}
50
51pub fn txl(args: Vec<String>) -> Result<String, Error> {
57 use std::path::PathBuf;
58 let mut my_args = args.clone();
59 let cmd = format!("{FOLDER}/bin/txl{EXE}");
60 if Path::new(&cmd).exists() {
61 let (ext, grammar) = get_file_arg_ext(args.clone());
62 if !grammar.is_empty() {
63 let grammar_file = PathBuf::from(format!("{}/lib/{}/{}.txl", FOLDER, grammar, ext));
64 if grammar_file.exists() {
65 my_args.push("-i".to_string());
66 my_args.push(format!("{}/lib/{}/", FOLDER, grammar));
67 } else {
68 let grammar_file =
69 PathBuf::from(format!("{}/lib/{}/Txl/{}.txl", FOLDER, grammar, ext));
70 if grammar_file.exists() {
71 my_args.push("-i".to_string());
72 my_args.push(format!("{}/lib/{}/Txl/", FOLDER, grammar));
73 }
74 }
75 }
76 }
77 if let Ok(command) = Command::new(cmd)
78 .args(&my_args)
79 .stdout(Stdio::piped())
80 .stderr(Stdio::piped())
81 .spawn()
82 {
83 if let Ok(output) = command.wait_with_output() {
84 match String::from_utf8(output.stdout).ok() {
85 Some(s) => {
86 if let Ok(s0) = String::from_utf8(output.stderr) {
87 if let Ok(re) = Regex::new(".*: TXL0944E.* file '(.*).txl'") {
88 if re.is_match(&s0) {
89 let mut found = true;
90 re.captures_iter(&s0).for_each(|cap| {
91 if let Err(e) = download(&cap[1]) {
92 println!("{e}");
93 found = false;
94 }
95 });
96 if !found {
97 Err(Error::new(ErrorKind::Other, s0))
98 } else {
99 txl(my_args)
100 }
101 } else if s0.is_empty() {
102 Ok(s)
103 } else {
104 if let Ok(re) = Regex::new(".*: TXL(.*)E.*") {
105 if re.is_match(&s0) {
106 Err(Error::new(ErrorKind::Other, s0))
107 } else {
108 Ok(s)
109 }
110 } else {
111 Ok(s)
112 }
113 }
114 } else {
115 Ok(s)
116 }
117 } else {
118 Ok(s)
119 }
120 }
121 None => Err(Error::new(ErrorKind::Other, "output is not UTF8")),
122 }
123 } else {
124 println!("Cannot run txl {:?}", args);
125 Err(Error::new(
126 ErrorKind::Other,
127 format!("Cannot run `txl {:?}`", args),
128 ))
129 }
130 } else {
131 println!("txl not found, downlading...");
132 if let Ok(resp) = reqwest::blocking::get(URL) {
133 if let Ok(bytes) = resp.bytes() {
134 if URL.ends_with(".tar.gz") {
135 let tar = GzDecoder::new(&bytes[..]);
136 let mut archive = Archive::new(tar);
137 archive.unpack(".")?;
138 } else {
139 let reader = std::io::Cursor::new(bytes);
140 let mut zip = zip::ZipArchive::new(reader)?;
141 zip.extract(".").ok();
142 }
143 if let Ok(path) = env::var("PATH") {
144 env::set_var(
145 "PATH",
146 format!("{:?}/{}/bin:{path}", env::current_dir(), FOLDER),
147 );
148 }
149 download("rs")?;
150 txl(my_args)
151 } else {
152 Err(Error::new(ErrorKind::Other, "Bytes conversion error"))
153 }
154 } else {
155 Err(Error::new(ErrorKind::Other, "Command `txl` not found"))
156 }
157 }
158}
159
160fn lang_to_grammar(lang: &str) -> &str {
161 let mut grammar = lang;
162 match lang {
163 "atl" => {
164 grammar = "ATL";
165 }
166 "ada" => {
167 grammar = "Ada";
168 }
169 "c" => {
170 grammar = "C18";
171 }
172 "cpp" => {
173 grammar = "Cpp";
174 }
175 "cs" => {
176 grammar = "CSharp";
177 }
178 "delphi" => {
179 grammar = "Delphi2006";
180 }
181 "e" => {
182 grammar = "Eiffel";
183 }
184 "f77" => {
185 grammar = "Fortran";
186 }
187 "html" => {
188 grammar = "HTML";
189 }
190 "java" => {
191 grammar = "Java8";
192 }
193 "js" => {
194 grammar = "JavaScript";
195 }
196 "mod" => {
197 grammar = "Modula3";
198 }
199 "php" => {
200 grammar = "PHP";
201 }
202 "py" => {
203 grammar = "Python";
204 }
205 "rb" => {
206 grammar = "Ruby";
207 }
208 "grm" | "txl" => {
209 grammar = "TXL";
210 }
211 "rs" => {
212 grammar = "Rust";
213 }
214 "swift" | "SWIFT" => {
215 grammar = "Swift";
216 }
217 "vb" => {
218 grammar = "VisualBasic";
219 }
220 "xml" => {
221 grammar = "XML";
222 }
223 "y" => {
224 grammar = "Yacc";
225 }
226 _ => {
227 grammar = "unsupported";
228 }
229 };
230 grammar
231}
232
233fn download(lang: &str) -> Result<String, Error> {
234 let grammar = lang_to_grammar(lang);
235 let mut grammar_name;
236 match lang {
237 "ada" => {
238 grammar_name = "Ada_grammar";
239 }
240 "php" => {
241 grammar_name = "PHP345";
242 }
243 "y" => {
244 grammar_name = "YAXX";
245 }
246 _ => {
247 if lang_to_grammar(lang) == "unsupported" {
248 return Err(Error::new(
249 ErrorKind::Other,
250 format!("{lang} is not supported"),
251 ))
252 } else {
253 grammar_name = grammar;
254 }
255 }
256 }
257 if grammar_name.is_empty() {
258 grammar_name = grammar;
259 }
260 if let Ok(resp) = reqwest::blocking::get(format!(
261 "http://www.txl.ca/examples/Grammars/{grammar}/{grammar_name}.tar.gz"
262 )) {
263 if let Ok(bytes) = resp.bytes() {
264 let tar = GzDecoder::new(&bytes[..]);
265 let mut archive = Archive::new(tar);
266 archive.unpack(format!("{}/lib", FOLDER))?;
267 }
268 }
269 Ok("success".to_string())
270}